66
77import Foundation
88import ReadiumGCDWebServer
9+ import ReadiumInternal
910import ReadiumShared
1011import UIKit
1112
@@ -19,7 +20,8 @@ public enum GCDHTTPServerError: Error {
1920/// Implementation of `HTTPServer` using ReadiumGCDWebServer under the hood.
2021public class GCDHTTPServer : HTTPServer , Loggable {
2122 /// Shared instance of the HTTP server.
22- public static let shared = GCDHTTPServer ( )
23+ @available ( * , unavailable, message: " Create your own shared instance " )
24+ public static var shared : GCDHTTPServer { fatalError ( ) }
2325
2426 /// The actual underlying HTTP server instance.
2527 private let server = ReadiumGCDWebServer ( )
@@ -30,6 +32,8 @@ public class GCDHTTPServer: HTTPServer, Loggable {
3032 /// Mapping between endpoints and resource transformers.
3133 private var transformers : [ HTTPURL : [ ResourceTransformer ] ] = [ : ]
3234
35+ private let assetRetriever : AssetRetriever
36+
3337 private enum State {
3438 case stopped
3539 case started( port: UInt , baseURL: HTTPURL )
@@ -47,7 +51,12 @@ public class GCDHTTPServer: HTTPServer, Loggable {
4751 /// Creates a new instance of the HTTP server.
4852 ///
4953 /// - Parameter logLevel: See `ReadiumGCDWebServer.setLogLevel`.
50- public init ( logLevel: Int = 3 ) {
54+ public init (
55+ assetRetriever: AssetRetriever ,
56+ logLevel: Int = 3
57+ ) {
58+ self . assetRetriever = assetRetriever
59+
5160 ReadiumGCDWebServer . setLogLevel ( Int32 ( logLevel) )
5261
5362 NotificationCenter . default. addObserver ( self , selector: #selector( willEnterForeground) , name: UIApplication . willEnterForegroundNotification, object: nil )
@@ -85,31 +94,40 @@ public class GCDHTTPServer: HTTPServer, Loggable {
8594 }
8695
8796 private func handle( request: ReadiumGCDWebServerRequest , completion: @escaping ReadiumGCDWebServerCompletionBlock ) {
88- responseResource ( for: request) { httpServerRequest, resource, failureHandler in
89- let response : ReadiumGCDWebServerResponse
90- switch resource. length {
91- case let . success( length) :
92- response = ResourceResponse (
93- resource: resource,
94- length: length,
95- range: request. hasByteRange ( ) ? request. byteRange : nil
96- )
97- case let . failure( error) :
98- self . log ( . error, error)
99- failureHandler ? ( httpServerRequest, error)
100- response = ReadiumGCDWebServerErrorResponse (
101- statusCode: error. httpStatusCode,
102- error: error
103- )
104- }
97+ responseResource ( for: request) { httpServerRequest, httpServerResponse, failureHandler in
98+ Task {
99+ let response : ReadiumGCDWebServerResponse
100+ let resource = httpServerResponse. resource
101+
102+ func fail( _ error: ReadError ) -> ReadiumGCDWebServerResponse {
103+ self . log ( . error, error)
104+ failureHandler ? ( httpServerRequest, error)
105+ return ReadiumGCDWebServerErrorResponse (
106+ statusCode: 500 ,
107+ error: error
108+ )
109+ }
110+
111+ switch await resource. length ( ) {
112+ case let . success( length) :
113+ response = await ResourceResponse (
114+ resource: httpServerResponse. resource,
115+ length: length,
116+ range: request. hasByteRange ( ) ? request. byteRange : nil ,
117+ mediaType: httpServerResponse. mediaType ( using: self . assetRetriever)
118+ )
119+ case let . failure( error) :
120+ response = fail ( error)
121+ }
105122
106- completion ( response) // goes back to ReadiumGCDWebServerConnection.m
123+ completion ( response) // goes back to ReadiumGCDWebServerConnection.m
124+ }
107125 }
108126 }
109127
110128 private func responseResource(
111129 for request: ReadiumGCDWebServerRequest ,
112- completion: @escaping ( HTTPServerRequest , Resource , HTTPRequestHandler . OnFailure ? ) -> Void
130+ completion: @escaping ( HTTPServerRequest , HTTPServerResponse , HTTPRequestHandler . OnFailure ? ) -> Void
113131 ) {
114132 let completion = { request, resource, failureHandler in
115133 // Escape the queue to avoid deadlocks if something is using the
@@ -124,52 +142,40 @@ public class GCDHTTPServer: HTTPServer, Loggable {
124142 fatalError ( " Expected an HTTP URL " )
125143 }
126144
127- func transform( resource: Resource , at endpoint: HTTPURL ) -> Resource {
145+ func transform( resource: Resource , request : HTTPServerRequest , at endpoint: HTTPURL ) -> Resource {
128146 guard let transformers = transformers [ endpoint] , !transformers. isEmpty else {
129147 return resource
130148 }
149+ let href = request. href? . anyURL ?? request. url. anyURL
131150 var resource = resource
132151 for transformer in transformers {
133- resource = transformer ( resource)
152+ resource = transformer ( href , resource)
134153 }
135154 return resource
136155 }
137156
138157 let pathWithoutAnchor = url. removingQuery ( ) . removingFragment ( )
139158
140159 for (endpoint, handler) in handlers {
160+ let request : HTTPServerRequest
141161 if endpoint. isEquivalentTo ( pathWithoutAnchor) {
142- let request = HTTPServerRequest ( url: url, href: nil )
143- let resource = handler. onRequest ( request)
144- completion (
145- request,
146- transform ( resource: resource, at: endpoint) ,
147- handler. onFailure
148- )
149- return
150-
162+ request = HTTPServerRequest ( url: url, href: nil )
151163 } else if let href = endpoint. relativize ( url) {
152- let request = HTTPServerRequest (
153- url: url,
154- href: href
155- )
156- let resource = handler. onRequest ( request)
157- completion (
158- request,
159- transform ( resource: resource, at: endpoint) ,
160- handler. onFailure
161- )
162- return
164+ request = HTTPServerRequest ( url: url, href: href)
165+ } else {
166+ continue
163167 }
168+
169+ var response = handler. onRequest ( request)
170+ response. resource = transform ( resource: response. resource, request: request, at: endpoint)
171+ completion ( request, response, handler. onFailure)
172+ return
164173 }
165174
166175 log ( . warning, " Resource not found for request \( request) " )
167176 completion (
168177 HTTPServerRequest ( url: url, href: nil ) ,
169- FailureResource (
170- link: Link ( href: request. url. absoluteString) ,
171- error: . notFound( nil )
172- ) ,
178+ HTTPServerResponse ( error: . notFound) ,
173179 nil
174180 )
175181 }
@@ -320,3 +326,44 @@ public class GCDHTTPServer: HTTPServer, Loggable {
320326 return true
321327 }
322328}
329+
330+ private extension Resource {
331+ func length( ) async -> ReadResult < UInt64 > {
332+ await estimatedLength ( )
333+ . flatMap { length in
334+ if let length = length {
335+ return . success( length)
336+ } else {
337+ return await read ( ) . map { UInt64 ( $0. count) }
338+ }
339+ }
340+ }
341+ }
342+
343+ private extension HTTPServerResponse {
344+ func mediaType( using assetRetriever: AssetRetriever ) async -> MediaType {
345+ if let mediaType = mediaType {
346+ return mediaType
347+ }
348+
349+ if let properties = try ? await resource. properties ( ) . get ( ) {
350+ if let mediaType = properties. mediaType {
351+ return mediaType
352+ }
353+ if
354+ let filename = properties. filename,
355+ let uti = UTI . findFrom ( mediaTypes: [ ] , fileExtensions: [ URL ( fileURLWithPath: filename) . pathExtension] ) ,
356+ let type = uti. preferredTag ( withClass: . mediaType) ,
357+ let mediaType = MediaType ( type)
358+ {
359+ return mediaType
360+ }
361+ }
362+
363+ if let mediaType = try ? await assetRetriever. sniffFormat ( of: resource) . get ( ) . mediaType {
364+ return mediaType
365+ }
366+
367+ return . binary
368+ }
369+ }
0 commit comments