Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(146)

Side by Side Diff: mojo/services/network/http_server_apptest.cc

Issue 1128863004: Mojo service implementation for HTTP server - part 2 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « mojo/services/network/http_connection_impl.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2015 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 "base/macros.h"
6 #include "base/memory/linked_ptr.h"
7 #include "base/memory/ref_counted.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/run_loop.h"
10 #include "base/strings/string_util.h"
11 #include "mojo/application/application_test_base_chromium.h"
12 #include "mojo/application/public/cpp/application_connection.h"
13 #include "mojo/application/public/cpp/application_impl.h"
14 #include "mojo/common/data_pipe_utils.h"
15 #include "mojo/services/network/net_address_type_converters.h"
16 #include "mojo/services/network/public/interfaces/http_server.mojom.h"
17 #include "mojo/services/network/public/interfaces/network_service.mojom.h"
18 #include "net/base/io_buffer.h"
19 #include "net/base/net_errors.h"
20 #include "net/base/test_completion_callback.h"
21 #include "net/http/http_response_headers.h"
22 #include "net/http/http_util.h"
23 #include "net/socket/tcp_client_socket.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25
26 namespace mojo {
27 namespace {
28
29 const int kMaxExpectedResponseLength = 2048;
30
31 NetAddressPtr GetLocalHostWithAnyPort() {
32 NetAddressPtr addr(NetAddress::New());
33 addr->family = NET_ADDRESS_FAMILY_IPV4;
34 addr->ipv4 = NetAddressIPv4::New();
35 addr->ipv4->port = 0;
36 addr->ipv4->addr.resize(4);
37 addr->ipv4->addr[0] = 127;
38 addr->ipv4->addr[1] = 0;
39 addr->ipv4->addr[2] = 0;
40 addr->ipv4->addr[3] = 1;
41
42 return addr.Pass();
43 }
44
45 using TestHeaders = std::vector<std::pair<std::string, std::string>>;
46
47 struct TestRequest {
48 std::string method;
49 std::string url;
50 TestHeaders headers;
51 scoped_ptr<std::string> body;
52 };
53
54 struct TestResponse {
55 uint32_t status_code;
56 TestHeaders headers;
57 scoped_ptr<std::string> body;
58 };
59
60 std::string MakeRequestMessage(const TestRequest& data) {
61 std::string message = data.method + " " + data.url + " HTTP/1.1\r\n";
62 for (const auto& item : data.headers)
63 message += item.first + ": " + item.second + "\r\n";
64 message += "\r\n";
65 if (data.body)
66 message += *data.body;
67
68 return message;
69 }
70
71 URLResponsePtr MakeResponseStruct(const TestResponse& data) {
72 URLResponsePtr response(URLResponse::New());
73 response->status_code = data.status_code;
74 response->headers.resize(data.headers.size());
75 size_t index = 0;
76 for (const auto& item : data.headers) {
77 HTTPHeaderPtr header(HTTPHeader::New());
78 header->name = item.first;
79 header->value = item.second;
80 response->headers[index++] = header.Pass();
81 }
82
83 if (data.body) {
84 uint32_t num_bytes = static_cast<uint32_t>(data.body->size());
85 MojoCreateDataPipeOptions options;
86 options.struct_size = sizeof(MojoCreateDataPipeOptions);
87 options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE;
88 options.element_num_bytes = 1;
89 options.capacity_num_bytes = num_bytes;
90 DataPipe data_pipe(options);
91 response->body = data_pipe.consumer_handle.Pass();
92 MojoResult result =
93 WriteDataRaw(data_pipe.producer_handle.get(), data.body->data(),
94 &num_bytes, MOJO_WRITE_DATA_FLAG_ALL_OR_NONE);
95 EXPECT_EQ(MOJO_RESULT_OK, result);
96 }
97
98 return response.Pass();
99 }
100
101 void CheckHeaders(const TestHeaders& expected,
102 const Array<HTTPHeaderPtr>& headers) {
103 // The server impl fiddles with Content-Length and Content-Type. So we don't
104 // do a strict check here.
105 std::map<std::string, std::string> header_map;
106 for (size_t i = 0; i < headers.size(); ++i) {
107 std::string lower_name =
108 base::StringToLowerASCII(headers[i]->name.To<std::string>());
109 header_map[lower_name] = headers[i]->value;
110 }
111
112 for (const auto& item : expected) {
113 std::string lower_name = base::StringToLowerASCII(item.first);
114 EXPECT_NE(header_map.end(), header_map.find(lower_name));
115 EXPECT_EQ(item.second, header_map[lower_name]);
116 }
117 }
118
119 void CheckRequest(const TestRequest& expected, URLRequestPtr request) {
120 EXPECT_EQ(expected.method, request->method);
121 EXPECT_EQ(expected.url, request->url);
122 CheckHeaders(expected.headers, request->headers);
123 if (expected.body) {
124 EXPECT_EQ(1u, request->body.size());
125 std::string body;
126 common::BlockingCopyToString(request->body[0].Pass(), &body);
127 EXPECT_EQ(*expected.body, body);
128 } else {
129 EXPECT_EQ(0u, request->body.size());
130 }
131 }
132
133 void CheckResponse(const TestResponse& expected, const std::string& response) {
134 int header_end =
135 net::HttpUtil::LocateEndOfHeaders(response.c_str(), response.size());
136 std::string assembled_headers =
137 net::HttpUtil::AssembleRawHeaders(response.c_str(), header_end);
138 scoped_refptr<net::HttpResponseHeaders> parsed_headers(
139 new net::HttpResponseHeaders(assembled_headers));
140 EXPECT_EQ(expected.status_code,
141 static_cast<uint32_t>(parsed_headers->response_code()));
142 for (const auto& item : expected.headers)
143 EXPECT_TRUE(parsed_headers->HasHeaderValue(item.first, item.second));
144
145 if (expected.body) {
146 EXPECT_NE(-1, header_end);
147 std::string body(response, static_cast<size_t>(header_end));
148 EXPECT_EQ(*expected.body, body);
149 } else {
150 EXPECT_EQ(response.size(), static_cast<size_t>(header_end));
151 }
152 }
153
154 class TestHttpClient {
155 public:
156 TestHttpClient() : connect_result_(net::OK) {}
157
158 void Connect(const net::IPEndPoint& address) {
159 net::AddressList addresses(address);
160 net::NetLog::Source source;
161 socket_.reset(new net::TCPClientSocket(addresses, NULL, source));
162
163 base::RunLoop run_loop;
164 connect_result_ = socket_->Connect(base::Bind(&TestHttpClient::OnConnect,
165 base::Unretained(this),
166 run_loop.QuitClosure()));
167 if (connect_result_ == net::ERR_IO_PENDING)
168 run_loop.Run();
169
170 ASSERT_EQ(net::OK, connect_result_);
171 }
172
173 void Send(const std::string& data) {
174 write_buffer_ = new net::DrainableIOBuffer(new net::StringIOBuffer(data),
175 data.length());
176 Write();
177 }
178
179 // Note: This method determines the end of the response only by Content-Length
180 // and connection termination. Besides, it doesn't truncate at the end of the
181 // response, so |message| may return more data (e.g., part of the next
182 // response).
183 void ReadResponse(std::string* message) {
184 if (!Read(message, 1))
185 return;
186 while (!IsCompleteResponse(*message)) {
187 std::string chunk;
188 if (!Read(&chunk, 1))
189 return;
190 message->append(chunk);
191 }
192 return;
193 }
194
195 private:
196 void OnConnect(const base::Closure& quit_loop, int result) {
197 connect_result_ = result;
198 quit_loop.Run();
199 }
200
201 void Write() {
202 int result = socket_->Write(
203 write_buffer_.get(), write_buffer_->BytesRemaining(),
204 base::Bind(&TestHttpClient::OnWrite, base::Unretained(this)));
205 if (result != net::ERR_IO_PENDING)
206 OnWrite(result);
207 }
208
209 void OnWrite(int result) {
210 ASSERT_GT(result, 0);
211 write_buffer_->DidConsume(result);
212 if (write_buffer_->BytesRemaining())
213 Write();
214 }
215
216 bool Read(std::string* message, int expected_bytes) {
217 int total_bytes_received = 0;
218 message->clear();
219 while (total_bytes_received < expected_bytes) {
220 net::TestCompletionCallback callback;
221 ReadInternal(callback.callback());
222 int bytes_received = callback.WaitForResult();
223 if (bytes_received <= 0)
224 return false;
225
226 total_bytes_received += bytes_received;
227 message->append(read_buffer_->data(), bytes_received);
228 }
229 return true;
230 }
231
232 void ReadInternal(const net::CompletionCallback& callback) {
233 read_buffer_ = new net::IOBufferWithSize(kMaxExpectedResponseLength);
234 int result =
235 socket_->Read(read_buffer_.get(), kMaxExpectedResponseLength, callback);
236 if (result != net::ERR_IO_PENDING)
237 callback.Run(result);
238 }
239
240 bool IsCompleteResponse(const std::string& response) {
241 // Check end of headers first.
242 int end_of_headers =
243 net::HttpUtil::LocateEndOfHeaders(response.data(), response.size());
244 if (end_of_headers < 0)
245 return false;
246
247 // Return true if response has data equal to or more than content length.
248 int64 body_size = static_cast<int64>(response.size()) - end_of_headers;
249 DCHECK_LE(0, body_size);
250 scoped_refptr<net::HttpResponseHeaders> headers(
251 new net::HttpResponseHeaders(net::HttpUtil::AssembleRawHeaders(
252 response.data(), end_of_headers)));
253 return body_size >= headers->GetContentLength();
254 }
255
256 scoped_refptr<net::IOBufferWithSize> read_buffer_;
257 scoped_refptr<net::DrainableIOBuffer> write_buffer_;
258 scoped_ptr<net::TCPClientSocket> socket_;
259 int connect_result_;
260
261 DISALLOW_COPY_AND_ASSIGN(TestHttpClient);
262 };
263
264 class HttpConnectionDelegateImpl : public HttpConnectionDelegate {
265 public:
266 struct PendingRequest {
267 URLRequestPtr request;
268 OnReceivedRequestCallback callback;
269 };
270
271 HttpConnectionDelegateImpl(HttpConnectionPtr connection,
272 InterfaceRequest<HttpConnectionDelegate> request)
273 : connection_(connection.Pass()),
274 binding_(this, request.Pass()),
275 run_loop_(nullptr),
276 wait_for_request_count_(0) {}
277 ~HttpConnectionDelegateImpl() override {}
278
279 // HttpConnectionDelegate implementation:
280 void OnReceivedRequest(URLRequestPtr request,
281 const OnReceivedRequestCallback& callback) override {
282 linked_ptr<PendingRequest> pending_request(new PendingRequest);
283 pending_request->request = request.Pass();
284 pending_request->callback = callback;
285 pending_requests_.push_back(pending_request);
286 if (run_loop_ && pending_requests_.size() >= wait_for_request_count_) {
287 wait_for_request_count_ = 0;
288 run_loop_->Quit();
289 }
290 }
291
292 void OnReceivedWebSocketRequest(
293 URLRequestPtr request,
294 const OnReceivedWebSocketRequestCallback& callback) override {
295 NOTREACHED();
296 }
297
298 void SendResponse(URLResponsePtr response) {
299 ASSERT_FALSE(pending_requests_.empty());
300 linked_ptr<PendingRequest> request = pending_requests_[0];
301 pending_requests_.erase(pending_requests_.begin());
302 request->callback.Run(response.Pass());
303 }
304
305 void WaitForRequest(size_t count) {
306 DCHECK(!run_loop_);
307
308 wait_for_request_count_ = count;
309 base::RunLoop run_loop;
310 run_loop_ = &run_loop;
311 run_loop.Run();
312 run_loop_ = nullptr;
313 }
314
315 std::vector<linked_ptr<PendingRequest>>& pending_requests() {
316 return pending_requests_;
317 }
318
319 private:
320 HttpConnectionPtr connection_;
321 Binding<HttpConnectionDelegate> binding_;
322 std::vector<linked_ptr<PendingRequest>> pending_requests_;
323 // Pointing to a stack-allocated RunLoop instance.
324 base::RunLoop* run_loop_;
325 size_t wait_for_request_count_;
326
327 DISALLOW_COPY_AND_ASSIGN(HttpConnectionDelegateImpl);
328 };
329
330 class HttpServerDelegateImpl : public HttpServerDelegate {
331 public:
332 explicit HttpServerDelegateImpl(HttpServerDelegatePtr* delegate_ptr)
333 : binding_(this, delegate_ptr),
334 run_loop_(nullptr),
335 wait_for_connection_count_(0) {}
336 ~HttpServerDelegateImpl() override {}
337
338 // HttpServerDelegate implementation.
339 void OnConnected(HttpConnectionPtr connection,
340 InterfaceRequest<HttpConnectionDelegate> delegate) override {
341 connections_.push_back(make_linked_ptr(
342 new HttpConnectionDelegateImpl(connection.Pass(), delegate.Pass())));
343 if (run_loop_ && connections_.size() >= wait_for_connection_count_) {
344 wait_for_connection_count_ = 0;
345 run_loop_->Quit();
346 }
347 }
348
349 void WaitForConnection(size_t count) {
350 DCHECK(!run_loop_);
351
352 wait_for_connection_count_ = count;
353 base::RunLoop run_loop;
354 run_loop_ = &run_loop;
355 run_loop.Run();
356 run_loop_ = nullptr;
357 }
358
359 std::vector<linked_ptr<HttpConnectionDelegateImpl>>& connections() {
360 return connections_;
361 }
362
363 private:
364 Binding<HttpServerDelegate> binding_;
365 std::vector<linked_ptr<HttpConnectionDelegateImpl>> connections_;
366 // Pointing to a stack-allocated RunLoop instance.
367 base::RunLoop* run_loop_;
368 size_t wait_for_connection_count_;
369
370 DISALLOW_COPY_AND_ASSIGN(HttpServerDelegateImpl);
371 };
372
373 class HttpServerAppTest : public test::ApplicationTestBase {
374 public:
375 HttpServerAppTest() : message_loop_(base::MessageLoop::TYPE_IO) {}
376 ~HttpServerAppTest() override {}
377
378 protected:
379 bool ShouldCreateDefaultRunLoop() override { return false; }
380
381 void SetUp() override {
382 ApplicationTestBase::SetUp();
383
384 ApplicationConnection* connection =
385 application_impl()->ConnectToApplication("mojo:network_service");
386 connection->ConnectToService(&network_service_);
387 }
388
389 void CreateHttpServer(HttpServerDelegatePtr delegate,
390 NetAddressPtr* out_bound_to) {
391 network_service_->CreateHttpServer(
392 GetLocalHostWithAnyPort(), delegate.Pass(),
393 [out_bound_to](NetworkErrorPtr result, NetAddressPtr bound_to) {
394 ASSERT_EQ(net::OK, result->code);
395 EXPECT_NE(0u, bound_to->ipv4->port);
396 *out_bound_to = bound_to.Pass();
397 });
398 network_service_.WaitForIncomingMethodCall();
399 }
400
401 NetworkServicePtr network_service_;
402
403 private:
404 base::MessageLoop message_loop_;
405
406 DISALLOW_COPY_AND_ASSIGN(HttpServerAppTest);
407 };
408
409 } // namespace
410
411 TEST_F(HttpServerAppTest, BasicHttpRequestResponse) {
412 NetAddressPtr bound_to;
413 HttpServerDelegatePtr server_delegate_ptr;
414 HttpServerDelegateImpl server_delegate_impl(&server_delegate_ptr);
415 CreateHttpServer(server_delegate_ptr.Pass(), &bound_to);
416
417 TestHttpClient client;
418 client.Connect(bound_to.To<net::IPEndPoint>());
419
420 server_delegate_impl.WaitForConnection(1);
421 HttpConnectionDelegateImpl& connection =
422 *server_delegate_impl.connections()[0];
423
424 TestRequest request_data = {"HEAD", "/test", {{"Hello", "World"}}, nullptr};
425 client.Send(MakeRequestMessage(request_data));
426
427 connection.WaitForRequest(1);
428
429 CheckRequest(request_data, connection.pending_requests()[0]->request.Pass());
430
431 TestResponse response_data = {200, {{"Content-Length", "4"}}, nullptr};
432 connection.SendResponse(MakeResponseStruct(response_data));
433 // This causes the underlying TCP connection to be closed. The client can
434 // determine the end of the response based on that.
435 server_delegate_impl.connections().clear();
436
437 std::string response_message;
438 client.ReadResponse(&response_message);
439
440 CheckResponse(response_data, response_message);
441 }
442
443 TEST_F(HttpServerAppTest, HttpRequestResponseWithBody) {
444 NetAddressPtr bound_to;
445 HttpServerDelegatePtr server_delegate_ptr;
446 HttpServerDelegateImpl server_delegate_impl(&server_delegate_ptr);
447 CreateHttpServer(server_delegate_ptr.Pass(), &bound_to);
448
449 TestHttpClient client;
450 client.Connect(bound_to.To<net::IPEndPoint>());
451
452 server_delegate_impl.WaitForConnection(1);
453 HttpConnectionDelegateImpl& connection =
454 *server_delegate_impl.connections()[0];
455
456 TestRequest request_data = {
457 "Post",
458 "/test",
459 {{"Hello", "World"},
460 {"Content-Length", "23"},
461 {"Content-Type", "text/plain"}},
462 make_scoped_ptr(new std::string("This is a test request!"))};
463 client.Send(MakeRequestMessage(request_data));
464
465 connection.WaitForRequest(1);
466
467 CheckRequest(request_data, connection.pending_requests()[0]->request.Pass());
468
469 TestResponse response_data = {
470 200,
471 {{"Content-Length", "26"}},
472 make_scoped_ptr(new std::string("This is a test response..."))};
473 connection.SendResponse(MakeResponseStruct(response_data));
474
475 std::string response_message;
476 client.ReadResponse(&response_message);
477
478 CheckResponse(response_data, response_message);
479 }
480
481 } // namespace mojo
OLDNEW
« no previous file with comments | « mojo/services/network/http_connection_impl.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698