Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / test / chromedriver / net / port_server.cc
blob32bb4442dcdd5c42cd22f5b2b9fbd99055c870de
1 // Copyright (c) 2013 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.
5 #include "chrome/test/chromedriver/net/port_server.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/logging.h"
10 #include "base/process/process_handle.h"
11 #include "base/rand_util.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/sync_socket.h"
14 #include "chrome/test/chromedriver/chrome/status.h"
15 #include "net/base/net_errors.h"
16 #include "net/base/net_util.h"
17 #include "net/base/sys_addrinfo.h"
18 #include "net/log/net_log.h"
19 #include "net/socket/tcp_server_socket.h"
21 #if defined(OS_LINUX)
22 #include <sys/socket.h>
23 #include <sys/un.h>
24 #endif
26 PortReservation::PortReservation(const base::Closure& on_free_func, uint16 port)
27 : on_free_func_(on_free_func), port_(port) {}
29 PortReservation::~PortReservation() {
30 if (!on_free_func_.is_null())
31 on_free_func_.Run();
34 void PortReservation::Leak() {
35 LOG(ERROR) << "Port leaked: " << port_;
36 on_free_func_.Reset();
39 PortServer::PortServer(const std::string& path) : path_(path) {
40 CHECK(path_.size() && path_[0] == 0)
41 << "path must be for Linux abstract namespace";
44 PortServer::~PortServer() {}
46 Status PortServer::ReservePort(uint16* port,
47 scoped_ptr<PortReservation>* reservation) {
48 uint16 port_to_use = 0;
50 base::AutoLock lock(free_lock_);
51 if (free_.size()) {
52 port_to_use = free_.front();
53 free_.pop_front();
56 if (!port_to_use) {
57 Status status = RequestPort(&port_to_use);
58 if (status.IsError())
59 return status;
61 *port = port_to_use;
62 reservation->reset(new PortReservation(
63 base::Bind(&PortServer::ReleasePort, base::Unretained(this), port_to_use),
64 port_to_use));
65 return Status(kOk);
68 Status PortServer::RequestPort(uint16* port) {
69 // The client sends its PID + \n, and the server responds with a port + \n,
70 // which is valid for the lifetime of the referred process.
71 #if defined(OS_LINUX)
72 int sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
73 if (sock_fd < 0)
74 return Status(kUnknownError, "unable to create socket");
75 base::SyncSocket sock(sock_fd);
76 struct timeval tv;
77 tv.tv_sec = 10;
78 tv.tv_usec = 0;
79 if (setsockopt(sock_fd,
80 SOL_SOCKET,
81 SO_RCVTIMEO,
82 reinterpret_cast<char*>(&tv),
83 sizeof(tv)) < 0 ||
84 setsockopt(sock_fd,
85 SOL_SOCKET,
86 SO_SNDTIMEO,
87 reinterpret_cast<char*>(&tv),
88 sizeof(tv)) < 0) {
89 return Status(kUnknownError, "unable to set socket timeout");
92 struct sockaddr_un addr;
93 memset(&addr, 0, sizeof(addr));
94 addr.sun_family = AF_UNIX;
95 memcpy(addr.sun_path, &path_[0], path_.length());
96 if (connect(sock.handle(),
97 reinterpret_cast<struct sockaddr*>(&addr),
98 sizeof(sa_family_t) + path_.length())) {
99 return Status(kUnknownError, "unable to connect");
102 int proc_id = static_cast<int>(base::GetCurrentProcId());
103 std::string request = base::IntToString(proc_id);
104 request += "\n";
105 VLOG(0) << "PORTSERVER REQUEST " << request;
106 if (sock.Send(request.c_str(), request.length()) != request.length())
107 return Status(kUnknownError, "failed to send portserver request");
109 std::string response;
110 do {
111 char c = 0;
112 size_t rv = sock.Receive(&c, 1);
113 if (!rv)
114 break;
115 response.push_back(c);
116 } while (sock.Peek());
117 if (response.empty())
118 return Status(kUnknownError, "failed to receive portserver response");
119 VLOG(0) << "PORTSERVER RESPONSE " << response;
121 int new_port = 0;
122 if (*response.rbegin() != '\n' ||
123 !base::StringToInt(response.substr(0, response.length() - 1),
124 &new_port) ||
125 new_port < 0 || new_port > 65535)
126 return Status(kUnknownError, "failed to parse portserver response");
127 *port = static_cast<uint16>(new_port);
128 return Status(kOk);
129 #else
130 return Status(kUnknownError, "not implemented for this platform");
131 #endif
134 void PortServer::ReleasePort(uint16 port) {
135 base::AutoLock lock(free_lock_);
136 free_.push_back(port);
139 PortManager::PortManager(uint16 min_port, uint16 max_port)
140 : min_port_(min_port), max_port_(max_port) {
141 CHECK_GE(max_port_, min_port_);
144 PortManager::~PortManager() {}
146 uint16 PortManager::FindAvailablePort() const {
147 uint16 start = static_cast<uint16>(base::RandInt(min_port_, max_port_));
148 bool wrapped = false;
149 for (uint32 try_port = start; try_port != start || !wrapped; ++try_port) {
150 if (try_port > max_port_) {
151 wrapped = true;
152 if (min_port_ == max_port_)
153 break;
154 try_port = min_port_;
156 uint16 try_port_uint16 = static_cast<uint16>(try_port);
157 if (taken_.count(try_port_uint16))
158 continue;
160 char parts[] = {127, 0, 0, 1};
161 net::IPAddressNumber address(parts, parts + arraysize(parts));
162 net::NetLog::Source source;
163 net::TCPServerSocket sock(NULL, source);
164 if (sock.Listen(net::IPEndPoint(address, try_port_uint16), 1) == net::OK)
165 return try_port_uint16;
167 return 0;
170 Status PortManager::ReservePort(uint16* port,
171 scoped_ptr<PortReservation>* reservation) {
172 base::AutoLock lock(lock_);
173 uint16 port_to_use = FindAvailablePort();
174 if (!port_to_use)
175 return Status(kUnknownError, "unable to find open port");
177 taken_.insert(port_to_use);
178 *port = port_to_use;
179 reservation->reset(new PortReservation(
180 base::Bind(&PortManager::ReleasePort, base::Unretained(this),
181 port_to_use),
182 port_to_use));
183 return Status(kOk);
186 Status PortManager::ReservePortFromPool(
187 uint16* port,
188 scoped_ptr<PortReservation>* reservation) {
189 base::AutoLock lock(lock_);
190 uint16 port_to_use = 0;
191 if (unused_forwarded_port_.size()) {
192 port_to_use = unused_forwarded_port_.front();
193 unused_forwarded_port_.pop_front();
194 } else {
195 port_to_use = FindAvailablePort();
197 if (!port_to_use)
198 return Status(kUnknownError, "unable to find open port");
200 taken_.insert(port_to_use);
201 *port = port_to_use;
202 reservation->reset(new PortReservation(
203 base::Bind(&PortManager::ReleasePortToPool, base::Unretained(this),
204 port_to_use),
205 port_to_use));
206 return Status(kOk);
209 void PortManager::ReleasePort(uint16 port) {
210 base::AutoLock lock(lock_);
211 taken_.erase(port);
214 void PortManager::ReleasePortToPool(uint16 port) {
215 base::AutoLock lock(lock_);
216 taken_.erase(port);
217 unused_forwarded_port_.push_back(port);