appium-mac2-driver 4.2.0 → 4.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/WebDriverAgentMac/IntegrationTests/AMW3CActionsTests.m +7 -9
  3. package/WebDriverAgentMac/WebDriverAgentLib/Routing/AMHTTPServer.h +84 -0
  4. package/WebDriverAgentMac/WebDriverAgentLib/Routing/AMHTTPServer.m +493 -0
  5. package/WebDriverAgentMac/WebDriverAgentLib/Routing/AMTCPSocket.h +109 -0
  6. package/WebDriverAgentMac/WebDriverAgentLib/Routing/AMTCPSocket.m +235 -0
  7. package/WebDriverAgentMac/WebDriverAgentLib/Routing/FBWebServer.h +1 -1
  8. package/WebDriverAgentMac/WebDriverAgentLib/Routing/FBWebServer.m +9 -21
  9. package/WebDriverAgentMac/WebDriverAgentLib/Routing/RouteRequest.h +36 -0
  10. package/WebDriverAgentMac/WebDriverAgentLib/Routing/RouteRequest.m +33 -0
  11. package/WebDriverAgentMac/WebDriverAgentLib/Routing/RouteResponse.h +37 -0
  12. package/WebDriverAgentMac/WebDriverAgentLib/Routing/RouteResponse.m +62 -0
  13. package/WebDriverAgentMac/WebDriverAgentMac.xcodeproj/project.pbxproj +32 -176
  14. package/npm-shrinkwrap.json +107 -167
  15. package/package.json +3 -2
  16. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaAsyncSocket/GCDAsyncSocket.h +0 -1226
  17. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaAsyncSocket/GCDAsyncSocket.m +0 -8513
  18. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaAsyncSocket/GCDAsyncUdpSocket.h +0 -1036
  19. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaAsyncSocket/GCDAsyncUdpSocket.m +0 -5632
  20. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/Categories/DDNumber.h +0 -12
  21. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/Categories/DDNumber.m +0 -88
  22. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/Categories/DDRange.h +0 -56
  23. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/Categories/DDRange.m +0 -106
  24. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPConnection.h +0 -107
  25. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPConnection.m +0 -2238
  26. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPLogging.h +0 -122
  27. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPMessage.h +0 -48
  28. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPMessage.m +0 -114
  29. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPResponse.h +0 -149
  30. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPServer.h +0 -200
  31. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPServer.m +0 -720
  32. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/Responses/HTTPDataResponse.h +0 -13
  33. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/Responses/HTTPDataResponse.m +0 -83
  34. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/Responses/HTTPErrorResponse.h +0 -9
  35. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/Responses/HTTPErrorResponse.m +0 -40
  36. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/RoutingHTTPServer/HTTPResponseProxy.h +0 -13
  37. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/RoutingHTTPServer/HTTPResponseProxy.m +0 -84
  38. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/RoutingHTTPServer/Route.h +0 -18
  39. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/RoutingHTTPServer/Route.m +0 -11
  40. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/RoutingHTTPServer/RouteRequest.h +0 -16
  41. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/RoutingHTTPServer/RouteRequest.m +0 -50
  42. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/RoutingHTTPServer/RouteResponse.h +0 -20
  43. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/RoutingHTTPServer/RouteResponse.m +0 -66
  44. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/RoutingHTTPServer/RoutingConnection.h +0 -5
  45. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/RoutingHTTPServer/RoutingConnection.m +0 -142
  46. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/RoutingHTTPServer/RoutingHTTPServer.h +0 -55
  47. package/WebDriverAgentMac/WebDriverAgentLib/Vendor/RoutingHTTPServer/RoutingHTTPServer.m +0 -288
