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

Unified Diff: sdk/lib/io/http_impl.dart

Issue 11453006: Fix a number of HTTP issues (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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | sdk/lib/io/http_parser.dart » ('j') | sdk/lib/io/secure_socket.dart » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sdk/lib/io/http_impl.dart
diff --git a/sdk/lib/io/http_impl.dart b/sdk/lib/io/http_impl.dart
index 5539f3adb92b29bf49eb70840d19d4d080c7c5ef..978634bb50e753e0507e276845ffcbc22ef555fb 100644
--- a/sdk/lib/io/http_impl.dart
+++ b/sdk/lib/io/http_impl.dart
@@ -758,36 +758,12 @@ abstract class _HttpConnectionBase {
_socket.outputStream.onError = (e) => null;
}
- bool _write(List<int> data, [bool copyBuffer = false]) {
- return _socket.outputStream.write(data, copyBuffer);
- }
-
- bool _writeFrom(List<int> buffer, [int offset, int len]) {
- return _socket.outputStream.writeFrom(buffer, offset, len);
- }
-
- bool _flush() {
- _socket.outputStream.flush();
- }
-
- bool _close() {
- _socket.outputStream.close();
- }
-
- bool _destroy() {
- _socket.close();
- }
-
- DetachedSocket _detachSocket() {
- _socket.onData = null;
- _socket.onClosed = null;
- _socket.onError = null;
- _socket.outputStream.onNoPendingWrites = null;
- Socket socket = _socket;
- _socket = null;
- if (onDetach != null) onDetach();
- return new _DetachedSocket(socket, _httpParser.readUnparsedData());
- }
+ bool _write(List<int> data, [bool copyBuffer = false]);
+ bool _writeFrom(List<int> buffer, [int offset, int len]);
+ bool _flush();
+ bool _close();
+ bool _destroy();
+ DetachedSocket _detachSocket();
HttpConnectionInfo get connectionInfo {
if (_socket == null) return null;
@@ -835,11 +811,72 @@ class _HttpConnection extends _HttpConnectionBase {
};
}
+ void _bufferData(List<int> data, [bool copyBuffer = false]) {
+ if (_buffer == null) _buffer = new _BufferList();
+ if (copyBuffer) data = data.getRange(0, data.length);
+ _buffer.add(data);
+ }
+
+ void _releaseBuffer() {
Mads Ager (google) 2012/12/05 16:02:18 Maybe name this something like _writeBufferedRespo
Søren Gjesse 2012/12/06 17:23:08 Done.
+ if (_buffer != null) {
+ while (!_buffer.isEmpty) {
+ var data = _buffer.first;
+ _socket.outputStream.write(data, false);
+ _buffer.removeBytes(data.length);
+ }
+ _buffer = null;
+ }
+ }
+
+ bool _write(List<int> data, [bool copyBuffer = false]) {
+ if (_isRequestDone) {
+ return _socket.outputStream.write(data, copyBuffer);
+ } else {
+ _bufferData(data, copyBuffer);
+ return false;
+ }
+ }
+
+ bool _writeFrom(List<int> data, [int offset, int len]) {
+ if (_isRequestDone) {
+ return _socket.outputStream.writeFrom(data, offset, len);
+ } else {
+ if (offset == null) offset = 0;
+ if (len == null) len = buffer.length - offset;
+ _bufferData(data.getRange(offset, len), false);
+ return false;
+ }
+ }
+
+ bool _flush() {
+ _socket.outputStream.flush();
+ }
+
+ bool _close() {
+ _socket.outputStream.close();
+ }
+
+ bool _destroy() {
+ _socket.close();
+ }
+
void _onClosed() {
_state |= _HttpConnectionBase.READ_CLOSED;
_checkDone();
}
+ DetachedSocket _detachSocket() {
+ _socket.onData = null;
+ _socket.onClosed = null;
+ _socket.onError = null;
+ _socket.outputStream.onNoPendingWrites = null;
+ _releaseBuffer();
+ Socket socket = _socket;
+ _socket = null;
+ if (onDetach != null) onDetach();
+ return new _DetachedSocket(socket, _httpParser.readUnparsedData());
+ }
+
void _onError(e) {
onError(e);
// Propagate the error to the streams.
@@ -893,13 +930,23 @@ class _HttpConnection extends _HttpConnectionBase {
} else {
_state = _HttpConnectionBase.IDLE;
}
+ } else if (_isResponseDone) {
+ // If the response is closed before the request is fully read
+ // close this connection. If there is buffered output
+ // (e.g. error response for invalid request where the server did
+ // not care to read the request body) this is send.
+ assert(!_isRequestDone);
+ _releaseBuffer();
+ _close();
+ _server._closeQueue.add(this);
}
}
void _onDataEnd(bool close) {
- _request._onDataEnd();
+ // Start sending queued response if any.
+ _releaseBuffer();
_state |= _HttpConnectionBase.REQUEST_DONE;
- _checkDone();
+ _request._onDataEnd();
}
void _responseClosed() {
@@ -911,6 +958,9 @@ class _HttpConnection extends _HttpConnectionBase {
HttpRequest _request;
HttpResponse _response;
+ // Buffer for data written before full response have been processed.
Mads Ager (google) 2012/12/05 16:02:18 have -> has
Søren Gjesse 2012/12/06 17:23:08 Done.
+ _BufferList _buffer;
+
// Callbacks.
Function onRequestReceived;
Function onError;
@@ -1121,11 +1171,13 @@ class _HttpClientRequest
// Delegate functions for the HttpOutputStream implementation.
bool _streamWrite(List<int> buffer, bool copyBuffer) {
if (_done) throw new HttpException("Request closed");
+ _emptyBody = false;
Mads Ager (google) 2012/12/05 16:02:18 Should this be _emptyBody = buffer.length != 0 |
Søren Gjesse 2012/12/06 17:23:08 Good point, however it should be _emptyBody = buf
return _write(buffer, copyBuffer);
}
bool _streamWriteFrom(List<int> buffer, int offset, int len) {
if (_done) throw new HttpException("Request closed");
+ _emptyBody = false;
Mads Ager (google) 2012/12/05 16:02:18 Ditto
Søren Gjesse 2012/12/06 17:23:08 Done.
return _writeList(buffer, offset, len);
}
@@ -1216,6 +1268,7 @@ class _HttpClientRequest
_HttpClientConnection _connection;
_HttpOutputStream _outputStream;
Function _streamErrorHandler;
+ bool _emptyBody = true;
}
@@ -1424,6 +1477,37 @@ class _HttpClientConnection
_httpParser = new _HttpParser.responseParser();
}
+ bool _write(List<int> data, [bool copyBuffer = false]) {
+ return _socket.outputStream.write(data, copyBuffer);
+ }
+
+ bool _writeFrom(List<int> data, [int offset, int len]) {
+ return _socket.outputStream.writeFrom(data, offset, len);
+ }
+
+ bool _flush() {
+ _socket.outputStream.flush();
+ }
+
+ bool _close() {
+ _socket.outputStream.close();
+ }
+
+ bool _destroy() {
+ _socket.close();
+ }
+
+ DetachedSocket _detachSocket() {
+ _socket.onData = null;
+ _socket.onClosed = null;
+ _socket.onError = null;
+ _socket.outputStream.onNoPendingWrites = null;
+ Socket socket = _socket;
+ _socket = null;
+ if (onDetach != null) onDetach();
+ return new _DetachedSocket(socket, _httpParser.readUnparsedData());
+ }
+
void _connectionEstablished(_SocketConnection socketConn) {
super._connectionEstablished(socketConn._socket);
_socketConn = socketConn;
@@ -1472,7 +1556,17 @@ class _HttpClientConnection
_method = method;
// Tell the HTTP parser the method it is expecting a response to.
_httpParser.responseToMethod = method;
- _request = new _HttpClientRequest(method, uri, this);
+ // If the connection already have a request this is a retry of a
Mads Ager (google) 2012/12/05 16:02:18 have -> has
Søren Gjesse 2012/12/06 17:23:08 Done.
+ // request. In this case the request object is reused to ensure
+ // that the same headers are send.
+ if (_request != null) {
+ _request._method = method;
+ _request._uri = uri;
+ _request._headers._mutable = true;
+ _request._state = _HttpRequestResponseBase.START;
+ } else {
+ _request = new _HttpClientRequest(method, uri, this);
+ }
_response = new _HttpClientResponse(this);
return _request;
}
@@ -1492,13 +1586,26 @@ class _HttpClientConnection
if (_socketConn != null) {
_client._closeSocketConnection(_socketConn);
}
- // Report the error.
- if (_response != null && _response._streamErrorHandler != null) {
- _response._streamErrorHandler(e);
- } else if (_onErrorCallback != null) {
- _onErrorCallback(e);
+
+ // If it looks as if we got a bad connection from the connection
+ // pool and the request can be retried do a retry.
+ if (_socketConn != null && _socketConn._fromPool && _request._emptyBody) {
+ String method = _request._method;
+ Uri uri = _request._uri;
+ _socketConn = null;
+
+ // Retry the URL using the same connection instance.
+ _httpParser.restart();
+ _client._openUrl(method, uri, this);
} else {
- throw e;
+ // Report the error.
+ if (_response != null && _response._streamErrorHandler != null) {
+ _response._streamErrorHandler(e);
+ } else if (_onErrorCallback != null) {
+ _onErrorCallback(e);
+ } else {
+ throw e;
+ }
}
}
@@ -1539,8 +1646,6 @@ class _HttpClientConnection
void _doRetry(_RedirectInfo retry) {
assert(_socketConn == null);
- _request = null;
- _response = null;
// Retry the URL using the same connection instance.
_state = _HttpConnectionBase.IDLE;
@@ -1615,10 +1720,19 @@ class _SocketConnection {
Socket this._socket);
void _markReturned() {
+ // Any activity on the socket while waiting in the pool will
+ // invalidate the connection os that it is not reused.
Mads Ager (google) 2012/12/05 16:02:18 os -> so
Søren Gjesse 2012/12/06 17:23:08 Done.
+ _socket.onData = _invalidate;
+ _socket.onClosed = _invalidate;
+ _socket.onError = (_) => _invalidate();
+ _returnTime = new Date.now();
+ _httpClientConnection = null;
+ }
+
+ void _markRetreived() {
Mads Ager (google) 2012/12/05 16:02:18 _markRetrieved
Søren Gjesse 2012/12/06 17:23:08 Done.
_socket.onData = null;
_socket.onClosed = null;
_socket.onError = null;
- _returnTime = new Date.now();
_httpClientConnection = null;
}
@@ -1632,12 +1746,20 @@ class _SocketConnection {
Duration _idleTime(Date now) => now.difference(_returnTime);
+ bool get _fromPool => _returnTime != null;
+
+ void _invalidate() {
+ _valid = false;
+ _close();
+ }
+
int get hashCode => _socket.hashCode;
String _host;
int _port;
Socket _socket;
Date _returnTime;
+ bool _valid = true;
HttpClientConnection _httpClientConnection;
}
@@ -1847,11 +1969,13 @@ class _HttpClient implements HttpClient {
// otherwise create a new one.
String key = _connectionKey(connectHost, connectPort);
Queue socketConnections = _openSockets[key];
- // Remove active connections that are of the wrong type (HTTP or HTTPS).
+ // Remove active connections that are not valid any more or of
+ // the wrong type (HTTP or HTTPS).
while (socketConnections != null &&
!socketConnections.isEmpty &&
- secure != (socketConnections.first._socket is SecureSocket)) {
- socketConnection.removeFirst()._close();
+ (!socketConnections.first._valid ||
+ secure != (socketConnections.first._socket is SecureSocket))) {
+ socketConnections.removeFirst()._close();
}
if (socketConnections == null || socketConnections.isEmpty) {
Socket socket = secure ? new SecureSocket(connectHost, connectPort) :
@@ -1884,6 +2008,7 @@ class _HttpClient implements HttpClient {
};
} else {
_SocketConnection socketConn = socketConnections.removeFirst();
+ socketConn._markRetreived();
_activeSockets.add(socketConn);
new Timer(0, (ignored) =>
_connectionOpened(socketConn, connection, !proxy.isDirect));
« no previous file with comments | « no previous file | sdk/lib/io/http_parser.dart » ('j') | sdk/lib/io/secure_socket.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698