diff options
author | pfeldman@chromium.org <pfeldman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-05 11:32:40 +0000 |
---|---|---|
committer | pfeldman@chromium.org <pfeldman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-05 11:32:40 +0000 |
commit | 0569d86c57dd9385556e4ab4792e74d552c9df59 (patch) | |
tree | b197fd2a9f1296b7ba49601ba0f31b3ba540de1a /net | |
parent | ca311ce0f5002da40625bf87a24858d294d1d1bc (diff) | |
download | chromium_src-0569d86c57dd9385556e4ab4792e74d552c9df59.zip chromium_src-0569d86c57dd9385556e4ab4792e74d552c9df59.tar.gz chromium_src-0569d86c57dd9385556e4ab4792e74d552c9df59.tar.bz2 |
Brushed up listen socket:
- Upstreamed support for partial results from devtools' version
- Made DidRead receive data and length (in order to support websockets data)
- Fixed all the clients.
Added net/server with http socket implementation that supports websockets. Will remove net/tools fetch client and server later.
Review URL: http://codereview.chromium.org/2868036
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@51635 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/base/listen_socket.cc | 35 | ||||
-rw-r--r-- | net/base/listen_socket.h | 4 | ||||
-rw-r--r-- | net/base/listen_socket_unittest.cc | 6 | ||||
-rw-r--r-- | net/base/listen_socket_unittest.h | 2 | ||||
-rw-r--r-- | net/base/telnet_server.cc | 4 | ||||
-rw-r--r-- | net/net.gyp | 15 | ||||
-rw-r--r-- | net/server/http_listen_socket.cc | 316 | ||||
-rw-r--r-- | net/server/http_listen_socket.h | 65 | ||||
-rw-r--r-- | net/server/http_server_request_info.h | 34 | ||||
-rw-r--r-- | net/socket/tcp_client_socket_unittest.cc | 2 | ||||
-rw-r--r-- | net/socket/tcp_pinger_unittest.cc | 2 | ||||
-rw-r--r-- | net/tools/fetch/http_listen_socket.cc | 5 | ||||
-rw-r--r-- | net/tools/fetch/http_listen_socket.h | 2 |
13 files changed, 471 insertions, 21 deletions
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: |