summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/debugger/devtools_protocol_handler.cc20
-rw-r--r--chrome/browser/debugger/devtools_protocol_handler.h11
-rw-r--r--chrome/browser/debugger/devtools_remote.h2
-rw-r--r--chrome/browser/debugger/devtools_remote_listen_socket.cc50
-rw-r--r--chrome/browser/debugger/devtools_remote_listen_socket.h12
-rw-r--r--chrome/browser/debugger/devtools_remote_listen_socket_unittest.cc23
-rw-r--r--chrome/browser/debugger/devtools_remote_listen_socket_unittest.h7
-rw-r--r--chrome_frame/test/test_server.cc13
-rw-r--r--chrome_frame/test/test_server.h6
-rw-r--r--net/base/listen_socket.cc35
-rw-r--r--net/base/listen_socket.h4
-rw-r--r--net/base/listen_socket_unittest.cc6
-rw-r--r--net/base/listen_socket_unittest.h2
-rw-r--r--net/base/telnet_server.cc4
-rw-r--r--net/net.gyp15
-rw-r--r--net/server/http_listen_socket.cc316
-rw-r--r--net/server/http_listen_socket.h65
-rw-r--r--net/server/http_server_request_info.h34
-rw-r--r--net/socket/tcp_client_socket_unittest.cc2
-rw-r--r--net/socket/tcp_pinger_unittest.cc2
-rw-r--r--net/tools/fetch/http_listen_socket.cc5
-rw-r--r--net/tools/fetch/http_listen_socket.h2
22 files changed, 522 insertions, 114 deletions
diff --git a/chrome/browser/debugger/devtools_protocol_handler.cc b/chrome/browser/debugger/devtools_protocol_handler.cc
index d88aad0..4a73efa 100644
--- a/chrome/browser/debugger/devtools_protocol_handler.cc
+++ b/chrome/browser/debugger/devtools_protocol_handler.cc
@@ -32,7 +32,7 @@ void DevToolsProtocolHandler::Start() {
void DevToolsProtocolHandler::Init() {
server_ = DevToolsRemoteListenSocket::Listen(
- "127.0.0.1", port_, this, this);
+ "127.0.0.1", port_, this);
}
void DevToolsProtocolHandler::Stop() {
@@ -85,26 +85,14 @@ void DevToolsProtocolHandler::Send(const DevToolsRemoteMessage& message) {
}
}
-void DevToolsProtocolHandler::DidAccept(ListenSocket *server,
- ListenSocket *connection) {
+void DevToolsProtocolHandler::OnAcceptConnection(ListenSocket *connection) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
- if (connection_ == NULL) {
- connection_ = connection;
- connection_->AddRef();
- }
- // else the connection will get deleted itself with scoped_refptr
-}
-
-void DevToolsProtocolHandler::DidRead(ListenSocket *connection,
- const std::string& data) {
- // Not used.
+ connection_ = connection;
}
-void DevToolsProtocolHandler::DidClose(ListenSocket *sock) {
+void DevToolsProtocolHandler::OnConnectionLost() {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
- DCHECK(connection_ == sock);
connection_ = NULL;
- sock->Release();
for (ToolToListenerMap::const_iterator it = tool_to_listener_map_.begin(),
end = tool_to_listener_map_.end();
it != end;
diff --git a/chrome/browser/debugger/devtools_protocol_handler.h b/chrome/browser/debugger/devtools_protocol_handler.h
index 8c14a82..fff65eb 100644
--- a/chrome/browser/debugger/devtools_protocol_handler.h
+++ b/chrome/browser/debugger/devtools_protocol_handler.h
@@ -21,8 +21,7 @@ class DevToolsRemoteMessage;
// based on the "Tool" message header value.
class DevToolsProtocolHandler
: public DevToolsRemoteListener,
- public OutboundSocketDelegate,
- public ListenSocket::ListenSocketDelegate {
+ public OutboundSocketDelegate {
public:
typedef base::hash_map< std::string, scoped_refptr<DevToolsRemoteListener> >
ToolToListenerMap;
@@ -54,16 +53,12 @@ class DevToolsProtocolHandler
// DevToolsRemoteListener interface
virtual void HandleMessage(const DevToolsRemoteMessage& message);
- virtual void OnConnectionLost() {}
+ virtual void OnAcceptConnection(ListenSocket *connection);
+ virtual void OnConnectionLost();
// OutboundSocketDelegate interface
virtual void Send(const DevToolsRemoteMessage& message);
- // ListenSocket::ListenSocketDelegate interface
- virtual void DidAccept(ListenSocket *server, ListenSocket *connection);
- virtual void DidRead(ListenSocket *connection, const std::string& data);
- virtual void DidClose(ListenSocket *sock);
-
private:
virtual ~DevToolsProtocolHandler();
diff --git a/chrome/browser/debugger/devtools_remote.h b/chrome/browser/debugger/devtools_remote.h
index 713df3d..a0edc6d 100644
--- a/chrome/browser/debugger/devtools_remote.h
+++ b/chrome/browser/debugger/devtools_remote.h
@@ -9,6 +9,7 @@
#include "base/ref_counted.h"
class DevToolsRemoteMessage;
+class ListenSocket;
// This interface should be implemented by a class that wants to handle
// DevToolsRemoteMessages dispatched by some entity. It must extend
@@ -20,6 +21,7 @@ class DevToolsRemoteListener
// This method is invoked on the UI thread whenever the debugger connection
// has been lost.
virtual void OnConnectionLost() = 0;
+ virtual void OnAcceptConnection(ListenSocket* connection) {}
protected:
friend class base::RefCountedThreadSafe<DevToolsRemoteListener>;
diff --git a/chrome/browser/debugger/devtools_remote_listen_socket.cc b/chrome/browser/debugger/devtools_remote_listen_socket.cc
index e4a84cb..87b88d0 100644
--- a/chrome/browser/debugger/devtools_remote_listen_socket.cc
+++ b/chrome/browser/debugger/devtools_remote_listen_socket.cc
@@ -41,9 +41,8 @@ const int kReadBufSize = 200;
DevToolsRemoteListenSocket::DevToolsRemoteListenSocket(
SOCKET s,
- ListenSocketDelegate* del,
DevToolsRemoteListener* message_listener)
- : ListenSocket(s, del),
+ : ALLOW_THIS_IN_INITIALIZER_LIST(ListenSocket(s, this)),
state_(HANDSHAKE),
remaining_payload_length_(0),
message_listener_(message_listener),
@@ -86,55 +85,30 @@ DevToolsRemoteListenSocket::~DevToolsRemoteListenSocket() {}
DevToolsRemoteListenSocket*
DevToolsRemoteListenSocket::Listen(const std::string& ip,
int port,
- ListenSocketDelegate* del,
DevToolsRemoteListener* listener) {
SOCKET s = ListenSocket::Listen(ip, port);
if (s == INVALID_SOCKET) {
// TODO(apavlov): error handling
} else {
DevToolsRemoteListenSocket* sock =
- new DevToolsRemoteListenSocket(s, del, listener);
+ new DevToolsRemoteListenSocket(s, listener);
sock->Listen();
return sock;
}
return NULL;
}
-void DevToolsRemoteListenSocket::Read() {
- char buf[kReadBufSize];
- int len;
- do {
- len = HANDLE_EINTR(recv(socket_, buf, kReadBufSize, 0));
- if (len == SOCKET_ERROR) {
-#if defined(OS_WIN)
- int err = WSAGetLastError();
- if (err == WSAEWOULDBLOCK) {
-#elif defined(OS_POSIX)
- if (errno == EWOULDBLOCK || errno == EAGAIN) {
-#endif
- break;
- } else {
- // TODO(apavlov): some error handling required here
- break;
- }
- } else if (len == 0) {
- // In Windows, Close() is called by OnObjectSignaled. In POSIX, we need
- // to call it here.
-#if defined(OS_POSIX)
- Close();
-#endif
- } else {
- // TODO(apavlov): maybe change DidRead to take a length instead
- DCHECK(len > 0 && len <= kReadBufSize);
- this->DispatchRead(buf, len);
- }
- } while (len == kReadBufSize);
+void DevToolsRemoteListenSocket::DidAccept(ListenSocket *server,
+ ListenSocket *connection) {
+ connection->AddRef();
+ message_listener_->OnAcceptConnection(connection);
}
// Dispatches data from socket to socket_delegate_, extracting messages
// delimited by newlines.
-void DevToolsRemoteListenSocket::DispatchRead(char* buf, int len) {
- char* pBuf = buf;
+void DevToolsRemoteListenSocket::DidRead(ListenSocket* connection,
+ const char* pBuf,
+ int len) {
while (len > 0) {
if (state_ != PAYLOAD) {
if (cr_received_ && *pBuf == '\n') {
@@ -173,6 +147,11 @@ void DevToolsRemoteListenSocket::DispatchRead(char* buf, int len) {
}
}
+void DevToolsRemoteListenSocket::DidClose(ListenSocket *connection) {
+ message_listener_->OnConnectionLost();
+ connection->Release();
+}
+
void DevToolsRemoteListenSocket::DispatchField() {
static const std::string kHandshakeString = "ChromeDevToolsHandshake";
switch (state_) {
@@ -231,7 +210,6 @@ void DevToolsRemoteListenSocket::Accept() {
if (conn != INVALID_SOCKET) {
scoped_refptr<DevToolsRemoteListenSocket> sock =
new DevToolsRemoteListenSocket(conn,
- socket_delegate_,
message_listener_);
// it's up to the delegate to AddRef if it wants to keep it around
#if defined(OS_POSIX)
diff --git a/chrome/browser/debugger/devtools_remote_listen_socket.h b/chrome/browser/debugger/devtools_remote_listen_socket.h
index d53364f..9424a76 100644
--- a/chrome/browser/debugger/devtools_remote_listen_socket.h
+++ b/chrome/browser/debugger/devtools_remote_listen_socket.h
@@ -14,26 +14,30 @@ class DevToolsRemoteListener;
// Listens to remote debugger incoming connections, handles the V8ARDP protocol
// socket input and invokes the message handler when appropriate.
-class DevToolsRemoteListenSocket : public ListenSocket {
+class DevToolsRemoteListenSocket : public ListenSocket,
+ public ListenSocket::ListenSocketDelegate {
public:
// Listen on port for the specified IP address. Use 127.0.0.1 to only
// accept local connections.
static DevToolsRemoteListenSocket* Listen(
const std::string& ip,
int port,
- ListenSocketDelegate* del,
DevToolsRemoteListener* message_listener);
protected:
virtual void Listen() { ListenSocket::Listen(); }
virtual void Accept();
- virtual void Read();
virtual void Close();
virtual void SendInternal(const char* bytes, int len);
private:
virtual ~DevToolsRemoteListenSocket();
+ // ListenSocket::ListenSocketDelegate interface
+ virtual void DidAccept(ListenSocket *server, ListenSocket *connection);
+ virtual void DidRead(ListenSocket *connection, const char* data, int len);
+ virtual void DidClose(ListenSocket *connection);
+
// The protocol states while reading socket input
enum State {
INVALID = 0, // Bad handshake message received, retry
@@ -43,11 +47,9 @@ class DevToolsRemoteListenSocket : public ListenSocket {
};
DevToolsRemoteListenSocket(SOCKET s,
- ListenSocketDelegate *del,
DevToolsRemoteListener *listener);
void StartNextField();
void HandleMessage();
- void DispatchRead(char* buf, int len);
void DispatchField();
const std::string& GetHeader(const std::string& header_name,
const std::string& default_value) const;
diff --git a/chrome/browser/debugger/devtools_remote_listen_socket_unittest.cc b/chrome/browser/debugger/devtools_remote_listen_socket_unittest.cc
index 5a2688d..79933fe 100644
--- a/chrome/browser/debugger/devtools_remote_listen_socket_unittest.cc
+++ b/chrome/browser/debugger/devtools_remote_listen_socket_unittest.cc
@@ -48,7 +48,7 @@ static const char* kSemaphoreName = "chromium.listen_socket";
ListenSocket* DevToolsRemoteListenSocketTester::DoListen() {
- return DevToolsRemoteListenSocket::Listen(kLoopback, kTestPort, this, this);
+ return DevToolsRemoteListenSocket::Listen(kLoopback, kTestPort, this);
}
void DevToolsRemoteListenSocketTester::SetUp() {
@@ -211,8 +211,6 @@ int DevToolsRemoteListenSocketTester::ClearTestSocket() {
}
void DevToolsRemoteListenSocketTester::Shutdown() {
- connection_->Release();
- connection_ = NULL;
server_->Release();
server_ = NULL;
ReportAction(ListenSocketTestAction(ACTION_SHUTDOWN));
@@ -220,10 +218,8 @@ void DevToolsRemoteListenSocketTester::Shutdown() {
void DevToolsRemoteListenSocketTester::Listen() {
server_ = DoListen();
- if (server_) {
- server_->AddRef();
- ReportAction(ListenSocketTestAction(ACTION_LISTEN));
- }
+ server_->AddRef();
+ ReportAction(ListenSocketTestAction(ACTION_LISTEN));
}
void DevToolsRemoteListenSocketTester::SendFromTester() {
@@ -231,19 +227,14 @@ void DevToolsRemoteListenSocketTester::SendFromTester() {
ReportAction(ListenSocketTestAction(ACTION_SEND));
}
-void DevToolsRemoteListenSocketTester::DidAccept(ListenSocket *server,
- ListenSocket *connection) {
+void DevToolsRemoteListenSocketTester::OnAcceptConnection(
+ ListenSocket* connection) {
connection_ = connection;
- connection_->AddRef();
ReportAction(ListenSocketTestAction(ACTION_ACCEPT));
}
-void DevToolsRemoteListenSocketTester::DidRead(ListenSocket *connection,
- const std::string& data) {
- ReportAction(ListenSocketTestAction(ACTION_READ, data));
-}
-
-void DevToolsRemoteListenSocketTester::DidClose(ListenSocket *sock) {
+void DevToolsRemoteListenSocketTester::OnConnectionLost() {
+ connection_ = NULL;
ReportAction(ListenSocketTestAction(ACTION_CLOSE));
}
diff --git a/chrome/browser/debugger/devtools_remote_listen_socket_unittest.h b/chrome/browser/debugger/devtools_remote_listen_socket_unittest.h
index 91db537..b4987b8 100644
--- a/chrome/browser/debugger/devtools_remote_listen_socket_unittest.h
+++ b/chrome/browser/debugger/devtools_remote_listen_socket_unittest.h
@@ -78,7 +78,6 @@ class ListenSocketTestAction {
// This had to be split out into a separate class because I couldn't
// make a the testing::Test class refcounted.
class DevToolsRemoteListenSocketTester :
- public ListenSocket::ListenSocketDelegate,
public DevToolsRemoteListener {
public:
DevToolsRemoteListenSocketTester()
@@ -99,7 +98,8 @@ class DevToolsRemoteListenSocketTester :
// DevToolsRemoteMessageHandler interface
virtual void HandleMessage(const DevToolsRemoteMessage& message);
- virtual void OnConnectionLost() {}
+ virtual void OnAcceptConnection(ListenSocket* connection);
+ virtual void OnConnectionLost();
// read all pending data from the test socket
int ClearTestSocket();
@@ -107,9 +107,6 @@ class DevToolsRemoteListenSocketTester :
void Shutdown();
void Listen();
void SendFromTester();
- virtual void DidAccept(ListenSocket *server, ListenSocket *connection);
- virtual void DidRead(ListenSocket *connection, const std::string& data);
- virtual void DidClose(ListenSocket *sock);
virtual bool Send(SOCKET sock, const std::string& str);
// verify the send/read from client to server
void TestClientSend();
diff --git a/chrome_frame/test/test_server.cc b/chrome_frame/test/test_server.cc
index 942333a..88672b8 100644
--- a/chrome_frame/test/test_server.cc
+++ b/chrome_frame/test/test_server.cc
@@ -172,11 +172,13 @@ void SimpleWebServer::DidAccept(ListenSocket* server,
}
void SimpleWebServer::DidRead(ListenSocket* connection,
- const std::string& data) {
+ const char* data,
+ int len) {
Connection* c = FindConnection(connection);
DCHECK(c);
Request& r = c->request();
- r.OnDataReceived(data);
+ std::string str(data, len);
+ r.OnDataReceived(str);
if (r.AllContentReceived()) {
const Request& request = c->request();
Response* response = FindResponse(request);
@@ -247,11 +249,14 @@ void HTTPTestServer::DidAccept(ListenSocket* server, ListenSocket* socket) {
connection_list_.push_back(new ConfigurableConnection(socket));
}
-void HTTPTestServer::DidRead(ListenSocket* socket, const std::string& data) {
+void HTTPTestServer::DidRead(ListenSocket* socket,
+ const char* data,
+ int len) {
scoped_refptr<ConfigurableConnection> connection =
ConnectionFromSocket(socket);
if (connection) {
- connection->r_.OnDataReceived(data);
+ std::string str(data, len);
+ connection->r_.OnDataReceived(str);
if (connection->r_.AllContentReceived()) {
if (LowerCaseEqualsASCII(connection->r_.method(), "post"))
this->Post(connection, connection->r_.path(), connection->r_);
diff --git a/chrome_frame/test/test_server.h b/chrome_frame/test/test_server.h
index b827ceb..c3fab96 100644
--- a/chrome_frame/test/test_server.h
+++ b/chrome_frame/test/test_server.h
@@ -288,7 +288,7 @@ class SimpleWebServer : public ListenSocket::ListenSocketDelegate {
// ListenSocketDelegate overrides.
virtual void DidAccept(ListenSocket* server, ListenSocket* connection);
- virtual void DidRead(ListenSocket* connection, const std::string& data);
+ virtual void DidRead(ListenSocket* connection, const char* data, int len);
virtual void DidClose(ListenSocket* sock);
const ConnectionList& connections() const {
@@ -381,7 +381,7 @@ class HTTPTestServer : public ListenSocket::ListenSocketDelegate {
virtual void Post(ConfigurableConnection* connection,
const std::string& path, const Request& r) = 0;
-private:
+ private:
typedef std::list<scoped_refptr<ConfigurableConnection> > ConnectionList;
ConnectionList::iterator FindConnection(const ListenSocket* socket);
scoped_refptr<ConfigurableConnection> ConnectionFromSocket(
@@ -389,7 +389,7 @@ private:
// ListenSocketDelegate overrides.
virtual void DidAccept(ListenSocket* server, ListenSocket* socket);
- virtual void DidRead(ListenSocket* socket, const std::string& data);
+ virtual void DidRead(ListenSocket* socket, const char* data, int len);
virtual void DidClose(ListenSocket* socket);
scoped_refptr<ListenSocket> server_;
diff --git a/net/base/listen_socket.cc b/net/base/listen_socket.cc
index c2eb003..0cb529d 100644
--- a/net/base/listen_socket.cc
+++ b/net/base/listen_socket.cc
@@ -31,7 +31,7 @@ typedef int socklen_t;
namespace {
-const int kReadBufSize = 200;
+const int kReadBufSize = 4096;
} // namespace
@@ -160,7 +160,7 @@ void ListenSocket::Read() {
// TODO(ibrar): maybe change DidRead to take a length instead
DCHECK(len > 0 && len <= kReadBufSize);
buf[len] = 0; // already create a buffer with +1 length
- socket_delegate_->DidRead(this, buf);
+ socket_delegate_->DidRead(this, buf, len);
}
} while (len == kReadBufSize);
}
@@ -206,19 +206,32 @@ void ListenSocket::WatchSocket(WaitState state) {
}
void ListenSocket::SendInternal(const char* bytes, int len) {
- int sent = HANDLE_EINTR(send(socket_, bytes, len, 0));
- if (sent == kSocketError) {
+ char* send_buf = const_cast<char *>(bytes);
+ int len_left = len;
+ while (true) {
+ int sent = HANDLE_EINTR(send(socket_, send_buf, len_left, 0));
+ if (sent == len_left) { // A shortcut to avoid extraneous checks.
+ break;
+ }
+ if (sent == kSocketError) {
#if defined(OS_WIN)
- int err = WSAGetLastError();
- if (err == WSAEWOULDBLOCK) {
+ if (WSAGetLastError() != WSAEWOULDBLOCK) {
+ LOG(ERROR) << "send failed: WSAGetLastError()==" << WSAGetLastError();
#elif defined(OS_POSIX)
- if (errno == EWOULDBLOCK || errno == EAGAIN) {
+ if (errno != EWOULDBLOCK && errno != EAGAIN) {
+ LOG(ERROR) << "send failed: errno==" << errno;
#endif
- // TODO(ibrar): there should be logic here to handle this because
- // it is not an error
+ break;
+ }
+ // Otherwise we would block, and now we have to wait for a retry.
+ // Fall through to PlatformThread::YieldCurrentThread()
+ } else {
+ // sent != len_left according to the shortcut above.
+ // Shift the buffer start and send the remainder after a short while.
+ send_buf += sent;
+ len_left -= sent;
}
- } else if (sent != len) {
- LOG(ERROR) << "send failed: ";
+ PlatformThread::YieldCurrentThread();
}
}
diff --git a/net/base/listen_socket.h b/net/base/listen_socket.h
index 585a094..923d361 100644
--- a/net/base/listen_socket.h
+++ b/net/base/listen_socket.h
@@ -51,7 +51,9 @@ class ListenSocket : public base::RefCountedThreadSafe<ListenSocket>,
// Socket that was created. Ownership of connection is transferred
// to the delegate with this call.
virtual void DidAccept(ListenSocket *server, ListenSocket *connection) = 0;
- virtual void DidRead(ListenSocket *connection, const std::string& data) = 0;
+ virtual void DidRead(ListenSocket *connection,
+ const char* data,
+ int len) = 0;
virtual void DidClose(ListenSocket *sock) = 0;
};
diff --git a/net/base/listen_socket_unittest.cc b/net/base/listen_socket_unittest.cc
index ed2b566..f33ac3c 100644
--- a/net/base/listen_socket_unittest.cc
+++ b/net/base/listen_socket_unittest.cc
@@ -212,8 +212,10 @@ void ListenSocketTester::DidAccept(ListenSocket *server,
}
void ListenSocketTester::DidRead(ListenSocket *connection,
- const std::string& data) {
- ReportAction(ListenSocketTestAction(ACTION_READ, data));
+ const char* data,
+ int len) {
+ std::string str(data, len);
+ ReportAction(ListenSocketTestAction(ACTION_READ, str));
}
void ListenSocketTester::DidClose(ListenSocket *sock) {
diff --git a/net/base/listen_socket_unittest.h b/net/base/listen_socket_unittest.h
index 87bb24b..7adea1e 100644
--- a/net/base/listen_socket_unittest.h
+++ b/net/base/listen_socket_unittest.h
@@ -94,7 +94,7 @@ class ListenSocketTester :
void Listen();
void SendFromTester();
virtual void DidAccept(ListenSocket *server, ListenSocket *connection);
- virtual void DidRead(ListenSocket *connection, const std::string& data);
+ virtual void DidRead(ListenSocket *connection, const char* data, int len);
virtual void DidClose(ListenSocket *sock);
virtual bool Send(SOCKET sock, const std::string& str);
// verify the send/read from client to server
diff --git a/net/base/telnet_server.cc b/net/base/telnet_server.cc
index 33eb5b1..5b027d7 100644
--- a/net/base/telnet_server.cc
+++ b/net/base/telnet_server.cc
@@ -197,7 +197,9 @@ void TelnetServer::StateMachineStep(unsigned char c) {
case EXPECTING_NEW_LINE:
if (c == TelnetProtocol::LF) {
Send("\n", 1);
- socket_delegate_->DidRead(this, command_line_);
+ socket_delegate_->DidRead(this,
+ command_line_.c_str(),
+ command_line_.length());
command_line_ = "";
}
input_state_ = NOT_IN_IAC_OR_ESC_SEQUENCE;
diff --git a/net/net.gyp b/net/net.gyp
index 96aeb1f..d6f1921 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -987,6 +987,21 @@
],
},
{
+ 'target_name': 'http_listen_socket',
+ 'type': '<(library)',
+ 'dependencies': [
+ 'net',
+ '../base/base.gyp:base',
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'msvs_guid': 'FCB894A4-CC6C-48C2-B495-52C80527E9BE',
+ 'sources': [
+ 'server/http_listen_socket.cc',
+ 'server/http_listen_socket.h',
+ 'server/http_server_request_info.h',
+ ],
+ },
+ {
'target_name': 'hresolv',
'type': 'executable',
'dependencies': [
diff --git a/net/server/http_listen_socket.cc b/net/server/http_listen_socket.cc
new file mode 100644
index 0000000..8d73abc
--- /dev/null
+++ b/net/server/http_listen_socket.cc
@@ -0,0 +1,316 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifdef _WIN32
+#include <winsock2.h>
+#else
+#include <arpa/inet.h>
+#endif
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/md5.h"
+#include "base/string_util.h"
+#include "net/server/http_listen_socket.h"
+#include "net/server/http_server_request_info.h"
+
+// must run in the IO thread
+HttpListenSocket::HttpListenSocket(SOCKET s,
+ HttpListenSocket::Delegate* delegate)
+ : ALLOW_THIS_IN_INITIALIZER_LIST(ListenSocket(s, this)),
+ delegate_(delegate),
+ is_web_socket_(false) {
+}
+
+// must run in the IO thread
+HttpListenSocket::~HttpListenSocket() {
+}
+
+void HttpListenSocket::Accept() {
+ SOCKET conn = ListenSocket::Accept(socket_);
+ DCHECK_NE(conn, ListenSocket::kInvalidSocket);
+ if (conn == ListenSocket::kInvalidSocket) {
+ // TODO
+ } else {
+ scoped_refptr<HttpListenSocket> sock =
+ new HttpListenSocket(conn, delegate_);
+ // it's up to the delegate to AddRef if it wants to keep it around
+ DidAccept(this, sock);
+ }
+}
+
+HttpListenSocket* HttpListenSocket::Listen(
+ const std::string& ip,
+ int port,
+ HttpListenSocket::Delegate* delegate) {
+ SOCKET s = ListenSocket::Listen(ip, port);
+ if (s == ListenSocket::kInvalidSocket) {
+ // TODO (ibrar): error handling
+ } else {
+ HttpListenSocket *serv = new HttpListenSocket(s, delegate);
+ serv->Listen();
+ return serv;
+ }
+ return NULL;
+}
+
+std::string GetHeaderValue(
+ HttpServerRequestInfo* request,
+ const std::string& header_name) {
+ HttpServerRequestInfo::HeadersMap::iterator it =
+ request->headers.find(header_name);
+ if (it != request->headers.end())
+ return it->second;
+ return "";
+}
+
+uint32 WebSocketKeyFingerprint(const std::string& str) {
+ std::string result;
+ const char* pChar = str.c_str();
+ int length = str.length();
+ int spaces = 0;
+ for (int i = 0; i < length; ++i) {
+ if (pChar[i] >= '0' && pChar[i] <= '9')
+ result.append(&pChar[i], 1);
+ else if (pChar[i] == ' ')
+ spaces++;
+ }
+ if (spaces == 0)
+ return 0;
+ int64 number = 0;
+ if (!StringToInt64(result, &number))
+ return 0;
+ return htonl(static_cast<uint32>(number / spaces));
+}
+
+void HttpListenSocket::AcceptWebSocket(HttpServerRequestInfo* request) {
+ std::string key1 = GetHeaderValue(request, "Sec-WebSocket-Key1");
+ std::string key2 = GetHeaderValue(request, "Sec-WebSocket-Key2");
+
+ uint32 fp1 = WebSocketKeyFingerprint(key1);
+ uint32 fp2 = WebSocketKeyFingerprint(key2);
+
+ char data[16];
+ memcpy(data, &fp1, 4);
+ memcpy(data + 4, &fp2, 4);
+ memcpy(data + 8, &request->data[0], 8);
+
+ MD5Digest digest;
+ MD5Sum(data, 16, &digest);
+
+ std::string origin = GetHeaderValue(request, "Origin");
+ std::string host = GetHeaderValue(request, "Host");
+ std::string location = "ws://" + host + request->path;
+ is_web_socket_ = true;
+ Send(StringPrintf("HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
+ "Upgrade: WebSocket\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Origin: %s\r\n"
+ "Sec-WebSocket-Location: %s\r\n"
+ "\r\n",
+ origin.c_str(),
+ location.c_str()));
+ Send(reinterpret_cast<char*>(digest.a), 16);
+}
+
+void HttpListenSocket::SendOverWebSocket(const std::string& data) {
+ DCHECK(is_web_socket_);
+ char message_start = 0;
+ char message_end = -1;
+ Send(&message_start, 1);
+ Send(data);
+ Send(&message_end, 1);
+}
+
+//
+// HTTP Request Parser
+// This HTTP request parser uses a simple state machine to quickly parse
+// through the headers. The parser is not 100% complete, as it is designed
+// for use in this simple test driver.
+//
+// Known issues:
+// - does not handle whitespace on first HTTP line correctly. Expects
+// a single space between the method/url and url/protocol.
+
+// Input character types.
+enum header_parse_inputs {
+ INPUT_SPACE,
+ INPUT_CR,
+ INPUT_LF,
+ INPUT_COLON,
+ INPUT_00,
+ INPUT_FF,
+ INPUT_DEFAULT,
+ MAX_INPUTS,
+};
+
+// Parser states.
+enum header_parse_states {
+ ST_METHOD, // Receiving the method
+ ST_URL, // Receiving the URL
+ ST_PROTO, // Receiving the protocol
+ ST_HEADER, // Starting a Request Header
+ ST_NAME, // Receiving a request header name
+ ST_SEPARATOR, // Receiving the separator between header name and value
+ ST_VALUE, // Receiving a request header value
+ ST_WS_READY, // Ready to receive web socket frame
+ ST_WS_FRAME, // Receiving WebSocket frame
+ ST_WS_CLOSE, // Closing the connection WebSocket connection
+ ST_DONE, // Parsing is complete and successful
+ ST_ERR, // Parsing encountered invalid syntax.
+ MAX_STATES
+};
+
+// State transition table
+int parser_state[MAX_STATES][MAX_INPUTS] = {
+/* METHOD */ { ST_URL, ST_ERR, ST_ERR, ST_ERR, ST_ERR, ST_ERR, ST_METHOD },
+/* URL */ { ST_PROTO, ST_ERR, ST_ERR, ST_URL, ST_ERR, ST_ERR, ST_URL },
+/* PROTOCOL */ { ST_ERR, ST_HEADER, ST_NAME, ST_ERR, ST_ERR, ST_ERR, ST_PROTO },
+/* HEADER */ { ST_ERR, ST_ERR, ST_NAME, ST_ERR, ST_ERR, ST_ERR, ST_ERR },
+/* NAME */ { ST_SEPARATOR, ST_DONE, ST_ERR, ST_SEPARATOR, ST_ERR, ST_ERR, ST_NAME },
+/* SEPARATOR */ { ST_SEPARATOR, ST_ERR, ST_ERR, ST_SEPARATOR, ST_ERR, ST_ERR, ST_VALUE },
+/* VALUE */ { ST_VALUE, ST_HEADER, ST_NAME, ST_VALUE, ST_ERR, ST_ERR, ST_VALUE },
+/* WS_READY */ { ST_ERR, ST_ERR, ST_ERR, ST_ERR, ST_WS_FRAME, ST_WS_CLOSE, ST_ERR},
+/* WS_FRAME */ { ST_WS_FRAME, ST_WS_FRAME, ST_WS_FRAME, ST_WS_FRAME, ST_ERR, ST_WS_READY, ST_WS_FRAME },
+/* WS_CLOSE */ { ST_ERR, ST_ERR, ST_ERR, ST_ERR, ST_WS_CLOSE, ST_ERR, ST_ERR },
+/* DONE */ { ST_DONE, ST_DONE, ST_DONE, ST_DONE, ST_DONE, ST_DONE, ST_DONE },
+/* ERR */ { ST_ERR, ST_ERR, ST_ERR, ST_ERR, ST_ERR, ST_ERR, ST_ERR }
+};
+
+// Convert an input character to the parser's input token.
+int charToInput(char ch) {
+ switch(ch) {
+ case ' ':
+ return INPUT_SPACE;
+ case '\r':
+ return INPUT_CR;
+ case '\n':
+ return INPUT_LF;
+ case ':':
+ return INPUT_COLON;
+ case 0x0:
+ return INPUT_00;
+ case -1:
+ return INPUT_FF;
+ }
+ return INPUT_DEFAULT;
+}
+
+HttpServerRequestInfo* HttpListenSocket::ParseHeaders() {
+ int pos = 0;
+ int data_len = recv_data_.length();
+ int state = is_web_socket_ ? ST_WS_READY : ST_METHOD;
+ scoped_ptr<HttpServerRequestInfo> info(new HttpServerRequestInfo());
+ std::string buffer;
+ std::string header_name;
+ std::string header_value;
+ while (pos < data_len) {
+ char ch = recv_data_[pos++];
+ int input = charToInput(ch);
+ int next_state = parser_state[state][input];
+
+ bool transition = (next_state != state);
+ if (transition) {
+ // Do any actions based on state transitions.
+ switch (state) {
+ case ST_METHOD:
+ info->method = buffer;
+ buffer.clear();
+ break;
+ case ST_URL:
+ info->path = buffer;
+ buffer.clear();
+ break;
+ case ST_PROTO:
+ // TODO(mbelshe): Deal better with parsing protocol.
+ DCHECK(buffer == "HTTP/1.1");
+ buffer.clear();
+ break;
+ case ST_NAME:
+ header_name = buffer;
+ buffer.clear();
+ break;
+ case ST_VALUE:
+ header_value = buffer;
+ // TODO(mbelshe): Deal better with duplicate headers
+ DCHECK(info->headers.find(header_name) == info->headers.end());
+ info->headers[header_name] = header_value;
+ buffer.clear();
+ break;
+ case ST_SEPARATOR:
+ buffer.append(&ch, 1);
+ break;
+ case ST_WS_FRAME:
+ recv_data_ = recv_data_.substr(pos);
+ info->data = buffer;
+ buffer.clear();
+ return info.release();
+ break;
+ }
+ state = next_state;
+ } else {
+ // Do any actions based on current state
+ switch (state) {
+ case ST_METHOD:
+ case ST_URL:
+ case ST_PROTO:
+ case ST_VALUE:
+ case ST_NAME:
+ case ST_WS_FRAME:
+ buffer.append(&ch, 1);
+ break;
+ case ST_DONE:
+ recv_data_ = recv_data_.substr(pos);
+ info->data = recv_data_;
+ recv_data_.clear();
+ return info.release();
+ case ST_WS_CLOSE:
+ is_web_socket_ = false;
+ return NULL;
+ case ST_ERR:
+ return NULL;
+ }
+ }
+ }
+ // No more characters, but we haven't finished parsing yet.
+ return NULL;
+}
+
+void HttpListenSocket::DidAccept(ListenSocket* server,
+ ListenSocket* connection) {
+ connection->AddRef();
+}
+
+void HttpListenSocket::DidRead(ListenSocket*,
+ const char* data,
+ int len) {
+ recv_data_.append(data, len);
+ while (recv_data_.length()) {
+ scoped_ptr<HttpServerRequestInfo> request(ParseHeaders());
+ if (!request.get())
+ break;
+
+ if (is_web_socket_) {
+ delegate_->OnWebSocketMessage(this, request->data);
+ continue;
+ }
+
+ std::string connection = GetHeaderValue(request.get(), "Connection");
+ if (connection == "Upgrade") {
+ // Is this WebSocket and if yes, upgrade the connection.
+ std::string key1 = GetHeaderValue(request.get(), "Sec-WebSocket-Key1");
+ std::string key2 = GetHeaderValue(request.get(), "Sec-WebSocket-Key2");
+ if (!key1.empty() && !key2.empty()) {
+ delegate_->OnWebSocketRequest(this, request.get());
+ continue;
+ }
+ }
+ delegate_->OnHttpRequest(this, request.get());
+ }
+}
+
+void HttpListenSocket::DidClose(ListenSocket* sock) {
+ sock->Release();
+ delegate_->OnClose(this);
+}
diff --git a/net/server/http_listen_socket.h b/net/server/http_listen_socket.h
new file mode 100644
index 0000000..5517af0
--- /dev/null
+++ b/net/server/http_listen_socket.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_SERVER_HTTP_LISTEN_SOCKET_H_
+#define NET_SERVER_HTTP_LISTEN_SOCKET_H_
+
+#include "net/base/listen_socket.h"
+
+class HttpServerRequestInfo;
+
+// Implements a simple HTTP listen socket on top of the raw socket interface.
+class HttpListenSocket : public ListenSocket,
+ public ListenSocket::ListenSocketDelegate {
+ public:
+ class Delegate {
+ public:
+ virtual void OnHttpRequest(HttpListenSocket* socket,
+ HttpServerRequestInfo* info) = 0;
+
+ virtual void OnWebSocketRequest(HttpListenSocket* socket,
+ HttpServerRequestInfo* info) = 0;
+
+ virtual void OnWebSocketMessage(HttpListenSocket* socket,
+ const std::string& data) = 0;
+
+ virtual void OnClose(HttpListenSocket* socket) = 0;
+ protected:
+ virtual ~Delegate() {}
+ };
+
+ static HttpListenSocket* Listen(const std::string& ip,
+ int port,
+ HttpListenSocket::Delegate* delegate);
+
+ void AcceptWebSocket(HttpServerRequestInfo* request);
+
+ void SendOverWebSocket(const std::string& data);
+
+ void Listen() { ListenSocket::Listen(); }
+ virtual void Accept();
+
+ // ListenSocketDelegate
+ virtual void DidAccept(ListenSocket* server, ListenSocket* connection);
+ virtual void DidRead(ListenSocket* connection, const char* data, int len);
+ virtual void DidClose(ListenSocket* sock);
+
+ private:
+ static const int kReadBufSize = 16 * 1024;
+ HttpListenSocket(SOCKET s, HttpListenSocket::Delegate* del);
+ virtual ~HttpListenSocket();
+
+ // Expects the raw data to be stored in recv_data_. If parsing is successful,
+ // will remove the data parsed from recv_data_, leaving only the unused
+ // recv data.
+ HttpServerRequestInfo* ParseHeaders();
+
+ HttpListenSocket::Delegate* delegate_;
+ bool is_web_socket_;
+ std::string recv_data_;
+
+ DISALLOW_COPY_AND_ASSIGN(HttpListenSocket);
+};
+
+#endif // NET_SERVER_HTTP_LISTEN_SOCKET_H_
diff --git a/net/server/http_server_request_info.h b/net/server/http_server_request_info.h
new file mode 100644
index 0000000..64f0a78a
--- /dev/null
+++ b/net/server/http_server_request_info.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_SERVER_HTTP_SERVER_REQUEST_INFO_H_
+#define NET_SERVER_HTTP_SERVER_REQUEST_INFO_H_
+
+#include <string>
+
+#include "net/http/http_request_info.h"
+
+// Meta information about an HTTP request.
+// This is geared toward servers in that it keeps a map of the headers and
+// values rather than just a list of header strings (which net::HttpRequestInfo
+// does).
+class HttpServerRequestInfo {
+ public:
+ HttpServerRequestInfo() {}
+
+ // Request method.
+ std::string method;
+
+ // Request line.
+ std::string path;
+
+ // Request data.
+ std::string data;
+
+ // A map of the names -> values for HTTP headers.
+ typedef std::map<std::string, std::string> HeadersMap;
+ HeadersMap headers;
+};
+
+#endif // NET_SERVER_HTTP_SERVER_REQUEST_INFO_H_
diff --git a/net/socket/tcp_client_socket_unittest.cc b/net/socket/tcp_client_socket_unittest.cc
index 30628d3..52cdb88 100644
--- a/net/socket/tcp_client_socket_unittest.cc
+++ b/net/socket/tcp_client_socket_unittest.cc
@@ -33,7 +33,7 @@ class TCPClientSocketTest
virtual void DidAccept(ListenSocket* server, ListenSocket* connection) {
connected_sock_ = connection;
}
- virtual void DidRead(ListenSocket*, const std::string& s) {
+ virtual void DidRead(ListenSocket*, const char* str, int len) {
// TODO(dkegel): this might not be long enough to tickle some bugs.
connected_sock_->Send(kServerReply,
arraysize(kServerReply) - 1,
diff --git a/net/socket/tcp_pinger_unittest.cc b/net/socket/tcp_pinger_unittest.cc
index 8cd4ed4..63e0045 100644
--- a/net/socket/tcp_pinger_unittest.cc
+++ b/net/socket/tcp_pinger_unittest.cc
@@ -28,7 +28,7 @@ class TCPPingerTest
LOG(INFO) << "TCPPinger accepted connection";
connected_sock_ = connection;
}
- virtual void DidRead(ListenSocket*, const std::string& s) {
+ virtual void DidRead(ListenSocket*, const char* str, int len) {
// Not really needed yet, as TCPPinger doesn't support Read
connected_sock_->Send(std::string("HTTP/1.1 404 Not Found"), true);
connected_sock_ = NULL;
diff --git a/net/tools/fetch/http_listen_socket.cc b/net/tools/fetch/http_listen_socket.cc
index 033afc4..e135322 100644
--- a/net/tools/fetch/http_listen_socket.cc
+++ b/net/tools/fetch/http_listen_socket.cc
@@ -182,8 +182,9 @@ void HttpListenSocket::DidAccept(ListenSocket* server,
}
void HttpListenSocket::DidRead(ListenSocket* connection,
- const std::string& data) {
- recv_data_ += data;
+ const char* data,
+ int len) {
+ recv_data_.append(data, len);
while (recv_data_.length()) {
HttpServerRequestInfo* request = ParseHeaders();
if (!request)
diff --git a/net/tools/fetch/http_listen_socket.h b/net/tools/fetch/http_listen_socket.h
index 15df455..a1b77c5e 100644
--- a/net/tools/fetch/http_listen_socket.h
+++ b/net/tools/fetch/http_listen_socket.h
@@ -36,7 +36,7 @@ class HttpListenSocket : public ListenSocket,
// ListenSocketDelegate
virtual void DidAccept(ListenSocket* server, ListenSocket* connection);
- virtual void DidRead(ListenSocket* connection, const std::string& data);
+ virtual void DidRead(ListenSocket* connection, const char* data, int len);
virtual void DidClose(ListenSocket* sock);
private: