Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 class _HttpRequestResponseBase { | 5 class _HttpRequestResponseBase { |
| 6 _HttpRequestResponseBase(_HttpConnectionBase this._httpConnection) | 6 _HttpRequestResponseBase(_HttpConnectionBase this._httpConnection) |
| 7 : _contentLength = -1, | 7 : _contentLength = -1, |
| 8 _keepAlive = false, | 8 _keepAlive = false, |
| 9 _headers = new Map(); | 9 _headers = new Map(); |
| 10 | 10 |
| 11 int get contentLength() => _contentLength; | 11 int get contentLength() => _contentLength; |
| 12 bool get keepAlive() => _keepAlive; | 12 bool get keepAlive() => _keepAlive; |
| 13 Map get headers() => _headers; | 13 Map get headers() => _headers; |
| 14 | 14 |
| 15 void _setHeader(String name, String value) { | 15 void _setHeader(String name, String value) { |
| 16 _headers[name.toLowerCase()] = value; | 16 _headers[name.toLowerCase()] = value; |
| 17 } | 17 } |
| 18 | 18 |
| 19 bool _write(List<int> data, bool copyBuffer) { | 19 bool _write(List<int> data, bool copyBuffer) { |
| 20 bool allWritten = true; | 20 bool allWritten = true; |
| 21 if (data.length > 0) { | 21 if (data.length > 0) { |
| 22 if (_contentLength < 0) { | 22 if (_contentLength < 0) { |
| 23 // Write chunk size if transfer encoding is chunked. | 23 // Write chunk size if transfer encoding is chunked. |
| 24 _writeHexString(data.length); | 24 _writeHexString(data.length); |
| 25 _writeCRLF(); | 25 _writeCRLF(); |
| 26 _httpConnection.outputStream.write(data, copyBuffer); | 26 _httpConnection._write(data, copyBuffer); |
| 27 allWritten = _writeCRLF(); | 27 allWritten = _writeCRLF(); |
| 28 } else { | 28 } else { |
| 29 allWritten = _httpConnection.outputStream.write(data, copyBuffer); | 29 allWritten = _httpConnection._write(data, copyBuffer); |
| 30 } | 30 } |
| 31 } | 31 } |
| 32 return allWritten; | 32 return allWritten; |
| 33 } | 33 } |
| 34 | 34 |
| 35 bool _writeList(List<int> data, int offset, int count) { | 35 bool _writeList(List<int> data, int offset, int count) { |
| 36 bool allWritten = true; | 36 bool allWritten = true; |
| 37 if (count > 0) { | 37 if (count > 0) { |
| 38 if (_contentLength < 0) { | 38 if (_contentLength < 0) { |
| 39 // Write chunk size if transfer encoding is chunked. | 39 // Write chunk size if transfer encoding is chunked. |
| 40 _writeHexString(count); | 40 _writeHexString(count); |
| 41 _writeCRLF(); | 41 _writeCRLF(); |
| 42 _httpConnection.outputStream.writeFrom(data, offset, count); | 42 _httpConnection._writeFrom(data, offset, count); |
| 43 allWritten = _writeCRLF(); | 43 allWritten = _writeCRLF(); |
| 44 } else { | 44 } else { |
| 45 allWritten = _httpConnection.outputStream.writeFrom(data, offset, count) ; | 45 allWritten = _httpConnection._writeFrom(data, offset, count); |
| 46 } | 46 } |
| 47 } | 47 } |
| 48 return allWritten; | 48 return allWritten; |
| 49 } | 49 } |
| 50 | 50 |
| 51 bool _writeDone() { | 51 bool _writeDone() { |
| 52 bool allWritten = true; | 52 bool allWritten = true; |
| 53 if (_contentLength < 0) { | 53 if (_contentLength < 0) { |
| 54 // Terminate the content if transfer encoding is chunked. | 54 // Terminate the content if transfer encoding is chunked. |
| 55 allWritten = _httpConnection.outputStream.write(_Const.END_CHUNKED); | 55 allWritten = _httpConnection._write(_Const.END_CHUNKED); |
| 56 } | 56 } |
| 57 return allWritten; | 57 return allWritten; |
| 58 } | 58 } |
| 59 | 59 |
| 60 bool _writeHeaders() { | 60 bool _writeHeaders() { |
| 61 List<int> data; | 61 List<int> data; |
| 62 | 62 |
| 63 // Format headers. | 63 // Format headers. |
| 64 _headers.forEach((String name, String value) { | 64 _headers.forEach((String name, String value) { |
| 65 data = name.charCodes(); | 65 data = name.charCodes(); |
| 66 _httpConnection.outputStream.write(data); | 66 _httpConnection._write(data); |
| 67 data = ": ".charCodes(); | 67 data = ": ".charCodes(); |
| 68 _httpConnection.outputStream.write(data); | 68 _httpConnection._write(data); |
| 69 data = value.charCodes(); | 69 data = value.charCodes(); |
| 70 _httpConnection.outputStream.write(data); | 70 _httpConnection._write(data); |
| 71 _writeCRLF(); | 71 _writeCRLF(); |
| 72 }); | 72 }); |
| 73 // Terminate header. | 73 // Terminate header. |
| 74 return _writeCRLF(); | 74 return _writeCRLF(); |
| 75 } | 75 } |
| 76 | 76 |
| 77 bool _writeHexString(int x) { | 77 bool _writeHexString(int x) { |
| 78 final List<int> hexDigits = [0x30, 0x31, 0x32, 0x33, 0x34, | 78 final List<int> hexDigits = [0x30, 0x31, 0x32, 0x33, 0x34, |
| 79 0x35, 0x36, 0x37, 0x38, 0x39, | 79 0x35, 0x36, 0x37, 0x38, 0x39, |
| 80 0x41, 0x42, 0x43, 0x44, 0x45, 0x46]; | 80 0x41, 0x42, 0x43, 0x44, 0x45, 0x46]; |
| 81 ByteArray hex = new ByteArray(10); | 81 ByteArray hex = new ByteArray(10); |
| 82 int index = hex.length; | 82 int index = hex.length; |
| 83 while (x > 0) { | 83 while (x > 0) { |
| 84 index--; | 84 index--; |
| 85 hex[index] = hexDigits[x % 16]; | 85 hex[index] = hexDigits[x % 16]; |
| 86 x = x >> 4; | 86 x = x >> 4; |
| 87 } | 87 } |
| 88 return _httpConnection.outputStream.writeFrom(hex, index, hex.length - index ); | 88 return _httpConnection._writeFrom(hex, index, hex.length - index); |
| 89 } | 89 } |
| 90 | 90 |
| 91 bool _writeCRLF() { | 91 bool _writeCRLF() { |
| 92 final CRLF = const [_CharCode.CR, _CharCode.LF]; | 92 final CRLF = const [_CharCode.CR, _CharCode.LF]; |
| 93 return _httpConnection.outputStream.write(CRLF); | 93 return _httpConnection._write(CRLF); |
| 94 } | 94 } |
| 95 | 95 |
| 96 bool _writeSP() { | 96 bool _writeSP() { |
| 97 final SP = const [_CharCode.SP]; | 97 final SP = const [_CharCode.SP]; |
| 98 return _httpConnection.outputStream.write(SP); | 98 return _httpConnection._write(SP); |
| 99 } | 99 } |
| 100 | 100 |
| 101 _HttpConnectionBase _httpConnection; | 101 _HttpConnectionBase _httpConnection; |
| 102 Map<String, String> _headers; | 102 Map<String, String> _headers; |
| 103 | 103 |
| 104 // Length of the content body. If this is set to -1 (default value) | 104 // Length of the content body. If this is set to -1 (default value) |
| 105 // when starting to send data chunked transfer encoding will be | 105 // when starting to send data chunked transfer encoding will be |
| 106 // used. | 106 // used. |
| 107 int _contentLength; | 107 int _contentLength; |
| 108 bool _keepAlive; | 108 bool _keepAlive; |
| (...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 266 } | 266 } |
| 267 | 267 |
| 268 bool _streamWriteFrom(List<int> buffer, int offset, int len) { | 268 bool _streamWriteFrom(List<int> buffer, int offset, int len) { |
| 269 return _writeList(buffer, offset, len); | 269 return _writeList(buffer, offset, len); |
| 270 } | 270 } |
| 271 | 271 |
| 272 void _streamClose() { | 272 void _streamClose() { |
| 273 _httpConnection._phase = _HttpConnectionBase.PHASE_IDLE; | 273 _httpConnection._phase = _HttpConnectionBase.PHASE_IDLE; |
| 274 _state = DONE; | 274 _state = DONE; |
| 275 // Stop tracking no pending write events. | 275 // Stop tracking no pending write events. |
| 276 _httpConnection.outputStream.onNoPendingWrites = null; | 276 _httpConnection._onNoPendingWrites = null; |
| 277 // Ensure that any trailing data is written. | 277 // Ensure that any trailing data is written. |
| 278 _writeDone(); | 278 _writeDone(); |
| 279 // If the connection is closing then close the output stream to | 279 // If the connection is closing then close the output stream to |
| 280 // fully close the socket. | 280 // fully close the socket. |
| 281 if (_httpConnection._closing) { | 281 if (_httpConnection._closing) { |
| 282 _httpConnection.outputStream.close(); | 282 _httpConnection._close(); |
| 283 } | 283 } |
| 284 } | 284 } |
| 285 | 285 |
| 286 void _streamSetNoPendingWriteHandler(callback()) { | 286 void _streamSetNoPendingWriteHandler(callback()) { |
| 287 if (_state != DONE) { | 287 if (_state != DONE) { |
| 288 _httpConnection.outputStream.onNoPendingWrites = callback; | 288 _httpConnection._onNoPendingWrites = callback; |
| 289 } | 289 } |
| 290 } | 290 } |
| 291 | 291 |
| 292 void _streamSetCloseHandler(callback()) { | 292 void _streamSetCloseHandler(callback()) { |
| 293 // TODO(sgjesse): Handle this. | 293 // TODO(sgjesse): Handle this. |
| 294 } | 294 } |
| 295 | 295 |
| 296 void _streamSetErrorHandler(callback(Exception e)) { | 296 void _streamSetErrorHandler(callback(Exception e)) { |
| 297 _streamErrorHandler = callback; | 297 _streamErrorHandler = callback; |
| 298 } | 298 } |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 347 case HttpStatus.SERVICE_UNAVAILABLE: return "Service Unavailable"; | 347 case HttpStatus.SERVICE_UNAVAILABLE: return "Service Unavailable"; |
| 348 case HttpStatus.GATEWAY_TIMEOUT: return "Gateway Time-out"; | 348 case HttpStatus.GATEWAY_TIMEOUT: return "Gateway Time-out"; |
| 349 case HttpStatus.HTTP_VERSION_NOT_SUPPORTED: | 349 case HttpStatus.HTTP_VERSION_NOT_SUPPORTED: |
| 350 return "Http Version not supported"; | 350 return "Http Version not supported"; |
| 351 default: return "Status $statusCode"; | 351 default: return "Status $statusCode"; |
| 352 } | 352 } |
| 353 } | 353 } |
| 354 | 354 |
| 355 bool _writeHeader() { | 355 bool _writeHeader() { |
| 356 List<int> data; | 356 List<int> data; |
| 357 OutputStream stream = _httpConnection.outputStream; | |
| 358 | 357 |
| 359 // Write status line. | 358 // Write status line. |
| 360 stream.write(_Const.HTTP11); | 359 _httpConnection._write(_Const.HTTP11); |
| 361 _writeSP(); | 360 _writeSP(); |
| 362 data = _statusCode.toString().charCodes(); | 361 data = _statusCode.toString().charCodes(); |
| 363 stream.write(data); | 362 _httpConnection._write(data); |
| 364 _writeSP(); | 363 _writeSP(); |
| 365 data = reasonPhrase.charCodes(); | 364 data = reasonPhrase.charCodes(); |
| 366 stream.write(data); | 365 _httpConnection._write(data); |
| 367 _writeCRLF(); | 366 _writeCRLF(); |
| 368 | 367 |
| 369 // Determine the value of the "Connection" header | 368 // Determine the value of the "Connection" header |
| 370 // based on the keep alive state. | 369 // based on the keep alive state. |
| 371 setHeader("Connection", keepAlive ? "keep-alive" : "close"); | 370 setHeader("Connection", keepAlive ? "keep-alive" : "close"); |
| 372 // Determine the value of the "Transfer-Encoding" header based on | 371 // Determine the value of the "Transfer-Encoding" header based on |
| 373 // whether the content length is known. | 372 // whether the content length is known. |
| 374 if (_contentLength >= 0) { | 373 if (_contentLength >= 0) { |
| 375 setHeader("Content-Length", _contentLength.toString()); | 374 setHeader("Content-Length", _contentLength.toString()); |
| 376 } else { | 375 } else { |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 479 _httpParser = new _HttpParser(); | 478 _httpParser = new _HttpParser(); |
| 480 | 479 |
| 481 void _connectionEstablished(Socket socket) { | 480 void _connectionEstablished(Socket socket) { |
| 482 _socket = socket; | 481 _socket = socket; |
| 483 // Register handler for socket events. | 482 // Register handler for socket events. |
| 484 _socket.onData = _onData; | 483 _socket.onData = _onData; |
| 485 _socket.onClosed = _onClosed; | 484 _socket.onClosed = _onClosed; |
| 486 _socket.onError = _onError; | 485 _socket.onError = _onError; |
| 487 } | 486 } |
| 488 | 487 |
| 489 OutputStream get outputStream() { | 488 bool _write(List<int> data, [bool copyBuffer = false]) { |
| 490 return _socket.outputStream; | 489 if (!_error) { |
| 490 return _socket.outputStream.write(data, copyBuffer); | |
| 491 } | |
| 492 } | |
| 493 | |
| 494 bool _writeFrom(List<int> buffer, [int offset, int len]) { | |
| 495 if (!_error) { | |
| 496 return _socket.outputStream.writeFrom(buffer, offset, len); | |
| 497 } | |
| 498 } | |
| 499 | |
| 500 bool _close() { | |
| 501 _socket.close(); | |
| 491 } | 502 } |
| 492 | 503 |
| 493 void _onData() { | 504 void _onData() { |
| 494 int available = _socket.available(); | 505 int available = _socket.available(); |
| 495 if (available == 0) { | 506 if (available == 0) { |
| 496 return; | 507 return; |
| 497 } | 508 } |
| 498 | 509 |
| 499 ByteArray buffer = new ByteArray(available); | 510 ByteArray buffer = new ByteArray(available); |
| 500 int bytesRead = _socket.readList(buffer, 0, available); | 511 int bytesRead = _socket.readList(buffer, 0, available); |
| 501 if (bytesRead > 0) { | 512 if (bytesRead > 0) { |
| 502 int parsed = _httpParser.writeList(buffer, 0, bytesRead); | 513 int parsed = _httpParser.writeList(buffer, 0, bytesRead); |
| 503 if (parsed != bytesRead) { | 514 if (parsed != bytesRead) { |
| 504 // TODO(sgjesse): Error handling. | 515 // TODO(sgjesse): Error handling. |
| 505 _socket.close(); | 516 _socket.close(); |
| 506 } | 517 } |
| 507 } | 518 } |
| 508 } | 519 } |
| 509 | 520 |
| 510 void _onClosed() { | 521 void _onClosed() { |
|
Anders Johnsen
2012/04/02 08:42:32
Could the socket be closed normally from the clien
Søren Gjesse
2012/04/02 09:14:31
The client can half-close the socket when finished
| |
| 511 if (_phase != PHASE_IDLE) { | 522 if (_phase != PHASE_IDLE) { |
| 512 // Client closed socket for writing. Socket should still be open | 523 // Client closed socket for writing. Socket should still be open |
| 513 // for writing the response. | 524 // for writing the response. |
| 514 _closing = true; | 525 _closing = true; |
| 515 } else { | 526 } else { |
| 516 // The connection is currently not used by any request just close it. | 527 // The connection is currently not used by any request just close it. |
| 517 _socket.close(); | 528 _socket.close(); |
| 518 } | 529 } |
| 519 if (_onDisconnectCallback != null) _onDisconnectCallback(); | 530 if (_onDisconnectCallback != null) _onDisconnectCallback(); |
| 520 } | 531 } |
| 521 | 532 |
| 522 void _onError(Exception e) { | 533 void _onError(Exception e) { |
| 523 // If an error occurs, make sure to close the socket if one is associated. | 534 // If an error occurs, make sure to close the socket if one is associated. |
| 535 _error = true; | |
| 524 if (_socket != null) { | 536 if (_socket != null) { |
| 525 _socket.close(); | 537 _socket.close(); |
| 526 } | 538 } |
| 527 if (_onErrorCallback != null) { | 539 if (_onErrorCallback != null) { |
| 528 _onErrorCallback(e); | 540 _onErrorCallback(e); |
| 529 } | 541 } |
| 530 _propagateError(e); | 542 _propagateError(e); |
| 531 } | 543 } |
| 532 | 544 |
| 533 abstract void _propagateError(Exception e); | 545 abstract void _propagateError(Exception e); |
| 534 | 546 |
| 535 void set onDisconnect(void callback()) { | 547 void set onDisconnect(void callback()) { |
| 536 _onDisconnectCallback = callback; | 548 _onDisconnectCallback = callback; |
| 537 } | 549 } |
| 538 | 550 |
| 539 void set onError(void callback(Exception e)) { | 551 void set onError(void callback(Exception e)) { |
| 540 _onErrorCallback = callback; | 552 _onErrorCallback = callback; |
| 541 } | 553 } |
| 542 | 554 |
| 555 void set _onNoPendingWrites(void callback()) { | |
| 556 if (!_error) { | |
| 557 _socket.outputStream.onNoPendingWrites = callback; | |
| 558 } | |
| 559 } | |
| 560 | |
| 543 int hashCode() => _socket.hashCode(); | 561 int hashCode() => _socket.hashCode(); |
| 544 | 562 |
| 545 int _phase; | 563 int _phase; |
| 546 Socket _socket; | 564 Socket _socket; |
| 547 bool _closing = false; // Is the socket closed by the client? | 565 bool _closing = false; // Is the socket closed by the client? |
| 566 bool _error = false; // Is the socket closed due to an error? | |
| 548 _HttpParser _httpParser; | 567 _HttpParser _httpParser; |
| 549 | 568 |
| 550 Queue _sendBuffers; | 569 Queue _sendBuffers; |
| 551 | 570 |
| 552 Function _onDisconnectCallback; | 571 Function _onDisconnectCallback; |
| 553 Function _onErrorCallback; | 572 Function _onErrorCallback; |
| 554 } | 573 } |
| 555 | 574 |
| 556 | 575 |
| 557 // HTTP server connection over a socket. | 576 // HTTP server connection over a socket. |
| (...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 748 return _write(buffer, copyBuffer); | 767 return _write(buffer, copyBuffer); |
| 749 } | 768 } |
| 750 | 769 |
| 751 bool _streamWriteFrom(List<int> buffer, int offset, int len) { | 770 bool _streamWriteFrom(List<int> buffer, int offset, int len) { |
| 752 return _writeList(buffer, offset, len); | 771 return _writeList(buffer, offset, len); |
| 753 } | 772 } |
| 754 | 773 |
| 755 void _streamClose() { | 774 void _streamClose() { |
| 756 _state = DONE; | 775 _state = DONE; |
| 757 // Stop tracking no pending write events. | 776 // Stop tracking no pending write events. |
| 758 _httpConnection.outputStream.onNoPendingWrites = null; | 777 _httpConnection._onNoPendingWrites = null; |
| 759 // Ensure that any trailing data is written. | 778 // Ensure that any trailing data is written. |
| 760 _writeDone(); | 779 _writeDone(); |
| 761 // If the connection is closing then close the output stream to | 780 // If the connection is closing then close the output stream to |
| 762 // fully close the socket. | 781 // fully close the socket. |
| 763 if (_httpConnection._closing) { | 782 if (_httpConnection._closing) { |
| 764 _httpConnection.outputStream.close(); | 783 _httpConnection._close(); |
| 765 } | 784 } |
| 766 } | 785 } |
| 767 | 786 |
| 768 void _streamSetNoPendingWriteHandler(callback()) { | 787 void _streamSetNoPendingWriteHandler(callback()) { |
| 769 if (_state != DONE) { | 788 if (_state != DONE) { |
| 770 _httpConnection.outputStream.onNoPendingWrites = callback; | 789 _httpConnection._onNoPendingWrites = callback; |
| 771 } | 790 } |
| 772 } | 791 } |
| 773 | 792 |
| 774 void _streamSetCloseHandler(callback()) { | 793 void _streamSetCloseHandler(callback()) { |
| 775 // TODO(sgjesse): Handle this. | 794 // TODO(sgjesse): Handle this. |
| 776 } | 795 } |
| 777 | 796 |
| 778 void _streamSetErrorHandler(callback(Exception e)) { | 797 void _streamSetErrorHandler(callback(Exception e)) { |
| 779 _streamErrorHandler = callback; | 798 _streamErrorHandler = callback; |
| 780 } | 799 } |
| 781 | 800 |
| 782 void _writeHeader() { | 801 void _writeHeader() { |
| 783 List<int> data; | 802 List<int> data; |
| 784 OutputStream stream = _httpConnection.outputStream; | |
| 785 | 803 |
| 786 // Write request line. | 804 // Write request line. |
| 787 data = _method.toString().charCodes(); | 805 data = _method.toString().charCodes(); |
| 788 stream.write(data); | 806 _httpConnection._write(data); |
| 789 _writeSP(); | 807 _writeSP(); |
| 790 data = _uri.toString().charCodes(); | 808 data = _uri.toString().charCodes(); |
| 791 stream.write(data); | 809 _httpConnection._write(data); |
| 792 _writeSP(); | 810 _writeSP(); |
| 793 stream.write(_Const.HTTP11); | 811 _httpConnection._write(_Const.HTTP11); |
| 794 _writeCRLF(); | 812 _writeCRLF(); |
| 795 | 813 |
| 796 // Determine the value of the "Connection" header | 814 // Determine the value of the "Connection" header |
| 797 // based on the keep alive state. | 815 // based on the keep alive state. |
| 798 setHeader("Connection", keepAlive ? "keep-alive" : "close"); | 816 setHeader("Connection", keepAlive ? "keep-alive" : "close"); |
| 799 // Determine the value of the "Transfer-Encoding" header based on | 817 // Determine the value of the "Transfer-Encoding" header based on |
| 800 // whether the content length is known. | 818 // whether the content length is known. |
| 801 if (_contentLength >= 0) { | 819 if (_contentLength >= 0) { |
| 802 setHeader("Content-Length", _contentLength.toString()); | 820 setHeader("Content-Length", _contentLength.toString()); |
| 803 } else { | 821 } else { |
| (...skipping 390 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1194 sockets.addFirst(socketConn); | 1212 sockets.addFirst(socketConn); |
| 1195 socketConn._markReturned(); | 1213 socketConn._markReturned(); |
| 1196 } | 1214 } |
| 1197 | 1215 |
| 1198 Function _onOpen; | 1216 Function _onOpen; |
| 1199 Map<String, Queue<_SocketConnection>> _openSockets; | 1217 Map<String, Queue<_SocketConnection>> _openSockets; |
| 1200 Set<_SocketConnection> _activeSockets; | 1218 Set<_SocketConnection> _activeSockets; |
| 1201 Timer _evictionTimer; | 1219 Timer _evictionTimer; |
| 1202 bool _shutdown; // Has this HTTP client been shutdown? | 1220 bool _shutdown; // Has this HTTP client been shutdown? |
| 1203 } | 1221 } |
| OLD | NEW |