appium-webdriveragent 10.2.6 → 10.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.
@@ -1,21 +1,26 @@
1
1
  /**
2
- * The HTTPMessage class is a simple Objective-C wrapper around Apple's CFHTTPMessage class.
2
+ * The HTTPMessage class is a simple Objective-C wrapper for HTTP message parsing.
3
+ * Migrated from CFHTTPMessage to use Foundation and Network framework.
3
4
  **/
4
5
 
5
6
  #import <Foundation/Foundation.h>
6
7
 
7
- #if TARGET_OS_IPHONE
8
- // Note: You may need to add the CFNetwork Framework to your project
9
- #import <CFNetwork/CFNetwork.h>
10
- #endif
11
-
12
- #define HTTPVersion1_0 ((NSString *)kCFHTTPVersion1_0)
13
- #define HTTPVersion1_1 ((NSString *)kCFHTTPVersion1_1)
8
+ #define HTTPVersion1_0 @"HTTP/1.0"
9
+ #define HTTPVersion1_1 @"HTTP/1.1"
14
10
 
15
11
 
16
12
  @interface HTTPMessage : NSObject
17
13
  {
18
- CFHTTPMessageRef message;
14
+ NSMutableDictionary *_headers;
15
+ NSMutableData *_body;
16
+ NSString *_version;
17
+ NSString *_method;
18
+ NSURL *_url;
19
+ NSInteger _statusCode;
20
+ NSString *_statusDescription;
21
+ BOOL _isRequest;
22
+ BOOL _headerComplete;
23
+ NSMutableData *_rawData;
19
24
  }
20
25
 
21
26
  - (id)initEmptyRequest;
@@ -8,107 +8,350 @@
8
8
 
9
9
  @implementation HTTPMessage
10
10
 
11
- - (id)initEmptyRequest
11
+ - (id)init
12
12
  {
13
13
  if ((self = [super init]))
14
14
  {
15
- message = CFHTTPMessageCreateEmpty(NULL, YES);
15
+ _headers = [[NSMutableDictionary alloc] init];
16
+ _body = [[NSMutableData alloc] init];
17
+ _rawData = [[NSMutableData alloc] init];
18
+ _version = HTTPVersion1_1;
19
+ _headerComplete = NO;
20
+ _isRequest = YES;
21
+ }
22
+ return self;
23
+ }
24
+
25
+ - (id)initEmptyRequest
26
+ {
27
+ if ((self = [self init]))
28
+ {
29
+ _isRequest = YES;
16
30
  }
17
31
  return self;
18
32
  }
19
33
 
20
34
  - (id)initRequestWithMethod:(NSString *)method URL:(NSURL *)url version:(NSString *)version
21
35
  {
22
- if ((self = [super init]))
36
+ if ((self = [self init]))
23
37
  {
24
- message = CFHTTPMessageCreateRequest(NULL,
25
- (__bridge CFStringRef)method,
26
- (__bridge CFURLRef)url,
27
- (__bridge CFStringRef)version);
38
+ _isRequest = YES;
39
+ _method = [method copy];
40
+ _url = [url copy];
41
+ _version = version ? [version copy] : HTTPVersion1_1;
28
42
  }
29
43
  return self;
30
44
  }
31
45
 
32
46
  - (id)initResponseWithStatusCode:(NSInteger)code description:(NSString *)description version:(NSString *)version
33
47
  {
34
- if ((self = [super init]))
48
+ if ((self = [self init]))
35
49
  {
36
- message = CFHTTPMessageCreateResponse(NULL,
37
- (CFIndex)code,
38
- (__bridge CFStringRef)description,
39
- (__bridge CFStringRef)version);
50
+ _isRequest = NO;
51
+ _statusCode = code;
52
+ _statusDescription = [description copy];
53
+ _version = version ? [version copy] : HTTPVersion1_1;
40
54
  }
41
55
  return self;
42
56
  }
43
57
 
