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 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 |