GCDWebServerConnection.m 35 KB

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