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