44
- - (void)dealloc
58
+ - (BOOL)appendData:(NSData *)data
45
59
  {
46
- if (message)
60
+ if (!data || [data length] == 0)
47
61
  {
48
- CFRelease(message);
62
+ return NO;
49
63
  }
64
+
65
+ [_rawData appendData:data];
66
+
67
+ if (!_headerComplete)
68
+ {
69
+ // Look for the end of headers (CRLF CRLF or LF LF)
70
+ NSData *headerEndMarker = [@"\r\n\r\n" dataUsingEncoding:NSASCIIStringEncoding];
71
+ NSRange headerEndRange = [_rawData rangeOfData:headerEndMarker options:0 range:NSMakeRange(0, [_rawData length])];
72
+
73
+ if (headerEndRange.location == NSNotFound)
74
+ {
75
+ // Also check for LF LF (some clients use this)
76
+ NSData *lfMarker = [@"\n\n" dataUsingEncoding:NSASCIIStringEncoding];
77
+ headerEndRange = [_rawData rangeOfData:lfMarker options:0 range:NSMakeRange(0, [_rawData length])];
78
+ }
79
+
80
+ if (headerEndRange.location != NSNotFound)
81
+ {
82
+ _headerComplete = YES;
83
+
84
+ // Parse the header data
85
+ NSData *headerData = [_rawData subdataWithRange:NSMakeRange(0, headerEndRange.location + headerEndRange.length)];
86
+ NSString *headerString = [[NSString alloc] initWithData:headerData encoding:NSASCIIStringEncoding];
87
+
88
+ if (headerString)
89
+ {
90
+ [self parseHeaders:headerString];
91
+ }
92
+
93
+ // Extract body data if any
94
+ NSUInteger bodyStart = headerEndRange.location + headerEndRange.length;
95
+ if ([_rawData length] > bodyStart)
96
+ {
97
+ NSData *bodyData = [_rawData subdataWithRange:NSMakeRange(bodyStart, [_rawData length] - bodyStart)];
98
+ [_body appendData:bodyData];
99
+ }
100
+
101
+ [_rawData setLength:0];
102
+ }
103
+ }
104
+ else
105
+ {
106
+ // Headers are complete, append to body
107
+ [_body appendData:data];
108
+ }
109
+
110
+ return YES;
50
111
  }
51
112
 
