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_log.h"
17 #include "net/base/net_util.h"
18 #include "net/base/sys_addrinfo.h"
19 #include "net/socket/tcp_server_socket.h"
22 #include <sys/socket.h>
26 PortReservation::PortReservation(const base::Closure
& on_free_func
, int 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(int* port
,
47 scoped_ptr
<PortReservation
>* reservation
) {
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(int* 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), &new_port
))
124 return Status(kUnknownError
, "failed to parse portserver response");
128 return Status(kUnknownError
, "not implemented for this platform");
132 void PortServer::ReleasePort(int port
) {
133 base::AutoLock
lock(free_lock_
);
134 free_
.push_back(port
);
137 PortManager::PortManager(int min_port
, int max_port
)
138 : min_port_(min_port
), max_port_(max_port
) {
139 CHECK_GE(max_port_
, min_port_
);
142 PortManager::~PortManager() {}
144 int PortManager::FindAvailablePort() const {
145 int start
= base::RandInt(min_port_
, max_port_
);
146 bool wrapped
= false;
147 for (int try_port
= start
; try_port
!= start
|| !wrapped
; ++try_port
) {
148 if (try_port
> max_port_
) {
150 if (min_port_
== max_port_
)
152 try_port
= min_port_
;
154 if (taken_
.count(try_port
))
157 char parts
[] = {127, 0, 0, 1};
158 net::IPAddressNumber
address(parts
, parts
+ arraysize(parts
));
159 net::NetLog::Source source
;
160 net::TCPServerSocket
sock(NULL
, source
);
161 if (sock
.Listen(net::IPEndPoint(address
, try_port
), 1) == net::OK
)
167 Status
PortManager::ReservePort(int* port
,
168 scoped_ptr
<PortReservation
>* reservation
) {
169 base::AutoLock
lock(lock_
);
170 int port_to_use
= FindAvailablePort();
172 return Status(kUnknownError
, "unable to find open port");
174 taken_
.insert(port_to_use
);
176 reservation
->reset(new PortReservation(
177 base::Bind(&PortManager::ReleasePort
, base::Unretained(this),
183 Status
PortManager::ReservePortFromPool(
184 int* port
, scoped_ptr
<PortReservation
>* reservation
) {
185 base::AutoLock
lock(lock_
);
187 if (unused_forwarded_port_
.size()) {
188 port_to_use
= unused_forwarded_port_
.front();
189 unused_forwarded_port_
.pop_front();
191 port_to_use
= FindAvailablePort();
194 return Status(kUnknownError
, "unable to find open port");
196 taken_
.insert(port_to_use
);
198 reservation
->reset(new PortReservation(
199 base::Bind(&PortManager::ReleasePortToPool
, base::Unretained(this),
205 void PortManager::ReleasePort(int port
) {
206 base::AutoLock
lock(lock_
);
210 void PortManager::ReleasePortToPool(int port
) {
211 base::AutoLock
lock(lock_
);
213 unused_forwarded_port_
.push_back(port
);