@@ -0,0 +1,109 @@
1
+ /*
2
+ * Licensed under the Apache License, Version 2.0 (the "License");
3
+ * you may not use this file except in compliance with the License.
4
+ * See the NOTICE file distributed with this work for additional
5
+ * information regarding copyright ownership.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ #import <Foundation/Foundation.h>
18
+ #import <Network/Network.h>
19
+
20
+ NS_ASSUME_NONNULL_BEGIN
21
+
22
+ // A TCP listener/connection wrapper backed by Network.framework rather than BSD sockets.
23
+ @protocol AMTCPSocketDelegate <NSObject>
24
+
25
+ /**
26
+ The callback which is fired on new TCP client connection
27
+
28
+ @param newClient The newly connected client
29
+ */
30
+ - (void)didClientConnect:(nw_connection_t)newClient;
31
+
32
+ /**
33
+ The callback which is fired when the TCP server receives data from a connected client
34
+
35
+ @param client The client, which sent the data
36
+ @param data The received data
37
+ */
38
+ - (void)client:(nw_connection_t)client didReceiveData:(NSData *)data;
39
+
40
+ /**
41
+ The callback which is fired when TCP client disconnects
42
+
43
+ @param client The actual disconnected client
44
+ */
45
+ - (void)didClientDisconnect:(nw_connection_t)client;
46
+
47
+ @end
48
+
49
+
50
+ @interface AMTCPSocket : NSObject
51
+
52
+ @property (nullable, nonatomic, weak) id<AMTCPSocketDelegate> delegate;
53
+
54
+ /**
55
+ The port this socket is listening on. Equal to the port passed to -initWithPort: unless that was
56
+ 0 ("let the system assign a port"), in which case this reflects the actually assigned port once
57
+ -startWithError: has returned successfully.
58
+ */
59
+ @property (nonatomic, readonly) uint16_t port;
60
+
61
+ /**
62
+ The local IP address to bind the listener to, or nil to listen on all interfaces. Must be set
63
+ before -startWithError: is called.
64
+ */
65
+ @property (nonatomic, copy, nullable) NSString *interface;
66
+
67
+ /**
68
+ Creates TCP socket isntance which is going to be started on the specified port
69
+
70
+ @param port The actual port number
71
+ @return self instance
72
+ */
73
+ - (instancetype)initWithPort:(uint16_t)port;
74
+
75
+ /**
76
+ Starts TCP socket listener on the specified port
77
+
78
+ @param error The alias to the actual startup error descirption or nil if the socket has started and is listening
79
+ @return NO If there was an error
80
+ */
81
+ - (BOOL)startWithError:(NSError **)error;
82
+
83
+ /**
84
+ Stops the socket if it is running
85
+ */
86
+ - (void)stop;
87
+
88
+ /**
89
+ Writes data to the given connected client
90
+
91
+ @param data The data to send
92
+ @param client The destination client, as received via -didClientConnect: or -client:didReceiveData:
93
+ */
94
+ - (void)writeData:(NSData *)data toClient:(nw_connection_t)client;
95
+
96
+ /**
97
+ Like -writeData:toClient:, but invokes completion once the send actually goes out. Use this
98
+ before cancelling the connection - nw_connection_send is async, so cancelling right away can
99
+ drop the write before it's sent.
100
+
101
+ @param data The data to send
102
+ @param client The destination client
103
+ @param completion Called once the send attempt finishes
104
+ */
105
+ - (void)writeData:(NSData *)data toClient:(nw_connection_t)client completion:(nullable void (^)(void))completion;
106
+
107
+ @end
108
+
109
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,235 @@
1
+ /*
2
+ * Licensed under the Apache License, Version 2.0 (the "License");
3
+ * you may not use this file except in compliance with the License.
4
+ * See the NOTICE file distributed with this work for additional
5
+ * information regarding copyright ownership.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ #import "AMTCPSocket.h"
18
+
19
+ @interface AMTCPSocket ()
20
+ @property (readonly, nonatomic) dispatch_queue_t socketQueue;
21
+ @property (nullable, nonatomic) nw_listener_t listener;
22
+ @property (readonly, nonatomic) NSMutableArray<nw_connection_t> *connectedClients;
23
+ @end
24
+
25
+
26
+ @implementation AMTCPSocket
27
+
28
+ - (instancetype)initWithPort:(uint16_t)port
29
+ {
30
+ if ((self = [super init])) {
31
+ _socketQueue = dispatch_queue_create("socketQueue", NULL);
32
+ _connectedClients = [[NSMutableArray alloc] initWithCapacity:1];
33
+ _port = port;
34
+ _delegate = nil;
35
+ }
36
+ return self;
37
+ }
38
+
39
+ - (BOOL)startWithError:(NSError **)error
40
+ {
41
+ nw_parameters_t parameters = nw_parameters_create_secure_tcp(NW_PARAMETERS_DISABLE_PROTOCOL,
42
+ NW_PARAMETERS_DEFAULT_CONFIGURATION);
43
+ NSString *portString = [NSString stringWithFormat:@"%u", (unsigned int)self.port];
44
+ const char * _Nonnull portCString = (const char * _Nonnull)portString.UTF8String;
45
+
46
+ nw_listener_t listener;
47
+ if (nil != self.interface) {
48
+ const char * _Nonnull interfaceCString = (const char * _Nonnull)((NSString * _Nonnull)self.interface).UTF8String;
49
+ nw_endpoint_t localEndpoint = nw_endpoint_create_host(interfaceCString, portCString);
50
+ nw_parameters_set_local_endpoint(parameters, localEndpoint);
51
+ // The port is already encoded in localEndpoint above - do not also pass it to
52
+ // nw_listener_create_with_port, which would be ambiguous.
53
+ listener = nw_listener_create(parameters);
54
+ } else {
55
+ listener = nw_listener_create_with_port(portCString, parameters);
56
+ }
57
+ if (nil == listener) {
58
+ if (error) {
59
+ *error = [NSError errorWithDomain:@"AMTCPSocket"
60
+ code:1
61
+ userInfo:@{NSLocalizedDescriptionKey: @"Failed to create the TCP listener"}];
62
+ }
63
+ return NO;
64
+ }
65
+ self.listener = listener;
66
+
67
+ __weak typeof(self) weakSelf = self;
68
+ nw_listener_set_queue(listener, self.socketQueue);
69
+ nw_listener_set_new_connection_handler(listener, ^(nw_connection_t connection) {
70
+ [weakSelf acceptConnection:connection];
71
+ });
72
+
73
+ dispatch_semaphore_t startupSemaphore = dispatch_semaphore_create(0);
74
+ __block NSError *startupError = nil;
75
+ nw_listener_set_state_changed_handler(listener, ^(nw_listener_state_t state, nw_error_t nwError) {
76
+ if (nw_listener_state_ready == state) {
77
+ __strong typeof(weakSelf) strongSelf = weakSelf;
78
+ if (strongSelf) {
79
+ strongSelf->_port = nw_listener_get_port(listener);
80
+ }
81
+ dispatch_semaphore_signal(startupSemaphore);
82
+ } else if (nw_listener_state_failed == state || nw_listener_state_cancelled == state) {
83
+ // NSLocalizedDescriptionKey must be a string, not the underlying NSError itself, or
84
+ // -[NSError localizedDescription] crashes trying to treat it as one.
85
+ NSError *underlyingError = nwError ? (NSError *)CFBridgingRelease(nw_error_copy_cf_error(nwError)) : nil;
86
+ if ([underlyingError.domain isEqualToString:NSPOSIXErrorDomain]) {
87
+ // Surface POSIX errors (e.g. EADDRINUSE) directly, since callers like FBWebServer check
88
+ // for them by domain/code on the top-level error to decide whether to retry another port.
89
+ startupError = underlyingError;
90
+ } else {
91
+ NSMutableDictionary<NSString *, id> *userInfo = [NSMutableDictionary dictionary];
92
+ userInfo[NSLocalizedDescriptionKey] = underlyingError.localizedDescription ?: @"The TCP listener failed to start";
93
+ if (underlyingError) {
94
+ userInfo[NSUnderlyingErrorKey] = underlyingError;
95
+ }
96
+ startupError = [NSError errorWithDomain:@"AMTCPSocket" code:2 userInfo:userInfo];
97
+ }
98
+ dispatch_semaphore_signal(startupSemaphore);
99
+ }
100
+ });
101
+ nw_listener_start(listener);
102
+ BOOL didStartInTime = 0 == dispatch_semaphore_wait(startupSemaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)));
103
+ if (!didStartInTime) {
104
+ startupError = [NSError errorWithDomain:@"AMTCPSocket"
105
+ code:3
106
+ userInfo:@{NSLocalizedDescriptionKey: @"Timed out while starting the TCP listener"}];
107
+ }
108
+ if (nil != startupError) {
109
+ if (error) {
110
+ *error = startupError;
111
+ }
112
+ // Cancel rather than just dropping our reference - otherwise a late ready/failed callback
113
+ // can still fire and the port stays bound at the OS level even though the caller was told
114
+ // startup failed.
115
+ nw_listener_cancel(listener);
116
+ self.listener = nil;
117
+ return NO;
118
+ }
119
+ return YES;
120
+ }
121
+
122
+ - (void)acceptConnection:(nw_connection_t)connection
123
+ {
124
+ @synchronized (self.connectedClients) {
125
+ [self.connectedClients addObject:connection];
126
+ }
127
+
128
+ __weak typeof(self) weakSelf = self;
129
+ nw_connection_set_queue(connection, self.socketQueue);
130
+ nw_connection_set_state_changed_handler(connection, ^(nw_connection_state_t state, nw_error_t connectionError) {
131
+ if (nw_connection_state_ready == state) {
132
+ __strong typeof(weakSelf) strongSelf = weakSelf;
133
+ if (nil == strongSelf) {
134
+ return;
135
+ }
136
+ id<AMTCPSocketDelegate> delegate = strongSelf.delegate;
137
+ if (nil != delegate) {
138
+ [delegate didClientConnect:connection];
139
+ }
140
+ [strongSelf scheduleReceiveForConnection:connection];
141
+ } else if (nw_connection_state_failed == state || nw_connection_state_cancelled == state) {
142
+ [weakSelf handleDisconnectForConnection:connection];
143
+ }
144
+ });
145
+ nw_connection_start(connection);
146
+ }
147
+
148
+ - (void)scheduleReceiveForConnection:(nw_connection_t)connection
149
+ {
150
+ __weak typeof(self) weakSelf = self;
151
+ nw_connection_receive(connection, 1, UINT32_MAX, ^(dispatch_data_t _Nullable content,
152
+ nw_content_context_t _Nullable context,
153
+ bool isComplete,
154
+ nw_error_t _Nullable receiveError) {
155
+ __strong typeof(weakSelf) strongSelf = weakSelf;
156
+ if (nil == strongSelf) {
157
+ return;
158
+ }
159
+ if (nil != content) {
160
+ dispatch_data_t nonnullContent = (dispatch_data_t _Nonnull)content;
161
+ NSMutableData *data = [NSMutableData data];
162
+ dispatch_data_apply(nonnullContent, ^bool(dispatch_data_t _Nonnull region, size_t offset, const void * _Nonnull buffer, size_t size) {
163
+ [data appendBytes:buffer length:size];
164
+ return true;
165
+ });
166
+ if (data.length > 0) {
167
+ id<AMTCPSocketDelegate> delegate = strongSelf.delegate;
168
+ if (nil != delegate) {
169
+ [delegate client:connection didReceiveData:data];
170
+ }
171
+ }
172
+ }
173
+ if (nil != receiveError || (isComplete && nil == content)) {
174
+ [strongSelf handleDisconnectForConnection:connection];
175
+ return;
176
+ }
177
+ [strongSelf scheduleReceiveForConnection:connection];
178
+ });
179
+ }
180
+
181
+ - (void)handleDisconnectForConnection:(nw_connection_t)connection
182
+ {
183
+ BOOL wasConnected;
184
+ @synchronized (self.connectedClients) {
185
+ wasConnected = [self.connectedClients containsObject:connection];
186
+ [self.connectedClients removeObject:connection];
187
+ }
188
+ if (wasConnected) {
189
+ id<AMTCPSocketDelegate> delegate = self.delegate;
190
+ if (nil != delegate) {
191
+ [delegate didClientDisconnect:connection];
192
+ }
193
+ }
194
+ }
195
+
196
+ - (void)writeData:(NSData *)data toClient:(nw_connection_t)client
197
+ {
198
+ [self writeData:data toClient:client completion:nil];
199
+ }
200
+
201
+ - (void)writeData:(NSData *)data toClient:(nw_connection_t)client completion:(nullable void (^)(void))completion
202
+ {
203
+ dispatch_data_t dispatchData = dispatch_data_create(data.bytes, data.length, self.socketQueue, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
204
+ nw_connection_send(client, dispatchData, NW_CONNECTION_DEFAULT_STREAM_CONTEXT, false, ^(nw_error_t _Nullable sendError) {
205
+ if (completion) {
206
+ completion();
207
+ }
208
+ });
209
+ }
210
+
211
+ - (void)stop
212
+ {
213
+ NSArray<nw_connection_t> *clients;
214
+ @synchronized (self.connectedClients) {
215
+ clients = self.connectedClients.copy;
216
+ [self.connectedClients removeAllObjects];
217
+ }
218
+ // Cancel on socketQueue, the same queue every connection's send/receive is bound to (see
219
+ // -acceptConnection:), so a write already issued just before -stop (e.g. a shutdown route's
220
+ // response) is processed before the cancellation rather than racing it.
221
+ dispatch_async(self.socketQueue, ^{
222
+ for (nw_connection_t client in clients) {
223
+ nw_connection_cancel(client);
224
+ }
225
+ });
226
+
227
+ self.delegate = nil;
228
+ nw_listener_t listener = self.listener;
229
+ if (nil != listener) {
230
+ nw_listener_cancel((nw_listener_t _Nonnull)listener);
231
+ self.listener = nil;
232
+ }
233
+ }
234
+
235
+ @end
@@ -9,7 +9,7 @@
9
9
 
10
10
  #import <Foundation/Foundation.h>
11
11
 
12
- @class RouteResponse, RoutingHTTPServer, FBExceptionHandler;
12
+ @class RouteResponse, AMHTTPServer, FBExceptionHandler;
13
13
  @protocol FBWebServerDelegate;
14
14
 
15
15
  NS_ASSUME_NONNULL_BEGIN
@@ -9,8 +9,7 @@
9
9
 
10
10
  #import "FBWebServer.h"
11
11
 
12
- #import "RoutingConnection.h"
13
- #import "RoutingHTTPServer.h"
12
+ #import "AMHTTPServer.h"
14
13
 
15
14
  #import "FBCommandHandler.h"
16
15
  #import "FBErrorBuilder.h"
@@ -25,23 +24,9 @@
25
24
  static NSString *const FBServerURLBeginMarker = @"ServerURLHere->";
26
25
  static NSString *const FBServerURLEndMarker = @"<-ServerURLHere";
27
26
 
28
- @interface FBHTTPConnection : RoutingConnection
29
- @end
30
-
31
- @implementation FBHTTPConnection
32
-
33
- - (void)handleResourceNotFound
34
- {
35
- [FBLogger logFmt:@"Received request for %@ which we do not handle", self.requestURI];
36
- [super handleResourceNotFound];
37
- }
38
-
39
- @end
40
-
41
-
42
27
  @interface FBWebServer ()
43
28
  @property (nonatomic, strong) FBExceptionHandler *exceptionHandler;
44
- @property (nonatomic, strong) RoutingHTTPServer *server;
29
+ @property (nonatomic, strong) AMHTTPServer *server;
45
30
  @property (atomic, assign) BOOL keepAlive;
46
31
  @end
47
32
 
@@ -76,13 +61,12 @@ static NSString *const FBServerURLEndMarker = @"<-ServerURLHere";
76
61
 
77
62
  - (void)startHTTPServer
78
63
  {
79
- self.server = [[RoutingHTTPServer alloc] init];
64
+ self.server = [[AMHTTPServer alloc] init];
80
65
  [self.server setRouteQueue:dispatch_get_main_queue()];
81
66
  [self.server setInterface:FBConfiguration.sharedConfiguration.serverInterface];
82
67
  [self.server setDefaultHeader:@"Server" value:@"WebDriverAgent/1.0"];
83
68
  [self.server setDefaultHeader:@"Access-Control-Allow-Origin" value:@"*"];
84
69
  [self.server setDefaultHeader:@"Access-Control-Allow-Headers" value:@"Content-Type, X-Requested-With"];
85
- [self.server setConnectionClass:[FBHTTPConnection self]];
86
70
 
87
71
  [self registerRouteHandlers:[self.class collectCommandHandlerClasses]];
88
72
  [self registerServerKeyRouteHandlers];
@@ -119,7 +103,7 @@ static NSString *const FBServerURLEndMarker = @"<-ServerURLHere";
119
103
  self.keepAlive = NO;
120
104
  }
121
105
 
122
- - (BOOL)attemptToStartServer:(RoutingHTTPServer *)server onPort:(NSInteger)port withError:(NSError **)error
106
+ - (BOOL)attemptToStartServer:(AMHTTPServer *)server onPort:(NSInteger)port withError:(NSError **)error
123
107
  {
124
108
  server.port = (UInt16)port;
125
109
  NSError *innerError = nil;
@@ -195,7 +179,11 @@ static NSString *const FBServerURLEndMarker = @"<-ServerURLHere";
195
179
  [FBSession.activeSession kill];
196
180
  } @finally {
197
181
  [response respondWithString:@"Shutting down"];
198
- [self.delegate webServerDidRequestShutdown:self];
182
+ // Deferred so the "Shutting down" response is written to the client before
183
+ // webServerDidRequestShutdown: tears down the server's socket out from under it.
184
+ dispatch_async(dispatch_get_main_queue(), ^{
185
+ [self.delegate webServerDidRequestShutdown:self];
186
+ });
199
187
  }
200
188
  }];
201
189
 
@@ -0,0 +1,36 @@
1
+ /*
2
+ * Licensed under the Apache License, Version 2.0 (the "License");
3
+ * you may not use this file except in compliance with the License.
4
+ * See the NOTICE file distributed with this work for additional
5
+ * information regarding copyright ownership.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ // A minimal request value type, exposing just the surface FBWebServer's route blocks and
18
+ // FBRoute.decorateRequest: read.
19
+
20
+ #import <Foundation/Foundation.h>
21
+
22
+ NS_ASSUME_NONNULL_BEGIN
23
+
24
+ @interface RouteRequest : NSObject
25
+
26
+ @property (nonatomic, copy, readonly) NSURL *url;
27
+ @property (nonatomic, copy, readonly) NSDictionary<NSString *, NSString *> *params;
28
+ @property (nonatomic, copy, readonly) NSData *body;
29
+
30
+ - (instancetype)initWithURL:(NSURL *)url
31
+ params:(NSDictionary<NSString *, NSString *> *)params
32
+ body:(NSData *)body;
33
+
34
+ @end
35
+
36
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,33 @@
1
+ /*
2
+ * Licensed under the Apache License, Version 2.0 (the "License");
3
+ * you may not use this file except in compliance with the License.
4
+ * See the NOTICE file distributed with this work for additional
5
+ * information regarding copyright ownership.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ #import "RouteRequest.h"
18
+
19
+ @implementation RouteRequest
20
+
21
+ - (instancetype)initWithURL:(NSURL *)url
22
+ params:(NSDictionary<NSString *, NSString *> *)params
23
+ body:(NSData *)body
24
+ {
25
+ if ((self = [super init])) {
26
+ _url = url.copy;
27
+ _params = params.copy;
28
+ _body = body.copy;
29
+ }
30
+ return self;
31
+ }
32
+
33
+ @end
@@ -0,0 +1,37 @@
1
+ /*
2
+ * Licensed under the Apache License, Version 2.0 (the "License");
3
+ * you may not use this file except in compliance with the License.
4
+ * See the NOTICE file distributed with this work for additional
5
+ * information regarding copyright ownership.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ // A minimal response value type, exposing just the surface FBRoute.m/FBResponseJSONPayload.m
18
+ // call on it.
19
+
20
+ #import <Foundation/Foundation.h>
21
+
22
+ NS_ASSUME_NONNULL_BEGIN
23
+
24
+ @interface RouteResponse : NSObject
25
+
26
+ @property (nonatomic) NSInteger statusCode;
27
+ @property (nonatomic, copy, readonly) NSDictionary<NSString *, NSString *> *headers;
28
+ @property (nonatomic, copy, readonly, nullable) NSData *responseData;
29
+
30
+ - (void)setHeader:(NSString *)field value:(NSString *)value;
31
+ - (void)respondWithString:(NSString *)string;
32
+ - (void)respondWithString:(NSString *)string encoding:(NSStringEncoding)encoding;
33
+ - (void)respondWithData:(NSData *)data;
34
+
35
+ @end
36
+
37
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,62 @@
1
+ /*
2
+ * Licensed under the Apache License, Version 2.0 (the "License");
3
+ * you may not use this file except in compliance with the License.
4
+ * See the NOTICE file distributed with this work for additional
5
+ * information regarding copyright ownership.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ #import "RouteResponse.h"
18
+
19
+ @interface RouteResponse ()
20
+ @property (nonatomic, copy) NSMutableDictionary<NSString *, NSString *> *mutableHeaders;
21
+ @end
22
+
23
+ @implementation RouteResponse
24
+
25
+ - (instancetype)init
26
+ {
27
+ if ((self = [super init])) {
28
+ _statusCode = 200;
29
+ _mutableHeaders = [NSMutableDictionary dictionary];
30
+ }
31
+ return self;
32
+ }
33
+
34
+ - (NSDictionary<NSString *, NSString *> *)headers
35
+ {
36
+ return self.mutableHeaders.copy;
37
+ }
38
+
39
+ - (void)setHeader:(NSString *)field value:(NSString *)value
40
+ {
41
+ self.mutableHeaders[field] = value;
42
+ }
43
+
44
+ - (void)respondWithData:(NSData *)data
45
+ {
46
+ _responseData = data.copy;
47
+ if (nil == self.mutableHeaders[@"Content-Length"]) {
48
+ self.mutableHeaders[@"Content-Length"] = [NSString stringWithFormat:@"%lu", (unsigned long)data.length];
49
+ }
50
+ }
51
+
52
+ - (void)respondWithString:(NSString *)string
53
+ {
54
+ [self respondWithString:string encoding:NSUTF8StringEncoding];
55
+ }
56
+
57
+ - (void)respondWithString:(NSString *)string encoding:(NSStringEncoding)encoding
58
+ {
59
+ [self respondWithData:[string dataUsingEncoding:encoding] ?: [NSData data]];
60
+ }
61
+
62
+ @end