52
- - (BOOL)appendData:(NSData *)data
113
+ - (void)parseHeaders:(NSString *)headerString
53
114
  {
54
- return CFHTTPMessageAppendBytes(message, [data bytes], [data length]);
115
+ NSArray *lines;
116
+
117
+ // Try splitting by "\r\n" first (standard HTTP line ending)
118
+ // Check if the string actually contains "\r\n" delimiter
119
+ if ([headerString rangeOfString:@"\r\n"].location != NSNotFound)
120
+ {
121
+ // Found "\r\n" delimiter, use this split
122
+ lines = [headerString componentsSeparatedByString:@"\r\n"];
123
+ }
124
+ else
125
+ {
126
+ // No "\r\n" found, try "\n" (some clients use just LF)
127
+ lines = [headerString componentsSeparatedByString:@"\n"];
128
+ }
129
+
130
+ // componentsSeparatedByString: always returns at least one element,
131
+ // so check if we have meaningful content (non-empty first line)
132
+ if ([lines count] == 0 || [[lines objectAtIndex:0] length] == 0)
133
+ {
134
+ return;
135
+ }
136
+
137
+ // Parse first line (request line or status line)
138
+ NSString *firstLine = [lines objectAtIndex:0];
139
+ NSArray *firstLineParts = [firstLine componentsSeparatedByString:@" "];
140
+
141
+ if (_isRequest && [firstLineParts count] >= 3)
142
+ {
143
+ // Request line: METHOD URL VERSION
144
+ _method = [[firstLineParts objectAtIndex:0] copy];
145
+ NSString *urlString = [firstLineParts objectAtIndex:1];
146
+
147
+ // Handle both absolute URLs and relative paths
148
+ // Try absolute URL first
149
+ NSURL *parsedURL = [NSURL URLWithString:urlString];
150
+
151
+ // If that fails (nil), it's likely a relative path like "/endpoint"
152
+ // Create a URL with a base URL to handle relative paths
153
+ if (!parsedURL)
154
+ {
155
+ // Use a dummy base URL to allow relative path parsing
156
+ NSURL *baseURL = [NSURL URLWithString:@"http://localhost"];
157
+ parsedURL = [NSURL URLWithString:urlString relativeToURL:baseURL];
158
+ }
159
+
160
+ _url = [parsedURL copy];
161
+ if ([firstLineParts count] >= 3)
162
+ {
163
+ _version = [[firstLineParts objectAtIndex:2] copy];
164
+ }
165
+ }
166
+ else if (!_isRequest && [firstLineParts count] >= 3)
167
+ {
168
+ // Status line: VERSION CODE DESCRIPTION
169
+ _version = [[firstLineParts objectAtIndex:0] copy];
170
+ _statusCode = [[firstLineParts objectAtIndex:1] integerValue];
171
+ NSMutableArray *descParts = [NSMutableArray arrayWithArray:firstLineParts];
172
+ [descParts removeObjectAtIndex:0];
173
+ [descParts removeObjectAtIndex:0];
174
+ _statusDescription = [[descParts componentsJoinedByString:@" "] copy];
175
+ }
176
+
177
+ // Parse header fields
178
+ for (NSUInteger i = 1; i < [lines count]; i++)
179
+ {
180
+ NSString *line = [lines objectAtIndex:i];
181
+ if ([line length] == 0)
182
+ {
183
+ continue;
184
+ }
185
+
186
+ NSRange colonRange = [line rangeOfString:@":"];
187
+ if (colonRange.location != NSNotFound)
188
+ {
189
+ NSString *headerName = [[line substringToIndex:colonRange.location] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
190
+ NSString *headerValue = [[line substringFromIndex:colonRange.location + 1] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
191
+
192
+ if ([headerName length] > 0)
193
+ {
194
+ // HTTP headers are case-insensitive, but we'll store them with their original case
195
+ // For lookup, we'll use case-insensitive comparison
196
+ [_headers setObject:headerValue forKey:headerName];
197
+ }
198
+ }
199
+ }
55
200
  }
56
201
 
57
202
  - (BOOL)isHeaderComplete
58
203
  {
59
- return CFHTTPMessageIsHeaderComplete(message);
204
+ return _headerComplete;
60
205
  }
61
206
 
62
207
  - (NSString *)version
63
208
  {
64
- return (__bridge_transfer NSString *)CFHTTPMessageCopyVersion(message);
209
+ return _version;
65
210
  }
66
211
 
67
212
  - (NSString *)method
68
213
  {
69
- return (__bridge_transfer NSString *)CFHTTPMessageCopyRequestMethod(message);
214
+ return _method;
70
215
  }
71
216
 
72
217
  - (NSURL *)url
73
218
  {
74
- return (__bridge_transfer NSURL *)CFHTTPMessageCopyRequestURL(message);
219
+ return _url;
75
220
  }
76
221
 
77
222
  - (NSInteger)statusCode
78
223
  {
79
- return (NSInteger)CFHTTPMessageGetResponseStatusCode(message);
224
+ return _statusCode;
80
225
  }
81
226
 
82
227
  - (NSDictionary *)allHeaderFields
83
228
  {
84
- return (__bridge_transfer NSDictionary *)CFHTTPMessageCopyAllHeaderFields(message);
229
+ return [_headers copy];
85
230
  }
86
231
 
87
232
  - (NSString *)headerField:(NSString *)headerField
88
233
  {
89
- return (__bridge_transfer NSString *)CFHTTPMessageCopyHeaderFieldValue(message, (__bridge CFStringRef)headerField);
234
+ // Case-insensitive lookup
235
+ for (NSString *key in [_headers allKeys])
236
+ {
237
+ if ([key caseInsensitiveCompare:headerField] == NSOrderedSame)
238
+ {
239
+ return [_headers objectForKey:key];
240
+ }
241
+ }
242
+ return nil;
90
243
  }
91
244
 
92
245
  - (void)setHeaderField:(NSString *)headerField value:(NSString *)headerFieldValue
93
246
  {
94
- CFHTTPMessageSetHeaderFieldValue(message,
95
- (__bridge CFStringRef)headerField,
96
- (__bridge CFStringRef)headerFieldValue);
247
+ if (headerField && headerFieldValue)
248
+ {
249
+ // Remove existing header with same name (case-insensitive)
250
+ NSMutableArray *keysToRemove = [NSMutableArray array];
251
+ for (NSString *key in [_headers allKeys])
252
+ {
253
+ if ([key caseInsensitiveCompare:headerField] == NSOrderedSame)
254
+ {
255
+ [keysToRemove addObject:key];
256
+ }
257
+ }
258
+ [_headers removeObjectsForKeys:keysToRemove];
259
+
260
+ // Add new header
261
+ [_headers setObject:headerFieldValue forKey:headerField];
262
+ }
97
263
  }
98
264
 
99
265
  - (NSData *)messageData
100
266
  {
101
- return (__bridge_transfer NSData *)CFHTTPMessageCopySerializedMessage(message);
267
+ NSMutableString *messageString = [NSMutableString string];
268
+
269
+ if (_isRequest)
270
+ {
271
+ // Request line
272
+ // For relative URLs, use the path component; for absolute URLs, use absoluteString
273
+ NSString *urlString = nil;
274
+ if (_url)
275
+ {
276
+ // If it's a relative URL (has a base), use the relative path
277
+ // Otherwise use absoluteString or path
278
+ if ([_url baseURL])
279
+ {
280
+ // Relative URL - use the relative portion
281
+ urlString = [_url relativeString];
282
+ }
283
+ else
284
+ {
285
+ // Absolute URL
286
+ urlString = [_url absoluteString];
287
+ if (!urlString)
288
+ {
289
+ urlString = [_url path];
290
+ }
291
+ }
292
+ }
293
+ [messageString appendFormat:@"%@ %@ %@\r\n", _method ?: @"GET", urlString ?: @"/", _version ?: HTTPVersion1_1];
294
+ }
295
+ else
296
+ {
297
+ // Status line
298
+ [messageString appendFormat:@"%@ %ld %@\r\n", _version ?: HTTPVersion1_1, (long)_statusCode, _statusDescription ?: @""];
299
+ }
300
+
301
+ // Headers
302
+ for (NSString *key in [_headers allKeys])
303
+ {
304
+ NSString *value = [_headers objectForKey:key];
305
+ [messageString appendFormat:@"%@: %@\r\n", key, value];
306
+ }
307
+
308
+ // Empty line to separate headers from body
309
+ [messageString appendString:@"\r\n"];
310
+
311
+ NSMutableData *data = [NSMutableData dataWithData:(id)[messageString dataUsingEncoding:NSASCIIStringEncoding]];
312
+
313
+ // Append body if present
314
+ if ([_body length] > 0)
315
+ {
316
+ [data appendData:_body];
317
+ }
318
+
319
+ return data;
102
320
  }
103
321
 
104
322
  - (NSData *)body
105
323
  {
106
- return (__bridge_transfer NSData *)CFHTTPMessageCopyBody(message);
324
+ return [_body copy];
107
325
  }
108
326
 
109
327
  - (void)setBody:(NSData *)body
110
328
  {
111
- CFHTTPMessageSetBody(message, (__bridge CFDataRef)body);
329
+ if (body)
330
+ {
331
+ _body = [body mutableCopy];
332
+ }
333
+ else
334
+ {
335
+ _body = [[NSMutableData alloc] init];
336
+ }
337
+ }
338
+
339
+ - (void)dealloc
340
+ {
341
+ // ARC automatically releases all instance variables, but we include this
342
+ // for clarity and to match the pattern of the original CFNetwork implementation.
343
+ // All Objective-C objects (_headers, _body, _rawData, _version, _method, _url, _statusDescription)
344
+ // will be automatically released by ARC when this object is deallocated.
345
+ #if ! __has_feature(objc_arc)
346
+ [_headers release];
347
+ [_body release];
348
+ [_rawData release];
349
+ [_version release];
350
+ [_method release];
351
+ [_url release];
352
+ [_statusDescription release];
353
+ [super dealloc];
354
+ #endif
112
355
  }
113
356
 
114
357
  @end
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appium-webdriveragent",
3
- "version": "10.2.6",
3
+ "version": "10.3.0",
4
4
  "description": "Package bundling WebDriverAgent",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",