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 part of dart.io; | 5 part of dart.io; |
6 | 6 |
7 // The close queue handles graceful closing of HTTP connections. When | 7 // The close queue handles graceful closing of HTTP connections. When |
8 // a connection is added to the queue it will enter a wait state | 8 // a connection is added to the queue it will enter a wait state |
9 // waiting for all data written and possibly socket shutdown from | 9 // waiting for all data written and possibly socket shutdown from |
10 // peer. | 10 // peer. |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
80 | 80 |
81 class _HttpRequestResponseBase { | 81 class _HttpRequestResponseBase { |
82 static const int START = 0; | 82 static const int START = 0; |
83 static const int HEADER_SENT = 1; | 83 static const int HEADER_SENT = 1; |
84 static const int DONE = 2; | 84 static const int DONE = 2; |
85 static const int UPGRADED = 3; | 85 static const int UPGRADED = 3; |
86 | 86 |
87 _HttpRequestResponseBase(_HttpConnectionBase this._httpConnection) | 87 _HttpRequestResponseBase(_HttpConnectionBase this._httpConnection) |
88 : _state = START, _headResponse = false; | 88 : _state = START, _headResponse = false; |
89 | 89 |
90 int get contentLength => _contentLength; | 90 int get contentLength => _headers.contentLength; |
91 HttpHeaders get headers => _headers; | 91 HttpHeaders get headers => _headers; |
92 | 92 |
93 bool get persistentConnection { | 93 bool get persistentConnection { |
94 List<String> connection = headers[HttpHeaders.CONNECTION]; | 94 List<String> connection = headers[HttpHeaders.CONNECTION]; |
95 if (_protocolVersion == "1.1") { | 95 if (_protocolVersion == "1.1") { |
96 if (connection == null) return true; | 96 if (connection == null) return true; |
97 return !headers[HttpHeaders.CONNECTION].some( | 97 return !headers[HttpHeaders.CONNECTION].some( |
98 (value) => value.toLowerCase() == "close"); | 98 (value) => value.toLowerCase() == "close"); |
99 } else { | 99 } else { |
100 if (connection == null) return false; | 100 if (connection == null) return false; |
(...skipping 19 matching lines...) Expand all Loading... | |
120 headers.add(HttpHeaders.CONNECTION, "keep-alive"); | 120 headers.add(HttpHeaders.CONNECTION, "keep-alive"); |
121 } | 121 } |
122 } | 122 } |
123 | 123 |
124 | 124 |
125 bool _write(List<int> data, bool copyBuffer) { | 125 bool _write(List<int> data, bool copyBuffer) { |
126 if (_headResponse) return true; | 126 if (_headResponse) return true; |
127 _ensureHeadersSent(); | 127 _ensureHeadersSent(); |
128 bool allWritten = true; | 128 bool allWritten = true; |
129 if (data.length > 0) { | 129 if (data.length > 0) { |
130 if (_contentLength < 0) { | 130 if (_headers.contentLength < 0) { |
131 // Write chunk size if transfer encoding is chunked. | 131 // Write chunk size if transfer encoding is chunked. |
132 _writeHexString(data.length); | 132 _writeHexString(data.length); |
133 _writeCRLF(); | 133 _writeCRLF(); |
134 _httpConnection._write(data, copyBuffer); | 134 _httpConnection._write(data, copyBuffer); |
135 allWritten = _writeCRLF(); | 135 allWritten = _writeCRLF(); |
136 } else { | 136 } else { |
137 _updateContentLength(data.length); | 137 _updateContentLength(data.length); |
138 allWritten = _httpConnection._write(data, copyBuffer); | 138 allWritten = _httpConnection._write(data, copyBuffer); |
139 } | 139 } |
140 } | 140 } |
141 return allWritten; | 141 return allWritten; |
142 } | 142 } |
143 | 143 |
144 bool _writeList(List<int> data, int offset, int count) { | 144 bool _writeList(List<int> data, int offset, int count) { |
145 if (_headResponse) return true; | 145 if (_headResponse) return true; |
146 _ensureHeadersSent(); | 146 _ensureHeadersSent(); |
147 bool allWritten = true; | 147 bool allWritten = true; |
148 if (count > 0) { | 148 if (count > 0) { |
149 if (_contentLength < 0) { | 149 if (_headers.contentLength < 0) { |
150 // Write chunk size if transfer encoding is chunked. | 150 // Write chunk size if transfer encoding is chunked. |
151 _writeHexString(count); | 151 _writeHexString(count); |
152 _writeCRLF(); | 152 _writeCRLF(); |
153 _httpConnection._writeFrom(data, offset, count); | 153 _httpConnection._writeFrom(data, offset, count); |
154 allWritten = _writeCRLF(); | 154 allWritten = _writeCRLF(); |
155 } else { | 155 } else { |
156 _updateContentLength(count); | 156 _updateContentLength(count); |
157 allWritten = _httpConnection._writeFrom(data, offset, count); | 157 allWritten = _httpConnection._writeFrom(data, offset, count); |
158 } | 158 } |
159 } | 159 } |
160 return allWritten; | 160 return allWritten; |
161 } | 161 } |
162 | 162 |
163 bool _writeDone() { | 163 bool _writeDone() { |
164 bool allWritten = true; | 164 bool allWritten = true; |
165 if (_contentLength < 0) { | 165 if (_headers.contentLength < 0) { |
166 // Terminate the content if transfer encoding is chunked. | 166 // Terminate the content if transfer encoding is chunked. |
167 allWritten = _httpConnection._write(_Const.END_CHUNKED); | 167 allWritten = _httpConnection._write(_Const.END_CHUNKED); |
168 } else { | 168 } else { |
169 if (!_headResponse && _bodyBytesWritten < _contentLength) { | 169 if (!_headResponse && _bodyBytesWritten < _headers.contentLength) { |
170 throw new HttpException("Sending less than specified content length"); | 170 throw new HttpException("Sending less than specified content length"); |
171 } | 171 } |
172 assert(_headResponse || _bodyBytesWritten == _contentLength); | 172 assert(_headResponse || _bodyBytesWritten == _headers.contentLength); |
173 } | 173 } |
174 return allWritten; | 174 return allWritten; |
175 } | 175 } |
176 | 176 |
177 bool _writeHeaders() { | 177 bool _writeHeaders() { |
178 _headers._mutable = false; | 178 _headers._mutable = false; |
179 _headers._write(_httpConnection); | 179 _headers._write(_httpConnection); |
180 // Terminate header. | 180 // Terminate header. |
181 return _writeCRLF(); | 181 return _writeCRLF(); |
182 } | 182 } |
(...skipping 23 matching lines...) Expand all Loading... | |
206 } | 206 } |
207 | 207 |
208 void _ensureHeadersSent() { | 208 void _ensureHeadersSent() { |
209 // Ensure that headers are written. | 209 // Ensure that headers are written. |
210 if (_state == START) { | 210 if (_state == START) { |
211 _writeHeader(); | 211 _writeHeader(); |
212 } | 212 } |
213 } | 213 } |
214 | 214 |
215 void _updateContentLength(int bytes) { | 215 void _updateContentLength(int bytes) { |
216 if (_bodyBytesWritten + bytes > _contentLength) { | 216 if (_bodyBytesWritten + bytes > _headers.contentLength) { |
217 throw new HttpException("Writing more than specified content length"); | 217 throw new HttpException("Writing more than specified content length"); |
218 } | 218 } |
219 _bodyBytesWritten += bytes; | 219 _bodyBytesWritten += bytes; |
220 } | 220 } |
221 | 221 |
222 HttpConnectionInfo get connectionInfo => _httpConnection.connectionInfo; | 222 HttpConnectionInfo get connectionInfo => _httpConnection.connectionInfo; |
223 | 223 |
224 bool get _done => _state == DONE; | 224 bool get _done => _state == DONE; |
225 | 225 |
226 int _state; | 226 int _state; |
227 bool _headResponse; | 227 bool _headResponse; |
228 | 228 |
229 _HttpConnectionBase _httpConnection; | 229 _HttpConnectionBase _httpConnection; |
230 _HttpHeaders _headers; | 230 _HttpHeaders _headers; |
231 List<Cookie> _cookies; | 231 List<Cookie> _cookies; |
232 String _protocolVersion = "1.1"; | 232 String _protocolVersion = "1.1"; |
233 | 233 |
234 // Length of the content body. If this is set to -1 (default value) | |
235 // when starting to send data chunked transfer encoding will be | |
236 // used. | |
237 int _contentLength = -1; | |
238 // Number of body bytes written. This is only actual body data not | 234 // Number of body bytes written. This is only actual body data not |
239 // including headers or chunk information of using chinked transfer | 235 // including headers or chunk information of using chinked transfer |
240 // encoding. | 236 // encoding. |
241 int _bodyBytesWritten = 0; | 237 int _bodyBytesWritten = 0; |
242 } | 238 } |
243 | 239 |
244 | 240 |
245 // Parsed HTTP request providing information on the HTTP headers. | 241 // Parsed HTTP request providing information on the HTTP headers. |
246 class _HttpRequest extends _HttpRequestResponseBase implements HttpRequest { | 242 class _HttpRequest extends _HttpRequestResponseBase implements HttpRequest { |
247 _HttpRequest(_HttpConnection connection) : super(connection); | 243 _HttpRequest(_HttpConnection connection) : super(connection); |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
355 }); | 351 }); |
356 if (sessionId != null) { | 352 if (sessionId != null) { |
357 var sessionManager = _httpConnection._server._sessionManager; | 353 var sessionManager = _httpConnection._server._sessionManager; |
358 _session = sessionManager.getSession(sessionId); | 354 _session = sessionManager.getSession(sessionId); |
359 if (_session != null) { | 355 if (_session != null) { |
360 _session._markSeen(); | 356 _session._markSeen(); |
361 } | 357 } |
362 } | 358 } |
363 } | 359 } |
364 | 360 |
365 // Get parsed content length. | |
366 _contentLength = _httpConnection._httpParser.contentLength; | |
367 | |
368 // Prepare for receiving data. | 361 // Prepare for receiving data. |
369 _headers._mutable = false; | 362 _headers._mutable = false; |
370 _buffer = new _BufferList(); | 363 _buffer = new _BufferList(); |
371 } | 364 } |
372 | 365 |
373 void _onDataReceived(List<int> data) { | 366 void _onDataReceived(List<int> data) { |
374 _buffer.add(data); | 367 _buffer.add(data); |
375 if (_inputStream != null) _inputStream._dataReceived(); | 368 if (_inputStream != null) _inputStream._dataReceived(); |
376 } | 369 } |
377 | 370 |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
433 _HttpResponse(_HttpConnection httpConnection) | 426 _HttpResponse(_HttpConnection httpConnection) |
434 : super(httpConnection), | 427 : super(httpConnection), |
435 _statusCode = HttpStatus.OK { | 428 _statusCode = HttpStatus.OK { |
436 _headers = new _HttpHeaders(); | 429 _headers = new _HttpHeaders(); |
437 } | 430 } |
438 | 431 |
439 void set contentLength(int contentLength) { | 432 void set contentLength(int contentLength) { |
440 if (_state >= _HttpRequestResponseBase.HEADER_SENT) { | 433 if (_state >= _HttpRequestResponseBase.HEADER_SENT) { |
441 throw new HttpException("Header already sent"); | 434 throw new HttpException("Header already sent"); |
442 } | 435 } |
443 _contentLength = contentLength; | 436 _headers.contentLength = contentLength; |
444 } | 437 } |
445 | 438 |
446 int get statusCode => _statusCode; | 439 int get statusCode => _statusCode; |
447 void set statusCode(int statusCode) { | 440 void set statusCode(int statusCode) { |
448 if (_outputStream != null) throw new HttpException("Header already sent"); | 441 if (_outputStream != null) throw new HttpException("Header already sent"); |
449 _statusCode = statusCode; | 442 _statusCode = statusCode; |
450 } | 443 } |
451 | 444 |
452 String get reasonPhrase => _findReasonPhrase(_statusCode); | 445 String get reasonPhrase => _findReasonPhrase(_statusCode); |
453 void set reasonPhrase(String reasonPhrase) { | 446 void set reasonPhrase(String reasonPhrase) { |
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
593 data = _statusCode.toString().charCodes; | 586 data = _statusCode.toString().charCodes; |
594 _httpConnection._write(data); | 587 _httpConnection._write(data); |
595 _writeSP(); | 588 _writeSP(); |
596 data = reasonPhrase.charCodes; | 589 data = reasonPhrase.charCodes; |
597 _httpConnection._write(data); | 590 _httpConnection._write(data); |
598 _writeCRLF(); | 591 _writeCRLF(); |
599 | 592 |
600 // Determine the value of the "Transfer-Encoding" header based on | 593 // Determine the value of the "Transfer-Encoding" header based on |
601 // whether the content length is known. HTTP/1.0 does not support | 594 // whether the content length is known. HTTP/1.0 does not support |
602 // chunked. | 595 // chunked. |
603 if (_contentLength >= 0) { | 596 if (_headers.contentLength < 0 && _protocolVersion == "1.1") { |
604 _headers.set(HttpHeaders.CONTENT_LENGTH, _contentLength.toString()); | |
605 } else if (_contentLength < 0 && _protocolVersion == "1.1") { | |
606 _headers.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); | 597 _headers.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); |
607 } | 598 } |
608 | 599 |
609 var session = _httpConnection._request._session; | 600 var session = _httpConnection._request._session; |
610 if (session != null && !session._destroyed) { | 601 if (session != null && !session._destroyed) { |
611 // Make sure we only send the current session id. | 602 // Make sure we only send the current session id. |
612 bool found = false; | 603 bool found = false; |
613 for (int i = 0; i < cookies.length; i++) { | 604 for (int i = 0; i < cookies.length; i++) { |
614 if (cookies[i].name.toUpperCase() == _DART_SESSION_ID) { | 605 if (cookies[i].name.toUpperCase() == _DART_SESSION_ID) { |
615 cookies[i].value = session.id; | 606 cookies[i].value = session.id; |
(...skipping 314 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
930 if (_isReadClosed) { | 921 if (_isReadClosed) { |
931 // If the client closes the conversation is ended. | 922 // If the client closes the conversation is ended. |
932 _server._closeQueue.add(this); | 923 _server._closeQueue.add(this); |
933 } else if (_isAllDone) { | 924 } else if (_isAllDone) { |
934 // If we are done writing the response, and the connection is | 925 // If we are done writing the response, and the connection is |
935 // not persistent, we must close. Also if using HTTP 1.0 and the | 926 // not persistent, we must close. Also if using HTTP 1.0 and the |
936 // content length was not known we must close to indicate end of | 927 // content length was not known we must close to indicate end of |
937 // body. | 928 // body. |
938 bool close = | 929 bool close = |
939 !_response.persistentConnection || | 930 !_response.persistentConnection || |
940 (_response._protocolVersion == "1.0" && _response._contentLength < 0); | 931 (_response._protocolVersion == "1.0" && _response.contentLength < 0); |
941 _request = null; | 932 _request = null; |
942 _response = null; | 933 _response = null; |
943 if (close) { | 934 if (close) { |
944 _httpParser.cancel(); | 935 _httpParser.cancel(); |
945 _server._closeQueue.add(this); | 936 _server._closeQueue.add(this); |
946 } else { | 937 } else { |
947 _state = _HttpConnectionBase.IDLE; | 938 _state = _HttpConnectionBase.IDLE; |
948 } | 939 } |
949 } else if (_isResponseDone && _hasBody) { | 940 } else if (_isResponseDone && _hasBody) { |
950 // If the response is closed before the request is fully read | 941 // If the response is closed before the request is fully read |
(...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1160 class _HttpClientRequest | 1151 class _HttpClientRequest |
1161 extends _HttpRequestResponseBase implements HttpClientRequest { | 1152 extends _HttpRequestResponseBase implements HttpClientRequest { |
1162 _HttpClientRequest(String this._method, | 1153 _HttpClientRequest(String this._method, |
1163 Uri this._uri, | 1154 Uri this._uri, |
1164 _HttpClientConnection connection) | 1155 _HttpClientConnection connection) |
1165 : super(connection) { | 1156 : super(connection) { |
1166 _headers = new _HttpHeaders(); | 1157 _headers = new _HttpHeaders(); |
1167 _connection = connection; | 1158 _connection = connection; |
1168 // Default GET and HEAD requests to have no content. | 1159 // Default GET and HEAD requests to have no content. |
1169 if (_method == "GET" || _method == "HEAD") { | 1160 if (_method == "GET" || _method == "HEAD") { |
1170 _contentLength = 0; | 1161 contentLength = 0; |
1171 } | 1162 } |
1172 } | 1163 } |
1173 | 1164 |
1174 void set contentLength(int contentLength) { | 1165 void set contentLength(int contentLength) { |
1175 if (_state >= _HttpRequestResponseBase.HEADER_SENT) { | 1166 if (_state >= _HttpRequestResponseBase.HEADER_SENT) { |
1176 throw new HttpException("Header already sent"); | 1167 throw new HttpException("Header already sent"); |
1177 } | 1168 } |
1178 _contentLength = contentLength; | 1169 _headers.contentLength = contentLength; |
1179 } | 1170 } |
1180 | 1171 |
1181 List<Cookie> get cookies { | 1172 List<Cookie> get cookies { |
1182 if (_cookies == null) _cookies = new List<Cookie>(); | 1173 if (_cookies == null) _cookies = new List<Cookie>(); |
1183 return _cookies; | 1174 return _cookies; |
1184 } | 1175 } |
1185 | 1176 |
1186 OutputStream get outputStream { | 1177 OutputStream get outputStream { |
1187 if (_done) throw new HttpException("Request closed"); | 1178 if (_done) throw new HttpException("Request closed"); |
1188 if (_outputStream == null) { | 1179 if (_outputStream == null) { |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1256 data = _uri.toString().charCodes; | 1247 data = _uri.toString().charCodes; |
1257 } | 1248 } |
1258 _httpConnection._write(data); | 1249 _httpConnection._write(data); |
1259 _writeSP(); | 1250 _writeSP(); |
1260 _httpConnection._write(_Const.HTTP11); | 1251 _httpConnection._write(_Const.HTTP11); |
1261 _writeCRLF(); | 1252 _writeCRLF(); |
1262 | 1253 |
1263 // Determine the value of the "Transfer-Encoding" header based on | 1254 // Determine the value of the "Transfer-Encoding" header based on |
1264 // whether the content length is known. If there is no content | 1255 // whether the content length is known. If there is no content |
1265 // neither "Content-Length" nor "Transfer-Encoding" is set. | 1256 // neither "Content-Length" nor "Transfer-Encoding" is set. |
1266 if (_contentLength > 0) { | 1257 if (_headers.contentLength < 0) { |
1267 _headers.set(HttpHeaders.CONTENT_LENGTH, _contentLength.toString()); | |
1268 } else if (_contentLength < 0) { | |
1269 _headers.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); | 1258 _headers.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); |
1270 } | 1259 } |
1271 | 1260 |
1272 // Add the cookies to the headers. | 1261 // Add the cookies to the headers. |
1273 if (_cookies != null) { | 1262 if (_cookies != null) { |
1274 StringBuffer sb = new StringBuffer(); | 1263 StringBuffer sb = new StringBuffer(); |
1275 for (int i = 0; i < _cookies.length; i++) { | 1264 for (int i = 0; i < _cookies.length; i++) { |
1276 if (i > 0) sb.add("; "); | 1265 if (i > 0) sb.add("; "); |
1277 sb.add(_cookies[i].name); | 1266 sb.add(_cookies[i].name); |
1278 sb.add("="); | 1267 sb.add("="); |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1339 | 1328 |
1340 void _onResponseReceived(int statusCode, | 1329 void _onResponseReceived(int statusCode, |
1341 String reasonPhrase, | 1330 String reasonPhrase, |
1342 String version, | 1331 String version, |
1343 _HttpHeaders headers, | 1332 _HttpHeaders headers, |
1344 bool hasBody) { | 1333 bool hasBody) { |
1345 _statusCode = statusCode; | 1334 _statusCode = statusCode; |
1346 _reasonPhrase = reasonPhrase; | 1335 _reasonPhrase = reasonPhrase; |
1347 _headers = headers; | 1336 _headers = headers; |
1348 // Get parsed content length. | 1337 // Get parsed content length. |
1349 _contentLength = _httpConnection._httpParser.contentLength; | 1338 //_content-Length = _httpConnection._httpParser.contentLength; |
Anders Johnsen
2012/12/19 15:43:58
Out-commented code.
Søren Gjesse
2012/12/20 07:39:01
Done.
| |
1350 | 1339 |
1351 // Prepare for receiving data. | 1340 // Prepare for receiving data. |
1352 _headers._mutable = false; | 1341 _headers._mutable = false; |
1353 _buffer = new _BufferList(); | 1342 _buffer = new _BufferList(); |
1354 if (isRedirect && _connection.followRedirects) { | 1343 if (isRedirect && _connection.followRedirects) { |
1355 if (_connection._redirects == null || | 1344 if (_connection._redirects == null || |
1356 _connection._redirects.length < _connection.maxRedirects) { | 1345 _connection._redirects.length < _connection.maxRedirects) { |
1357 // Check the location header. | 1346 // Check the location header. |
1358 List<String> location = headers[HttpHeaders.LOCATION]; | 1347 List<String> location = headers[HttpHeaders.LOCATION]; |
1359 if (location == null || location.length > 1) { | 1348 if (location == null || location.length > 1) { |
(...skipping 968 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2328 | 2317 |
2329 | 2318 |
2330 class _RedirectInfo implements RedirectInfo { | 2319 class _RedirectInfo implements RedirectInfo { |
2331 const _RedirectInfo(int this.statusCode, | 2320 const _RedirectInfo(int this.statusCode, |
2332 String this.method, | 2321 String this.method, |
2333 Uri this.location); | 2322 Uri this.location); |
2334 final int statusCode; | 2323 final int statusCode; |
2335 final String method; | 2324 final String method; |
2336 final Uri location; | 2325 final Uri location; |
2337 } | 2326 } |
OLD | NEW |