devtools_remote_listen_socket.cc revision 513209b27ff55e2841eac0e4120199c23acce758
1// Copyright (c) 2009 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/debugger/devtools_remote_listen_socket.h" 6 7#include "build/build_config.h" 8 9#include <stdlib.h> 10 11#if defined(OS_WIN) 12// winsock2.h must be included first in order to ensure it is included before 13// windows.h. 14#include <winsock2.h> 15#elif defined(OS_POSIX) 16#include <errno.h> 17#include <sys/socket.h> 18#include "base/message_loop.h" 19#include "base/message_pump_libevent.h" 20#include "net/base/net_errors.h" 21#endif 22 23#include "base/compiler_specific.h" 24#include "base/eintr_wrapper.h" 25#include "base/platform_thread.h" 26#include "base/string_number_conversions.h" 27#include "chrome/browser/debugger/devtools_remote.h" 28#include "chrome/browser/debugger/devtools_remote_message.h" 29 30#define CONSUME_BUFFER_CHAR \ 31 pBuf++;\ 32 len-- 33 34#if defined(OS_POSIX) 35// Used same name as in Windows to avoid #ifdef where refrenced 36#define SOCKET int 37const int INVALID_SOCKET = -1; 38const int SOCKET_ERROR = -1; 39#endif 40 41const int kReadBufSize = 200; 42 43DevToolsRemoteListenSocket::DevToolsRemoteListenSocket( 44 SOCKET s, 45 DevToolsRemoteListener* message_listener) 46 : ALLOW_THIS_IN_INITIALIZER_LIST(ListenSocket(s, this)), 47 state_(HANDSHAKE), 48 remaining_payload_length_(0), 49 message_listener_(message_listener), 50 cr_received_(false) {} 51 52void DevToolsRemoteListenSocket::StartNextField() { 53 switch (state_) { 54 case INVALID: 55 state_ = HANDSHAKE; 56 break; 57 case HANDSHAKE: 58 state_ = HEADERS; 59 break; 60 case HEADERS: 61 if (protocol_field_.size() == 0) { // empty line - end of headers 62 const std::string& payload_length_string = GetHeader( 63 DevToolsRemoteMessageHeaders::kContentLength, "0"); 64 base::StringToInt(payload_length_string, &remaining_payload_length_); 65 state_ = PAYLOAD; 66 if (remaining_payload_length_ == 0) { // no payload 67 DispatchField(); 68 return; 69 } 70 } 71 break; 72 case PAYLOAD: 73 header_map_.clear(); 74 payload_.clear(); 75 state_ = HEADERS; 76 break; 77 default: 78 NOTREACHED(); 79 break; 80 } 81 protocol_field_.clear(); 82} 83 84DevToolsRemoteListenSocket::~DevToolsRemoteListenSocket() {} 85 86DevToolsRemoteListenSocket* 87 DevToolsRemoteListenSocket::Listen(const std::string& ip, 88 int port, 89 DevToolsRemoteListener* listener) { 90 SOCKET s = ListenSocket::Listen(ip, port); 91 if (s == INVALID_SOCKET) { 92 // TODO(apavlov): error handling 93 } else { 94 DevToolsRemoteListenSocket* sock = 95 new DevToolsRemoteListenSocket(s, listener); 96 sock->Listen(); 97 return sock; 98 } 99 return NULL; 100} 101 102void DevToolsRemoteListenSocket::DidAccept(ListenSocket *server, 103 ListenSocket *connection) { 104 connection->AddRef(); 105 message_listener_->OnAcceptConnection(connection); 106} 107 108// Dispatches data from socket to socket_delegate_, extracting messages 109// delimited by newlines. 110void DevToolsRemoteListenSocket::DidRead(ListenSocket* connection, 111 const char* pBuf, 112 int len) { 113 while (len > 0) { 114 if (state_ != PAYLOAD) { 115 if (cr_received_ && *pBuf == '\n') { 116 cr_received_ = false; 117 CONSUME_BUFFER_CHAR; 118 } else { 119 while (*pBuf != '\r' && len > 0) { 120 protocol_field_.push_back(*pBuf); 121 CONSUME_BUFFER_CHAR; 122 } 123 if (*pBuf == '\r') { 124 cr_received_ = true; 125 CONSUME_BUFFER_CHAR; 126 } 127 continue; 128 } 129 switch (state_) { 130 case HANDSHAKE: 131 case HEADERS: 132 DispatchField(); 133 break; 134 default: 135 NOTREACHED(); 136 break; 137 } 138 } else { // PAYLOAD 139 while (remaining_payload_length_ > 0 && len > 0) { 140 protocol_field_.push_back(*pBuf); 141 CONSUME_BUFFER_CHAR; 142 remaining_payload_length_--; 143 } 144 if (remaining_payload_length_ == 0) { 145 DispatchField(); 146 } 147 } 148 } 149} 150 151void DevToolsRemoteListenSocket::DidClose(ListenSocket *connection) { 152 message_listener_->OnConnectionLost(); 153 connection->Release(); 154} 155 156void DevToolsRemoteListenSocket::DispatchField() { 157 static const std::string kHandshakeString = "ChromeDevToolsHandshake"; 158 switch (state_) { 159 case HANDSHAKE: 160 if (protocol_field_.compare(kHandshakeString)) { 161 state_ = INVALID; 162 } else { 163 Send(kHandshakeString, true); 164 } 165 break; 166 case HEADERS: { 167 if (protocol_field_.size() > 0) { // not end-of-headers 168 std::string::size_type colon_pos = protocol_field_.find_first_of(":"); 169 if (colon_pos == std::string::npos) { 170 // TODO(apavlov): handle the error (malformed header) 171 } else { 172 const std::string header_name = protocol_field_.substr(0, colon_pos); 173 std::string header_val = protocol_field_.substr(colon_pos + 1); 174 header_map_[header_name] = header_val; 175 } 176 } 177 break; 178 } 179 case PAYLOAD: 180 payload_ = protocol_field_; 181 HandleMessage(); 182 break; 183 default: 184 NOTREACHED(); 185 break; 186 } 187 StartNextField(); 188} 189 190const std::string& DevToolsRemoteListenSocket::GetHeader( 191 const std::string& header_name, 192 const std::string& default_value) const { 193 DevToolsRemoteMessage::HeaderMap::const_iterator it = 194 header_map_.find(header_name); 195 if (it == header_map_.end()) { 196 return default_value; 197 } 198 return it->second; 199} 200 201// Handle header_map_ and payload_ 202void DevToolsRemoteListenSocket::HandleMessage() { 203 if (message_listener_ != NULL) { 204 DevToolsRemoteMessage message(header_map_, payload_); 205 message_listener_->HandleMessage(message); 206 } 207} 208 209void DevToolsRemoteListenSocket::Accept() { 210 SOCKET conn = ListenSocket::Accept(socket_); 211 if (conn != INVALID_SOCKET) { 212 scoped_refptr<DevToolsRemoteListenSocket> sock( 213 new DevToolsRemoteListenSocket(conn, 214 message_listener_)); 215 // it's up to the delegate to AddRef if it wants to keep it around 216#if defined(OS_POSIX) 217 sock->WatchSocket(WAITING_READ); 218#endif 219 socket_delegate_->DidAccept(this, sock); 220 } else { 221 // TODO(apavlov): some error handling required here 222 } 223} 224 225void DevToolsRemoteListenSocket::SendInternal(const char* bytes, int len) { 226 char* send_buf = const_cast<char *>(bytes); 227 int len_left = len; 228 while (true) { 229 int sent = HANDLE_EINTR(send(socket_, send_buf, len_left, 0)); 230 if (sent == len_left) { // A shortcut to avoid extraneous checks. 231 break; 232 } 233 if (sent == kSocketError) { 234#if defined(OS_WIN) 235 if (WSAGetLastError() != WSAEWOULDBLOCK) { 236 LOG(ERROR) << "send failed: WSAGetLastError()==" << WSAGetLastError(); 237#elif defined(OS_POSIX) 238 if (errno != EWOULDBLOCK && errno != EAGAIN) { 239 LOG(ERROR) << "send failed: errno==" << errno; 240#endif 241 break; 242 } 243 // Otherwise we would block, and now we have to wait for a retry. 244 // Fall through to PlatformThread::YieldCurrentThread() 245 } else { 246 // sent != len_left according to the shortcut above. 247 // Shift the buffer start and send the remainder after a short while. 248 send_buf += sent; 249 len_left -= sent; 250 } 251 PlatformThread::YieldCurrentThread(); 252 } 253} 254 255void DevToolsRemoteListenSocket::Close() { 256 ListenSocket::Close(); 257} 258