Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(187)

Side by Side Diff: sdk/lib/io/http_impl.dart

Issue 11637017: Change the HTTP Content-Length handling (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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
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
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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698