1/*
2 Copyright (c) 2012-2019, Pierre-Olivier Latour
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * The name of Pierre-Olivier Latour may not be used to endorse
13 or promote products derived from this software without specific
14 prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#if !__has_feature(objc_arc)
29#error GCDWebServer requires ARC
30#endif
31
32#import <TargetConditionals.h>
33#import <netdb.h>
34#ifdef __GCDWEBSERVER_ENABLE_TESTING__
35#import <libkern/OSAtomic.h>
36#endif
37
38#import "GCDWebServerPrivate.h"
39
40#define kHeadersReadCapacity (1 * 1024)
41#define kBodyReadCapacity (256 * 1024)
42
43typedef void (^ReadDataCompletionBlock)(BOOL success);
44typedef void (^ReadHeadersCompletionBlock)(NSData* extraData);
45typedef void (^ReadBodyCompletionBlock)(BOOL success);
46
47typedef void (^WriteDataCompletionBlock)(BOOL success);
48typedef void (^WriteHeadersCompletionBlock)(BOOL success);
49typedef void (^WriteBodyCompletionBlock)(BOOL success);
50
51static NSData* _CRLFData = nil;
52static NSData* _CRLFCRLFData = nil;
53static NSData* _continueData = nil;
54static NSData* _lastChunkData = nil;
55static NSString* _digestAuthenticationNonce = nil;
56#ifdef __GCDWEBSERVER_ENABLE_TESTING__
57static int32_t _connectionCounter = 0;
58#endif
59
60NS_ASSUME_NONNULL_BEGIN
61
62@interface GCDWebServerConnection (Read)
63- (void)readData:(NSMutableData*)data withLength:(NSUInteger)length completionBlock:(ReadDataCompletionBlock)block;
64- (void)readHeaders:(NSMutableData*)headersData withCompletionBlock:(ReadHeadersCompletionBlock)block;
65- (void)readBodyWithRemainingLength:(NSUInteger)length completionBlock:(ReadBodyCompletionBlock)block;
66- (void)readNextBodyChunk:(NSMutableData*)chunkData completionBlock:(ReadBodyCompletionBlock)block;
67@end
68
69@interface GCDWebServerConnection (Write)
70- (void)writeData:(NSData*)data withCompletionBlock:(WriteDataCompletionBlock)block;
71- (void)writeHeadersWithCompletionBlock:(WriteHeadersCompletionBlock)block;
72- (void)writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block;
73@end
74
75NS_ASSUME_NONNULL_END
76
77@implementation GCDWebServerConnection {
78  CFSocketNativeHandle _socket;
79  BOOL _virtualHEAD;
80
81  CFHTTPMessageRef _requestMessage;
82  GCDWebServerRequest* _request;
83  GCDWebServerHandler* _handler;
84  CFHTTPMessageRef _responseMessage;
85  GCDWebServerResponse* _response;
86  NSInteger _statusCode;
87
88  BOOL _opened;
89#ifdef __GCDWEBSERVER_ENABLE_TESTING__
90  NSUInteger _connectionIndex;
91  NSString* _requestPath;
92  int _requestFD;
93  NSString* _responsePath;
94  int _responseFD;
95#endif
96}
97
98+ (void)initialize {
99  if (_CRLFData == nil) {
100    _CRLFData = [[NSData alloc] initWithBytes:"\r\n" length:2];
101    GWS_DCHECK(_CRLFData);
102  }
103  if (_CRLFCRLFData == nil) {
104    _CRLFCRLFData = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4];
105    GWS_DCHECK(_CRLFCRLFData);
106  }
107  if (_continueData == nil) {
108    CFHTTPMessageRef message = CFHTTPMessageCreateResponse(kCFAllocatorDefault, 100, NULL, kCFHTTPVersion1_1);
109    _continueData = CFBridgingRelease(CFHTTPMessageCopySerializedMessage(message));
110    CFRelease(message);
111    GWS_DCHECK(_continueData);
112  }
113  if (_lastChunkData == nil) {
114    _lastChunkData = [[NSData alloc] initWithBytes:"0\r\n\r\n" length:5];
115  }
116  if (_digestAuthenticationNonce == nil) {
117    CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
118    _digestAuthenticationNonce = GCDWebServerComputeMD5Digest(@"%@", CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, uuid)));
119    CFRelease(uuid);
120  }
121}
122
123- (BOOL)isUsingIPv6 {
124  const struct sockaddr* localSockAddr = _localAddressData.bytes;
125  return (localSockAddr->sa_family == AF_INET6);
126}
127
128- (void)_initializeResponseHeadersWithStatusCode:(NSInteger)statusCode {
129  _statusCode = statusCode;
130  _responseMessage = CFHTTPMessageCreateResponse(kCFAllocatorDefault, statusCode, NULL, kCFHTTPVersion1_1);
131  CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Connection"), CFSTR("Close"));
132  CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Server"), (__bridge CFStringRef)_server.serverName);
133  CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Date"), (__bridge CFStringRef)GCDWebServerFormatRFC822([NSDate date]));
134}
135
136- (void)_startProcessingRequest {
137  GWS_DCHECK(_responseMessage == NULL);
138
139  GCDWebServerResponse* preflightResponse = [self preflightRequest:_request];
140  if (preflightResponse) {
141    [self _finishProcessingRequest:preflightResponse];
142  } else {
143    [self processRequest:_request
144              completion:^(GCDWebServerResponse* processResponse) {
145                [self _finishProcessingRequest:processResponse];
146              }];
147  }
148}
149
150// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
151- (void)_finishProcessingRequest:(GCDWebServerResponse*)response {
152  GWS_DCHECK(_responseMessage == NULL);
153  BOOL hasBody = NO;
154
155  if (response) {
156    response = [self overrideResponse:response forRequest:_request];
157  }
158  if (response) {
159    if ([response hasBody]) {
160      [response prepareForReading];
161      hasBody = !_virtualHEAD;
162    }
163    NSError* error = nil;
164    if (hasBody && ![response performOpen:&error]) {
165      GWS_LOG_ERROR(@"Failed opening response body for socket %i: %@", _socket, error);
166    } else {
167      _response = response;
168    }
169  }
170
171  if (_response) {
172    [self _initializeResponseHeadersWithStatusCode:_response.statusCode];
173    if (_response.lastModifiedDate) {
174      CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Last-Modified"), (__bridge CFStringRef)GCDWebServerFormatRFC822((NSDate*)_response.lastModifiedDate));
175    }
176    if (_response.eTag) {
177      CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("ETag"), (__bridge CFStringRef)_response.eTag);
178    }
179    if ((_response.statusCode >= 200) && (_response.statusCode < 300)) {
180      if (_response.cacheControlMaxAge > 0) {
181        CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Cache-Control"), (__bridge CFStringRef)[NSString stringWithFormat:@"max-age=%i, public", (int)_response.cacheControlMaxAge]);
182      } else {
183        CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Cache-Control"), CFSTR("no-cache"));
184      }
185    }
186    if (_response.contentType != nil) {
187      CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Content-Type"), (__bridge CFStringRef)GCDWebServerNormalizeHeaderValue(_response.contentType));
188    }
189    if (_response.contentLength != NSUIntegerMax) {
190      CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Content-Length"), (__bridge CFStringRef)[NSString stringWithFormat:@"%lu", (unsigned long)_response.contentLength]);
191    }
192    if (_response.usesChunkedTransferEncoding) {
193      CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Transfer-Encoding"), CFSTR("chunked"));
194    }
195    [_response.additionalHeaders enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL* stop) {
196      CFHTTPMessageSetHeaderFieldValue(self->_responseMessage, (__bridge CFStringRef)key, (__bridge CFStringRef)obj);
197    }];
198    [self writeHeadersWithCompletionBlock:^(BOOL success) {
199      if (success) {
200        if (hasBody) {
201          [self writeBodyWithCompletionBlock:^(BOOL successInner) {
202            [self->_response performClose];  // TODO: There's nothing we can do on failure as headers have already been sent
203          }];
204        }
205      } else if (hasBody) {
206        [self->_response performClose];
207      }
208    }];
209  } else {
210    [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
211  }
212}
213
214- (void)_readBodyWithLength:(NSUInteger)length initialData:(NSData*)initialData {
215  NSError* error = nil;
216  if (![_request performOpen:&error]) {
217    GWS_LOG_ERROR(@"Failed opening request body for socket %i: %@", _socket, error);
218    [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
219    return;
220  }
221
222  if (initialData.length) {
223    if (![_request performWriteData:initialData error:&error]) {
224      GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error);
225      if (![_request performClose:&error]) {
226        GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error);
227      }
228      [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
229      return;
230    }
231    length -= initialData.length;
232  }
233
234  if (length) {
235    [self readBodyWithRemainingLength:length
236                      completionBlock:^(BOOL success) {
237                        NSError* localError = nil;
238                        if ([self->_request performClose:&localError]) {
239                          [self _startProcessingRequest];
240                        } else {
241                          GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", self->_socket, error);
242                          [self abortRequest:self->_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
243                        }
244                      }];
245  } else {
246    if ([_request performClose:&error]) {
247      [self _startProcessingRequest];
248    } else {
249      GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error);
250      [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
251    }
252  }
253}
254
255- (void)_readChunkedBodyWithInitialData:(NSData*)initialData {
256  NSError* error = nil;
257  if (![_request performOpen:&error]) {
258    GWS_LOG_ERROR(@"Failed opening request body for socket %i: %@", _socket, error);
259    [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
260    return;
261  }
262
263  NSMutableData* chunkData = [[NSMutableData alloc] initWithData:initialData];
264  [self readNextBodyChunk:chunkData
265          completionBlock:^(BOOL success) {
266            NSError* localError = nil;
267            if ([self->_request performClose:&localError]) {
268              [self _startProcessingRequest];
269            } else {
270              GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", self->_socket, error);
271              [self abortRequest:self->_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
272            }
273          }];
274}
275
276- (void)_readRequestHeaders {
277  _requestMessage = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, true);
278  NSMutableData* headersData = [[NSMutableData alloc] initWithCapacity:kHeadersReadCapacity];
279  [self readHeaders:headersData
280      withCompletionBlock:^(NSData* extraData) {
281        if (extraData) {
282          NSString* requestMethod = CFBridgingRelease(CFHTTPMessageCopyRequestMethod(self->_requestMessage));  // Method verbs are case-sensitive and uppercase
283          if (self->_server.shouldAutomaticallyMapHEADToGET && [requestMethod isEqualToString:@"HEAD"]) {
284            requestMethod = @"GET";
285            self->_virtualHEAD = YES;
286          }
287          NSDictionary* requestHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(self->_requestMessage));  // Header names are case-insensitive but CFHTTPMessageCopyAllHeaderFields() will standardize the common ones
288          NSURL* requestURL = CFBridgingRelease(CFHTTPMessageCopyRequestURL(self->_requestMessage));
289          if (requestURL) {
290            requestURL = [self rewriteRequestURL:requestURL withMethod:requestMethod headers:requestHeaders];
291            GWS_DCHECK(requestURL);
292          }
293          NSString* urlPath = requestURL ? CFBridgingRelease(CFURLCopyPath((CFURLRef)requestURL)) : nil;  // Don't use -[NSURL path] which strips the ending slash
294          if (urlPath == nil) {
295            urlPath = @"/";  // CFURLCopyPath() returns NULL for a relative URL with path "//" contrary to -[NSURL path] which returns "/"
296          }
297          NSString* requestPath = urlPath ? GCDWebServerUnescapeURLString(urlPath) : nil;
298          NSString* queryString = requestURL ? CFBridgingRelease(CFURLCopyQueryString((CFURLRef)requestURL, NULL)) : nil;  // Don't use -[NSURL query] to make sure query is not unescaped;
299          NSDictionary* requestQuery = queryString ? GCDWebServerParseURLEncodedForm(queryString) : @{};
300          if (requestMethod && requestURL && requestHeaders && requestPath && requestQuery) {
301            for (self->_handler in self->_server.handlers) {
302              self->_request = self->_handler.matchBlock(requestMethod, requestURL, requestHeaders, requestPath, requestQuery);
303              if (self->_request) {
304                break;
305              }
306            }
307            if (self->_request) {
308              self->_request.localAddressData = self.localAddressData;
309              self->_request.remoteAddressData = self.remoteAddressData;
310              if ([self->_request hasBody]) {
311                [self->_request prepareForWriting];
312                if (self->_request.usesChunkedTransferEncoding || (extraData.length <= self->_request.contentLength)) {
313                  NSString* expectHeader = [requestHeaders objectForKey:@"Expect"];
314                  if (expectHeader) {
315                    if ([expectHeader caseInsensitiveCompare:@"100-continue"] == NSOrderedSame) {  // TODO: Actually validate request before continuing
316                      [self writeData:_continueData
317                          withCompletionBlock:^(BOOL success) {
318                            if (success) {
319                              if (self->_request.usesChunkedTransferEncoding) {
320                                [self _readChunkedBodyWithInitialData:extraData];
321                              } else {
322                                [self _readBodyWithLength:self->_request.contentLength initialData:extraData];
323                              }
324                            }
325                          }];
326                    } else {
327                      GWS_LOG_ERROR(@"Unsupported 'Expect' / 'Content-Length' header combination on socket %i", self->_socket);
328                      [self abortRequest:self->_request withStatusCode:kGCDWebServerHTTPStatusCode_ExpectationFailed];
329                    }
330                  } else {
331                    if (self->_request.usesChunkedTransferEncoding) {
332                      [self _readChunkedBodyWithInitialData:extraData];
333                    } else {
334                      [self _readBodyWithLength:self->_request.contentLength initialData:extraData];
335                    }
336                  }
337                } else {
338                  GWS_LOG_ERROR(@"Unexpected 'Content-Length' header value on socket %i", self->_socket);
339                  [self abortRequest:self->_request withStatusCode:kGCDWebServerHTTPStatusCode_BadRequest];
340                }
341              } else {
342                [self _startProcessingRequest];
343              }
344            } else {
345              self->_request = [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:requestPath query:requestQuery];
346              GWS_DCHECK(self->_request);
347              [self abortRequest:self->_request withStatusCode:kGCDWebServerHTTPStatusCode_NotImplemented];
348            }
349          } else {
350            [self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
351            GWS_DNOT_REACHED();
352          }
353        } else {
354          [self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
355        }
356      }];
357}
358
359- (instancetype)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket {
360  if ((self = [super init])) {
361    _server = server;
362    _localAddressData = localAddress;
363    _remoteAddressData = remoteAddress;
364    _socket = socket;
365    GWS_LOG_DEBUG(@"Did open connection on socket %i", _socket);
366
367    [_server willStartConnection:self];
368
369    if (![self open]) {
370      close(_socket);
371      return nil;
372    }
373    _opened = YES;
374
375    [self _readRequestHeaders];
376  }
377  return self;
378}
379
380- (NSString*)localAddressString {
381  return GCDWebServerStringFromSockAddr(_localAddressData.bytes, YES);
382}
383
384- (NSString*)remoteAddressString {
385  return GCDWebServerStringFromSockAddr(_remoteAddressData.bytes, YES);
386}
387
388- (void)dealloc {
389  int result = close(_socket);
390  if (result != 0) {
391    GWS_LOG_ERROR(@"Failed closing socket %i for connection: %s (%i)", _socket, strerror(errno), errno);
392  } else {
393    GWS_LOG_DEBUG(@"Did close connection on socket %i", _socket);
394  }
395
396  if (_opened) {
397    [self close];
398  }
399
400  [_server didEndConnection:self];
401
402  if (_requestMessage) {
403    CFRelease(_requestMessage);
404  }
405
406  if (_responseMessage) {
407    CFRelease(_responseMessage);
408  }
409}
410
411@end
412
413@implementation GCDWebServerConnection (Read)
414
415- (void)readData:(NSMutableData*)data withLength:(NSUInteger)length completionBlock:(ReadDataCompletionBlock)block {
416  dispatch_read(_socket, length, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(dispatch_data_t buffer, int error) {
417    @autoreleasepool {
418      if (error == 0) {
419        size_t size = dispatch_data_get_size(buffer);
420        if (size > 0) {
421          NSUInteger originalLength = data.length;
422          dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) {
423            [data appendBytes:chunkBytes length:chunkSize];
424            return true;
425          });
426          [self didReadBytes:((char*)data.bytes + originalLength) length:(data.length - originalLength)];
427          block(YES);
428        } else {
429          if (self->_totalBytesRead > 0) {
430            GWS_LOG_ERROR(@"No more data available on socket %i", self->_socket);
431          } else {
432            GWS_LOG_WARNING(@"No data received from socket %i", self->_socket);
433          }
434          block(NO);
435        }
436      } else {
437        GWS_LOG_ERROR(@"Error while reading from socket %i: %s (%i)", self->_socket, strerror(error), error);
438        block(NO);
439      }
440    }
441  });
442}
443
444- (void)readHeaders:(NSMutableData*)headersData withCompletionBlock:(ReadHeadersCompletionBlock)block {
445  GWS_DCHECK(_requestMessage);
446  [self readData:headersData
447           withLength:NSUIntegerMax
448      completionBlock:^(BOOL success) {
449        if (success) {
450          NSRange range = [headersData rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(0, headersData.length)];
451          if (range.location == NSNotFound) {
452            [self readHeaders:headersData withCompletionBlock:block];
453          } else {
454            NSUInteger length = range.location + range.length;
455            if (CFHTTPMessageAppendBytes(self->_requestMessage, headersData.bytes, length)) {
456              if (CFHTTPMessageIsHeaderComplete(self->_requestMessage)) {
457                block([headersData subdataWithRange:NSMakeRange(length, headersData.length - length)]);
458              } else {
459                GWS_LOG_ERROR(@"Failed parsing request headers from socket %i", self->_socket);
460                block(nil);
461              }
462            } else {
463              GWS_LOG_ERROR(@"Failed appending request headers data from socket %i", self->_socket);
464              block(nil);
465            }
466          }
467        } else {
468          block(nil);
469        }
470      }];
471}
472
473- (void)readBodyWithRemainingLength:(NSUInteger)length completionBlock:(ReadBodyCompletionBlock)block {
474  GWS_DCHECK([_request hasBody] && ![_request usesChunkedTransferEncoding]);
475  NSMutableData* bodyData = [[NSMutableData alloc] initWithCapacity:kBodyReadCapacity];
476  [self readData:bodyData
477           withLength:length
478      completionBlock:^(BOOL success) {
479        if (success) {
480          if (bodyData.length <= length) {
481            NSError* error = nil;
482            if ([self->_request performWriteData:bodyData error:&error]) {
483              NSUInteger remainingLength = length - bodyData.length;
484              if (remainingLength) {
485                [self readBodyWithRemainingLength:remainingLength completionBlock:block];
486              } else {
487                block(YES);
488              }
489            } else {
490              GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", self->_socket, error);
491              block(NO);
492            }
493          } else {
494            GWS_LOG_ERROR(@"Unexpected extra content reading request body on socket %i", self->_socket);
495            block(NO);
496            GWS_DNOT_REACHED();
497          }
498        } else {
499          block(NO);
500        }
501      }];
502}
503
504static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
505  char buffer[size + 1];
506  bcopy(bytes, buffer, size);
507  buffer[size] = 0;
508  char* end = NULL;
509  long result = strtol(buffer, &end, 16);
510  return ((end != NULL) && (*end == 0) && (result >= 0) ? result : NSNotFound);
511}
512
513- (void)readNextBodyChunk:(NSMutableData*)chunkData completionBlock:(ReadBodyCompletionBlock)block {
514  GWS_DCHECK([_request hasBody] && [_request usesChunkedTransferEncoding]);
515
516  while (1) {
517    NSRange range = [chunkData rangeOfData:_CRLFData options:0 range:NSMakeRange(0, chunkData.length)];
518    if (range.location == NSNotFound) {
519      break;
520    }
521    NSRange extensionRange = [chunkData rangeOfData:[NSData dataWithBytes:";" length:1] options:0 range:NSMakeRange(0, range.location)];  // Ignore chunk extensions
522    NSUInteger length = _ScanHexNumber((char*)chunkData.bytes, extensionRange.location != NSNotFound ? extensionRange.location : range.location);
523    if (length != NSNotFound) {
524      if (length) {
525        if (chunkData.length < range.location + range.length + length + 2) {
526          break;
527        }
528        const char* ptr = (char*)chunkData.bytes + range.location + range.length + length;
529        if ((*ptr == '\r') && (*(ptr + 1) == '\n')) {
530          NSError* error = nil;
531          if ([_request performWriteData:[chunkData subdataWithRange:NSMakeRange(range.location + range.length, length)] error:&error]) {
532            [chunkData replaceBytesInRange:NSMakeRange(0, range.location + range.length + length + 2) withBytes:NULL length:0];
533          } else {
534            GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error);
535            block(NO);
536            return;
537          }
538        } else {
539          GWS_LOG_ERROR(@"Missing terminating CRLF sequence for chunk reading request body on socket %i", _socket);
540          block(NO);
541          return;
542        }
543      } else {
544        NSRange trailerRange = [chunkData rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(range.location, chunkData.length - range.location)];  // Ignore trailers
545        if (trailerRange.location != NSNotFound) {
546          block(YES);
547          return;
548        }
549      }
550    } else {
551      GWS_LOG_ERROR(@"Invalid chunk length reading request body on socket %i", _socket);
552      block(NO);
553      return;
554    }
555  }
556
557  [self readData:chunkData
558           withLength:NSUIntegerMax
559      completionBlock:^(BOOL success) {
560        if (success) {
561          [self readNextBodyChunk:chunkData completionBlock:block];
562        } else {
563          block(NO);
564        }
565      }];
566}
567
568@end
569
570@implementation GCDWebServerConnection (Write)
571
572- (void)writeData:(NSData*)data withCompletionBlock:(WriteDataCompletionBlock)block {
573  dispatch_data_t buffer = dispatch_data_create(data.bytes, data.length, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^{
574    [data self];  // Keeps ARC from releasing data too early
575  });
576  dispatch_write(_socket, buffer, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(dispatch_data_t remainingData, int error) {
577    @autoreleasepool {
578      if (error == 0) {
579        GWS_DCHECK(remainingData == NULL);
580        [self didWriteBytes:data.bytes length:data.length];
581        block(YES);
582      } else {
583        GWS_LOG_ERROR(@"Error while writing to socket %i: %s (%i)", self->_socket, strerror(error), error);
584        block(NO);
585      }
586    }
587  });
588#if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE
589  dispatch_release(buffer);
590#endif
591}
592
593- (void)writeHeadersWithCompletionBlock:(WriteHeadersCompletionBlock)block {
594  GWS_DCHECK(_responseMessage);
595  CFDataRef data = CFHTTPMessageCopySerializedMessage(_responseMessage);
596  [self writeData:(__bridge NSData*)data withCompletionBlock:block];
597  CFRelease(data);
598}
599
600- (void)writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block {
601  GWS_DCHECK([_response hasBody]);
602  [_response performReadDataWithCompletion:^(NSData* data, NSError* error) {
603    if (data) {
604      if (data.length) {
605        if (self->_response.usesChunkedTransferEncoding) {
606          const char* hexString = [[NSString stringWithFormat:@"%lx", (unsigned long)data.length] UTF8String];
607          size_t hexLength = strlen(hexString);
608          NSData* chunk = [NSMutableData dataWithLength:(hexLength + 2 + data.length + 2)];
609          if (chunk == nil) {
610            GWS_LOG_ERROR(@"Failed allocating memory for response body chunk for socket %i: %@", self->_socket, error);
611            block(NO);
612            return;
613          }
614          char* ptr = (char*)[(NSMutableData*)chunk mutableBytes];
615          bcopy(hexString, ptr, hexLength);
616          ptr += hexLength;
617          *ptr++ = '\r';
618          *ptr++ = '\n';
619          bcopy(data.bytes, ptr, data.length);
620          ptr += data.length;
621          *ptr++ = '\r';
622          *ptr = '\n';
623          data = chunk;
624        }
625        [self writeData:data
626            withCompletionBlock:^(BOOL success) {
627              if (success) {
628                [self writeBodyWithCompletionBlock:block];
629              } else {
630                block(NO);
631              }
632            }];
633      } else {
634        if (self->_response.usesChunkedTransferEncoding) {
635          [self writeData:_lastChunkData
636              withCompletionBlock:^(BOOL success) {
637                block(success);
638              }];
639        } else {
640          block(YES);
641        }
642      }
643    } else {
644      GWS_LOG_ERROR(@"Failed reading response body for socket %i: %@", self->_socket, error);
645      block(NO);
646    }
647  }];
648}
649
650@end
651
652@implementation GCDWebServerConnection (Subclassing)
653
654- (BOOL)open {
655#ifdef __GCDWEBSERVER_ENABLE_TESTING__
656  if (_server.recordingEnabled) {
657#pragma clang diagnostic push
658#pragma clang diagnostic ignored "-Wdeprecated-declarations"
659    _connectionIndex = OSAtomicIncrement32(&_connectionCounter);
660#pragma clang diagnostic pop
661
662    _requestPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]];
663    _requestFD = open([_requestPath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
664    GWS_DCHECK(_requestFD > 0);
665
666    _responsePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]];
667    _responseFD = open([_responsePath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
668    GWS_DCHECK(_responseFD > 0);
669  }
670#endif
671
672  return YES;
673}
674
675- (void)didReadBytes:(const void*)bytes length:(NSUInteger)length {
676  GWS_LOG_DEBUG(@"Connection received %lu bytes on socket %i", (unsigned long)length, _socket);
677  _totalBytesRead += length;
678
679#ifdef __GCDWEBSERVER_ENABLE_TESTING__
680  if ((_requestFD > 0) && (write(_requestFD, bytes, length) != (ssize_t)length)) {
681    GWS_LOG_ERROR(@"Failed recording request data: %s (%i)", strerror(errno), errno);
682    close(_requestFD);
683    _requestFD = 0;
684  }
685#endif
686}
687
688- (void)didWriteBytes:(const void*)bytes length:(NSUInteger)length {
689  GWS_LOG_DEBUG(@"Connection sent %lu bytes on socket %i", (unsigned long)length, _socket);
690  _totalBytesWritten += length;
691
692#ifdef __GCDWEBSERVER_ENABLE_TESTING__
693  if ((_responseFD > 0) && (write(_responseFD, bytes, length) != (ssize_t)length)) {
694    GWS_LOG_ERROR(@"Failed recording response data: %s (%i)", strerror(errno), errno);
695    close(_responseFD);
696    _responseFD = 0;
697  }
698#endif
699}
700
701- (NSURL*)rewriteRequestURL:(NSURL*)url withMethod:(NSString*)method headers:(NSDictionary<NSString*, NSString*>*)headers {
702  return url;
703}
704
705// https://tools.ietf.org/html/rfc2617
706- (GCDWebServerResponse*)preflightRequest:(GCDWebServerRequest*)request {
707  GWS_LOG_DEBUG(@"Connection on socket %i preflighting request \"%@ %@\" with %lu bytes body", _socket, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_totalBytesRead);
708  GCDWebServerResponse* response = nil;
709  if (_server.authenticationBasicAccounts) {
710    __block BOOL authenticated = NO;
711    NSString* authorizationHeader = [request.headers objectForKey:@"Authorization"];
712    if ([authorizationHeader hasPrefix:@"Basic "]) {
713      NSString* basicAccount = [authorizationHeader substringFromIndex:6];
714      [_server.authenticationBasicAccounts enumerateKeysAndObjectsUsingBlock:^(NSString* username, NSString* digest, BOOL* stop) {
715        if ([basicAccount isEqualToString:digest]) {
716          authenticated = YES;
717          *stop = YES;
718        }
719      }];
720    }
721    if (!authenticated) {
722      response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_Unauthorized];
723      [response setValue:[NSString stringWithFormat:@"Basic realm=\"%@\"", _server.authenticationRealm] forAdditionalHeader:@"WWW-Authenticate"];
724    }
725  } else if (_server.authenticationDigestAccounts) {
726    BOOL authenticated = NO;
727    BOOL isStaled = NO;
728    NSString* authorizationHeader = [request.headers objectForKey:@"Authorization"];
729    if ([authorizationHeader hasPrefix:@"Digest "]) {
730      NSString* realm = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"realm");
731      if (realm && [_server.authenticationRealm isEqualToString:realm]) {
732        NSString* nonce = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"nonce");
733        if ([nonce isEqualToString:_digestAuthenticationNonce]) {
734          NSString* username = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"username");
735          NSString* uri = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"uri");
736          NSString* actualResponse = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"response");
737          NSString* ha1 = [_server.authenticationDigestAccounts objectForKey:username];
738          NSString* ha2 = GCDWebServerComputeMD5Digest(@"%@:%@", request.method, uri);  // We cannot use "request.path" as the query string is required
739          NSString* expectedResponse = GCDWebServerComputeMD5Digest(@"%@:%@:%@", ha1, _digestAuthenticationNonce, ha2);
740          if ([actualResponse isEqualToString:expectedResponse]) {
741            authenticated = YES;
742          }
743        } else if (nonce.length) {
744          isStaled = YES;
745        }
746      }
747    }
748    if (!authenticated) {
749      response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_Unauthorized];
750      [response setValue:[NSString stringWithFormat:@"Digest realm=\"%@\", nonce=\"%@\"%@", _server.authenticationRealm, _digestAuthenticationNonce, isStaled ? @", stale=TRUE" : @""] forAdditionalHeader:@"WWW-Authenticate"];  // TODO: Support Quality of Protection ("qop")
751    }
752  }
753  return response;
754}
755
756- (void)processRequest:(GCDWebServerRequest*)request completion:(GCDWebServerCompletionBlock)completion {
757  GWS_LOG_DEBUG(@"Connection on socket %i processing request \"%@ %@\" with %lu bytes body", _socket, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_totalBytesRead);
758  _handler.asyncProcessBlock(request, [completion copy]);
759}
760
761// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25
762// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26
763static inline BOOL _CompareResources(NSString* responseETag, NSString* requestETag, NSDate* responseLastModified, NSDate* requestLastModified) {
764  if (requestLastModified && responseLastModified) {
765    if ([responseLastModified compare:requestLastModified] != NSOrderedDescending) {
766      return YES;
767    }
768  }
769  if (requestETag && responseETag) {  // Per the specs "If-None-Match" must be checked after "If-Modified-Since"
770    if ([requestETag isEqualToString:@"*"]) {
771      return YES;
772    }
773    if ([responseETag isEqualToString:requestETag]) {
774      return YES;
775    }
776  }
777  return NO;
778}
779
780- (GCDWebServerResponse*)overrideResponse:(GCDWebServerResponse*)response forRequest:(GCDWebServerRequest*)request {
781  if ((response.statusCode >= 200) && (response.statusCode < 300) && _CompareResources(response.eTag, request.ifNoneMatch, response.lastModifiedDate, request.ifModifiedSince)) {
782    NSInteger code = [request.method isEqualToString:@"HEAD"] || [request.method isEqualToString:@"GET"] ? kGCDWebServerHTTPStatusCode_NotModified : kGCDWebServerHTTPStatusCode_PreconditionFailed;
783    GCDWebServerResponse* newResponse = [GCDWebServerResponse responseWithStatusCode:code];
784    newResponse.cacheControlMaxAge = response.cacheControlMaxAge;
785    newResponse.lastModifiedDate = response.lastModifiedDate;
786    newResponse.eTag = response.eTag;
787    GWS_DCHECK(newResponse);
788    return newResponse;
789  }
790  return response;
791}
792
793- (void)abortRequest:(GCDWebServerRequest*)request withStatusCode:(NSInteger)statusCode {
794  GWS_DCHECK(_responseMessage == NULL);
795  GWS_DCHECK((statusCode >= 400) && (statusCode < 600));
796  [self _initializeResponseHeadersWithStatusCode:statusCode];
797  [self writeHeadersWithCompletionBlock:^(BOOL success) {
798    ;  // Nothing more to do
799  }];
800  GWS_LOG_DEBUG(@"Connection aborted with status code %i on socket %i", (int)statusCode, _socket);
801}
802
803- (void)close {
804#ifdef __GCDWEBSERVER_ENABLE_TESTING__
805  if (_requestPath) {
806    BOOL success = NO;
807    NSError* error = nil;
808    if (_requestFD > 0) {
809      close(_requestFD);
810      NSString* name = [NSString stringWithFormat:@"%03lu-%@.request", (unsigned long)_connectionIndex, _virtualHEAD ? @"HEAD" : _request.method];
811      success = [[NSFileManager defaultManager] moveItemAtPath:_requestPath toPath:[[[NSFileManager defaultManager] currentDirectoryPath] stringByAppendingPathComponent:name] error:&error];
812    }
813    if (!success) {
814      GWS_LOG_ERROR(@"Failed saving recorded request: %@", error);
815      GWS_DNOT_REACHED();
816    }
817    unlink([_requestPath fileSystemRepresentation]);
818  }
819
820  if (_responsePath) {
821    BOOL success = NO;
822    NSError* error = nil;
823    if (_responseFD > 0) {
824      close(_responseFD);
825      NSString* name = [NSString stringWithFormat:@"%03lu-%i.response", (unsigned long)_connectionIndex, (int)_statusCode];
826      success = [[NSFileManager defaultManager] moveItemAtPath:_responsePath toPath:[[[NSFileManager defaultManager] currentDirectoryPath] stringByAppendingPathComponent:name] error:&error];
827    }
828    if (!success) {
829      GWS_LOG_ERROR(@"Failed saving recorded response: %@", error);
830      GWS_DNOT_REACHED();
831    }
832    unlink([_responsePath fileSystemRepresentation]);
833  }
834#endif
835
836  if (_request) {
837    GWS_LOG_VERBOSE(@"[%@] %@ %i \"%@ %@\" (%lu | %lu)", self.localAddressString, self.remoteAddressString, (int)_statusCode, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_totalBytesRead, (unsigned long)_totalBytesWritten);
838  } else {
839    GWS_LOG_VERBOSE(@"[%@] %@ %i \"(invalid request)\" (%lu | %lu)", self.localAddressString, self.remoteAddressString, (int)_statusCode, (unsigned long)_totalBytesRead, (unsigned long)_totalBytesWritten);
840  }
841}
842
843@end
844