appium-webdriveragent 16.6.0 → 16.7.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.
Files changed (50) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/WebDriverAgent.xcodeproj/project.pbxproj +45 -303
  3. package/WebDriverAgentLib/Info.plist +2 -2
  4. package/WebDriverAgentLib/Routing/{WatchOS/FBWatchHTTPServer.h → FBHTTPServer.h} +11 -7
  5. package/WebDriverAgentLib/Routing/{WatchOS/FBWatchHTTPServer.m → FBHTTPServer.m} +26 -13
  6. package/WebDriverAgentLib/Routing/FBTCPSocket.h +10 -45
  7. package/WebDriverAgentLib/Routing/FBTCPSocket.m +12 -94
  8. package/WebDriverAgentLib/Routing/FBWebServer.h +1 -1
  9. package/WebDriverAgentLib/Routing/FBWebServer.m +4 -49
  10. package/WebDriverAgentLib/Routing/{WatchOS/RouteRequest.h → RouteRequest.h} +2 -4
  11. package/WebDriverAgentLib/Routing/{WatchOS/RouteResponse.h → RouteResponse.h} +2 -2
  12. package/WebDriverAgentLib/Utilities/FBMjpegServer.h +3 -5
  13. package/WebDriverAgentLib/Utilities/FBMjpegServer.m +1 -66
  14. package/package.json +1 -1
  15. package/WebDriverAgentLib/Vendor/CocoaAsyncSocket/GCDAsyncSocket.h +0 -1220
  16. package/WebDriverAgentLib/Vendor/CocoaAsyncSocket/GCDAsyncSocket.m +0 -8786
  17. package/WebDriverAgentLib/Vendor/CocoaAsyncSocket/GCDAsyncUdpSocket.h +0 -1036
  18. package/WebDriverAgentLib/Vendor/CocoaAsyncSocket/GCDAsyncUdpSocket.m +0 -5868
  19. package/WebDriverAgentLib/Vendor/CocoaHTTPServer/Categories/DDNumber.h +0 -12
  20. package/WebDriverAgentLib/Vendor/CocoaHTTPServer/Categories/DDNumber.m +0 -88
  21. package/WebDriverAgentLib/Vendor/CocoaHTTPServer/Categories/DDRange.h +0 -56
  22. package/WebDriverAgentLib/Vendor/CocoaHTTPServer/Categories/DDRange.m +0 -100
  23. package/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPConnection.h +0 -109
  24. package/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPConnection.m +0 -2281
  25. package/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPLogging.h +0 -122
  26. package/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPMessage.h +0 -53
  27. package/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPMessage.m +0 -357
  28. package/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPResponse.h +0 -149
  29. package/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPServer.h +0 -126
  30. package/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPServer.m +0 -372
  31. package/WebDriverAgentLib/Vendor/CocoaHTTPServer/LICENSE +0 -18
  32. package/WebDriverAgentLib/Vendor/CocoaHTTPServer/Responses/HTTPDataResponse.h +0 -13
  33. package/WebDriverAgentLib/Vendor/CocoaHTTPServer/Responses/HTTPDataResponse.m +0 -83
  34. package/WebDriverAgentLib/Vendor/CocoaHTTPServer/Responses/HTTPErrorResponse.h +0 -9
  35. package/WebDriverAgentLib/Vendor/CocoaHTTPServer/Responses/HTTPErrorResponse.m +0 -38
  36. package/WebDriverAgentLib/Vendor/RoutingHTTPServer/HTTPResponseProxy.h +0 -13
  37. package/WebDriverAgentLib/Vendor/RoutingHTTPServer/HTTPResponseProxy.m +0 -84
  38. package/WebDriverAgentLib/Vendor/RoutingHTTPServer/LICENSE +0 -19
  39. package/WebDriverAgentLib/Vendor/RoutingHTTPServer/Route.h +0 -18
  40. package/WebDriverAgentLib/Vendor/RoutingHTTPServer/Route.m +0 -11
  41. package/WebDriverAgentLib/Vendor/RoutingHTTPServer/RouteRequest.h +0 -16
  42. package/WebDriverAgentLib/Vendor/RoutingHTTPServer/RouteRequest.m +0 -50
  43. package/WebDriverAgentLib/Vendor/RoutingHTTPServer/RouteResponse.h +0 -20
  44. package/WebDriverAgentLib/Vendor/RoutingHTTPServer/RouteResponse.m +0 -66
  45. package/WebDriverAgentLib/Vendor/RoutingHTTPServer/RoutingConnection.h +0 -5
  46. package/WebDriverAgentLib/Vendor/RoutingHTTPServer/RoutingConnection.m +0 -142
  47. package/WebDriverAgentLib/Vendor/RoutingHTTPServer/RoutingHTTPServer.h +0 -55
  48. package/WebDriverAgentLib/Vendor/RoutingHTTPServer/RoutingHTTPServer.m +0 -303
  49. /package/WebDriverAgentLib/Routing/{WatchOS/RouteRequest.m → RouteRequest.m} +0 -0
  50. /package/WebDriverAgentLib/Routing/{WatchOS/RouteResponse.m → RouteResponse.m} +0 -0
