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

Side by Side Diff: services/http_server/http_server.cc

Issue 715153004: Add support for HTTP handlers that are in self-contained apps. Also add an example app. (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 6 years, 1 month 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
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <stdio.h> 5 #include <stdio.h>
6 6
7 #if defined(OS_WIN) 7 #if defined(OS_WIN)
8 #include <winsock2.h> 8 #include <winsock2.h>
9 #elif defined(OS_POSIX) 9 #elif defined(OS_POSIX)
10 #include <arpa/inet.h> 10 #include <arpa/inet.h>
11 #endif 11 #endif
12 12
13 #include "base/bind.h" 13 #include "base/bind.h"
14 #include "base/format_macros.h" 14 #include "base/format_macros.h"
15 #include "base/memory/scoped_ptr.h" 15 #include "base/memory/scoped_ptr.h"
16 #include "base/memory/weak_ptr.h" 16 #include "base/memory/weak_ptr.h"
17 #include "base/strings/stringprintf.h" 17 #include "base/strings/stringprintf.h"
18 #include "mojo/public/c/system/main.h" 18 #include "mojo/public/c/system/main.h"
19 #include "mojo/public/cpp/application/application_delegate.h" 19 #include "mojo/public/cpp/application/application_delegate.h"
20 #include "mojo/public/cpp/application/application_impl.h" 20 #include "mojo/public/cpp/application/application_impl.h"
21 #include "mojo/public/cpp/application/application_runner.h" 21 #include "mojo/public/cpp/application/application_runner.h"
22 #include "mojo/public/cpp/application/interface_factory.h"
22 #include "mojo/public/cpp/environment/async_waiter.h" 23 #include "mojo/public/cpp/environment/async_waiter.h"
23 #include "mojo/public/cpp/system/data_pipe.h" 24 #include "mojo/public/cpp/system/data_pipe.h"
24 #include "mojo/services/public/interfaces/network/network_service.mojom.h" 25 #include "mojo/services/public/interfaces/network/network_service.mojom.h"
25 #include "services/http_server/http_request_parser.h" 26 #include "services/http_server/http_request_parser.h"
26 #include "services/http_server/public/http_request.mojom.h" 27 #include "services/http_server/public/http_request.mojom.h"
27 #include "services/http_server/public/http_response.mojom.h" 28 #include "services/http_server/public/http_response.mojom.h"
29 #include "services/http_server/public/http_server.mojom.h"
28 #include "third_party/re2/re2/re2.h" 30 #include "third_party/re2/re2/re2.h"
29 31
30 namespace mojo { 32 namespace mojo {
31 namespace examples { 33 namespace examples {
32 34
33 class Connection; 35 class Connection;
34 36
35 typedef base::Callback<HttpResponsePtr( 37 typedef base::Callback<void(HttpRequestPtr, Connection*)> HandleRequestCallback;
36 const HttpRequest& request)> HandleRequestCallback;
37 38
38 const char* GetHttpReasonPhrase(uint32_t code_in) { 39 const char* GetHttpReasonPhrase(uint32_t code_in) {
39 switch (code_in) { 40 switch (code_in) {
40 #define HTTP_STATUS(label, code, reason) case code: return reason; 41 #define HTTP_STATUS(label, code, reason) case code: return reason;
41 #include "net/http/http_status_code_list.h" 42 #include "net/http/http_status_code_list.h"
42 #undef HTTP_STATUS 43 #undef HTTP_STATUS
43 44
44 default: 45 default:
45 NOTREACHED() << "unknown HTTP status code " << code_in; 46 NOTREACHED() << "unknown HTTP status code " << code_in;
46 } 47 }
47 48
48 return ""; 49 return "";
49 } 50 }
50 51
51 // Represents one connection to a client. This connection will manage its own 52 // Represents one connection to a client. This connection will manage its own
52 // lifetime and will delete itself when the connection is closed. 53 // lifetime and will delete itself when the connection is closed.
53 class Connection { 54 class Connection {
54 public: 55 public:
55 // Callback called when a request is parsed. Response should be sent 56 // Callback called when a request is parsed. Response should be sent
56 // using Connection::SendResponse() on the |connection| argument. 57 // using Connection::SendResponse() on the |connection| argument.
57 typedef base::Callback<void(Connection*, const HttpRequest&)> Callback; 58 typedef base::Callback<void(Connection*, HttpRequestPtr)> Callback;
58 59
59 Connection(TCPConnectedSocketPtr conn, 60 Connection(TCPConnectedSocketPtr conn,
60 ScopedDataPipeProducerHandle sender, 61 ScopedDataPipeProducerHandle sender,
61 ScopedDataPipeConsumerHandle receiver, 62 ScopedDataPipeConsumerHandle receiver,
62 const Callback& callback) 63 const Callback& callback)
63 : connection_(conn.Pass()), 64 : connection_(conn.Pass()),
64 sender_(sender.Pass()), 65 sender_(sender.Pass()),
65 receiver_(receiver.Pass()), 66 receiver_(receiver.Pass()),
66 request_waiter_(receiver_.get(), MOJO_HANDLE_SIGNAL_READABLE, 67 request_waiter_(receiver_.get(), MOJO_HANDLE_SIGNAL_READABLE,
67 base::Bind(&Connection::OnRequestDataReady, 68 base::Bind(&Connection::OnRequestDataReady,
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
109 content_ = response->body.Pass(); 110 content_ = response->body.Pass();
110 WriteMore(); 111 WriteMore();
111 } 112 }
112 113
113 private: 114 private:
114 // Called when we have more data available from the request. 115 // Called when we have more data available from the request.
115 void OnRequestDataReady(MojoResult result) { 116 void OnRequestDataReady(MojoResult result) {
116 uint32_t num_bytes = 0; 117 uint32_t num_bytes = 0;
117 result = ReadDataRaw(receiver_.get(), NULL, &num_bytes, 118 result = ReadDataRaw(receiver_.get(), NULL, &num_bytes,
118 MOJO_READ_DATA_FLAG_QUERY); 119 MOJO_READ_DATA_FLAG_QUERY);
119 printf("ReadDataRaw result = %d, num_bytes = %d\n", result, num_bytes);
120 if (!num_bytes) 120 if (!num_bytes)
121 return; 121 return;
122 122
123 scoped_ptr<uint8_t[]> buffer(new uint8_t[num_bytes]); 123 scoped_ptr<uint8_t[]> buffer(new uint8_t[num_bytes]);
124 result = ReadDataRaw(receiver_.get(), buffer.get(), &num_bytes, 124 result = ReadDataRaw(receiver_.get(), buffer.get(), &num_bytes,
125 MOJO_READ_DATA_FLAG_ALL_OR_NONE); 125 MOJO_READ_DATA_FLAG_ALL_OR_NONE);
126 126
127 request_parser_.ProcessChunk(reinterpret_cast<char*>(buffer.get())); 127 request_parser_.ProcessChunk(reinterpret_cast<char*>(buffer.get()));
128 if (request_parser_.ParseRequest() == HttpRequestParser::ACCEPTED) { 128 if (request_parser_.ParseRequest() == HttpRequestParser::ACCEPTED) {
129 HttpRequestPtr http_request = request_parser_.GetRequest(); 129 handle_request_callback_.Run(this, request_parser_.GetRequest());
130 handle_request_callback_.Run(this, *http_request.get());
131 } 130 }
132 } 131 }
133 132
134 void WriteMore() { 133 void WriteMore() {
135 uint32_t response_bytes_available = 134 uint32_t response_bytes_available =
136 static_cast<uint32_t>(response_.size() - response_offset_); 135 static_cast<uint32_t>(response_.size() - response_offset_);
137 if (response_bytes_available) { 136 if (response_bytes_available) {
138 MojoResult result = WriteDataRaw( 137 MojoResult result = WriteDataRaw(
139 sender_.get(), &response_[response_offset_], 138 sender_.get(), &response_[response_offset_],
140 &response_bytes_available, 0); 139 &response_bytes_available, 0);
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after
228 227
229 // Callback to run once all of the request has been read. 228 // Callback to run once all of the request has been read.
230 const Callback handle_request_callback_; 229 const Callback handle_request_callback_;
231 230
232 // Contains response data to write to the pipe. Initially it is the headers, 231 // Contains response data to write to the pipe. Initially it is the headers,
233 // and then when they're written it contains chunks of the body. 232 // and then when they're written it contains chunks of the body.
234 std::string response_; 233 std::string response_;
235 size_t response_offset_; 234 size_t response_offset_;
236 }; 235 };
237 236
238 HttpResponsePtr CreateResponse(int code, const std::string& data) { 237 HttpResponsePtr CreateResponseHelper(int code, const std::string& data) {
239 HttpResponsePtr response = HttpResponse::New(); 238 HttpResponsePtr response = HttpResponse::New();
240 239
241 ScopedDataPipeProducerHandle producer_handle; 240 ScopedDataPipeProducerHandle producer_handle;
242 MojoCreateDataPipeOptions options = {sizeof(MojoCreateDataPipeOptions), 241 MojoCreateDataPipeOptions options = {sizeof(MojoCreateDataPipeOptions),
243 MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, 242 MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,
244 1, 243 1,
245 data.size()}; 244 data.size()};
246 MojoResult result = CreateDataPipe( 245 MojoResult result = CreateDataPipe(
247 &options, &producer_handle, &response->body); 246 &options, &producer_handle, &response->body);
248 DCHECK_EQ(MOJO_RESULT_OK, result); 247 DCHECK_EQ(MOJO_RESULT_OK, result);
249 uint32_t num_bytes = data.size(); 248 uint32_t num_bytes = data.size();
250 result = WriteDataRaw( 249 result = WriteDataRaw(
251 producer_handle.get(), data.c_str(), &num_bytes, 250 producer_handle.get(), data.c_str(), &num_bytes,
252 MOJO_WRITE_DATA_FLAG_ALL_OR_NONE); 251 MOJO_WRITE_DATA_FLAG_ALL_OR_NONE);
253 DCHECK_EQ(MOJO_RESULT_OK, result); 252 DCHECK_EQ(MOJO_RESULT_OK, result);
254 response->content_length = num_bytes; 253 response->content_length = num_bytes;
255 return response.Pass(); 254 return response.Pass();
256 } 255 }
257 256
258 HttpResponsePtr FooHandler(const HttpRequest& request) { 257 void FooHandler(HttpRequestPtr request, Connection* connection) {
259 return CreateResponse(200, "Foo").Pass(); 258 connection->SendResponse(CreateResponseHelper(200, "Foo").Pass());
260 } 259 }
261 260
262 HttpResponsePtr BarHandler(const HttpRequest& request) { 261 void BarHandler(HttpRequestPtr request, Connection* connection) {
263 return CreateResponse(200, "Bar").Pass(); 262 connection->SendResponse(CreateResponseHelper(200, "Bar").Pass());
264 } 263 }
265 264
266 class HttpServerApp : public ApplicationDelegate { 265 class HttpServerApp;
266
267 class HttpServerServiceImpl : public InterfaceImpl<HttpServerService> {
268 public:
269 HttpServerServiceImpl(ApplicationConnection* connection,
270 HttpServerApp* app)
271 : app_(app) {}
272 virtual ~HttpServerServiceImpl();
273
274 private:
275 // HttpServerService:
276 void AddHandler(const mojo::String& path,
277 const mojo::Callback<void(bool)>& callback) override;
278 void RemoveHandler(const mojo::String& path,
279 const mojo::Callback<void(bool)>& callback) override;
280
281 void CreateResponse(
282 uint32_t status_code,
283 const mojo::String& body,
284 const mojo::Callback<void(mojo::HttpResponsePtr)>& callback) {
285 callback.Run(CreateResponseHelper(status_code, body));
286 }
287
288 void OnRequest(HttpRequestPtr request, Connection* connection) {
289 client()->OnHandleRequest(
290 request.Pass(),
291 base::Bind(&HttpServerServiceImpl::OnResponse, base::Unretained(this),
292 connection));
293 }
294
295 void OnResponse(Connection* connection, HttpResponsePtr response) {
296 connection->SendResponse(response.Pass());
297 }
298
299 HttpServerApp* app_;
300 std::vector<std::string> paths_;
301 };
302
303 class HttpServerApp : public ApplicationDelegate,
304 public InterfaceFactory<HttpServerService> {
267 public: 305 public:
268 HttpServerApp() : weak_ptr_factory_(this) {} 306 HttpServerApp() : weak_ptr_factory_(this) {}
269 virtual void Initialize(ApplicationImpl* app) override { 307 virtual void Initialize(ApplicationImpl* app) override {
270 app->ConnectToService("mojo:network_service", &network_service_); 308 app->ConnectToService("mojo:network_service", &network_service_);
271 309
272 AddHandler("/foo", base::Bind(FooHandler)); 310 AddHandler("/foo", base::Bind(FooHandler));
273 AddHandler("/bar", base::Bind(BarHandler)); 311 AddHandler("/bar", base::Bind(BarHandler));
274 312
275 Start(); 313 Start();
276 } 314 }
277 315
278 // Add a handler for the given regex path. 316 // Add a handler for the given regex path.
279 void AddHandler(const std::string& path, 317 bool AddHandler(const std::string& path,
280 const HandleRequestCallback& handler) { 318 const HandleRequestCallback& handler) {
319 for (auto& handler : handlers_) {
320 if (handler.pattern->pattern() == path)
321 return false;
322 }
323
281 handlers_.push_back(Handler(path, handler)); 324 handlers_.push_back(Handler(path, handler));
325 return true;
326 }
327
328 bool RemoveHandler(const std::string& path) {
329 for (size_t i = 0; i < handlers_.size(); ++i) {
yzshen1 2014/11/12 17:38:33 nit: using iterator here might be a little more cl
jam 2014/11/12 20:48:40 Done.
330 if (handlers_[i].pattern->pattern() == path) {
331 handlers_.erase(handlers_.begin() + i);
332 return true;
333 }
334 }
335
336 return false;
282 } 337 }
283 338
284 private: 339 private:
340 // ApplicationDelegate:
341 bool ConfigureIncomingConnection(
342 ApplicationConnection* connection) override {
343 connection->AddService(this);
344 return true;
345 }
346
347 // InterfaceFactory<HttpServerService>:
348 void Create(ApplicationConnection* connection,
349 InterfaceRequest<HttpServerService> request) override {
350 mojo::BindToRequest(new HttpServerServiceImpl(connection, this), &request);
351 }
352
285 void OnSocketBound(NetworkErrorPtr err, NetAddressPtr bound_address) { 353 void OnSocketBound(NetworkErrorPtr err, NetAddressPtr bound_address) {
286 if (err->code != 0) { 354 if (err->code != 0) {
287 printf("Bound err = %d\n", err->code); 355 printf("Bound err = %d\n", err->code);
288 return; 356 return;
289 } 357 }
290 358
291 printf("Got address %d.%d.%d.%d:%d\n", 359 printf("Got address %d.%d.%d.%d:%d\n",
292 (int)bound_address->ipv4->addr[0], 360 (int)bound_address->ipv4->addr[0],
293 (int)bound_address->ipv4->addr[1], 361 (int)bound_address->ipv4->addr[1],
294 (int)bound_address->ipv4->addr[2], 362 (int)bound_address->ipv4->addr[2],
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
342 410
343 void Start() { 411 void Start() {
344 NetAddressPtr net_address(NetAddress::New()); 412 NetAddressPtr net_address(NetAddress::New());
345 net_address->family = NET_ADDRESS_FAMILY_IPV4; 413 net_address->family = NET_ADDRESS_FAMILY_IPV4;
346 net_address->ipv4 = NetAddressIPv4::New(); 414 net_address->ipv4 = NetAddressIPv4::New();
347 net_address->ipv4->addr.resize(4); 415 net_address->ipv4->addr.resize(4);
348 net_address->ipv4->addr[0] = 127; 416 net_address->ipv4->addr[0] = 127;
349 net_address->ipv4->addr[1] = 0; 417 net_address->ipv4->addr[1] = 0;
350 net_address->ipv4->addr[2] = 0; 418 net_address->ipv4->addr[2] = 0;
351 net_address->ipv4->addr[3] = 1; 419 net_address->ipv4->addr[3] = 1;
352 net_address->ipv4->port = 0; 420 net_address->ipv4->port = 80;
yzshen1 2014/11/12 17:38:33 This requires that there is nothing occupying that
jam 2014/11/12 20:48:40 I think right now it's easier if it's 80 so that w
353 421
354 // Note that we can start using the proxies right away even thought the 422 // Note that we can start using the proxies right away even thought the
355 // callbacks have not been called yet. If a previous step fails, they'll 423 // callbacks have not been called yet. If a previous step fails, they'll
356 // all fail. 424 // all fail.
357 network_service_->CreateTCPBoundSocket( 425 network_service_->CreateTCPBoundSocket(
358 net_address.Pass(), 426 net_address.Pass(),
359 GetProxy(&bound_socket_), 427 GetProxy(&bound_socket_),
360 base::Bind(&HttpServerApp::OnSocketBound, base::Unretained(this))); 428 base::Bind(&HttpServerApp::OnSocketBound, base::Unretained(this)));
361 bound_socket_->StartListening(GetProxy( 429 bound_socket_->StartListening(GetProxy(
362 &server_socket_), 430 &server_socket_),
363 base::Bind(&HttpServerApp::OnSocketListening, base::Unretained(this))); 431 base::Bind(&HttpServerApp::OnSocketListening, base::Unretained(this)));
364 WaitForNextConnection(); 432 WaitForNextConnection();
365 } 433 }
366 434
367 void HandleRequest(Connection* connection, const HttpRequest& request) { 435 void HandleRequest(Connection* connection, HttpRequestPtr request) {
368 printf("HandleRequest for %s\n", request.relative_url.data());
369 for (auto& handler : handlers_) { 436 for (auto& handler : handlers_) {
370 if (RE2::FullMatch(request.relative_url.data(), *handler.pattern)) { 437 if (RE2::FullMatch(request->relative_url.data(), *handler.pattern)) {
371 connection->SendResponse(handler.callback.Run(request).Pass()); 438 handler.callback.Run(request.Pass(), connection);
372 return; 439 return;
373 } 440 }
374 } 441 }
375 442
376 connection->SendResponse( 443 connection->SendResponse(
377 CreateResponse(404, "No registered handler").Pass()); 444 CreateResponseHelper(404, "No registered handler").Pass());
378 } 445 }
379 446
380 struct Handler { 447 struct Handler {
381 Handler(const std::string& pattern, 448 Handler(const std::string& pattern,
382 const HandleRequestCallback& callback) 449 const HandleRequestCallback& callback)
383 : pattern(new RE2(pattern.c_str())), callback(callback) {} 450 : pattern(new RE2(pattern.c_str())), callback(callback) {}
384 Handler(const Handler& handler) 451 Handler(const Handler& handler)
385 : pattern(new RE2(handler.pattern->pattern())), 452 : pattern(new RE2(handler.pattern->pattern())),
386 callback(handler.callback) {} 453 callback(handler.callback) {}
387 Handler& operator=(const Handler& handler) { 454 Handler& operator=(const Handler& handler) {
(...skipping 13 matching lines...) Expand all
401 TCPBoundSocketPtr bound_socket_; 468 TCPBoundSocketPtr bound_socket_;
402 TCPServerSocketPtr server_socket_; 469 TCPServerSocketPtr server_socket_;
403 470
404 ScopedDataPipeProducerHandle pending_send_handle_; 471 ScopedDataPipeProducerHandle pending_send_handle_;
405 ScopedDataPipeConsumerHandle pending_receive_handle_; 472 ScopedDataPipeConsumerHandle pending_receive_handle_;
406 TCPConnectedSocketPtr pending_connected_socket_; 473 TCPConnectedSocketPtr pending_connected_socket_;
407 474
408 std::vector<Handler> handlers_; 475 std::vector<Handler> handlers_;
409 }; 476 };
410 477
478 HttpServerServiceImpl::~HttpServerServiceImpl() {
479 for (auto& path : paths_)
480 app_->RemoveHandler(path);
481 }
482
483 void HttpServerServiceImpl::AddHandler(
484 const mojo::String& path,
485 const mojo::Callback<void(bool)>& callback) {
486 bool rv = app_->AddHandler(path,
487 base::Bind(&HttpServerServiceImpl::OnRequest,
488 base::Unretained(this)));
489 callback.Run(rv);
490 if (rv)
491 paths_.push_back(path);
492 }
493
494 void HttpServerServiceImpl::RemoveHandler(
495 const mojo::String& path,
496 const mojo::Callback<void(bool)>& callback) {
497 bool rv = app_->RemoveHandler(path);
498 callback.Run(rv);
499 for (size_t i = 0; i < paths_.size(); ++i) {
500 if (paths_[i] == path) {
501 paths_.erase(paths_.begin() + i);
502 break;
503 }
504 }
505 }
506
411 } // namespace examples 507 } // namespace examples
412 } // namespace mojo 508 } // namespace mojo
413 509
414 MojoResult MojoMain(MojoHandle shell_handle) { 510 MojoResult MojoMain(MojoHandle shell_handle) {
415 mojo::ApplicationRunner runner(new mojo::examples::HttpServerApp); 511 mojo::ApplicationRunner runner(new mojo::examples::HttpServerApp);
416 return runner.Run(shell_handle); 512 return runner.Run(shell_handle);
417 } 513 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698