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"
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"
22 #include <sys/socket.h>
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())
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_
);
52 port_to_use
= free_
.front();
57 Status status
= RequestPort(&port_to_use
);
62 reservation
->reset(new PortReservation(
63 base::Bind(&PortServer::ReleasePort
, base::Unretained(this), port_to_use
),
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.
72 int sock_fd
= socket(AF_UNIX
, SOCK_STREAM
, 0);
74 return Status(kUnknownError
, "unable to create socket");
75 base::SyncSocket
sock(sock_fd
);
79 if (setsockopt(sock_fd
,
82 reinterpret_cast<char*>(&tv
),
87 reinterpret_cast<char*>(&tv
),
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
);
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
;
112 size_t rv
= sock
.Receive(&c
, 1);
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
;
122 if (*response
.rbegin() != '\n' ||
123 !base::StringToInt(response
.substr(0, response
.length() - 1),
125 new_port
< 0 || new_port
> 65535)
126 return Status(kUnknownError
, "failed to parse portserver response");
127 *port
= static_cast<uint16
>(new_port
);
130 return Status(kUnknownError
, "not implemented for this platform");
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_
) {
152 if (min_port_
== max_port_
)
154 try_port
= min_port_
;
156 uint16 try_port_uint16
= static_cast<uint16
>(try_port
);
157 if (taken_
.count(try_port_uint16
))
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
;
170 Status
PortManager::ReservePort(uint16
* port
,
171 scoped_ptr
<PortReservation
>* reservation
) {
172 base::AutoLock
lock(lock_
);
173 uint16 port_to_use
= FindAvailablePort();
175 return Status(kUnknownError
, "unable to find open port");
177 taken_
.insert(port_to_use
);
179 reservation
->reset(new PortReservation(
180 base::Bind(&PortManager::ReleasePort
, base::Unretained(this),
186 Status
PortManager::ReservePortFromPool(
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();
195 port_to_use
= FindAvailablePort();
198 return Status(kUnknownError
, "unable to find open port");
200 taken_
.insert(port_to_use
);
202 reservation
->reset(new PortReservation(
203 base::Bind(&PortManager::ReleasePortToPool
, base::Unretained(this),
209 void PortManager::ReleasePort(uint16 port
) {
210 base::AutoLock
lock(lock_
);
214 void PortManager::ReleasePortToPool(uint16 port
) {
215 base::AutoLock
lock(lock_
);
217 unused_forwarded_port_
.push_back(port
);