| // Copyright (c) 2012 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. |
| |
| #include "content/browser/devtools/tethering_handler.h" |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/stl_util.h" |
| #include "base/values.h" |
| #include "content/browser/devtools/devtools_http_handler_impl.h" |
| #include "content/public/browser/devtools_client_host.h" |
| #include "content/public/browser/devtools_http_handler_delegate.h" |
| #include "net/base/io_buffer.h" |
| #include "net/base/ip_endpoint.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/net_log.h" |
| #include "net/socket/stream_listen_socket.h" |
| #include "net/socket/stream_socket.h" |
| #include "net/socket/tcp_server_socket.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| const char kTetheringBind[] = "Tethering.bind"; |
| const char kTetheringUnbind[] = "Tethering.unbind"; |
| |
| const char kTetheringAccepted[] = "Tethering.accepted"; |
| |
| const char kPortParam[] = "port"; |
| const char kConnectionIdParam[] = "connectionId"; |
| |
| const char kLocalhost[] = "127.0.0.1"; |
| |
| const int kListenBacklog = 5; |
| const int kBufferSize = 16 * 1024; |
| |
| const int kMinTetheringPort = 5000; |
| const int kMaxTetheringPort = 10000; |
| |
| class SocketPump : public net::StreamListenSocket::Delegate { |
| public: |
| SocketPump(DevToolsHttpHandlerDelegate* delegate, |
| net::StreamSocket* client_socket) |
| : client_socket_(client_socket), |
| delegate_(delegate), |
| wire_buffer_size_(0), |
| pending_destruction_(false) { |
| } |
| |
| std::string Init() { |
| std::string channel_name; |
| server_socket_ = delegate_->CreateSocketForTethering(this, &channel_name); |
| if (!server_socket_.get() || channel_name.empty()) |
| SelfDestruct(); |
| return channel_name; |
| } |
| |
| virtual ~SocketPump() { } |
| |
| private: |
| virtual void DidAccept(net::StreamListenSocket* server, |
| scoped_ptr<net::StreamListenSocket> socket) OVERRIDE { |
| if (accepted_socket_.get()) |
| return; |
| |
| buffer_ = new net::IOBuffer(kBufferSize); |
| wire_buffer_ = new net::GrowableIOBuffer(); |
| wire_buffer_->SetCapacity(kBufferSize); |
| |
| accepted_socket_ = socket.Pass(); |
| int result = client_socket_->Read( |
| buffer_.get(), |
| kBufferSize, |
| base::Bind(&SocketPump::OnClientRead, base::Unretained(this))); |
| if (result != net::ERR_IO_PENDING) |
| OnClientRead(result); |
| } |
| |
| virtual void DidRead(net::StreamListenSocket* socket, |
| const char* data, |
| int len) OVERRIDE { |
| int old_size = wire_buffer_size_; |
| wire_buffer_size_ += len; |
| while (wire_buffer_->capacity() < wire_buffer_size_) |
| wire_buffer_->SetCapacity(wire_buffer_->capacity() * 2); |
| memcpy(wire_buffer_->StartOfBuffer() + old_size, data, len); |
| if (old_size != wire_buffer_->offset()) |
| return; |
| OnClientWrite(0); |
| } |
| |
| virtual void DidClose(net::StreamListenSocket* socket) OVERRIDE { |
| SelfDestruct(); |
| } |
| |
| void OnClientRead(int result) { |
| if (result <= 0) { |
| SelfDestruct(); |
| return; |
| } |
| |
| accepted_socket_->Send(buffer_->data(), result); |
| result = client_socket_->Read( |
| buffer_.get(), |
| kBufferSize, |
| base::Bind(&SocketPump::OnClientRead, base::Unretained(this))); |
| if (result != net::ERR_IO_PENDING) |
| OnClientRead(result); |
| } |
| |
| void OnClientWrite(int result) { |
| if (result < 0) |
| SelfDestruct(); |
| |
| wire_buffer_->set_offset(wire_buffer_->offset() + result); |
| |
| int remaining = wire_buffer_size_ - wire_buffer_->offset(); |
| if (remaining == 0) { |
| if (pending_destruction_) |
| SelfDestruct(); |
| return; |
| } |
| |
| |
| if (remaining > kBufferSize) |
| remaining = kBufferSize; |
| |
| scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(remaining); |
| memcpy(buffer->data(), wire_buffer_->data(), remaining); |
| result = client_socket_->Write( |
| buffer.get(), |
| remaining, |
| base::Bind(&SocketPump::OnClientWrite, base::Unretained(this))); |
| |
| // Shrink buffer |
| int offset = wire_buffer_->offset(); |
| if (offset > kBufferSize) { |
| memcpy(wire_buffer_->StartOfBuffer(), wire_buffer_->data(), |
| wire_buffer_size_ - offset); |
| wire_buffer_size_ -= offset; |
| wire_buffer_->set_offset(0); |
| } |
| |
| if (result != net::ERR_IO_PENDING) |
| OnClientWrite(result); |
| return; |
| } |
| |
| void SelfDestruct() { |
| if (wire_buffer_ && wire_buffer_->offset() != wire_buffer_size_) { |
| pending_destruction_ = true; |
| return; |
| } |
| delete this; |
| } |
| |
| private: |
| scoped_ptr<net::StreamSocket> client_socket_; |
| scoped_ptr<net::StreamListenSocket> server_socket_; |
| scoped_ptr<net::StreamListenSocket> accepted_socket_; |
| scoped_refptr<net::IOBuffer> buffer_; |
| scoped_refptr<net::GrowableIOBuffer> wire_buffer_; |
| DevToolsHttpHandlerDelegate* delegate_; |
| int wire_buffer_size_; |
| bool pending_destruction_; |
| }; |
| |
| } // namespace |
| |
| const char TetheringHandler::kDomain[] = "Tethering"; |
| |
| class TetheringHandler::BoundSocket { |
| public: |
| BoundSocket(TetheringHandler* handler, |
| DevToolsHttpHandlerDelegate* delegate) |
| : handler_(handler), |
| delegate_(delegate), |
| socket_(new net::TCPServerSocket(NULL, net::NetLog::Source())), |
| port_(0) { |
| } |
| |
| virtual ~BoundSocket() { |
| } |
| |
| bool Listen(int port) { |
| port_ = port; |
| net::IPAddressNumber ip_number; |
| if (!net::ParseIPLiteralToNumber(kLocalhost, &ip_number)) |
| return false; |
| |
| net::IPEndPoint end_point(ip_number, port); |
| int result = socket_->Listen(end_point, kListenBacklog); |
| if (result < 0) |
| return false; |
| |
| net::IPEndPoint local_address; |
| result = socket_->GetLocalAddress(&local_address); |
| if (result < 0) |
| return false; |
| |
| DoAccept(); |
| return true; |
| } |
| |
| private: |
| typedef std::map<net::IPEndPoint, net::StreamSocket*> AcceptedSocketsMap; |
| |
| void DoAccept() { |
| while (true) { |
| int result = socket_->Accept( |
| &accept_socket_, |
| base::Bind(&BoundSocket::OnAccepted, base::Unretained(this))); |
| if (result == net::ERR_IO_PENDING) |
| break; |
| else |
| HandleAcceptResult(result); |
| } |
| } |
| |
| void OnAccepted(int result) { |
| HandleAcceptResult(result); |
| if (result == net::OK) |
| DoAccept(); |
| } |
| |
| void HandleAcceptResult(int result) { |
| if (result != net::OK) |
| return; |
| |
| SocketPump* pump = new SocketPump(delegate_, accept_socket_.release()); |
| std::string name = pump->Init(); |
| if (!name.empty()) |
| handler_->Accepted(port_, name); |
| } |
| |
| TetheringHandler* handler_; |
| DevToolsHttpHandlerDelegate* delegate_; |
| scoped_ptr<net::ServerSocket> socket_; |
| scoped_ptr<net::StreamSocket> accept_socket_; |
| int port_; |
| }; |
| |
| TetheringHandler::TetheringHandler(DevToolsHttpHandlerDelegate* delegate) |
| : delegate_(delegate) { |
| RegisterCommandHandler(kTetheringBind, |
| base::Bind(&TetheringHandler::OnBind, |
| base::Unretained(this))); |
| RegisterCommandHandler(kTetheringUnbind, |
| base::Bind(&TetheringHandler::OnUnbind, |
| base::Unretained(this))); |
| } |
| |
| TetheringHandler::~TetheringHandler() { |
| STLDeleteContainerPairSecondPointers(bound_sockets_.begin(), |
| bound_sockets_.end()); |
| } |
| |
| void TetheringHandler::Accepted(int port, const std::string& name) { |
| base::DictionaryValue* params = new base::DictionaryValue(); |
| params->SetInteger(kPortParam, port); |
| params->SetString(kConnectionIdParam, name); |
| SendNotification(kTetheringAccepted, params); |
| } |
| |
| static int GetPort(scoped_refptr<DevToolsProtocol::Command> command) { |
| base::DictionaryValue* params = command->params(); |
| int port = 0; |
| if (!params || !params->GetInteger(kPortParam, &port) || |
| port < kMinTetheringPort || port > kMaxTetheringPort) |
| return 0; |
| return port; |
| } |
| |
| scoped_refptr<DevToolsProtocol::Response> |
| TetheringHandler::OnBind(scoped_refptr<DevToolsProtocol::Command> command) { |
| int port = GetPort(command); |
| if (port == 0) |
| return command->InvalidParamResponse(kPortParam); |
| |
| if (bound_sockets_.find(port) != bound_sockets_.end()) |
| return command->InternalErrorResponse("Port already bound"); |
| |
| scoped_ptr<BoundSocket> bound_socket(new BoundSocket(this, delegate_)); |
| if (!bound_socket->Listen(port)) |
| return command->InternalErrorResponse("Could not bind port"); |
| |
| bound_sockets_[port] = bound_socket.release(); |
| return command->SuccessResponse(NULL); |
| } |
| |
| scoped_refptr<DevToolsProtocol::Response> |
| TetheringHandler::OnUnbind(scoped_refptr<DevToolsProtocol::Command> command) { |
| int port = GetPort(command); |
| if (port == 0) |
| return command->InvalidParamResponse(kPortParam); |
| |
| BoundSockets::iterator it = bound_sockets_.find(port); |
| if (it == bound_sockets_.end()) |
| return command->InternalErrorResponse("Port is not bound"); |
| |
| delete it->second; |
| bound_sockets_.erase(it); |
| return command->SuccessResponse(NULL); |
| } |
| |
| } // namespace content |