appium-mac2-driver 4.2.0 → 4.3.0
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.
- package/CHANGELOG.md +6 -0
- package/WebDriverAgentMac/IntegrationTests/AMW3CActionsTests.m +7 -9
- package/WebDriverAgentMac/WebDriverAgentLib/Routing/AMHTTPServer.h +84 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Routing/AMHTTPServer.m +493 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Routing/AMTCPSocket.h +109 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Routing/AMTCPSocket.m +235 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Routing/FBWebServer.h +1 -1
- package/WebDriverAgentMac/WebDriverAgentLib/Routing/FBWebServer.m +9 -21
- package/WebDriverAgentMac/WebDriverAgentLib/Routing/RouteRequest.h +36 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Routing/RouteRequest.m +33 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Routing/RouteResponse.h +37 -0
- package/WebDriverAgentMac/WebDriverAgentLib/Routing/RouteResponse.m +62 -0
- package/WebDriverAgentMac/WebDriverAgentMac.xcodeproj/project.pbxproj +32 -176
- package/npm-shrinkwrap.json +75 -146
- package/package.json +2 -1
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaAsyncSocket/GCDAsyncSocket.h +0 -1226
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaAsyncSocket/GCDAsyncSocket.m +0 -8513
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaAsyncSocket/GCDAsyncUdpSocket.h +0 -1036
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaAsyncSocket/GCDAsyncUdpSocket.m +0 -5632
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/Categories/DDNumber.h +0 -12
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/Categories/DDNumber.m +0 -88
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/Categories/DDRange.h +0 -56
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/Categories/DDRange.m +0 -106
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPConnection.h +0 -107
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPConnection.m +0 -2238
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPLogging.h +0 -122
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPMessage.h +0 -48
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPMessage.m +0 -114
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPResponse.h +0 -149
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPServer.h +0 -200
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPServer.m +0 -720
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/Responses/HTTPDataResponse.h +0 -13
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/Responses/HTTPDataResponse.m +0 -83
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/Responses/HTTPErrorResponse.h +0 -9
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/CocoaHTTPServer/Responses/HTTPErrorResponse.m +0 -40
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/RoutingHTTPServer/HTTPResponseProxy.h +0 -13
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/RoutingHTTPServer/HTTPResponseProxy.m +0 -84
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/RoutingHTTPServer/Route.h +0 -18
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/RoutingHTTPServer/Route.m +0 -11
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/RoutingHTTPServer/RouteRequest.h +0 -16
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/RoutingHTTPServer/RouteRequest.m +0 -50
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/RoutingHTTPServer/RouteResponse.h +0 -20
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/RoutingHTTPServer/RouteResponse.m +0 -66
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/RoutingHTTPServer/RoutingConnection.h +0 -5
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/RoutingHTTPServer/RoutingConnection.m +0 -142
- package/WebDriverAgentMac/WebDriverAgentLib/Vendor/RoutingHTTPServer/RoutingHTTPServer.h +0 -55
- 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,8 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
#import "FBWebServer.h"
|
|
11
11
|
|
|
12
|
-
#import "
|
|
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)
|
|
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 = [[
|
|
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:(
|
|
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
|
-
|
|
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
|