@@ -1,2281 +0,0 @@
1
- #import "HTTPServer.h"
2
- #import "HTTPConnection.h"
3
- #import "HTTPMessage.h"
4
- #import "HTTPResponse.h"
5
- #import "DDNumber.h"
6
- #import "DDRange.h"
7
- #import "HTTPLogging.h"
8
-
9
- #import "GCDAsyncSocket.h"
10
-
11
- #if ! __has_feature(objc_arc)
12
- #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
13
- #endif
14
-
15
- #pragma clang diagnostic ignored "-Wunknown-warning-option"
16
- #pragma clang diagnostic ignored "-Wdirect-ivar-access"
17
- #pragma clang diagnostic ignored "-Wimplicit-retain-self"
18
- #pragma clang diagnostic ignored "-Wformat-non-iso"
19
- #pragma clang diagnostic ignored "-Wunused-variable"
20
- #pragma clang diagnostic ignored "-Wsign-compare"
21
- #pragma clang diagnostic ignored "-Wformat-nonliteral"
22
- #pragma clang diagnostic ignored "-Wunreachable-code"
23
- #pragma clang diagnostic ignored "-Wfloat-conversion"
24
-
25
- // Log levels: off, error, warn, info, verbose
26
- // Other flags: trace
27
- static const int httpLogLevel = HTTP_LOG_LEVEL_WARN; // | HTTP_LOG_FLAG_TRACE;
28
-
29
- // Define chunk size used to read in data for responses
30
- // This is how much data will be read from disk into RAM at a time
31
- #if TARGET_OS_IPHONE
32
- #define READ_CHUNKSIZE (1024 * 256)
33
- #else
34
- #define READ_CHUNKSIZE (1024 * 512)
35
- #endif
36
-
37
- // Define chunk size used to read in POST upload data
38
- #if TARGET_OS_IPHONE
39
- #define POST_CHUNKSIZE (1024 * 256)
40
- #else
41
- #define POST_CHUNKSIZE (1024 * 512)
42
- #endif
43
-
44
- // Define the various timeouts (in seconds) for various parts of the HTTP process
45
- #define TIMEOUT_READ_FIRST_HEADER_LINE 30
46
- #define TIMEOUT_READ_SUBSEQUENT_HEADER_LINE 30
47
- #define TIMEOUT_READ_BODY -1
48
- #define TIMEOUT_WRITE_HEAD 30
49
- #define TIMEOUT_WRITE_BODY -1
50
- #define TIMEOUT_WRITE_ERROR 30
51
- #define TIMEOUT_NONCE 300
52
-
53
- // Define the various limits
54
- // MAX_HEADER_LINE_LENGTH: Max length (in bytes) of any single line in a header (including \r\n)
55
- // MAX_HEADER_LINES : Max number of lines in a single header (including first GET line)
56
- #define MAX_HEADER_LINE_LENGTH 8190
57
- #define MAX_HEADER_LINES 100
58
- // MAX_CHUNK_LINE_LENGTH : For accepting chunked transfer uploads, max length of chunk size line (including \r\n)
59
- #define MAX_CHUNK_LINE_LENGTH 200
60
-
61
- // Define the various tags we'll use to differentiate what it is we're currently doing
62
- #define HTTP_REQUEST_HEADER 10
63
- #define HTTP_REQUEST_BODY 11
64
- #define HTTP_REQUEST_CHUNK_SIZE 12
65
- #define HTTP_REQUEST_CHUNK_DATA 13
66
- #define HTTP_REQUEST_CHUNK_TRAILER 14
67
- #define HTTP_REQUEST_CHUNK_FOOTER 15
68
- #define HTTP_PARTIAL_RESPONSE 20
69
- #define HTTP_PARTIAL_RESPONSE_HEADER 21
70
- #define HTTP_PARTIAL_RESPONSE_BODY 22
71
- #define HTTP_CHUNKED_RESPONSE_HEADER 30
72
- #define HTTP_CHUNKED_RESPONSE_BODY 31
73
- #define HTTP_CHUNKED_RESPONSE_FOOTER 32
74
- #define HTTP_PARTIAL_RANGE_RESPONSE_BODY 40
75
- #define HTTP_PARTIAL_RANGES_RESPONSE_BODY 50
76
- #define HTTP_RESPONSE 90
77
- #define HTTP_FINAL_RESPONSE 91
78
-
79
- // A quick note about the tags:
80
- //
81
- // The HTTP_RESPONSE and HTTP_FINAL_RESPONSE are designated tags signalling that the response is completely sent.
82
- // That is, in the onSocket:didWriteDataWithTag: method, if the tag is HTTP_RESPONSE or HTTP_FINAL_RESPONSE,
83
- // it is assumed that the response is now completely sent.
84
- // Use HTTP_RESPONSE if it's the end of a response, and you want to start reading more requests afterwards.
85
- // Use HTTP_FINAL_RESPONSE if you wish to terminate the connection after sending the response.
86
- //
87
- // If you are sending multiple data segments in a custom response, make sure that only the last segment has
88
- // the HTTP_RESPONSE tag. For all other segments prior to the last segment use HTTP_PARTIAL_RESPONSE, or some other
89
- // tag of your own invention.
90
-
91
- @interface HTTPConnection (PrivateAPI)
92
- - (void)startReadingRequest;
93
- - (void)sendResponseHeadersAndBody;
94
- @end
95
-
96
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
97
- #pragma mark -
98
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
99
-
100
- @implementation HTTPConnection
101
-
102
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
103
- #pragma mark Init, Dealloc:
104
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
105
-
106
- /**
107
- * Sole Constructor.
108
- * Associates this new HTTP connection with the given AsyncSocket.
109
- * This HTTP connection object will become the socket's delegate and take over responsibility for the socket.
110
- **/
111
- - (id)initWithAsyncSocket:(GCDAsyncSocket *)newSocket configuration:(HTTPConfig *)aConfig
112
- {
113
- if ((self = [super init]))
114
- {
115
- HTTPLogTrace();
116
-
117
- if (aConfig.queue)
118
- {
119
- connectionQueue = aConfig.queue;
120
- #if !OS_OBJECT_USE_OBJC
121
- dispatch_retain(connectionQueue);
122
- #endif
123
- }
124
- else
125
- {
126
- connectionQueue = dispatch_queue_create("HTTPConnection", NULL);
127
- }
128
-
129
- // Take over ownership of the socket
130
- asyncSocket = newSocket;
131
- [asyncSocket setDelegate:(id<GCDAsyncSocketDelegate>)self delegateQueue:connectionQueue];
132
-
133
-
134
- // Store configuration
135
- config = aConfig;
136
-
137
- // Create a new HTTP message
138
- request = [[HTTPMessage alloc] initEmptyRequest];
139
-
140
- numHeaderLines = 0;
141
-
142
- responseDataSizes = [[NSMutableArray alloc] initWithCapacity:5];
143
- }
144
- return self;
145
- }
146
-
147
- /**
148
- * Standard Deconstructor.
149
- **/
150
- - (void)dealloc
151
- {
152
- HTTPLogTrace();
153
-
154
- #if !OS_OBJECT_USE_OBJC
155
- dispatch_release(connectionQueue);
156
- #endif
157
-
158
- [asyncSocket setDelegate:nil delegateQueue:NULL];
159
- [asyncSocket disconnect];
160
-
161
- if ([httpResponse respondsToSelector:@selector(connectionDidClose)])
162
- {
163
- [httpResponse connectionDidClose];
164
- }
165
- }
166
-
167
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
168
- #pragma mark Method Support
169
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
170
-
171
- /**
172
- * Returns whether or not the server will accept messages of a given method
173
- * at a particular URI.
174
- **/
175
- - (BOOL)supportsMethod:(NSString *)method atPath:(NSString *)path
176
- {
177
- HTTPLogTrace();
178
-
179
- // Override me to support methods such as POST.
180
- //
181
- // Things you may want to consider:
182
- // - Does the given path represent a resource that is designed to accept this method?
183
- // - If accepting an upload, is the size of the data being uploaded too big?
184
- // To do this you can check the requestContentLength variable.
185
- //
186
- // For more information, you can always access the HTTPMessage request variable.
187
- //
188
- // You should fall through with a call to [super supportsMethod:method atPath:path]
189
- //
190
- // See also: expectsRequestBodyFromMethod:atPath:
191
-
192
- if ([method isEqualToString:@"GET"])
193
- return YES;
194
-
195
- if ([method isEqualToString:@"HEAD"])
196
- return YES;
197
-
198
- return NO;
199
- }
200
-
201
- /**
202
- * Returns whether or not the server expects a body from the given method.
203
- *
204
- * In other words, should the server expect a content-length header and associated body from this method.
205
- * This would be true in the case of a POST, where the client is sending data,
206
- * or for something like PUT where the client is supposed to be uploading a file.
207
- **/
208
- - (BOOL)expectsRequestBodyFromMethod:(NSString *)method atPath:(NSString *)path
209
- {
210
- HTTPLogTrace();
211
-
212
- // Override me to add support for other methods that expect the client
213
- // to send a body along with the request header.
214
- //
215
- // You should fall through with a call to [super expectsRequestBodyFromMethod:method atPath:path]
216
- //
217
- // See also: supportsMethod:atPath:
218
-
219
- if ([method isEqualToString:@"POST"])
220
- return YES;
221
-
222
- if ([method isEqualToString:@"PUT"])
223
- return YES;
224
-
225
- return NO;
226
- }
227
-
228
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
229
- #pragma mark Core
230
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
231
-
232
- /**
233
- * Starting point for the HTTP connection after it has been fully initialized (including subclasses).
234
- * This method is called by the HTTP server.
235
- **/
236
- - (void)start
237
- {
238
- dispatch_async(connectionQueue, ^{ @autoreleasepool {
239
-
240
- if (!started)
241
- {
242
- started = YES;
243
- [self startConnection];
244
- }
245
- }});
246
- }
247
-
248
- /**
249
- * This method is called by the HTTPServer if it is asked to stop.
250
- * The server, in turn, invokes stop on each HTTPConnection instance.
251
- **/
252
- - (void)stop
253
- {
254
- dispatch_async(connectionQueue, ^{ @autoreleasepool {
255
-
256
- // Disconnect the socket.
257
- // The socketDidDisconnect delegate method will handle everything else.
258
- [asyncSocket disconnect];
259
- }});
260
- }
261
-
262
- /**
263
- * Starting point for the HTTP connection.
264
- **/
265
- - (void)startConnection
266
- {
267
- // Override me to do any custom work before the connection starts.
268
- //
269
- // Be sure to invoke [super startConnection] when you're done.
270
-
271
- HTTPLogTrace();
272
-
273
- [self startReadingRequest];
274
- }
275
-
276
- /**
277
- * Starts reading an HTTP request.
278
- **/
279
- - (void)startReadingRequest
280
- {
281
- HTTPLogTrace();
282
-
283
- [asyncSocket readDataToData:[GCDAsyncSocket CRLFData]
284
- withTimeout:TIMEOUT_READ_FIRST_HEADER_LINE
285
- maxLength:MAX_HEADER_LINE_LENGTH
286
- tag:HTTP_REQUEST_HEADER];
287
- }
288
-
289
- /**
290
- * Parses the given query string.
291
- *
292
- * For example, if the query is "q=John%20Mayer%20Trio&num=50"
293
- * then this method would return the following dictionary:
294
- * {
295
- * q = "John Mayer Trio"
296
- * num = "50"
297
- * }
298
- **/
299
- - (NSDictionary *)parseParams:(NSString *)query
300
- {
301
- NSArray *components = [query componentsSeparatedByString:@"&"];
302
- NSMutableDictionary *result = [NSMutableDictionary dictionaryWithCapacity:[components count]];
303
-
304
- NSUInteger i;
305
- for (i = 0; i < [components count]; i++)
306
- {
307
- NSString *component = [components objectAtIndex:i];
308
- if ([component length] > 0)
309
- {
310
- NSRange range = [component rangeOfString:@"="];
311
- if (range.location != NSNotFound)
312
- {
313
- NSString *escapedKey = [component substringToIndex:(range.location + 0)];
314
- NSString *escapedValue = [component substringFromIndex:(range.location + 1)];
315
-
316
- if ([escapedKey length] > 0)
317
- {
318
- CFStringRef k, v;
319
-
320
- k = CFURLCreateStringByReplacingPercentEscapes(NULL, (__bridge CFStringRef)escapedKey, CFSTR(""));
321
- v = CFURLCreateStringByReplacingPercentEscapes(NULL, (__bridge CFStringRef)escapedValue, CFSTR(""));
322
-
323
- NSString *key, *value;
324
-
325
- key = (__bridge_transfer NSString *)k;
326
- value = (__bridge_transfer NSString *)v;
327
-
328
- if (key)
329
- {
330
- if (value)
331
- [result setObject:value forKey:key];
332
- else
333
- [result setObject:[NSNull null] forKey:key];
334
- }
335
- }
336
- }
337
- }
338
- }
339
-
340
- return result;
341
- }
342
-
343
- /**
344
- * Parses the query variables in the request URI.
345
- *
346
- * For example, if the request URI was "/search.html?q=John%20Mayer%20Trio&num=50"
347
- * then this method would return the following dictionary:
348
- * {
349
- * q = "John Mayer Trio"
350
- * num = "50"
351
- * }
352
- **/
353
- - (NSDictionary *)parseGetParams
354
- {
355
- if(![request isHeaderComplete]) return nil;
356
-
357
- NSDictionary *result = nil;
358
-
359
- NSURL *url = [request url];
360
- if(url)
361
- {
362
- NSString *query = [url query];
363
- if (query)
364
- {
365
- result = [self parseParams:query];
366
- }
367
- }
368
-
369
- return result;
370
- }
371
-
372
- /**
373
- * Attempts to parse the given range header into a series of sequential non-overlapping ranges.
374
- * If successfull, the variables 'ranges' and 'rangeIndex' will be updated, and YES will be returned.
375
- * Otherwise, NO is returned, and the range request should be ignored.
376
- **/
377
- - (BOOL)parseRangeRequest:(NSString *)rangeHeader withContentLength:(UInt64)contentLength
378
- {
379
- HTTPLogTrace();
380
-
381
- // Examples of byte-ranges-specifier values (assuming an entity-body of length 10000):
382
- //
383
- // - The first 500 bytes (byte offsets 0-499, inclusive): bytes=0-499
384
- //
385
- // - The second 500 bytes (byte offsets 500-999, inclusive): bytes=500-999
386
- //
387
- // - The final 500 bytes (byte offsets 9500-9999, inclusive): bytes=-500
388
- //
389
- // - Or bytes=9500-
390
- //
391
- // - The first and last bytes only (bytes 0 and 9999): bytes=0-0,-1
392
- //
393
- // - Several legal but not canonical specifications of the second 500 bytes (byte offsets 500-999, inclusive):
394
- // bytes=500-600,601-999
395
- // bytes=500-700,601-999
396
- //
397
-
398
- NSRange eqsignRange = [rangeHeader rangeOfString:@"="];
399
-
400
- if(eqsignRange.location == NSNotFound) return NO;
401
-
402
- NSUInteger tIndex = eqsignRange.location;
403
- NSUInteger fIndex = eqsignRange.location + eqsignRange.length;
404
-
405
- NSMutableString *rangeType = [[rangeHeader substringToIndex:tIndex] mutableCopy];
406
- NSMutableString *rangeValue = [[rangeHeader substringFromIndex:fIndex] mutableCopy];
407
-
408
- CFStringTrimWhitespace((__bridge CFMutableStringRef)rangeType);
409
- CFStringTrimWhitespace((__bridge CFMutableStringRef)rangeValue);
410
-
411
- if([rangeType caseInsensitiveCompare:@"bytes"] != NSOrderedSame) return NO;
412
-
413
- NSArray *rangeComponents = [rangeValue componentsSeparatedByString:@","];
414
-
415
- if([rangeComponents count] == 0) return NO;
416
-
417
- ranges = [[NSMutableArray alloc] initWithCapacity:[rangeComponents count]];
418
-
419
- rangeIndex = 0;
420
-
421
- // Note: We store all range values in the form of DDRange structs, wrapped in NSValue objects.
422
- // Since DDRange consists of UInt64 values, the range extends up to 16 exabytes.
423
-
424
- NSUInteger i;
425
- for (i = 0; i < [rangeComponents count]; i++)
426
- {
427
- NSString *rangeComponent = [rangeComponents objectAtIndex:i];
428
-
429
- NSRange dashRange = [rangeComponent rangeOfString:@"-"];
430
-
431
- if (dashRange.location == NSNotFound)
432
- {
433
- // We're dealing with an individual byte number
434
-
435
- UInt64 byteIndex;
436
- if(![NSNumber parseString:rangeComponent intoUInt64:&byteIndex]) return NO;
437
-
438
- if(byteIndex >= contentLength) return NO;
439
-
440
- [ranges addObject:[NSValue valueWithDDRange:DDMakeRange(byteIndex, 1)]];
441
- }
442
- else
443
- {
444
- // We're dealing with a range of bytes
445
-
446
- tIndex = dashRange.location;
447
- fIndex = dashRange.location + dashRange.length;
448
-
449
- NSString *r1str = [rangeComponent substringToIndex:tIndex];
450
- NSString *r2str = [rangeComponent substringFromIndex:fIndex];
451
-
452
- UInt64 r1, r2;
453
-
454
- BOOL hasR1 = [NSNumber parseString:r1str intoUInt64:&r1];
455
- BOOL hasR2 = [NSNumber parseString:r2str intoUInt64:&r2];
456
-
457
- if (!hasR1)
458
- {
459
- // We're dealing with a "-[#]" range
460
- //
461
- // r2 is the number of ending bytes to include in the range
462
-
463
- if(!hasR2) return NO;
464
- if(r2 > contentLength) return NO;
465
-
466
- UInt64 startIndex = contentLength - r2;
467
-
468
- [ranges addObject:[NSValue valueWithDDRange:DDMakeRange(startIndex, r2)]];
469
- }
470
- else if (!hasR2)
471
- {
472
- // We're dealing with a "[#]-" range
473
- //
474
- // r1 is the starting index of the range, which goes all the way to the end
475
-
476
- if(r1 >= contentLength) return NO;
477
-
478
- [ranges addObject:[NSValue valueWithDDRange:DDMakeRange(r1, contentLength - r1)]];
479
- }
480
- else
481
- {
482
- // We're dealing with a normal "[#]-[#]" range
483
- //
484
- // Note: The range is inclusive. So 0-1 has a length of 2 bytes.
485
-
486
- if(r1 > r2) return NO;
487
- if(r2 >= contentLength) return NO;
488
-
489
- [ranges addObject:[NSValue valueWithDDRange:DDMakeRange(r1, r2 - r1 + 1)]];
490
- }
491
- }
492
- }
493
-
494
- if([ranges count] == 0) return NO;
495
-
496
- // Now make sure none of the ranges overlap
497
-
498
- for (i = 0; i < [ranges count] - 1; i++)
499
- {
500
- DDRange range1 = [[ranges objectAtIndex:i] ddrangeValue];
501
-
502
- NSUInteger j;
503
- for (j = i+1; j < [ranges count]; j++)
504
- {
505
- DDRange range2 = [[ranges objectAtIndex:j] ddrangeValue];
506
-
507
- DDRange iRange = DDIntersectionRange(range1, range2);
508
-
509
- if(iRange.length != 0)
510
- {
511
- return NO;
512
- }
513
- }
514
- }
515
-
516
- // Sort the ranges
517
-
518
- [ranges sortUsingSelector:@selector(ddrangeCompare:)];
519
-
520
- return YES;
521
- }
522
-
523
- - (NSString *)requestURI
524
- {
525
- if(request == nil) return nil;
526
-
527
- return [[request url] relativeString];
528
- }
529
-
530
- /**
531
- * This method is called after a full HTTP request has been received.
532
- * The current request is in the HTTPMessage request variable.
533
- **/
534
- - (void)replyToHTTPRequest
535
- {
536
- HTTPLogTrace();
537
-
538
- if (HTTP_LOG_VERBOSE)
539
- {
540
- NSData *tempData = [request messageData];
541
-
542
- NSString *tempStr = [[NSString alloc] initWithData:tempData encoding:NSUTF8StringEncoding];
543
- HTTPLogVerbose(@"%@[%p]: Received HTTP request:\n%@", THIS_FILE, self, tempStr);
544
- }
545
-
546
- // Check the HTTP version
547
- // We only support version 1.0 and 1.1
548
-
549
- NSString *version = [request version];
550
- if (![version isEqualToString:HTTPVersion1_1] && ![version isEqualToString:HTTPVersion1_0])
551
- {
552
- [self handleVersionNotSupported:version];
553
- return;
554
- }
555
-
556
- // Extract requested URI
557
- NSString *uri = [self requestURI];
558
-
559
- // Extract the method
560
- NSString *method = [request method];
561
-
562
- // Note: We already checked to ensure the method was supported in onSocket:didReadData:withTag:
563
-
564
- // Respond properly to HTTP 'GET' and 'HEAD' commands
565
- httpResponse = [self httpResponseForMethod:method URI:uri];
566
-
567
- if (httpResponse == nil)
568
- {
569
- [self handleResourceNotFound];
570
- return;
571
- }
572
-
573
- [self sendResponseHeadersAndBody];
574
- }
575
-
576
- /**
577
- * Prepares a single-range response.
578
- *
579
- * Note: The returned HTTPMessage is owned by the sender, who is responsible for releasing it.
580
- **/
581
- - (HTTPMessage *)newUniRangeResponse:(UInt64)contentLength
582
- {
583
- HTTPLogTrace();
584
-
585
- // Status Code 206 - Partial Content
586
- HTTPMessage *response = [[HTTPMessage alloc] initResponseWithStatusCode:206 description:nil version:HTTPVersion1_1];
587
-
588
- DDRange range = [[ranges objectAtIndex:0] ddrangeValue];
589
-
590
- NSString *contentLengthStr = [NSString stringWithFormat:@"%qu", range.length];
591
- [response setHeaderField:@"Content-Length" value:contentLengthStr];
592
-
593
- NSString *rangeStr = [NSString stringWithFormat:@"%qu-%qu", range.location, DDMaxRange(range) - 1];
594
- NSString *contentRangeStr = [NSString stringWithFormat:@"bytes %@/%qu", rangeStr, contentLength];
595
- [response setHeaderField:@"Content-Range" value:contentRangeStr];
596
-
597
- return response;
598
- }
599
-
600
- /**
601
- * Prepares a multi-range response.
602
- *
603
- * Note: The returned HTTPMessage is owned by the sender, who is responsible for releasing it.
604
- **/
605
- - (HTTPMessage *)newMultiRangeResponse:(UInt64)contentLength
606
- {
607
- HTTPLogTrace();
608
-
609
- // Status Code 206 - Partial Content
610
- HTTPMessage *response = [[HTTPMessage alloc] initResponseWithStatusCode:206 description:nil version:HTTPVersion1_1];
611
-
612
- // We have to send each range using multipart/byteranges
613
- // So each byterange has to be prefix'd and suffix'd with the boundry
614
- // Example:
615
- //
616
- // HTTP/1.1 206 Partial Content
617
- // Content-Length: 220
618
- // Content-Type: multipart/byteranges; boundary=4554d24e986f76dd6
619
- //
620
- //
621
- // --4554d24e986f76dd6
622
- // Content-Range: bytes 0-25/4025
623
- //
624
- // [...]
625
- // --4554d24e986f76dd6
626
- // Content-Range: bytes 3975-4024/4025
627
- //
628
- // [...]
629
- // --4554d24e986f76dd6--
630
-
631
- ranges_headers = [[NSMutableArray alloc] initWithCapacity:[ranges count]];
632
-
633
- CFUUIDRef theUUID = CFUUIDCreate(NULL);
634
- ranges_boundry = (__bridge_transfer NSString *)CFUUIDCreateString(NULL, theUUID);
635
- CFRelease(theUUID);
636
-
637
- NSString *startingBoundryStr = [NSString stringWithFormat:@"\r\n--%@\r\n", ranges_boundry];
638
- NSString *endingBoundryStr = [NSString stringWithFormat:@"\r\n--%@--\r\n", ranges_boundry];
639
-
640
- UInt64 actualContentLength = 0;
641
-
642
- NSUInteger i;
643
- for (i = 0; i < [ranges count]; i++)
644
- {
645
- DDRange range = [[ranges objectAtIndex:i] ddrangeValue];
646
-
647
- NSString *rangeStr = [NSString stringWithFormat:@"%qu-%qu", range.location, DDMaxRange(range) - 1];
648
- NSString *contentRangeVal = [NSString stringWithFormat:@"bytes %@/%qu", rangeStr, contentLength];
649
- NSString *contentRangeStr = [NSString stringWithFormat:@"Content-Range: %@\r\n\r\n", contentRangeVal];
650
-
651
- NSString *fullHeader = [startingBoundryStr stringByAppendingString:contentRangeStr];
652
- NSData *fullHeaderData = [fullHeader dataUsingEncoding:NSUTF8StringEncoding];
653
-
654
- [ranges_headers addObject:fullHeaderData];
655
-
656
- actualContentLength += [fullHeaderData length];
657
- actualContentLength += range.length;
658
- }
659
-
660
- NSData *endingBoundryData = [endingBoundryStr dataUsingEncoding:NSUTF8StringEncoding];
661
-
662
- actualContentLength += [endingBoundryData length];
663
-
664
- NSString *contentLengthStr = [NSString stringWithFormat:@"%qu", actualContentLength];
665
- [response setHeaderField:@"Content-Length" value:contentLengthStr];
666
-
667
- NSString *contentTypeStr = [NSString stringWithFormat:@"multipart/byteranges; boundary=%@", ranges_boundry];
668
- [response setHeaderField:@"Content-Type" value:contentTypeStr];
669
-
670
- return response;
671
- }
672
-
673
- /**
674
- * Returns the chunk size line that must precede each chunk of data when using chunked transfer encoding.
675
- * This consists of the size of the data, in hexadecimal, followed by a CRLF.
676
- **/
677
- - (NSData *)chunkedTransferSizeLineForLength:(NSUInteger)length
678
- {
679
- return [[NSString stringWithFormat:@"%lx\r\n", (unsigned long)length] dataUsingEncoding:NSUTF8StringEncoding];
680
- }
681
-
682
- /**
683
- * Returns the data that signals the end of a chunked transfer.
684
- **/
685
- - (NSData *)chunkedTransferFooter
686
- {
687
- // Each data chunk is preceded by a size line (in hex and including a CRLF),
688
- // followed by the data itself, followed by another CRLF.
689
- // After every data chunk has been sent, a zero size line is sent,
690
- // followed by optional footer (which are just more headers),
691
- // and followed by a CRLF on a line by itself.
692
-
693
- return [@"\r\n0\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding];
694
- }
695
-
696
- - (void)sendResponseHeadersAndBody
697
- {
698
- if ([httpResponse respondsToSelector:@selector(delayResponseHeaders)])
699
- {
700
- if ([httpResponse delayResponseHeaders])
701
- {
702
- return;
703
- }
704
- }
705
-
706
- BOOL isChunked = NO;
707
-
708
- if ([httpResponse respondsToSelector:@selector(isChunked)])
709
- {
710
- isChunked = [httpResponse isChunked];
711
- }
712
-
713
- // If a response is "chunked", this simply means the HTTPResponse object
714
- // doesn't know the content-length in advance.
715
-
716
- UInt64 contentLength = 0;
717
-
718
- if (!isChunked)
719
- {
720
- contentLength = [httpResponse contentLength];
721
- }
722
-
723
- // Check for specific range request
724
- NSString *rangeHeader = [request headerField:@"Range"];
725
-
726
- BOOL isRangeRequest = NO;
727
-
728
- // If the response is "chunked" then we don't know the exact content-length.
729
- // This means we'll be unable to process any range requests.
730
- // This is because range requests might include a range like "give me the last 100 bytes"
731
-
732
- if (!isChunked && rangeHeader)
733
- {
734
- if ([self parseRangeRequest:rangeHeader withContentLength:contentLength])
735
- {
736
- isRangeRequest = YES;
737
- }
738
- }
739
-
740
- HTTPMessage *response;
741
-
742
- if (!isRangeRequest)
743
- {
744
- // Create response
745
- // Default status code: 200 - OK
746
- NSInteger status = 200;
747
-
748
- if ([httpResponse respondsToSelector:@selector(status)])
749
- {
750
- status = [httpResponse status];
751
- }
752
- response = [[HTTPMessage alloc] initResponseWithStatusCode:status description:nil version:HTTPVersion1_1];
753
-
754
- if (isChunked)
755
- {
756
- [response setHeaderField:@"Transfer-Encoding" value:@"chunked"];
757
- }
758
- else
759
- {
760
- NSString *contentLengthStr = [NSString stringWithFormat:@"%qu", contentLength];
761
- [response setHeaderField:@"Content-Length" value:contentLengthStr];
762
- }
763
- }
764
- else
765
- {
766
- if ([ranges count] == 1)
767
- {
768
- response = [self newUniRangeResponse:contentLength];
769
- }
770
- else
771
- {
772
- response = [self newMultiRangeResponse:contentLength];
773
- }
774
- }
775
-
776
- BOOL isZeroLengthResponse = !isChunked && (contentLength == 0);
777
-
778
- // If they issue a 'HEAD' command, we don't have to include the file
779
- // If they issue a 'GET' command, we need to include the file
780
-
781
- if ([[request method] isEqualToString:@"HEAD"] || isZeroLengthResponse)
782
- {
783
- NSData *responseData = [self preprocessResponse:response];
784
- [asyncSocket writeData:responseData withTimeout:TIMEOUT_WRITE_HEAD tag:HTTP_RESPONSE];
785
-
786
- sentResponseHeaders = YES;
787
- }
788
- else
789
- {
790
- // Write the header response
791
- NSData *responseData = [self preprocessResponse:response];
792
- [asyncSocket writeData:responseData withTimeout:TIMEOUT_WRITE_HEAD tag:HTTP_PARTIAL_RESPONSE_HEADER];
793
-
794
- sentResponseHeaders = YES;
795
-
796
- // Now we need to send the body of the response
797
- if (!isRangeRequest)
798
- {
799
- // Regular request
800
- NSData *data = [httpResponse readDataOfLength:READ_CHUNKSIZE];
801
-
802
- if ([data length] > 0)
803
- {
804
- [responseDataSizes addObject:[NSNumber numberWithUnsignedInteger:[data length]]];
805
-
806
- if (isChunked)
807
- {
808
- NSData *chunkSize = [self chunkedTransferSizeLineForLength:[data length]];
809
- [asyncSocket writeData:chunkSize withTimeout:TIMEOUT_WRITE_HEAD tag:HTTP_CHUNKED_RESPONSE_HEADER];
810
-
811
- [asyncSocket writeData:data withTimeout:TIMEOUT_WRITE_BODY tag:HTTP_CHUNKED_RESPONSE_BODY];
812
-
813
- if ([httpResponse isDone])
814
- {
815
- NSData *footer = [self chunkedTransferFooter];
816
- [asyncSocket writeData:footer withTimeout:TIMEOUT_WRITE_HEAD tag:HTTP_RESPONSE];
817
- }
818
- else
819
- {
820
- NSData *footer = [GCDAsyncSocket CRLFData];
821
- [asyncSocket writeData:footer withTimeout:TIMEOUT_WRITE_HEAD tag:HTTP_CHUNKED_RESPONSE_FOOTER];
822
- }
823
- }
824
- else
825
- {
826
- long tag = [httpResponse isDone] ? HTTP_RESPONSE : HTTP_PARTIAL_RESPONSE_BODY;
827
- [asyncSocket writeData:data withTimeout:TIMEOUT_WRITE_BODY tag:tag];
828
- }
829
- }
830
- }
831
- else
832
- {
833
- // Client specified a byte range in request
834
-
835
- if ([ranges count] == 1)
836
- {
837
- // Client is requesting a single range
838
- DDRange range = [[ranges objectAtIndex:0] ddrangeValue];
839
-
840
- [httpResponse setOffset:range.location];
841
-
842
- NSUInteger bytesToRead = range.length < READ_CHUNKSIZE ? (NSUInteger)range.length : READ_CHUNKSIZE;
843
-
844
- NSData *data = [httpResponse readDataOfLength:bytesToRead];
845
-
846
- if ([data length] > 0)
847
- {
848
- [responseDataSizes addObject:[NSNumber numberWithUnsignedInteger:[data length]]];
849
-
850
- long tag = [data length] == range.length ? HTTP_RESPONSE : HTTP_PARTIAL_RANGE_RESPONSE_BODY;
851
- [asyncSocket writeData:data withTimeout:TIMEOUT_WRITE_BODY tag:tag];
852
- }
853
- }
854
- else
855
- {
856
- // Client is requesting multiple ranges
857
- // We have to send each range using multipart/byteranges
858
-
859
- // Write range header
860
- NSData *rangeHeaderData = [ranges_headers objectAtIndex:0];
861
- [asyncSocket writeData:rangeHeaderData withTimeout:TIMEOUT_WRITE_HEAD tag:HTTP_PARTIAL_RESPONSE_HEADER];
862
-
863
- // Start writing range body
864
- DDRange range = [[ranges objectAtIndex:0] ddrangeValue];
865
-
866
- [httpResponse setOffset:range.location];
867
-
868
- NSUInteger bytesToRead = range.length < READ_CHUNKSIZE ? (NSUInteger)range.length : READ_CHUNKSIZE;
869
-
870
- NSData *data = [httpResponse readDataOfLength:bytesToRead];
871
-
872
- if ([data length] > 0)
873
- {
874
- [responseDataSizes addObject:[NSNumber numberWithUnsignedInteger:[data length]]];
875
-
876
- [asyncSocket writeData:data withTimeout:TIMEOUT_WRITE_BODY tag:HTTP_PARTIAL_RANGES_RESPONSE_BODY];
877
- }
878
- }
879
- }
880
- }
881
-
882
- }
883
-
884
- /**
885
- * Returns the number of bytes of the http response body that are sitting in asyncSocket's write queue.
886
- *
887
- * We keep track of this information in order to keep our memory footprint low while
888
- * working with asynchronous HTTPResponse objects.
889
- **/
890
- - (NSUInteger)writeQueueSize
891
- {
892
- NSUInteger result = 0;
893
-
894
- NSUInteger i;
895
- for(i = 0; i < [responseDataSizes count]; i++)
896
- {
897
- result += [[responseDataSizes objectAtIndex:i] unsignedIntegerValue];
898
- }
899
-
900
- return result;
901
- }
902
-
903
- /**
904
- * Sends more data, if needed, without growing the write queue over its approximate size limit.
905
- * The last chunk of the response body will be sent with a tag of HTTP_RESPONSE.
906
- *
907
- * This method should only be called for standard (non-range) responses.
908
- **/
909
- - (void)continueSendingStandardResponseBody
910
- {
911
- HTTPLogTrace();
912
-
913
- // This method is called when either asyncSocket has finished writing one of the response data chunks,
914
- // or when an asynchronous HTTPResponse object informs us that it has more available data for us to send.
915
- // In the case of the asynchronous HTTPResponse, we don't want to blindly grab the new data,
916
- // and shove it onto asyncSocket's write queue.
917
- // Doing so could negatively affect the memory footprint of the application.
918
- // Instead, we always ensure that we place no more than READ_CHUNKSIZE bytes onto the write queue.
919
- //
920
- // Note that this does not affect the rate at which the HTTPResponse object may generate data.
921
- // The HTTPResponse is free to do as it pleases, and this is up to the application's developer.
922
- // If the memory footprint is a concern, the developer creating the custom HTTPResponse object may freely
923
- // use the calls to readDataOfLength as an indication to start generating more data.
924
- // This provides an easy way for the HTTPResponse object to throttle its data allocation in step with the rate
925
- // at which the socket is able to send it.
926
-
927
- NSUInteger writeQueueSize = [self writeQueueSize];
928
-
929
- if(writeQueueSize >= READ_CHUNKSIZE) return;
930
-
931
- NSUInteger available = READ_CHUNKSIZE - writeQueueSize;
932
- NSData *data = [httpResponse readDataOfLength:available];
933
-
934
- if ([data length] > 0)
935
- {
936
- [responseDataSizes addObject:[NSNumber numberWithUnsignedInteger:[data length]]];
937
-
938
- BOOL isChunked = NO;
939
-
940
- if ([httpResponse respondsToSelector:@selector(isChunked)])
941
- {
942
- isChunked = [httpResponse isChunked];
943
- }
944
-
945
- if (isChunked)
946
- {
947
- NSData *chunkSize = [self chunkedTransferSizeLineForLength:[data length]];
948
- [asyncSocket writeData:chunkSize withTimeout:TIMEOUT_WRITE_HEAD tag:HTTP_CHUNKED_RESPONSE_HEADER];
949
-
950
- [asyncSocket writeData:data withTimeout:TIMEOUT_WRITE_BODY tag:HTTP_CHUNKED_RESPONSE_BODY];
951
-
952
- if([httpResponse isDone])
953
- {
954
- NSData *footer = [self chunkedTransferFooter];
955
- [asyncSocket writeData:footer withTimeout:TIMEOUT_WRITE_HEAD tag:HTTP_RESPONSE];
956
- }
957
- else
958
- {
959
- NSData *footer = [GCDAsyncSocket CRLFData];
960
- [asyncSocket writeData:footer withTimeout:TIMEOUT_WRITE_HEAD tag:HTTP_CHUNKED_RESPONSE_FOOTER];
961
- }
962
- }
963
- else
964
- {
965
- long tag = [httpResponse isDone] ? HTTP_RESPONSE : HTTP_PARTIAL_RESPONSE_BODY;
966
- [asyncSocket writeData:data withTimeout:TIMEOUT_WRITE_BODY tag:tag];
967
- }
968
- }
969
- }
970
-
971
- /**
972
- * Sends more data, if needed, without growing the write queue over its approximate size limit.
973
- * The last chunk of the response body will be sent with a tag of HTTP_RESPONSE.
974
- *
975
- * This method should only be called for single-range responses.
976
- **/
977
- - (void)continueSendingSingleRangeResponseBody
978
- {
979
- HTTPLogTrace();
980
-
981
- // This method is called when either asyncSocket has finished writing one of the response data chunks,
982
- // or when an asynchronous response informs us that is has more available data for us to send.
983
- // In the case of the asynchronous response, we don't want to blindly grab the new data,
984
- // and shove it onto asyncSocket's write queue.
985
- // Doing so could negatively affect the memory footprint of the application.
986
- // Instead, we always ensure that we place no more than READ_CHUNKSIZE bytes onto the write queue.
987
- //
988
- // Note that this does not affect the rate at which the HTTPResponse object may generate data.
989
- // The HTTPResponse is free to do as it pleases, and this is up to the application's developer.
990
- // If the memory footprint is a concern, the developer creating the custom HTTPResponse object may freely
991
- // use the calls to readDataOfLength as an indication to start generating more data.
992
- // This provides an easy way for the HTTPResponse object to throttle its data allocation in step with the rate
993
- // at which the socket is able to send it.
994
-
995
- NSUInteger writeQueueSize = [self writeQueueSize];
996
-
997
- if(writeQueueSize >= READ_CHUNKSIZE) return;
998
-
999
- DDRange range = [[ranges objectAtIndex:0] ddrangeValue];
1000
-
1001
- UInt64 offset = [httpResponse offset];
1002
- UInt64 bytesRead = offset - range.location;
1003
- UInt64 bytesLeft = range.length - bytesRead;
1004
-
1005
- if (bytesLeft > 0)
1006
- {
1007
- NSUInteger available = READ_CHUNKSIZE - writeQueueSize;
1008
- NSUInteger bytesToRead = bytesLeft < available ? (NSUInteger)bytesLeft : available;
1009
-
1010
- NSData *data = [httpResponse readDataOfLength:bytesToRead];
1011
-
1012
- if ([data length] > 0)
1013
- {
1014
- [responseDataSizes addObject:[NSNumber numberWithUnsignedInteger:[data length]]];
1015
-
1016
- long tag = [data length] == bytesLeft ? HTTP_RESPONSE : HTTP_PARTIAL_RANGE_RESPONSE_BODY;
1017
- [asyncSocket writeData:data withTimeout:TIMEOUT_WRITE_BODY tag:tag];
1018
- }
1019
- }
1020
- }
1021
-
1022
- /**
1023
- * Sends more data, if needed, without growing the write queue over its approximate size limit.
1024
- * The last chunk of the response body will be sent with a tag of HTTP_RESPONSE.
1025
- *
1026
- * This method should only be called for multi-range responses.
1027
- **/
1028
- - (void)continueSendingMultiRangeResponseBody
1029
- {
1030
- HTTPLogTrace();
1031
-
1032
- // This method is called when either asyncSocket has finished writing one of the response data chunks,
1033
- // or when an asynchronous HTTPResponse object informs us that is has more available data for us to send.
1034
- // In the case of the asynchronous HTTPResponse, we don't want to blindly grab the new data,
1035
- // and shove it onto asyncSocket's write queue.
1036
- // Doing so could negatively affect the memory footprint of the application.
1037
- // Instead, we always ensure that we place no more than READ_CHUNKSIZE bytes onto the write queue.
1038
- //
1039
- // Note that this does not affect the rate at which the HTTPResponse object may generate data.
1040
- // The HTTPResponse is free to do as it pleases, and this is up to the application's developer.
1041
- // If the memory footprint is a concern, the developer creating the custom HTTPResponse object may freely
1042
- // use the calls to readDataOfLength as an indication to start generating more data.
1043
- // This provides an easy way for the HTTPResponse object to throttle its data allocation in step with the rate
1044
- // at which the socket is able to send it.
1045
-
1046
- NSUInteger writeQueueSize = [self writeQueueSize];
1047
-
1048
- if(writeQueueSize >= READ_CHUNKSIZE) return;
1049
-
1050
- DDRange range = [[ranges objectAtIndex:rangeIndex] ddrangeValue];
1051
-
1052
- UInt64 offset = [httpResponse offset];
1053
- UInt64 bytesRead = offset - range.location;
1054
- UInt64 bytesLeft = range.length - bytesRead;
1055
-
1056
- if (bytesLeft > 0)
1057
- {
1058
- NSUInteger available = READ_CHUNKSIZE - writeQueueSize;
1059
- NSUInteger bytesToRead = bytesLeft < available ? (NSUInteger)bytesLeft : available;
1060
-
1061
- NSData *data = [httpResponse readDataOfLength:bytesToRead];
1062
-
1063
- if ([data length] > 0)
1064
- {
1065
- [responseDataSizes addObject:[NSNumber numberWithUnsignedInteger:[data length]]];
1066
-
1067
- [asyncSocket writeData:data withTimeout:TIMEOUT_WRITE_BODY tag:HTTP_PARTIAL_RANGES_RESPONSE_BODY];
1068
- }
1069
- }
1070
- else
1071
- {
1072
- if (++rangeIndex < [ranges count])
1073
- {
1074
- // Write range header
1075
- NSData *rangeHeader = [ranges_headers objectAtIndex:rangeIndex];
1076
- [asyncSocket writeData:rangeHeader withTimeout:TIMEOUT_WRITE_HEAD tag:HTTP_PARTIAL_RESPONSE_HEADER];
1077
-
1078
- // Start writing range body
1079
- range = [[ranges objectAtIndex:rangeIndex] ddrangeValue];
1080
-
1081
- [httpResponse setOffset:range.location];
1082
-
1083
- NSUInteger available = READ_CHUNKSIZE - writeQueueSize;
1084
- NSUInteger bytesToRead = range.length < available ? (NSUInteger)range.length : available;
1085
-
1086
- NSData *data = [httpResponse readDataOfLength:bytesToRead];
1087
-
1088
- if ([data length] > 0)
1089
- {
1090
- [responseDataSizes addObject:[NSNumber numberWithUnsignedInteger:[data length]]];
1091
-
1092
- [asyncSocket writeData:data withTimeout:TIMEOUT_WRITE_BODY tag:HTTP_PARTIAL_RANGES_RESPONSE_BODY];
1093
- }
1094
- }
1095
- else
1096
- {
1097
- // We're not done yet - we still have to send the closing boundry tag
1098
- NSString *endingBoundryStr = [NSString stringWithFormat:@"\r\n--%@--\r\n", ranges_boundry];
1099
- NSData *endingBoundryData = [endingBoundryStr dataUsingEncoding:NSUTF8StringEncoding];
1100
-
1101
- [asyncSocket writeData:endingBoundryData withTimeout:TIMEOUT_WRITE_HEAD tag:HTTP_RESPONSE];
1102
- }
1103
- }
1104
- }
1105
-
1106
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1107
- #pragma mark Responses
1108
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1109
-
1110
- /**
1111
- * Returns an array of possible index pages.
1112
- * For example: {"index.html", "index.htm"}
1113
- **/
1114
- - (NSArray *)directoryIndexFileNames
1115
- {
1116
- HTTPLogTrace();
1117
-
1118
- // Override me to support other index pages.
1119
-
1120
- return [NSArray arrayWithObjects:@"index.html", @"index.htm", nil];
1121
- }
1122
-
1123
- - (NSString *)filePathForURI:(NSString *)path
1124
- {
1125
- return [self filePathForURI:path allowDirectory:NO];
1126
- }
1127
-
1128
- /**
1129
- * Converts relative URI path into full file-system path.
1130
- **/
1131
- - (NSString *)filePathForURI:(NSString *)path allowDirectory:(BOOL)allowDirectory
1132
- {
1133
- HTTPLogTrace();
1134
-
1135
- // Override me to perform custom path mapping.
1136
- // For example you may want to use a default file other than index.html, or perhaps support multiple types.
1137
-
1138
- NSString *documentRoot = [config documentRoot];
1139
-
1140
- // Part 0: Validate document root setting.
1141
- //
1142
- // If there is no configured documentRoot,
1143
- // then it makes no sense to try to return anything.
1144
-
1145
- if (documentRoot == nil)
1146
- {
1147
- HTTPLogWarn(@"%@[%p]: No configured document root", THIS_FILE, self);
1148
- return nil;
1149
- }
1150
-
1151
- // Part 1: Strip parameters from the url
1152
- //
1153
- // E.g.: /page.html?q=22&var=abc -> /page.html
1154
-
1155
- NSURL *docRoot = [NSURL fileURLWithPath:documentRoot isDirectory:YES];
1156
- if (docRoot == nil)
1157
- {
1158
- HTTPLogWarn(@"%@[%p]: Document root is invalid file path", THIS_FILE, self);
1159
- return nil;
1160
- }
1161
-
1162
- NSString *relativePath = [[NSURL URLWithString:path relativeToURL:docRoot] relativePath];
1163
-
1164
- // Part 2: Append relative path to document root (base path)
1165
- //
1166
- // E.g.: relativePath="/images/icon.png"
1167
- // documentRoot="/Users/robbie/Sites"
1168
- // fullPath="/Users/robbie/Sites/images/icon.png"
1169
- //
1170
- // We also standardize the path.
1171
- //
1172
- // E.g.: "Users/robbie/Sites/images/../index.html" -> "/Users/robbie/Sites/index.html"
1173
-
1174
- NSString *fullPath = [[documentRoot stringByAppendingPathComponent:relativePath] stringByStandardizingPath];
1175
-
1176
- if ([relativePath isEqualToString:@"/"])
1177
- {
1178
- fullPath = [fullPath stringByAppendingString:@"/"];
1179
- }
1180
-
1181
- // Part 3: Prevent serving files outside the document root.
1182
- //
1183
- // Sneaky requests may include ".." in the path.
1184
- //
1185
- // E.g.: relativePath="../Documents/TopSecret.doc"
1186
- // documentRoot="/Users/robbie/Sites"
1187
- // fullPath="/Users/robbie/Documents/TopSecret.doc"
1188
- //
1189
- // E.g.: relativePath="../Sites_Secret/TopSecret.doc"
1190
- // documentRoot="/Users/robbie/Sites"
1191
- // fullPath="/Users/robbie/Sites_Secret/TopSecret"
1192
-
1193
- if (![documentRoot hasSuffix:@"/"])
1194
- {
1195
- documentRoot = [documentRoot stringByAppendingString:@"/"];
1196
- }
1197
-
1198
- if (![fullPath hasPrefix:documentRoot])
1199
- {
1200
- HTTPLogWarn(@"%@[%p]: Request for file outside document root", THIS_FILE, self);
1201
- return nil;
1202
- }
1203
-
1204
- // Part 4: Search for index page if path is pointing to a directory
1205
- if (!allowDirectory)
1206
- {
1207
- BOOL isDir = NO;
1208
- if ([[NSFileManager defaultManager] fileExistsAtPath:fullPath isDirectory:&isDir] && isDir)
1209
- {
1210
- NSArray *indexFileNames = [self directoryIndexFileNames];
1211
-
1212
- for (NSString *indexFileName in indexFileNames)
1213
- {
1214
- NSString *indexFilePath = [fullPath stringByAppendingPathComponent:indexFileName];
1215
-
1216
- if ([[NSFileManager defaultManager] fileExistsAtPath:indexFilePath isDirectory:&isDir] && !isDir)
1217
- {
1218
- return indexFilePath;
1219
- }
1220
- }
1221
-
1222
- // No matching index files found in directory
1223
- return nil;
1224
- }
1225
- }
1226
-
1227
- return fullPath;
1228
- }
1229
-
1230
- /**
1231
- * This method is called to get a response for a request.
1232
- * You may return any object that adopts the HTTPResponse protocol.
1233
- * The HTTPServer comes with two such classes: HTTPFileResponse and HTTPDataResponse.
1234
- * HTTPFileResponse is a wrapper for an NSFileHandle object, and is the preferred way to send a file response.
1235
- * HTTPDataResponse is a wrapper for an NSData object, and may be used to send a custom response.
1236
- **/
1237
- - (NSObject<HTTPResponse> *)httpResponseForMethod:(NSString *)method URI:(NSString *)path
1238
- {
1239
- HTTPLogTrace();
1240
-
1241
- // Override me to provide custom responses.
1242
-
1243
- return nil;
1244
- }
1245
-
1246
- - (WebSocket *)webSocketForURI:(NSString *)path
1247
- {
1248
- HTTPLogTrace();
1249
-
1250
- // Override me to provide custom WebSocket responses.
1251
- // To do so, simply override the base WebSocket implementation, and add your custom functionality.
1252
- // Then return an instance of your custom WebSocket here.
1253
- //
1254
- // For example:
1255
- //
1256
- // if ([path isEqualToString:@"/myAwesomeWebSocketStream"])
1257
- // {
1258
- // return [[[MyWebSocket alloc] initWithRequest:request socket:asyncSocket] autorelease];
1259
- // }
1260
- //
1261
- // return [super webSocketForURI:path];
1262
-
1263
- return nil;
1264
- }
1265
-
1266
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1267
- #pragma mark Uploads
1268
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1269
-
1270
- /**
1271
- * This method is called after receiving all HTTP headers, but before reading any of the request body.
1272
- **/
1273
- - (void)prepareForBodyWithSize:(UInt64)contentLength
1274
- {
1275
- // Override me to allocate buffers, file handles, etc.
1276
- }
1277
-
1278
- /**
1279
- * This method is called to handle data read from a POST / PUT.
1280
- * The given data is part of the request body.
1281
- **/
1282
- - (void)processBodyData:(NSData *)postDataChunk
1283
- {
1284
- // Override me to do something useful with a POST / PUT.
1285
- // If the post is small, such as a simple form, you may want to simply append the data to the request.
1286
- // If the post is big, such as a file upload, you may want to store the file to disk.
1287
- //
1288
- // Remember: In order to support LARGE POST uploads, the data is read in chunks.
1289
- // This prevents a 50 MB upload from being stored in RAM.
1290
- // The size of the chunks are limited by the POST_CHUNKSIZE definition.
1291
- // Therefore, this method may be called multiple times for the same POST request.
1292
- }
1293
-
1294
- /**
1295
- * This method is called after the request body has been fully read but before the HTTP request is processed.
1296
- **/
1297
- - (void)finishBody
1298
- {
1299
- // Override me to perform any final operations on an upload.
1300
- // For example, if you were saving the upload to disk this would be
1301
- // the hook to flush any pending data to disk and maybe close the file.
1302
- }
1303
-
1304
- /**
1305
- * Returns the maximum request body size this connection accepts.
1306
- **/
1307
- - (UInt64)maxRequestBodySize
1308
- {
1309
- return (UInt64)-1;
1310
- }
1311
-
1312
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1313
- #pragma mark Errors
1314
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1315
-
1316
- /**
1317
- * Called if the HTML version is other than what is supported
1318
- **/
1319
- - (void)handleVersionNotSupported:(NSString *)version
1320
- {
1321
- // Override me for custom error handling of unsupported http version responses
1322
- // If you simply want to add a few extra header fields, see the preprocessErrorResponse: method.
1323
- // You can also use preprocessErrorResponse: to add an optional HTML body.
1324
-
1325
- HTTPLogWarn(@"HTTP Server: Error 505 - Version Not Supported: %@ (%@)", version, [self requestURI]);
1326
-
1327
- HTTPMessage *response = [[HTTPMessage alloc] initResponseWithStatusCode:505 description:nil version:HTTPVersion1_1];
1328
- [response setHeaderField:@"Content-Length" value:@"0"];
1329
-
1330
- NSData *responseData = [self preprocessErrorResponse:response];
1331
- [asyncSocket writeData:responseData withTimeout:TIMEOUT_WRITE_ERROR tag:HTTP_RESPONSE];
1332
-
1333
- }
1334
-
1335
- /**
1336
- * Called if the HTTP request body is larger than the configured limit.
1337
- **/
1338
- - (void)handleRequestBodyTooLarge
1339
- {
1340
- HTTPLogWarn(@"HTTP Server: Error 413 - Request Entity Too Large (%@)", [self requestURI]);
1341
-
1342
- HTTPMessage *response = [[HTTPMessage alloc] initResponseWithStatusCode:413 description:nil version:HTTPVersion1_1];
1343
- [response setHeaderField:@"Content-Length" value:@"0"];
1344
- [response setHeaderField:@"Connection" value:@"close"];
1345
-
1346
- NSData *responseData = [self preprocessErrorResponse:response];
1347
- [asyncSocket writeData:responseData withTimeout:TIMEOUT_WRITE_ERROR tag:HTTP_FINAL_RESPONSE];
1348
- }
1349
-
1350
- /**
1351
- * Called if we receive some sort of malformed HTTP request.
1352
- * The data parameter is the invalid HTTP header line, including CRLF, as read from GCDAsyncSocket.
1353
- * The data parameter may also be nil if the request as a whole was invalid, such as a POST with no Content-Length.
1354
- **/
1355
- - (void)handleInvalidRequest:(NSData *)data
1356
- {
1357
- // Override me for custom error handling of invalid HTTP requests
1358
- // If you simply want to add a few extra header fields, see the preprocessErrorResponse: method.
1359
- // You can also use preprocessErrorResponse: to add an optional HTML body.
1360
-
1361
- HTTPLogWarn(@"HTTP Server: Error 400 - Bad Request (%@)", [self requestURI]);
1362
-
1363
- // Status Code 400 - Bad Request
1364
- HTTPMessage *response = [[HTTPMessage alloc] initResponseWithStatusCode:400 description:nil version:HTTPVersion1_1];
1365
- [response setHeaderField:@"Content-Length" value:@"0"];
1366
- [response setHeaderField:@"Connection" value:@"close"];
1367
-
1368
- NSData *responseData = [self preprocessErrorResponse:response];
1369
- [asyncSocket writeData:responseData withTimeout:TIMEOUT_WRITE_ERROR tag:HTTP_FINAL_RESPONSE];
1370
-
1371
-
1372
- // Note: We used the HTTP_FINAL_RESPONSE tag to disconnect after the response is sent.
1373
- // We do this because we couldn't parse the request,
1374
- // so we won't be able to recover and move on to another request afterwards.
1375
- // In other words, we wouldn't know where the first request ends and the second request begins.
1376
- }
1377
-
1378
- /**
1379
- * Called if we receive a HTTP request with a method other than GET or HEAD.
1380
- **/
1381
- - (void)handleUnknownMethod:(NSString *)method
1382
- {
1383
- // Override me for custom error handling of 405 method not allowed responses.
1384
- // If you simply want to add a few extra header fields, see the preprocessErrorResponse: method.
1385
- // You can also use preprocessErrorResponse: to add an optional HTML body.
1386
- //
1387
- // See also: supportsMethod:atPath:
1388
-
1389
- HTTPLogWarn(@"HTTP Server: Error 405 - Method Not Allowed: %@ (%@)", method, [self requestURI]);
1390
-
1391
- // Status code 405 - Method Not Allowed
1392
- HTTPMessage *response = [[HTTPMessage alloc] initResponseWithStatusCode:405 description:nil version:HTTPVersion1_1];
1393
- [response setHeaderField:@"Content-Length" value:@"0"];
1394
- [response setHeaderField:@"Connection" value:@"close"];
1395
-
1396
- NSData *responseData = [self preprocessErrorResponse:response];
1397
- [asyncSocket writeData:responseData withTimeout:TIMEOUT_WRITE_ERROR tag:HTTP_FINAL_RESPONSE];
1398
-
1399
-
1400
- // Note: We used the HTTP_FINAL_RESPONSE tag to disconnect after the response is sent.
1401
- // We do this because the method may include an http body.
1402
- // Since we can't be sure, we should close the connection.
1403
- }
1404
-
1405
- /**
1406
- * Called if we're unable to find the requested resource.
1407
- **/
1408
- - (void)handleResourceNotFound
1409
- {
1410
- // Override me for custom error handling of 404 not found responses
1411
- // If you simply want to add a few extra header fields, see the preprocessErrorResponse: method.
1412
- // You can also use preprocessErrorResponse: to add an optional HTML body.
1413
-
1414
- HTTPLogInfo(@"HTTP Server: Error 404 - Not Found (%@)", [self requestURI]);
1415
-
1416
- // Status Code 404 - Not Found
1417
- HTTPMessage *response = [[HTTPMessage alloc] initResponseWithStatusCode:404 description:nil version:HTTPVersion1_1];
1418
- [response setHeaderField:@"Content-Length" value:@"0"];
1419
-
1420
- NSData *responseData = [self preprocessErrorResponse:response];
1421
- [asyncSocket writeData:responseData withTimeout:TIMEOUT_WRITE_ERROR tag:HTTP_RESPONSE];
1422
-
1423
- }
1424
-
1425
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1426
- #pragma mark Headers
1427
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1428
-
1429
- /**
1430
- * Gets the current date and time, formatted properly (according to RFC) for insertion into an HTTP header.
1431
- **/
1432
- - (NSString *)dateAsString:(NSDate *)date
1433
- {
1434
- // From Apple's Documentation (Data Formatting Guide -> Date Formatters -> Cache Formatters for Efficiency):
1435
- //
1436
- // "Creating a date formatter is not a cheap operation. If you are likely to use a formatter frequently,
1437
- // it is typically more efficient to cache a single instance than to create and dispose of multiple instances.
1438
- // One approach is to use a static variable."
1439
- //
1440
- // This was discovered to be true in massive form via issue #46:
1441
- //
1442
- // "Was doing some performance benchmarking using instruments and httperf. Using this single optimization
1443
- // I got a 26% speed improvement - from 1000req/sec to 3800req/sec. Not insignificant.
1444
- // The culprit? Why, NSDateFormatter, of course!"
1445
- //
1446
- // Thus, we are using a static NSDateFormatter here.
1447
-
1448
- static NSDateFormatter *df;
1449
-
1450
- static dispatch_once_t onceToken;
1451
- dispatch_once(&onceToken, ^{
1452
-
1453
- // Example: Sun, 06 Nov 1994 08:49:37 GMT
1454
-
1455
- df = [[NSDateFormatter alloc] init];
1456
- [df setFormatterBehavior:NSDateFormatterBehavior10_4];
1457
- [df setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT"]];
1458
- [df setDateFormat:@"EEE, dd MMM y HH:mm:ss 'GMT'"];
1459
- [df setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]];
1460
-
1461
- // For some reason, using zzz in the format string produces GMT+00:00
1462
- });
1463
-
1464
- return [df stringFromDate:date];
1465
- }
1466
-
1467
- /**
1468
- * This method is called immediately prior to sending the response headers.
1469
- * This method adds standard header fields, and then converts the response to an NSData object.
1470
- **/
1471
- - (NSData *)preprocessResponse:(HTTPMessage *)response
1472
- {
1473
- HTTPLogTrace();
1474
-
1475
- // Override me to customize the response headers
1476
- // You'll likely want to add your own custom headers, and then return [super preprocessResponse:response]
1477
-
1478
- // Add standard headers
1479
- NSString *now = [self dateAsString:[NSDate date]];
1480
- [response setHeaderField:@"Date" value:now];
1481
-
1482
- // Add server capability headers
1483
- [response setHeaderField:@"Accept-Ranges" value:@"bytes"];
1484
-
1485
- // Add optional response headers
1486
- if ([httpResponse respondsToSelector:@selector(httpHeaders)])
1487
- {
1488
- NSDictionary *responseHeaders = [httpResponse httpHeaders];
1489
-
1490
- NSEnumerator *keyEnumerator = [responseHeaders keyEnumerator];
1491
- NSString *key;
1492
-
1493
- while ((key = [keyEnumerator nextObject]))
1494
- {
1495
- NSString *value = [responseHeaders objectForKey:key];
1496
-
1497
- [response setHeaderField:key value:value];
1498
- }
1499
- }
1500
-
1501
- return [response messageData];
1502
- }
1503
-
1504
- /**
1505
- * This method is called immediately prior to sending the response headers (for an error).
1506
- * This method adds standard header fields, and then converts the response to an NSData object.
1507
- **/
1508
- - (NSData *)preprocessErrorResponse:(HTTPMessage *)response
1509
- {
1510
- HTTPLogTrace();
1511
-
1512
- // Override me to customize the error response headers
1513
- // You'll likely want to add your own custom headers, and then return [super preprocessErrorResponse:response]
1514
- //
1515
- // Notes:
1516
- // You can use [response statusCode] to get the type of error.
1517
- // You can use [response setBody:data] to add an optional HTML body.
1518
- // If you add a body, don't forget to update the Content-Length.
1519
- //
1520
- // if ([response statusCode] == 404)
1521
- // {
1522
- // NSString *msg = @"<html><body>Error 404 - Not Found</body></html>";
1523
- // NSData *msgData = [msg dataUsingEncoding:NSUTF8StringEncoding];
1524
- //
1525
- // [response setBody:msgData];
1526
- //
1527
- // NSString *contentLengthStr = [NSString stringWithFormat:@"%lu", (unsigned long)[msgData length]];
1528
- // [response setHeaderField:@"Content-Length" value:contentLengthStr];
1529
- // }
1530
-
1531
- // Add standard headers
1532
- NSString *now = [self dateAsString:[NSDate date]];
1533
- [response setHeaderField:@"Date" value:now];
1534
-
1535
- // Add server capability headers
1536
- [response setHeaderField:@"Accept-Ranges" value:@"bytes"];
1537
-
1538
- // Add optional response headers
1539
- if ([httpResponse respondsToSelector:@selector(httpHeaders)])
1540
- {
1541
- NSDictionary *responseHeaders = [httpResponse httpHeaders];
1542
-
1543
- NSEnumerator *keyEnumerator = [responseHeaders keyEnumerator];
1544
- NSString *key;
1545
-
1546
- while((key = [keyEnumerator nextObject]))
1547
- {
1548
- NSString *value = [responseHeaders objectForKey:key];
1549
-
1550
- [response setHeaderField:key value:value];
1551
- }
1552
- }
1553
-
1554
- return [response messageData];
1555
- }
1556
-
1557
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1558
- #pragma mark GCDAsyncSocket Delegate
1559
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1560
-
1561
- /**
1562
- * This method is called after the socket has successfully read data from the stream.
1563
- * Remember that this method will only be called after the socket reaches a CRLF, or after it's read the proper length.
1564
- **/
1565
- - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData*)data withTag:(long)tag
1566
- {
1567
- if (tag == HTTP_REQUEST_HEADER)
1568
- {
1569
- // Append the header line to the http message
1570
- BOOL result = [request appendData:data];
1571
- if (!result)
1572
- {
1573
- HTTPLogWarn(@"%@[%p]: Malformed request", THIS_FILE, self);
1574
-
1575
- [self handleInvalidRequest:data];
1576
- }
1577
- else if (![request isHeaderComplete])
1578
- {
1579
- // We don't have a complete header yet
1580
- // That is, we haven't yet received a CRLF on a line by itself, indicating the end of the header
1581
- if (++numHeaderLines > MAX_HEADER_LINES)
1582
- {
1583
- // Reached the maximum amount of header lines in a single HTTP request
1584
- // This could be an attempted DOS attack
1585
- [asyncSocket disconnect];
1586
-
1587
- // Explictly return to ensure we don't do anything after the socket disconnect
1588
- return;
1589
- }
1590
- else
1591
- {
1592
- [asyncSocket readDataToData:[GCDAsyncSocket CRLFData]
1593
- withTimeout:TIMEOUT_READ_SUBSEQUENT_HEADER_LINE
1594
- maxLength:MAX_HEADER_LINE_LENGTH
1595
- tag:HTTP_REQUEST_HEADER];
1596
- }
1597
- }
1598
- else
1599
- {
1600
- // We have an entire HTTP request header from the client
1601
-
1602
- // Extract the method (such as GET, HEAD, POST, etc)
1603
- NSString *method = [request method];
1604
-
1605
- // Extract the uri (such as "/index.html")
1606
- NSString *uri = [self requestURI];
1607
-
1608
- // Check for a Transfer-Encoding field
1609
- NSString *transferEncoding = [request headerField:@"Transfer-Encoding"];
1610
-
1611
- // Check for a Content-Length field
1612
- NSString *contentLength = [request headerField:@"Content-Length"];
1613
-
1614
- // Content-Length MUST be present for upload methods (such as POST or PUT)
1615
- // and MUST NOT be present for other methods.
1616
- BOOL expectsUpload = [self expectsRequestBodyFromMethod:method atPath:uri];
1617
-
1618
- if (expectsUpload)
1619
- {
1620
- if (transferEncoding && ![transferEncoding caseInsensitiveCompare:@"Chunked"])
1621
- {
1622
- requestContentLength = -1;
1623
- }
1624
- else
1625
- {
1626
- if (contentLength == nil)
1627
- {
1628
- HTTPLogWarn(@"%@[%p]: Method expects request body, but had no specified Content-Length",
1629
- THIS_FILE, self);
1630
-
1631
- [self handleInvalidRequest:nil];
1632
- return;
1633
- }
1634
-
1635
- if (![NSNumber parseString:(NSString *)contentLength intoUInt64:&requestContentLength])
1636
- {
1637
- HTTPLogWarn(@"%@[%p]: Unable to parse Content-Length header into a valid number",
1638
- THIS_FILE, self);
1639
-
1640
- [self handleInvalidRequest:nil];
1641
- return;
1642
- }
1643
-
1644
- if (requestContentLength > [self maxRequestBodySize])
1645
- {
1646
- HTTPLogWarn(@"%@[%p]: Request body size %llu exceeds the configured limit %llu",
1647
- THIS_FILE, self, requestContentLength, [self maxRequestBodySize]);
1648
-
1649
- [self handleRequestBodyTooLarge];
1650
- return;
1651
- }
1652
- }
1653
- }
1654
- else
1655
- {
1656
- if (contentLength != nil)
1657
- {
1658
- // Received Content-Length header for method not expecting an upload.
1659
- // This better be zero...
1660
-
1661
- if (![NSNumber parseString:(NSString *)contentLength intoUInt64:&requestContentLength])
1662
- {
1663
- HTTPLogWarn(@"%@[%p]: Unable to parse Content-Length header into a valid number",
1664
- THIS_FILE, self);
1665
-
1666
- [self handleInvalidRequest:nil];
1667
- return;
1668
- }
1669
-
1670
- if (requestContentLength > 0)
1671
- {
1672
- HTTPLogWarn(@"%@[%p]: Method not expecting request body had non-zero Content-Length",
1673
- THIS_FILE, self);
1674
-
1675
- [self handleInvalidRequest:nil];
1676
- return;
1677
- }
1678
- }
1679
-
1680
- requestContentLength = 0;
1681
- requestContentLengthReceived = 0;
1682
- }
1683
-
1684
- // Check to make sure the given method is supported
1685
- if (![self supportsMethod:method atPath:uri])
1686
- {
1687
- // The method is unsupported - either in general, or for this specific request
1688
- // Send a 405 - Method not allowed response
1689
- [self handleUnknownMethod:method];
1690
- return;
1691
- }
1692
-
1693
- if (expectsUpload)
1694
- {
1695
- // Reset the total amount of data received for the upload
1696
- requestContentLengthReceived = 0;
1697
-
1698
- // Prepare for the upload
1699
- [self prepareForBodyWithSize:requestContentLength];
1700
-
1701
- if (requestContentLength > 0)
1702
- {
1703
- // Start reading the request body
1704
- if (requestContentLength == -1)
1705
- {
1706
- // Chunked transfer
1707
-
1708
- [asyncSocket readDataToData:[GCDAsyncSocket CRLFData]
1709
- withTimeout:TIMEOUT_READ_BODY
1710
- maxLength:MAX_CHUNK_LINE_LENGTH
1711
- tag:HTTP_REQUEST_CHUNK_SIZE];
1712
- }
1713
- else
1714
- {
1715
- NSUInteger bytesToRead;
1716
- if (requestContentLength < POST_CHUNKSIZE)
1717
- bytesToRead = (NSUInteger)requestContentLength;
1718
- else
1719
- bytesToRead = POST_CHUNKSIZE;
1720
-
1721
- [asyncSocket readDataToLength:bytesToRead
1722
- withTimeout:TIMEOUT_READ_BODY
1723
- tag:HTTP_REQUEST_BODY];
1724
- }
1725
- }
1726
- else
1727
- {
1728
- // Empty upload
1729
- [self finishBody];
1730
- [self replyToHTTPRequest];
1731
- }
1732
- }
1733
- else
1734
- {
1735
- // Now we need to reply to the request
1736
- [self replyToHTTPRequest];
1737
- }
1738
- }
1739
- }
1740
- else
1741
- {
1742
- BOOL doneReadingRequest = NO;
1743
-
1744
- // A chunked message body contains a series of chunks,
1745
- // followed by a line with "0" (zero),
1746
- // followed by optional footers (just like headers),
1747
- // and a blank line.
1748
- //
1749
- // Each chunk consists of two parts:
1750
- //
1751
- // 1. A line with the size of the chunk data, in hex,
1752
- // possibly followed by a semicolon and extra parameters you can ignore (none are currently standard),
1753
- // and ending with CRLF.
1754
- // 2. The data itself, followed by CRLF.
1755
- //
1756
- // Part 1 is represented by HTTP_REQUEST_CHUNK_SIZE
1757
- // Part 2 is represented by HTTP_REQUEST_CHUNK_DATA and HTTP_REQUEST_CHUNK_TRAILER
1758
- // where the trailer is the CRLF that follows the data.
1759
- //
1760
- // The optional footers and blank line are represented by HTTP_REQUEST_CHUNK_FOOTER.
1761
-
1762
- if (tag == HTTP_REQUEST_CHUNK_SIZE)
1763
- {
1764
- // We have just read in a line with the size of the chunk data, in hex,
1765
- // possibly followed by a semicolon and extra parameters that can be ignored,
1766
- // and ending with CRLF.
1767
-
1768
- NSString *sizeLine = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
1769
-
1770
- errno = 0; // Reset errno before calling strtoull() to ensure it is always zero on success
1771
- requestChunkSize = (UInt64)strtoull([sizeLine UTF8String], NULL, 16);
1772
- requestChunkSizeReceived = 0;
1773
-
1774
- if (errno != 0)
1775
- {
1776
- HTTPLogWarn(@"%@[%p]: Method expects chunk size, but received something else", THIS_FILE, self);
1777
-
1778
- [self handleInvalidRequest:nil];
1779
- return;
1780
- }
1781
-
1782
- if (requestChunkSize > 0)
1783
- {
1784
- UInt64 maxRequestBodySize = [self maxRequestBodySize];
1785
- if (requestChunkSize > maxRequestBodySize ||
1786
- requestContentLengthReceived > maxRequestBodySize - requestChunkSize)
1787
- {
1788
- HTTPLogWarn(@"%@[%p]: Chunked request body exceeds the configured limit %llu",
1789
- THIS_FILE, self, maxRequestBodySize);
1790
-
1791
- [self handleRequestBodyTooLarge];
1792
- return;
1793
- }
1794
-
1795
- NSUInteger bytesToRead;
1796
- bytesToRead = (requestChunkSize < POST_CHUNKSIZE) ? (NSUInteger)requestChunkSize : POST_CHUNKSIZE;
1797
-
1798
- [asyncSocket readDataToLength:bytesToRead
1799
- withTimeout:TIMEOUT_READ_BODY
1800
- tag:HTTP_REQUEST_CHUNK_DATA];
1801
- }
1802
- else
1803
- {
1804
- // This is the "0" (zero) line,
1805
- // which is to be followed by optional footers (just like headers) and finally a blank line.
1806
-
1807
- [asyncSocket readDataToData:[GCDAsyncSocket CRLFData]
1808
- withTimeout:TIMEOUT_READ_BODY
1809
- maxLength:MAX_HEADER_LINE_LENGTH
1810
- tag:HTTP_REQUEST_CHUNK_FOOTER];
1811
- }
1812
-
1813
- return;
1814
- }
1815
- else if (tag == HTTP_REQUEST_CHUNK_DATA)
1816
- {
1817
- // We just read part of the actual data.
1818
-
1819
- requestContentLengthReceived += [data length];
1820
- requestChunkSizeReceived += [data length];
1821
-
1822
- [self processBodyData:data];
1823
-
1824
- UInt64 bytesLeft = requestChunkSize - requestChunkSizeReceived;
1825
- if (bytesLeft > 0)
1826
- {
1827
- NSUInteger bytesToRead = (bytesLeft < POST_CHUNKSIZE) ? (NSUInteger)bytesLeft : POST_CHUNKSIZE;
1828
-
1829
- [asyncSocket readDataToLength:bytesToRead
1830
- withTimeout:TIMEOUT_READ_BODY
1831
- tag:HTTP_REQUEST_CHUNK_DATA];
1832
- }
1833
- else
1834
- {
1835
- // We've read in all the data for this chunk.
1836
- // The data is followed by a CRLF, which we need to read (and basically ignore)
1837
-
1838
- [asyncSocket readDataToLength:2
1839
- withTimeout:TIMEOUT_READ_BODY
1840
- tag:HTTP_REQUEST_CHUNK_TRAILER];
1841
- }
1842
-
1843
- return;
1844
- }
1845
- else if (tag == HTTP_REQUEST_CHUNK_TRAILER)
1846
- {
1847
- // This should be the CRLF following the data.
1848
- // Just ensure it's a CRLF.
1849
-
1850
- if (![data isEqualToData:[GCDAsyncSocket CRLFData]])
1851
- {
1852
- HTTPLogWarn(@"%@[%p]: Method expects chunk trailer, but is missing", THIS_FILE, self);
1853
-
1854
- [self handleInvalidRequest:nil];
1855
- return;
1856
- }
1857
-
1858
- // Now continue with the next chunk
1859
-
1860
- [asyncSocket readDataToData:[GCDAsyncSocket CRLFData]
1861
- withTimeout:TIMEOUT_READ_BODY
1862
- maxLength:MAX_CHUNK_LINE_LENGTH
1863
- tag:HTTP_REQUEST_CHUNK_SIZE];
1864
-
1865
- }
1866
- else if (tag == HTTP_REQUEST_CHUNK_FOOTER)
1867
- {
1868
- if (++numHeaderLines > MAX_HEADER_LINES)
1869
- {
1870
- // Reached the maximum amount of header lines in a single HTTP request
1871
- // This could be an attempted DOS attack
1872
- [asyncSocket disconnect];
1873
-
1874
- // Explictly return to ensure we don't do anything after the socket disconnect
1875
- return;
1876
- }
1877
-
1878
- if ([data length] > 2)
1879
- {
1880
- // We read in a footer.
1881
- // In the future we may want to append these to the request.
1882
- // For now we ignore, and continue reading the footers, waiting for the final blank line.
1883
-
1884
- [asyncSocket readDataToData:[GCDAsyncSocket CRLFData]
1885
- withTimeout:TIMEOUT_READ_BODY
1886
- maxLength:MAX_HEADER_LINE_LENGTH
1887
- tag:HTTP_REQUEST_CHUNK_FOOTER];
1888
- }
1889
- else
1890
- {
1891
- doneReadingRequest = YES;
1892
- }
1893
- }
1894
- else // HTTP_REQUEST_BODY
1895
- {
1896
- // Handle a chunk of data from the POST body
1897
-
1898
- requestContentLengthReceived += [data length];
1899
- [self processBodyData:data];
1900
-
1901
- if (requestContentLengthReceived < requestContentLength)
1902
- {
1903
- // We're not done reading the post body yet...
1904
-
1905
- UInt64 bytesLeft = requestContentLength - requestContentLengthReceived;
1906
-
1907
- NSUInteger bytesToRead = bytesLeft < POST_CHUNKSIZE ? (NSUInteger)bytesLeft : POST_CHUNKSIZE;
1908
-
1909
- [asyncSocket readDataToLength:bytesToRead
1910
- withTimeout:TIMEOUT_READ_BODY
1911
- tag:HTTP_REQUEST_BODY];
1912
- }
1913
- else
1914
- {
1915
- doneReadingRequest = YES;
1916
- }
1917
- }
1918
-
1919
- // Now that the entire body has been received, we need to reply to the request
1920
-
1921
- if (doneReadingRequest)
1922
- {
1923
- [self finishBody];
1924
- [self replyToHTTPRequest];
1925
- }
1926
- }
1927
- }
1928
-
1929
- /**
1930
- * This method is called after the socket has successfully written data to the stream.
1931
- **/
1932
- - (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
1933
- {
1934
- BOOL doneSendingResponse = NO;
1935
-
1936
- if (tag == HTTP_PARTIAL_RESPONSE_BODY)
1937
- {
1938
- // Update the amount of data we have in asyncSocket's write queue
1939
- if ([responseDataSizes count] > 0) {
1940
- [responseDataSizes removeObjectAtIndex:0];
1941
- }
1942
-
1943
- // We only wrote a part of the response - there may be more
1944
- [self continueSendingStandardResponseBody];
1945
- }
1946
- else if (tag == HTTP_CHUNKED_RESPONSE_BODY)
1947
- {
1948
- // Update the amount of data we have in asyncSocket's write queue.
1949
- // This will allow asynchronous responses to continue sending more data.
1950
- if ([responseDataSizes count] > 0) {
1951
- [responseDataSizes removeObjectAtIndex:0];
1952
- }
1953
- // Don't continue sending the response yet.
1954
- // The chunked footer that was sent after the body will tell us if we have more data to send.
1955
- }
1956
- else if (tag == HTTP_CHUNKED_RESPONSE_FOOTER)
1957
- {
1958
- // Normal chunked footer indicating we have more data to send (non final footer).
1959
- [self continueSendingStandardResponseBody];
1960
- }
1961
- else if (tag == HTTP_PARTIAL_RANGE_RESPONSE_BODY)
1962
- {
1963
- // Update the amount of data we have in asyncSocket's write queue
1964
- if ([responseDataSizes count] > 0) {
1965
- [responseDataSizes removeObjectAtIndex:0];
1966
- }
1967
- // We only wrote a part of the range - there may be more
1968
- [self continueSendingSingleRangeResponseBody];
1969
- }
1970
- else if (tag == HTTP_PARTIAL_RANGES_RESPONSE_BODY)
1971
- {
1972
- // Update the amount of data we have in asyncSocket's write queue
1973
- if ([responseDataSizes count] > 0) {
1974
- [responseDataSizes removeObjectAtIndex:0];
1975
- }
1976
- // We only wrote part of the range - there may be more, or there may be more ranges
1977
- [self continueSendingMultiRangeResponseBody];
1978
- }
1979
- else if (tag == HTTP_RESPONSE || tag == HTTP_FINAL_RESPONSE)
1980
- {
1981
- // Update the amount of data we have in asyncSocket's write queue
1982
- if ([responseDataSizes count] > 0)
1983
- {
1984
- [responseDataSizes removeObjectAtIndex:0];
1985
- }
1986
-
1987
- doneSendingResponse = YES;
1988
- }
1989
-
1990
- if (doneSendingResponse)
1991
- {
1992
- // Inform the http response that we're done
1993
- if ([httpResponse respondsToSelector:@selector(connectionDidClose)])
1994
- {
1995
- [httpResponse connectionDidClose];
1996
- }
1997
-
1998
-
1999
- if (tag == HTTP_FINAL_RESPONSE)
2000
- {
2001
- // Cleanup after the last request
2002
- [self finishResponse];
2003
-
2004
- // Terminate the connection
2005
- [asyncSocket disconnect];
2006
-
2007
- // Explictly return to ensure we don't do anything after the socket disconnects
2008
- return;
2009
- }
2010
- else
2011
- {
2012
- if ([self shouldDie])
2013
- {
2014
- // Cleanup after the last request
2015
- // Note: Don't do this before calling shouldDie, as it needs the request object still.
2016
- [self finishResponse];
2017
-
2018
- // The only time we should invoke [self die] is from socketDidDisconnect,
2019
- // or if the socket gets taken over by someone else like a WebSocket.
2020
-
2021
- [asyncSocket disconnect];
2022
- }
2023
- else
2024
- {
2025
- // Cleanup after the last request
2026
- [self finishResponse];
2027
-
2028
- // Prepare for the next request
2029
-
2030
- // If this assertion fails, it likely means you overrode the
2031
- // finishBody method and forgot to call [super finishBody].
2032
- NSAssert(request == nil, @"Request not properly released in finishBody");
2033
-
2034
- request = [[HTTPMessage alloc] initEmptyRequest];
2035
-
2036
- numHeaderLines = 0;
2037
- sentResponseHeaders = NO;
2038
-
2039
- // And start listening for more requests
2040
- [self startReadingRequest];
2041
- }
2042
- }
2043
- }
2044
- }
2045
-
2046
- /**
2047
- * Sent after the socket has been disconnected.
2048
- **/
2049
- - (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
2050
- {
2051
- HTTPLogTrace();
2052
-
2053
- asyncSocket = nil;
2054
-
2055
- [self die];
2056
- }
2057
-
2058
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2059
- #pragma mark HTTPResponse Notifications
2060
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2061
-
2062
- /**
2063
- * This method may be called by asynchronous HTTPResponse objects.
2064
- * That is, HTTPResponse objects that return YES in their "- (BOOL)isAsynchronous" method.
2065
- *
2066
- * This informs us that the response object has generated more data that we may be able to send.
2067
- **/
2068
- - (void)responseHasAvailableData:(NSObject<HTTPResponse> *)sender
2069
- {
2070
- HTTPLogTrace();
2071
-
2072
- // We always dispatch this asynchronously onto our connectionQueue,
2073
- // even if the connectionQueue is the current queue.
2074
- //
2075
- // We do this to give the HTTPResponse classes the flexibility to call
2076
- // this method whenever they want, even from within a readDataOfLength method.
2077
-
2078
- dispatch_async(connectionQueue, ^{ @autoreleasepool {
2079
-
2080
- if (sender != httpResponse)
2081
- {
2082
- HTTPLogWarn(@"%@[%p]: %@ - Sender is not current httpResponse", THIS_FILE, self, THIS_METHOD);
2083
- return;
2084
- }
2085
-
2086
- if (!sentResponseHeaders)
2087
- {
2088
- [self sendResponseHeadersAndBody];
2089
- }
2090
- else
2091
- {
2092
- if (ranges == nil)
2093
- {
2094
- [self continueSendingStandardResponseBody];
2095
- }
2096
- else
2097
- {
2098
- if ([ranges count] == 1)
2099
- [self continueSendingSingleRangeResponseBody];
2100
- else
2101
- [self continueSendingMultiRangeResponseBody];
2102
- }
2103
- }
2104
- }});
2105
- }
2106
-
2107
- /**
2108
- * This method is called if the response encounters some critical error,
2109
- * and it will be unable to fullfill the request.
2110
- **/
2111
- - (void)responseDidAbort:(NSObject<HTTPResponse> *)sender
2112
- {
2113
- HTTPLogTrace();
2114
-
2115
- // We always dispatch this asynchronously onto our connectionQueue,
2116
- // even if the connectionQueue is the current queue.
2117
- //
2118
- // We do this to give the HTTPResponse classes the flexibility to call
2119
- // this method whenever they want, even from within a readDataOfLength method.
2120
-
2121
- dispatch_async(connectionQueue, ^{ @autoreleasepool {
2122
-
2123
- if (sender != httpResponse)
2124
- {
2125
- HTTPLogWarn(@"%@[%p]: %@ - Sender is not current httpResponse", THIS_FILE, self, THIS_METHOD);
2126
- return;
2127
- }
2128
-
2129
- [asyncSocket disconnectAfterWriting];
2130
- }});
2131
- }
2132
-
2133
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2134
- #pragma mark Post Request
2135
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2136
-
2137
- /**
2138
- * This method is called after each response has been fully sent.
2139
- * Since a single connection may handle multiple request/responses, this method may be called multiple times.
2140
- * That is, it will be called after completion of each response.
2141
- **/
2142
- - (void)finishResponse
2143
- {
2144
- HTTPLogTrace();
2145
-
2146
- // Override me if you want to perform any custom actions after a response has been fully sent.
2147
- // This is the place to release memory or resources associated with the last request.
2148
- //
2149
- // If you override this method, you should take care to invoke [super finishResponse] at some point.
2150
-
2151
- request = nil;
2152
-
2153
- httpResponse = nil;
2154
-
2155
- ranges = nil;
2156
- ranges_headers = nil;
2157
- ranges_boundry = nil;
2158
- }
2159
-
2160
- /**
2161
- * This method is called after each successful response has been fully sent.
2162
- * It determines whether the connection should stay open and handle another request.
2163
- **/
2164
- - (BOOL)shouldDie
2165
- {
2166
- HTTPLogTrace();
2167
-
2168
- // Override me if you have any need to force close the connection.
2169
- // You may do so by simply returning YES.
2170
- //
2171
- // If you override this method, you should take care to fall through with [super shouldDie]
2172
- // instead of returning NO.
2173
-
2174
-
2175
- BOOL shouldDie = NO;
2176
-
2177
- NSString *version = [request version];
2178
- if ([version isEqualToString:HTTPVersion1_1])
2179
- {
2180
- // HTTP version 1.1
2181
- // Connection should only be closed if request included "Connection: close" header
2182
-
2183
- NSString *connection = [request headerField:@"Connection"];
2184
-
2185
- shouldDie = (connection && ([connection caseInsensitiveCompare:@"close"] == NSOrderedSame));
2186
- }
2187
- else if ([version isEqualToString:HTTPVersion1_0])
2188
- {
2189
- // HTTP version 1.0
2190
- // Connection should be closed unless request included "Connection: Keep-Alive" header
2191
-
2192
- NSString *connection = [request headerField:@"Connection"];
2193
-
2194
- if (connection == nil)
2195
- shouldDie = YES;
2196
- else
2197
- shouldDie = [connection caseInsensitiveCompare:@"Keep-Alive"] != NSOrderedSame;
2198
- }
2199
-
2200
- return shouldDie;
2201
- }
2202
-
2203
- - (void)die
2204
- {
2205
- HTTPLogTrace();
2206
-
2207
- // Override me if you want to perform any custom actions when a connection is closed.
2208
- // Then call [super die] when you're done.
2209
- //
2210
- // See also the finishResponse method.
2211
- //
2212
- // Important: There is a rare timing condition where this method might get invoked twice.
2213
- // If you override this method, you should be prepared for this situation.
2214
-
2215
- // Inform the http response that we're done
2216
- if ([httpResponse respondsToSelector:@selector(connectionDidClose)])
2217
- {
2218
- [httpResponse connectionDidClose];
2219
- }
2220
-
2221
- // Release the http response so we don't call it's connectionDidClose method again in our dealloc method
2222
- httpResponse = nil;
2223
-
2224
- // Post notification of dead connection
2225
- // This will allow our server to release us from its array of connections
2226
- [[NSNotificationCenter defaultCenter] postNotificationName:HTTPConnectionDidDieNotification object:self];
2227
- }
2228
-
2229
- @end
2230
-
2231
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2232
- #pragma mark -
2233
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2234
-
2235
- @implementation HTTPConfig
2236
-
2237
- @synthesize server;
2238
- @synthesize documentRoot;
2239
- @synthesize queue;
2240
-
2241
- - (id)initWithServer:(HTTPServer *)aServer documentRoot:(NSString *)aDocumentRoot
2242
- {
2243
- if ((self = [super init]))
2244
- {
2245
- server = aServer;
2246
- documentRoot = aDocumentRoot;
2247
- }
2248
- return self;
2249
- }
2250
-
2251
- - (id)initWithServer:(HTTPServer *)aServer documentRoot:(NSString *)aDocumentRoot queue:(dispatch_queue_t)q
2252
- {
2253
- if ((self = [super init]))
2254
- {
2255
- server = aServer;
2256
-
2257
- documentRoot = [aDocumentRoot stringByStandardizingPath];
2258
- if ([documentRoot hasSuffix:@"/"])
2259
- {
2260
- documentRoot = [documentRoot stringByAppendingString:@"/"];
2261
- }
2262
-
2263
- if (q)
2264
- {
2265
- queue = q;
2266
- #if !OS_OBJECT_USE_OBJC
2267
- dispatch_retain(queue);
2268
- #endif
2269
- }
2270
- }
2271
- return self;
2272
- }
2273
-
2274
- - (void)dealloc
2275
- {
2276
- #if !OS_OBJECT_USE_OBJC
2277
- if (queue) dispatch_release(queue);
2278
- #endif
2279
- }
2280
-
2281
- @end