1 // Copyright 2018 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 <stdint.h>
6
7 #include <string>
8 #include <utility>
9 #include <vector>
10
11 #include "base/bind.h"
12 #include "base/run_loop.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/test/bind_test_util.h"
15 #include "base/test/task_environment.h"
16 #include "build/build_config.h"
17 #include "mojo/public/cpp/bindings/pending_remote.h"
18 #include "mojo/public/cpp/bindings/remote.h"
19 #include "mojo/public/cpp/system/data_pipe_utils.h"
20 #include "mojo/public/cpp/system/simple_watcher.h"
21 #include "mojo/public/cpp/system/wait.h"
22 #include "net/base/address_list.h"
23 #include "net/base/ip_address.h"
24 #include "net/base/ip_endpoint.h"
25 #include "net/base/net_errors.h"
26 #include "net/test/embedded_test_server/embedded_test_server.h"
27 #include "net/test/embedded_test_server/http_request.h"
28 #include "net/test/embedded_test_server/http_response.h"
29 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
30 #include "net/url_request/url_request_test_util.h"
31 #include "services/network/mojo_socket_test_util.h"
32 #include "services/network/public/mojom/tcp_socket.mojom.h"
33 #include "services/network/socket_factory.h"
34 #include "testing/gtest/include/gtest/gtest.h"
35
36 namespace network {
37 namespace {
38
39 class TCPBoundSocketTest : public testing::Test {
40 public:
TCPBoundSocketTest()41 TCPBoundSocketTest()
42 : task_environment_(base::test::TaskEnvironment::MainThreadType::IO),
43 factory_(nullptr /* net_log */, &url_request_context_) {}
~TCPBoundSocketTest()44 ~TCPBoundSocketTest() override {}
45
factory()46 SocketFactory* factory() { return &factory_; }
47
BindSocket(const net::IPEndPoint & ip_endpoint_in,mojo::Remote<mojom::TCPBoundSocket> * bound_socket,net::IPEndPoint * ip_endpoint_out)48 int BindSocket(const net::IPEndPoint& ip_endpoint_in,
49 mojo::Remote<mojom::TCPBoundSocket>* bound_socket,
50 net::IPEndPoint* ip_endpoint_out) {
51 base::RunLoop run_loop;
52 int bind_result = net::ERR_IO_PENDING;
53 factory()->CreateTCPBoundSocket(
54 ip_endpoint_in, TRAFFIC_ANNOTATION_FOR_TESTS,
55 bound_socket->BindNewPipeAndPassReceiver(),
56 base::BindLambdaForTesting(
57 [&](int net_error,
58 const base::Optional<net::IPEndPoint>& local_addr) {
59 bind_result = net_error;
60 if (net_error == net::OK) {
61 *ip_endpoint_out = *local_addr;
62 } else {
63 EXPECT_FALSE(local_addr);
64 }
65 run_loop.Quit();
66 }));
67 run_loop.Run();
68
69 // On error, |bound_socket| should be closed.
70 if (bind_result != net::OK && bound_socket->is_connected()) {
71 base::RunLoop close_pipe_run_loop;
72 bound_socket->set_disconnect_handler(close_pipe_run_loop.QuitClosure());
73 close_pipe_run_loop.Run();
74 }
75 return bind_result;
76 }
77
Listen(mojo::Remote<mojom::TCPBoundSocket> bound_socket,mojo::Remote<mojom::TCPServerSocket> * server_socket)78 int Listen(mojo::Remote<mojom::TCPBoundSocket> bound_socket,
79 mojo::Remote<mojom::TCPServerSocket>* server_socket) {
80 base::RunLoop bound_socket_destroyed_run_loop;
81 bound_socket.set_disconnect_handler(
82 bound_socket_destroyed_run_loop.QuitClosure());
83
84 base::RunLoop run_loop;
85 int listen_result = net::ERR_IO_PENDING;
86 bound_socket->Listen(1 /* backlog */,
87 server_socket->BindNewPipeAndPassReceiver(),
88 base::BindLambdaForTesting([&](int net_error) {
89 listen_result = net_error;
90 run_loop.Quit();
91 }));
92 run_loop.Run();
93
94 // Whether Bind() fails or succeeds, |bound_socket| is destroyed.
95 bound_socket_destroyed_run_loop.Run();
96
97 // On error, |server_socket| should be closed.
98 if (listen_result != net::OK && server_socket->is_connected()) {
99 base::RunLoop close_pipe_run_loop;
100 server_socket->set_disconnect_handler(close_pipe_run_loop.QuitClosure());
101 close_pipe_run_loop.Run();
102 }
103
104 return listen_result;
105 }
106
Connect(mojo::Remote<mojom::TCPBoundSocket> bound_socket,const net::IPEndPoint & expected_local_addr,const net::IPEndPoint & connect_to_addr,mojom::TCPConnectedSocketOptionsPtr tcp_connected_socket_options,mojo::Remote<mojom::TCPConnectedSocket> * connected_socket,mojo::PendingRemote<mojom::SocketObserver> socket_observer,mojo::ScopedDataPipeConsumerHandle * client_socket_receive_handle,mojo::ScopedDataPipeProducerHandle * client_socket_send_handle)107 int Connect(mojo::Remote<mojom::TCPBoundSocket> bound_socket,
108 const net::IPEndPoint& expected_local_addr,
109 const net::IPEndPoint& connect_to_addr,
110 mojom::TCPConnectedSocketOptionsPtr tcp_connected_socket_options,
111 mojo::Remote<mojom::TCPConnectedSocket>* connected_socket,
112 mojo::PendingRemote<mojom::SocketObserver> socket_observer,
113 mojo::ScopedDataPipeConsumerHandle* client_socket_receive_handle,
114 mojo::ScopedDataPipeProducerHandle* client_socket_send_handle) {
115 base::RunLoop bound_socket_destroyed_run_loop;
116 bound_socket.set_disconnect_handler(
117 bound_socket_destroyed_run_loop.QuitClosure());
118
119 int connect_result = net::ERR_IO_PENDING;
120 base::RunLoop run_loop;
121 bound_socket->Connect(
122 net::AddressList(connect_to_addr),
123 std::move(tcp_connected_socket_options),
124 connected_socket->BindNewPipeAndPassReceiver(),
125 std::move(socket_observer),
126 base::BindLambdaForTesting(
127 [&](int net_error,
128 const base::Optional<net::IPEndPoint>& local_addr,
129 const base::Optional<net::IPEndPoint>& remote_addr,
130 mojo::ScopedDataPipeConsumerHandle receive_stream,
131 mojo::ScopedDataPipeProducerHandle send_stream) {
132 connect_result = net_error;
133 if (net_error == net::OK) {
134 EXPECT_EQ(expected_local_addr, *local_addr);
135 EXPECT_EQ(connect_to_addr, *remote_addr);
136 *client_socket_receive_handle = std::move(receive_stream);
137 *client_socket_send_handle = std::move(send_stream);
138 } else {
139 EXPECT_FALSE(local_addr);
140 EXPECT_FALSE(remote_addr);
141 EXPECT_FALSE(receive_stream.is_valid());
142 EXPECT_FALSE(send_stream.is_valid());
143 }
144 run_loop.Quit();
145 }));
146 run_loop.Run();
147
148 // Whether Bind() fails or succeeds, |bound_socket| is destroyed.
149 bound_socket_destroyed_run_loop.Run();
150
151 // On error, |connected_socket| should be closed.
152 if (connect_result != net::OK && connected_socket->is_connected()) {
153 base::RunLoop close_pipe_run_loop;
154 connected_socket->set_disconnect_handler(
155 close_pipe_run_loop.QuitClosure());
156 close_pipe_run_loop.Run();
157 }
158
159 return connect_result;
160 }
161
162 // Attempts to read exactly |expected_bytes| from |receive_handle|, or reads
163 // until the pipe is closed if |expected_bytes| is 0.
ReadData(mojo::DataPipeConsumerHandle receive_handle,uint32_t expected_bytes=0)164 std::string ReadData(mojo::DataPipeConsumerHandle receive_handle,
165 uint32_t expected_bytes = 0) {
166 std::string read_data;
167 while (expected_bytes == 0 || read_data.size() < expected_bytes) {
168 const void* buffer;
169 uint32_t num_bytes = expected_bytes - read_data.size();
170 MojoResult result = receive_handle.BeginReadData(
171 &buffer, &num_bytes, MOJO_READ_DATA_FLAG_NONE);
172 if (result == MOJO_RESULT_SHOULD_WAIT) {
173 task_environment_.RunUntilIdle();
174 continue;
175 }
176 if (result != MOJO_RESULT_OK) {
177 if (expected_bytes != 0)
178 ADD_FAILURE() << "Read failed";
179 return read_data;
180 }
181 read_data.append(static_cast<const char*>(buffer), num_bytes);
182 receive_handle.EndReadData(num_bytes);
183 }
184
185 return read_data;
186 }
187
LocalHostWithAnyPort()188 static net::IPEndPoint LocalHostWithAnyPort() {
189 return net::IPEndPoint(net::IPAddress::IPv4Localhost(), 0 /* port */);
190 }
191
task_environment()192 base::test::TaskEnvironment* task_environment() { return &task_environment_; }
193
194 private:
195 base::test::TaskEnvironment task_environment_;
196 net::TestURLRequestContext url_request_context_;
197 SocketFactory factory_;
198
199 DISALLOW_COPY_AND_ASSIGN(TCPBoundSocketTest);
200 };
201
202 // Try to bind a socket to an address already being listened on, which should
203 // fail.
TEST_F(TCPBoundSocketTest,BindError)204 TEST_F(TCPBoundSocketTest, BindError) {
205 // Set up a listening socket.
206 mojo::Remote<mojom::TCPBoundSocket> bound_socket1;
207 net::IPEndPoint bound_address1;
208 ASSERT_EQ(net::OK, BindSocket(LocalHostWithAnyPort(), &bound_socket1,
209 &bound_address1));
210 mojo::Remote<mojom::TCPServerSocket> server_socket;
211 ASSERT_EQ(net::OK, Listen(std::move(bound_socket1), &server_socket));
212
213 // Try to bind another socket to the listening socket's address.
214 mojo::Remote<mojom::TCPBoundSocket> bound_socket2;
215 net::IPEndPoint bound_address2;
216 int result = BindSocket(bound_address1, &bound_socket2, &bound_address2);
217 // Depending on platform, can get different errors. Some platforms can return
218 // either error.
219 EXPECT_TRUE(result == net::ERR_ADDRESS_IN_USE ||
220 result == net::ERR_INVALID_ARGUMENT);
221 }
222
223 // Test the case of a connect error. To cause a connect error, bind a socket,
224 // but don't listen on it, and then try connecting to it using another bound
225 // socket.
226 //
227 // Don't run on Apple platforms because this pattern ends in a connect timeout
228 // on OSX (after 25+ seconds) instead of connection refused.
229 #if !defined(OS_MACOSX) && !defined(OS_IOS)
TEST_F(TCPBoundSocketTest,ConnectError)230 TEST_F(TCPBoundSocketTest, ConnectError) {
231 mojo::Remote<mojom::TCPBoundSocket> bound_socket1;
232 net::IPEndPoint bound_address1;
233 ASSERT_EQ(net::OK, BindSocket(LocalHostWithAnyPort(), &bound_socket1,
234 &bound_address1));
235
236 // Trying to bind to an address currently being used for listening should
237 // fail.
238 mojo::Remote<mojom::TCPBoundSocket> bound_socket2;
239 net::IPEndPoint bound_address2;
240 ASSERT_EQ(net::OK, BindSocket(LocalHostWithAnyPort(), &bound_socket2,
241 &bound_address2));
242 mojo::Remote<mojom::TCPConnectedSocket> connected_socket;
243 mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
244 mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
245 EXPECT_EQ(net::ERR_CONNECTION_REFUSED,
246 Connect(std::move(bound_socket2), bound_address2, bound_address1,
247 nullptr /* tcp_connected_socket_options */,
248 &connected_socket, mojo::NullRemote(),
249 &client_socket_receive_handle, &client_socket_send_handle));
250 }
251 #endif // !defined(OS_MACOSX) && !defined(OS_IOS)
252
253 // Test listen failure.
254
255 // All platforms except Windows use SO_REUSEADDR on server sockets by default,
256 // which allows binding multiple sockets to the same port at once, as long as
257 // nothing is listening on it yet.
258 //
259 // Apple platforms don't allow binding multiple TCP sockets to the same port
260 // even with SO_REUSEADDR enabled.
261 #if !defined(OS_WIN) && !defined(OS_MACOSX) && !defined(OS_IOS)
TEST_F(TCPBoundSocketTest,ListenError)262 TEST_F(TCPBoundSocketTest, ListenError) {
263 // Bind a socket.
264 mojo::Remote<mojom::TCPBoundSocket> bound_socket1;
265 net::IPEndPoint bound_address1;
266 ASSERT_EQ(net::OK, BindSocket(LocalHostWithAnyPort(), &bound_socket1,
267 &bound_address1));
268
269 // Bind another socket to the same address, which should succeed, due to
270 // SO_REUSEADDR.
271 mojo::Remote<mojom::TCPBoundSocket> bound_socket2;
272 net::IPEndPoint bound_address2;
273 ASSERT_EQ(net::OK,
274 BindSocket(bound_address1, &bound_socket2, &bound_address2));
275
276 // Listen on the first socket, which should also succeed.
277 mojo::Remote<mojom::TCPServerSocket> server_socket1;
278 ASSERT_EQ(net::OK, Listen(std::move(bound_socket1), &server_socket1));
279
280 // Listen on the second socket should fail.
281 mojo::Remote<mojom::TCPServerSocket> server_socket2;
282 int result = Listen(std::move(bound_socket2), &server_socket2);
283 // Depending on platform, can get different errors. Some platforms can return
284 // either error.
285 EXPECT_TRUE(result == net::ERR_ADDRESS_IN_USE ||
286 result == net::ERR_INVALID_ARGUMENT);
287 }
288 #endif // !defined(OS_WIN) && !defined(OS_MACOSX) && !defined(OS_IOS)
289
290 // Test the case bind succeeds, and transfer some data.
TEST_F(TCPBoundSocketTest,ReadWrite)291 TEST_F(TCPBoundSocketTest, ReadWrite) {
292 // Set up a listening socket.
293 mojo::Remote<mojom::TCPBoundSocket> bound_socket1;
294 net::IPEndPoint server_address;
295 ASSERT_EQ(net::OK, BindSocket(LocalHostWithAnyPort(), &bound_socket1,
296 &server_address));
297 mojo::Remote<mojom::TCPServerSocket> server_socket;
298 ASSERT_EQ(net::OK, Listen(std::move(bound_socket1), &server_socket));
299
300 // Connect to the socket with another socket.
301 mojo::Remote<mojom::TCPBoundSocket> bound_socket2;
302 net::IPEndPoint client_address;
303 ASSERT_EQ(net::OK, BindSocket(LocalHostWithAnyPort(), &bound_socket2,
304 &client_address));
305 mojo::Remote<mojom::TCPConnectedSocket> client_socket;
306 TestSocketObserver socket_observer;
307 mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
308 mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
309 EXPECT_EQ(net::OK,
310 Connect(std::move(bound_socket2), client_address, server_address,
311 nullptr /* tcp_connected_socket_options */, &client_socket,
312 socket_observer.GetObserverRemote(),
313 &client_socket_receive_handle, &client_socket_send_handle));
314
315 base::RunLoop run_loop;
316 mojo::Remote<mojom::TCPConnectedSocket> accept_socket;
317 mojo::ScopedDataPipeConsumerHandle accept_socket_receive_handle;
318 mojo::ScopedDataPipeProducerHandle accept_socket_send_handle;
319 server_socket->Accept(
320 mojo::NullRemote() /* ovserver */,
321 base::BindLambdaForTesting(
322 [&](int net_error, const base::Optional<net::IPEndPoint>& remote_addr,
323 mojo::PendingRemote<mojom::TCPConnectedSocket> connected_socket,
324 mojo::ScopedDataPipeConsumerHandle receive_stream,
325 mojo::ScopedDataPipeProducerHandle send_stream) {
326 EXPECT_EQ(net_error, net::OK);
327 EXPECT_EQ(*remote_addr, client_address);
328 accept_socket.Bind(std::move(connected_socket));
329 accept_socket_receive_handle = std::move(receive_stream);
330 accept_socket_send_handle = std::move(send_stream);
331 run_loop.Quit();
332 }));
333 run_loop.Run();
334
335 const std::string kData = "Jumbo Shrimp";
336 ASSERT_TRUE(mojo::BlockingCopyFromString(kData, client_socket_send_handle));
337 EXPECT_EQ(kData, ReadData(accept_socket_receive_handle.get(), kData.size()));
338
339 ASSERT_TRUE(mojo::BlockingCopyFromString(kData, accept_socket_send_handle));
340 EXPECT_EQ(kData, ReadData(client_socket_receive_handle.get(), kData.size()));
341
342 // Close the accept socket.
343 accept_socket.reset();
344
345 // Wait for read error on the client socket.
346 EXPECT_EQ(net::OK, socket_observer.WaitForReadError());
347
348 // Write data to the client socket until there's an error.
349 while (true) {
350 void* buffer = nullptr;
351 uint32_t buffer_num_bytes = 0;
352 MojoResult result = client_socket_send_handle->BeginWriteData(
353 &buffer, &buffer_num_bytes, MOJO_WRITE_DATA_FLAG_NONE);
354 if (result == MOJO_RESULT_SHOULD_WAIT) {
355 task_environment()->RunUntilIdle();
356 continue;
357 }
358 if (result != MOJO_RESULT_OK)
359 break;
360 memset(buffer, 0, buffer_num_bytes);
361 client_socket_send_handle->EndWriteData(buffer_num_bytes);
362 }
363 // Wait for write error on the client socket. Don't check exact error, out of
364 // paranoia.
365 EXPECT_LT(socket_observer.WaitForWriteError(), 0);
366 }
367
368 // Establish a connection while passing in some options. This test doesn't check
369 // that the options are actually set, since there's no API for that.
TEST_F(TCPBoundSocketTest,ConnectWithOptions)370 TEST_F(TCPBoundSocketTest, ConnectWithOptions) {
371 // Set up a listening socket.
372 mojo::Remote<mojom::TCPBoundSocket> bound_socket1;
373 net::IPEndPoint server_address;
374 ASSERT_EQ(net::OK, BindSocket(LocalHostWithAnyPort(), &bound_socket1,
375 &server_address));
376 mojo::Remote<mojom::TCPServerSocket> server_socket;
377 ASSERT_EQ(net::OK, Listen(std::move(bound_socket1), &server_socket));
378
379 // Connect to the socket with another socket.
380 mojo::Remote<mojom::TCPBoundSocket> bound_socket2;
381 net::IPEndPoint client_address;
382 ASSERT_EQ(net::OK, BindSocket(LocalHostWithAnyPort(), &bound_socket2,
383 &client_address));
384 mojo::Remote<mojom::TCPConnectedSocket> client_socket;
385 TestSocketObserver socket_observer;
386 mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
387 mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
388 mojom::TCPConnectedSocketOptionsPtr tcp_connected_socket_options =
389 mojom::TCPConnectedSocketOptions::New();
390 tcp_connected_socket_options->send_buffer_size = 32 * 1024;
391 tcp_connected_socket_options->receive_buffer_size = 64 * 1024;
392 tcp_connected_socket_options->no_delay = false;
393
394 EXPECT_EQ(net::OK,
395 Connect(std::move(bound_socket2), client_address, server_address,
396 std::move(tcp_connected_socket_options), &client_socket,
397 socket_observer.GetObserverRemote(),
398 &client_socket_receive_handle, &client_socket_send_handle));
399
400 base::RunLoop run_loop;
401 mojo::Remote<mojom::TCPConnectedSocket> accept_socket;
402 mojo::ScopedDataPipeConsumerHandle accept_socket_receive_handle;
403 mojo::ScopedDataPipeProducerHandle accept_socket_send_handle;
404 server_socket->Accept(
405 mojo::NullRemote() /* ovserver */,
406 base::BindLambdaForTesting(
407 [&](int net_error, const base::Optional<net::IPEndPoint>& remote_addr,
408 mojo::PendingRemote<mojom::TCPConnectedSocket> connected_socket,
409 mojo::ScopedDataPipeConsumerHandle receive_stream,
410 mojo::ScopedDataPipeProducerHandle send_stream) {
411 EXPECT_EQ(net_error, net::OK);
412 EXPECT_EQ(*remote_addr, client_address);
413 accept_socket.Bind(std::move(connected_socket));
414 accept_socket_receive_handle = std::move(receive_stream);
415 accept_socket_send_handle = std::move(send_stream);
416 run_loop.Quit();
417 }));
418 run_loop.Run();
419
420 const std::string kData = "Jumbo Shrimp";
421 ASSERT_TRUE(mojo::BlockingCopyFromString(kData, client_socket_send_handle));
422 EXPECT_EQ(kData, ReadData(accept_socket_receive_handle.get(), kData.size()));
423
424 ASSERT_TRUE(mojo::BlockingCopyFromString(kData, accept_socket_send_handle));
425 EXPECT_EQ(kData, ReadData(client_socket_receive_handle.get(), kData.size()));
426 }
427
428 // Test that a TCPBoundSocket can be upgraded to TLS once connected.
TEST_F(TCPBoundSocketTest,UpgradeToTLS)429 TEST_F(TCPBoundSocketTest, UpgradeToTLS) {
430 // Simplest way to set up an TLS server is to use the embedded test server.
431 net::test_server::EmbeddedTestServer test_server(
432 net::test_server::EmbeddedTestServer::TYPE_HTTPS);
433 test_server.RegisterRequestHandler(base::BindRepeating(
434 [](const net::test_server::HttpRequest& request)
435 -> std::unique_ptr<net::test_server::HttpResponse> {
436 std::unique_ptr<net::test_server::BasicHttpResponse> basic_response =
437 std::make_unique<net::test_server::BasicHttpResponse>();
438 basic_response->set_content(request.relative_url);
439 return basic_response;
440 }));
441 ASSERT_TRUE(test_server.Start());
442
443 mojo::Remote<mojom::TCPBoundSocket> bound_socket;
444 net::IPEndPoint client_address;
445 ASSERT_EQ(net::OK,
446 BindSocket(LocalHostWithAnyPort(), &bound_socket, &client_address));
447 mojo::Remote<mojom::TCPConnectedSocket> client_socket;
448 TestSocketObserver socket_observer;
449 mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
450 mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
451
452 EXPECT_EQ(net::OK,
453 Connect(std::move(bound_socket), client_address,
454 net::IPEndPoint(net::IPAddress::IPv4Localhost(),
455 test_server.host_port_pair().port()),
456 nullptr /* tcp_connected_socket_options */, &client_socket,
457 socket_observer.GetObserverRemote(),
458 &client_socket_receive_handle, &client_socket_send_handle));
459
460 // Need to closed these pipes for UpgradeToTLS to complete.
461 client_socket_receive_handle.reset();
462 client_socket_send_handle.reset();
463
464 base::RunLoop run_loop;
465 mojo::Remote<mojom::TLSClientSocket> tls_client_socket;
466 client_socket->UpgradeToTLS(
467 test_server.host_port_pair(), nullptr /* options */,
468 net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
469 tls_client_socket.BindNewPipeAndPassReceiver(),
470 mojo::NullRemote() /* observer */,
471 base::BindLambdaForTesting(
472 [&](int net_error,
473 mojo::ScopedDataPipeConsumerHandle receive_pipe_handle,
474 mojo::ScopedDataPipeProducerHandle send_pipe_handle,
475 const base::Optional<net::SSLInfo>& ssl_info) {
476 EXPECT_EQ(net::OK, net_error);
477 client_socket_receive_handle = std::move(receive_pipe_handle);
478 client_socket_send_handle = std::move(send_pipe_handle);
479 run_loop.Quit();
480 }));
481 run_loop.Run();
482
483 const char kPath[] = "/foo";
484
485 // Send an HTTP request.
486 std::string request = base::StringPrintf("GET %s HTTP/1.0\r\n\r\n", kPath);
487 EXPECT_TRUE(mojo::BlockingCopyFromString(request, client_socket_send_handle));
488
489 // Read the response, and make sure it looks reasonable.
490 std::string response = ReadData(client_socket_receive_handle.get());
491 EXPECT_EQ("HTTP/", response.substr(0, 5));
492 // The response body should be the path, so make sure the response ends with
493 // the path.
494 EXPECT_EQ(kPath, response.substr(response.length() - strlen(kPath)));
495 }
496
497 } // namespace
498 } // namespace network
499