Differences From Artifact [e03c4b06ca]:
- File
src/OFHTTPServer.m
— part of check-in
[3b43d51006]
at
2020-01-14 00:16:04
on branch trunk
— More consistent -[close] behavior
This means refusing to close twice, calling -[close] from -[dealloc] and
not calling -[cancelAsyncRequests].Calling -[cancelAsyncRequests] in -[close] is too dangerous, as -[close]
gets called by -[dealloc]: If the queue is the last reference to the
object, at the point where -[cancelAsyncRequests] removes it from the
queue, the object will start to deallocate and call into
-[cancelAsyncRequests] again, which is still in the middle of removing
it and now finds itself with an inconsistent state. (user: js, size: 20441) [annotate] [blame] [check-ins using] [more...]
To Artifact [9e1f23cc19]:
- File src/OFHTTPServer.m — part of check-in [13a8f43898] at 2020-04-26 18:10:31 on branch trunk — Move accept and listen OF{TCP -> Stream}Socket (user: js, size: 21408) [annotate] [blame] [check-ins using] [more...]
︙ | ︙ | |||
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | #import "OFTLSSocket.h" #import "OFThread.h" #import "OFTimer.h" #import "OFURL.h" #import "OFAlreadyConnectedException.h" #import "OFInvalidArgumentException.h" #import "OFInvalidFormatException.h" #import "OFNotOpenException.h" #import "OFOutOfMemoryException.h" #import "OFOutOfRangeException.h" #import "OFUnsupportedProtocolException.h" #import "OFWriteFailedException.h" #import "socket_helpers.h" #define BUFFER_SIZE 1024 /* * FIXME: Key normalization replaces headers like "DNT" with "Dnt". * FIXME: Errors are not reported to the user. */ @interface OFHTTPServer () <OFTCPSocketDelegate> @end @interface OFHTTPServerResponse: OFHTTPResponse <OFReadyForWritingObserving> { | > > | | | | | > | | | > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | #import "OFTLSSocket.h" #import "OFThread.h" #import "OFTimer.h" #import "OFURL.h" #import "OFAlreadyConnectedException.h" #import "OFInvalidArgumentException.h" #import "OFInvalidEncodingException.h" #import "OFInvalidFormatException.h" #import "OFNotOpenException.h" #import "OFOutOfMemoryException.h" #import "OFOutOfRangeException.h" #import "OFTruncatedDataException.h" #import "OFUnsupportedProtocolException.h" #import "OFWriteFailedException.h" #import "socket_helpers.h" #define BUFFER_SIZE 1024 /* * FIXME: Key normalization replaces headers like "DNT" with "Dnt". * FIXME: Errors are not reported to the user. */ @interface OFHTTPServer () <OFTCPSocketDelegate> @end @interface OFHTTPServerResponse: OFHTTPResponse <OFReadyForWritingObserving> { OFStreamSocket *_socket; OFHTTPServer *_server; OFHTTPRequest *_request; bool _chunked, _headersSent; } - (instancetype)initWithSocket: (OFStreamSocket *)sock server: (OFHTTPServer *)server request: (OFHTTPRequest *)request; @end @interface OFHTTPServerConnection: OFObject <OFTCPSocketDelegate> { @public OFStreamSocket *_socket; OFHTTPServer *_server; OFTimer *_timer; enum { AWAITING_PROLOG, PARSING_HEADERS, SEND_RESPONSE } _state; uint8_t _HTTPMinorVersion; of_http_request_method_t _method; OFString *_host, *_path; uint16_t _port; OFMutableDictionary *_headers; size_t _contentLength; OFStream *_requestBody; } - (instancetype)initWithSocket: (OFStreamSocket *)sock server: (OFHTTPServer *)server; - (bool)parseProlog: (OFString *)line; - (bool)parseHeaders: (OFString *)line; - (bool)sendErrorAndClose: (short)statusCode; - (void)createResponse; @end @interface OFHTTPServerRequestBodyStream: OFStream <OFReadyForReadingObserving> { OFStreamSocket *_socket; bool _chunked; intmax_t _toRead; bool _atEndOfStream, _setAtEndOfStream; } - (instancetype)initWithSocket: (OFStreamSocket *)sock chunked: (bool)chunked contentLength: (uintmax_t)contentLength; @end #ifdef OF_HAVE_THREADS @interface OFHTTPServerThread: OFThread - (void)stop; @end #endif static OF_INLINE OFString * normalizedKey(OFString *key) { char *cString = of_strdup(key.UTF8String); unsigned char *tmp = (unsigned char *)cString; bool firstLetter = true; |
︙ | ︙ | |||
229 230 231 232 233 234 235 | } return [OFString stringWithUTF8StringNoCopy: cString freeWhenDone: true]; } @implementation OFHTTPServerResponse | | | 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | } return [OFString stringWithUTF8StringNoCopy: cString freeWhenDone: true]; } @implementation OFHTTPServerResponse - (instancetype)initWithSocket: (OFStreamSocket *)sock server: (OFHTTPServer *)server request: (OFHTTPRequest *)request { self = [super init]; _statusCode = 500; _socket = [sock retain]; |
︙ | ︙ | |||
261 262 263 264 265 266 267 | - (void)of_sendHeaders { void *pool = objc_autoreleasePoolPush(); OFMutableDictionary OF_GENERIC(OFString *, OFString *) *headers; OFEnumerator *keyEnumerator, *valueEnumerator; OFString *key, *value; | | | | 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | - (void)of_sendHeaders { void *pool = objc_autoreleasePoolPush(); OFMutableDictionary OF_GENERIC(OFString *, OFString *) *headers; OFEnumerator *keyEnumerator, *valueEnumerator; OFString *key, *value; [_socket writeFormat: @"HTTP/%@ %d %@\r\n", self.protocolVersionString, _statusCode, of_http_status_code_to_string(_statusCode)]; headers = [[_headers mutableCopy] autorelease]; if ([headers objectForKey: @"Date"] == nil) { OFString *date = [[OFDate date] dateStringWithFormat: @"%a, %d %b %Y %H:%M:%S GMT"]; |
︙ | ︙ | |||
316 317 318 319 320 321 322 | [self of_sendHeaders]; if (!_chunked) return [_socket writeBuffer: buffer length: length]; pool = objc_autoreleasePoolPush(); | | | < | < | 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | [self of_sendHeaders]; if (!_chunked) return [_socket writeBuffer: buffer length: length]; pool = objc_autoreleasePoolPush(); [_socket writeString: [OFString stringWithFormat: @"%zX\r\n", length]]; objc_autoreleasePoolPop(pool); [_socket writeBuffer: buffer length: length]; [_socket writeString: @"\r\n"]; return length; } - (void)close { if (_socket == nil) @throw [OFNotOpenException exceptionWithObject: self]; @try { if (!_headersSent) [self of_sendHeaders]; if (_chunked) [_socket writeString: @"0\r\n\r\n"]; } @catch (OFWriteFailedException *e) { id <OFHTTPServerDelegate> delegate = _server.delegate; if ([delegate respondsToSelector: @selector(server: didReceiveExceptionForResponse:request:exception:)]) [delegate server: _server didReceiveExceptionForResponse: self |
︙ | ︙ | |||
366 367 368 369 370 371 372 | return -1; return _socket.fileDescriptorForWriting; } @end @implementation OFHTTPServerConnection | | | 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 | return -1; return _socket.fileDescriptorForWriting; } @end @implementation OFHTTPServerConnection - (instancetype)initWithSocket: (OFStreamSocket *)sock server: (OFHTTPServer *)server { self = [super init]; @try { _socket = [sock retain]; _server = [server retain]; |
︙ | ︙ | |||
457 458 459 460 461 462 463 | pos = [line rangeOfString: @" "].location; if (pos == OF_NOT_FOUND) return [self sendErrorAndClose: 400]; method = [line substringWithRange: of_range(0, pos)]; @try { | | | | 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 | pos = [line rangeOfString: @" "].location; if (pos == OF_NOT_FOUND) return [self sendErrorAndClose: 400]; method = [line substringWithRange: of_range(0, pos)]; @try { _method = of_http_request_method_from_string(method); } @catch (OFInvalidArgumentException *e) { return [self sendErrorAndClose: 405]; } @try { of_range_t range = of_range(pos + 1, line.length - pos - 10); path = [[[line substringWithRange: |
︙ | ︙ | |||
490 491 492 493 494 495 496 | - (bool)parseHeaders: (OFString *)line { OFString *key, *value, *old; size_t pos; if (line.length == 0) { | > > | > > | < | > < | > > > | 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 | - (bool)parseHeaders: (OFString *)line { OFString *key, *value, *old; size_t pos; if (line.length == 0) { bool chunked = [[_headers objectForKey: @"Transfer-Encoding"] isEqual: @"chunked"]; OFString *contentLengthString = [_headers objectForKey: @"Content-Length"]; intmax_t contentLength = 0; if (contentLengthString != nil) { if (chunked || contentLengthString.length == 0) return [self sendErrorAndClose: 400]; @try { contentLength = contentLengthString.decimalValue; } @catch (OFInvalidFormatException *e) { return [self sendErrorAndClose: 400]; } if (contentLength < 0) return [self sendErrorAndClose: 400]; } if (chunked || contentLengthString != nil) { [_requestBody release]; _requestBody = nil; _requestBody = [[OFHTTPServerRequestBodyStream alloc] initWithSocket: _socket chunked: chunked contentLength: contentLength]; [_timer invalidate]; [_timer release]; _timer = nil; } |
︙ | ︙ | |||
580 581 582 583 584 585 586 | } - (bool)sendErrorAndClose: (short)statusCode { OFString *date = [[OFDate date] dateStringWithFormat: @"%a, %d %b %Y %H:%M:%S GMT"]; | | | > | 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 | } - (bool)sendErrorAndClose: (short)statusCode { OFString *date = [[OFDate date] dateStringWithFormat: @"%a, %d %b %Y %H:%M:%S GMT"]; [_socket writeFormat: @"HTTP/1.1 %d %@\r\n" @"Date: %@\r\n" @"Server: %@\r\n" @"\r\n", statusCode, of_http_status_code_to_string(statusCode), date, _server.name]; return false; } - (void)createResponse { |
︙ | ︙ | |||
655 656 657 658 659 660 661 | response: response]; objc_autoreleasePoolPop(pool); } @end @implementation OFHTTPServerRequestBodyStream | | > > > > > | 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 | response: response]; objc_autoreleasePoolPop(pool); } @end @implementation OFHTTPServerRequestBodyStream - (instancetype)initWithSocket: (OFStreamSocket *)sock chunked: (bool)chunked contentLength: (uintmax_t)contentLength { self = [super init]; @try { _socket = [sock retain]; _chunked = chunked; _toRead = contentLength; if (_chunked && _toRead > 0) @throw [OFInvalidArgumentException exception]; } @catch (id e) { [self release]; @throw e; } return self; } |
︙ | ︙ | |||
687 688 689 690 691 692 693 | { return _atEndOfStream; } - (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length { | | > < | | > > > > > > | | | | | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 | { return _atEndOfStream; } - (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length { if (_socket == nil) @throw [OFNotOpenException exceptionWithObject: self]; if (_atEndOfStream) return 0; if (_socket.atEndOfStream) @throw [OFTruncatedDataException exception]; /* Content-Length */ if (!_chunked) { size_t ret; if (length > (uintmax_t)_toRead) length = (size_t)_toRead; ret = [_socket readIntoBuffer: buffer length: length]; _toRead -= ret; if (_toRead == 0) _atEndOfStream = true; return ret; } /* Chunked */ if (_toRead == -2) { char tmp[2]; switch ([_socket readIntoBuffer: tmp length: 2]) { case 2: _toRead++; if (tmp[1] != '\n') @throw [OFInvalidFormatException exception]; case 1: _toRead++; if (tmp[0] != '\r') @throw [OFInvalidFormatException exception]; } if (_setAtEndOfStream && _toRead == 0) _atEndOfStream = true; return 0; } else if (_toRead == -1) { char tmp; if ([_socket readIntoBuffer: &tmp length: 1] == 1) { _toRead++; if (tmp != '\n') @throw [OFInvalidFormatException exception]; } if (_setAtEndOfStream && _toRead == 0) _atEndOfStream = true; return 0; } else if (_toRead > 0) { if (length > (uintmax_t)_toRead) length = (size_t)_toRead; length = [_socket readIntoBuffer: buffer length: length]; _toRead -= length; if (_toRead == 0) _toRead = -2; return length; } else { void *pool = objc_autoreleasePoolPush(); OFString *line; of_range_t range; @try { line = [_socket tryReadLine]; } @catch (OFInvalidEncodingException *e) { @throw [OFInvalidFormatException exception]; } if (line == nil) return 0; range = [line rangeOfString: @";"]; if (range.location != OF_NOT_FOUND) line = [line substringWithRange: of_range(0, range.location)]; if (line.length < 1) { /* * We have read the empty string because the socket is * at end of stream. */ if (_socket.atEndOfStream && range.location == OF_NOT_FOUND) @throw [OFTruncatedDataException exception]; else @throw [OFInvalidFormatException exception]; } if ((_toRead = line.hexadecimalValue) < 0) @throw [OFOutOfRangeException exception]; if (_toRead == 0) { _setAtEndOfStream = true; _toRead = -2; } objc_autoreleasePoolPop(pool); return 0; } } - (bool)hasDataInReadBuffer { return (super.hasDataInReadBuffer || _socket.hasDataInReadBuffer); } |
︙ | ︙ | |||
946 947 948 949 950 951 952 | [thread stop]; [_threadPool release]; _threadPool = nil; #endif } | | | | | 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 | [thread stop]; [_threadPool release]; _threadPool = nil; #endif } - (void)of_handleAcceptedSocket: (OFStreamSocket *)acceptedSocket { OFHTTPServerConnection *connection = [[[OFHTTPServerConnection alloc] initWithSocket: acceptedSocket server: self] autorelease]; acceptedSocket.delegate = connection; [acceptedSocket asyncReadLine]; } - (bool)socket: (OFStreamSocket *)sock didAcceptSocket: (OFStreamSocket *)acceptedSocket exception: (id)exception { if (exception != nil) { if (![_delegate respondsToSelector: @selector(server:didReceiveExceptionOnListeningSocket:)]) return false; |
︙ | ︙ |