@@ -17,14 +17,19 @@ public enum GCDHTTPServerError: Error {
17
17
18
18
/// Implementation of `HTTPServer` using ReadiumGCDWebServer under the hood.
19
19
public class GCDHTTPServer : HTTPServer , Loggable {
20
+ private struct EndpointHandler {
21
+ let resourceHandler : ( HTTPServerRequest ) -> Resource
22
+ let failureHandler : HTTPServer . FailureHandler ?
23
+ }
24
+
20
25
/// Shared instance of the HTTP server.
21
26
public static let shared = GCDHTTPServer ( )
22
27
23
28
/// The actual underlying HTTP server instance.
24
29
private let server = ReadiumGCDWebServer ( )
25
30
26
31
/// Mapping between endpoints and their handlers.
27
- private var handlers : [ HTTPServerEndpoint : ( HTTPServerRequest ) -> Resource ] = [ : ]
32
+ private var handlers : [ HTTPServerEndpoint : EndpointHandler ] = [ : ]
28
33
29
34
/// Mapping between endpoints and resource transformers.
30
35
private var transformers : [ HTTPServerEndpoint : [ ResourceTransformer ] ] = [ : ]
@@ -84,7 +89,7 @@ public class GCDHTTPServer: HTTPServer, Loggable {
84
89
}
85
90
86
91
private func handle( request: ReadiumGCDWebServerRequest , completion: @escaping ReadiumGCDWebServerCompletionBlock ) {
87
- responseResource ( for: request) { resource in
92
+ responseResource ( for: request) { httpServerRequest , resource, failureHandler in
88
93
let response : ReadiumGCDWebServerResponse
89
94
switch resource. length {
90
95
case let . success( length) :
@@ -95,19 +100,26 @@ public class GCDHTTPServer: HTTPServer, Loggable {
95
100
)
96
101
case let . failure( error) :
97
102
self . log ( . error, error)
98
- response = ReadiumGCDWebServerErrorResponse ( statusCode: error. httpStatusCode)
103
+ failureHandler ? ( httpServerRequest, error)
104
+ response = ReadiumGCDWebServerErrorResponse (
105
+ statusCode: error. httpStatusCode,
106
+ error: error
107
+ )
99
108
}
100
109
101
- completion ( response)
110
+ completion ( response) // goes back to ReadiumGCDWebServerConnection.m
102
111
}
103
112
}
104
113
105
- private func responseResource( for request: ReadiumGCDWebServerRequest , completion: @escaping ( Resource ) -> Void ) {
106
- let completion = { resource in
114
+ private func responseResource(
115
+ for request: ReadiumGCDWebServerRequest ,
116
+ completion: @escaping ( HTTPServerRequest , Resource , FailureHandler ? ) -> Void
117
+ ) {
118
+ let completion = { request, resource, failureHandler in
107
119
// Escape the queue to avoid deadlocks if something is using the
108
120
// server in the handler.
109
121
DispatchQueue . global ( ) . async {
110
- completion ( resource)
122
+ completion ( request , resource, failureHandler )
111
123
}
112
124
}
113
125
@@ -130,36 +142,56 @@ public class GCDHTTPServer: HTTPServer, Loggable {
130
142
131
143
for (endpoint, handler) in handlers {
132
144
if endpoint == pathWithoutAnchor {
133
- let resource = handler ( HTTPServerRequest ( url: request. url, href: nil ) )
134
- completion ( transform ( resource: resource, at: endpoint) )
145
+ let request = HTTPServerRequest ( url: request. url, href: nil )
146
+ let resource = handler. resourceHandler ( request)
147
+ completion ( request,
148
+ transform ( resource: resource, at: endpoint) ,
149
+ handler. failureHandler)
135
150
return
136
151
137
152
} else if path. hasPrefix ( endpoint. addingSuffix ( " / " ) ) {
138
- let resource = handler ( HTTPServerRequest (
153
+ let request = HTTPServerRequest (
139
154
url: request. url,
140
155
href: path. removingPrefix ( endpoint. removingSuffix ( " / " ) )
141
- ) )
142
- completion ( transform ( resource: resource, at: endpoint) )
156
+ )
157
+ let resource = handler. resourceHandler ( request)
158
+ completion ( request,
159
+ transform ( resource: resource, at: endpoint) ,
160
+ handler. failureHandler)
143
161
return
144
162
}
145
163
}
146
164
147
- completion ( FailureResource ( link: Link ( href: request. url. absoluteString) , error: . notFound( nil ) ) )
165
+ log ( . warning, " Resource not found for request \( request) " )
166
+ completion ( HTTPServerRequest ( url: request. url, href: nil ) ,
167
+ FailureResource ( link: Link ( href: request. url. absoluteString) ,
168
+ error: . notFound( nil ) ) ,
169
+ nil )
148
170
}
149
171
}
150
172
151
173
// MARK: HTTPServer
152
174
153
175
public func serve( at endpoint: HTTPServerEndpoint , handler: @escaping ( HTTPServerRequest ) -> Resource ) throws -> URL {
176
+ try serve ( at: endpoint, handler: handler, failureHandler: nil )
177
+ }
178
+
179
+ public func serve(
180
+ at endpoint: HTTPServerEndpoint ,
181
+ handler: @escaping ( HTTPServerRequest ) -> Resource ,
182
+ failureHandler: FailureHandler ?
183
+ ) throws -> URL {
154
184
try queue. sync ( flags: . barrier) {
155
185
if case . stopped = state {
156
186
try start ( )
157
187
}
188
+
158
189
guard case let . started( port: _, baseURL: baseURL) = state else {
159
190
throw GCDHTTPServerError . serverNotStarted
160
191
}
161
192
162
- handlers [ endpoint] = handler
193
+ handlers [ endpoint] = EndpointHandler ( resourceHandler: handler,
194
+ failureHandler: failureHandler)
163
195
164
196
return baseURL. appendingPathComponent ( endpoint)
165
197
}
0 commit comments