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

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: review comments 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"
30 #include "services/http_server/public/http_server_util.h"
28 #include "third_party/re2/re2/re2.h" 31 #include "third_party/re2/re2/re2.h"
29 32
30 namespace mojo { 33 namespace mojo {
31 namespace examples { 34 namespace examples {
32 35
33 class Connection; 36 class Connection;
34 37
35 typedef base::Callback<HttpResponsePtr( 38 typedef base::Callback<void(HttpRequestPtr, Connection*)> HandleRequestCallback;
36 const HttpRequest& request)> HandleRequestCallback;
37 39
38 const char* GetHttpReasonPhrase(uint32_t code_in) { 40 const char* GetHttpReasonPhrase(uint32_t code_in) {
39 switch (code_in) { 41 switch (code_in) {
40 #define HTTP_STATUS(label, code, reason) case code: return reason; 42 #define HTTP_STATUS(label, code, reason) case code: return reason;
41 #include "net/http/http_status_code_list.h" 43 #include "net/http/http_status_code_list.h"
42 #undef HTTP_STATUS 44 #undef HTTP_STATUS
43 45
44 default: 46 default:
45 NOTREACHED() << "unknown HTTP status code " << code_in; 47 NOTREACHED() << "unknown HTTP status code " << code_in;
46 } 48 }
47 49
48 return ""; 50 return "";
49 } 51 }
50 52
51 // Represents one connection to a client. This connection will manage its own 53 // Represents one connection to a client. This connection will manage its own
52 // lifetime and will delete itself when the connection is closed. 54 // lifetime and will delete itself when the connection is closed.
53 class Connection { 55 class Connection {
54 public: 56 public:
55 // Callback called when a request is parsed. Response should be sent 57 // Callback called when a request is parsed. Response should be sent
56 // using Connection::SendResponse() on the |connection| argument. 58 // using Connection::SendResponse() on the |connection| argument.
57 typedef base::Callback<void(Connection*, const HttpRequest&)> Callback; 59 typedef base::Callback<void(Connection*, HttpRequestPtr)> Callback;
58 60
59 Connection(TCPConnectedSocketPtr conn, 61 Connection(TCPConnectedSocketPtr conn,
60 ScopedDataPipeProducerHandle sender, 62 ScopedDataPipeProducerHandle sender,
61 ScopedDataPipeConsumerHandle receiver, 63 ScopedDataPipeConsumerHandle receiver,
62 const Callback& callback) 64 const Callback& callback)
63 : connection_(conn.Pass()), 65 : connection_(conn.Pass()),
64 sender_(sender.Pass()), 66 sender_(sender.Pass()),
65 receiver_(receiver.Pass()), 67 receiver_(receiver.Pass()),
66 request_waiter_(receiver_.get(), MOJO_HANDLE_SIGNAL_READABLE, 68 request_waiter_(receiver_.get(), MOJO_HANDLE_SIGNAL_READABLE,
67 base::Bind(&Connection::OnRequestDataReady, 69 base::Bind(&Connection::OnRequestDataReady,
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
109 content_ = response->body.Pass(); 111 content_ = response->body.Pass();
110 WriteMore(); 112 WriteMore();
111 } 113 }
112 114
113 private: 115 private:
114 // Called when we have more data available from the request. 116 // Called when we have more data available from the request.
115 void OnRequestDataReady(MojoResult result) { 117 void OnRequestDataReady(MojoResult result) {
116 uint32_t num_bytes = 0; 118 uint32_t num_bytes = 0;
117 result = ReadDataRaw(receiver_.get(), NULL, &num_bytes, 119 result = ReadDataRaw(receiver_.get(), NULL, &num_bytes,
118 MOJO_READ_DATA_FLAG_QUERY); 120 MOJO_READ_DATA_FLAG_QUERY);
119 printf("ReadDataRaw result = %d, num_bytes = %d\n", result, num_bytes);
120 if (!num_bytes) 121 if (!num_bytes)
121 return; 122 return;
122 123
123 scoped_ptr<uint8_t[]> buffer(new uint8_t[num_bytes]); 124 scoped_ptr<uint8_t[]> buffer(new uint8_t[num_bytes]);
124 result = ReadDataRaw(receiver_.get(), buffer.get(), &num_bytes, 125 result = ReadDataRaw(receiver_.get(), buffer.get(), &num_bytes,
125 MOJO_READ_DATA_FLAG_ALL_OR_NONE); 126 MOJO_READ_DATA_FLAG_ALL_OR_NONE);
126 127
127 request_parser_.ProcessChunk(reinterpret_cast<char*>(buffer.get())); 128 request_parser_.ProcessChunk(reinterpret_cast<char*>(buffer.get()));
128 if (request_parser_.ParseRequest() == HttpRequestParser::ACCEPTED) { 129 if (request_parser_.ParseRequest() == HttpRequestParser::ACCEPTED) {
129 HttpRequestPtr http_request = request_parser_.GetRequest(); 130 handle_request_callback_.Run(this, request_parser_.GetRequest());
130 handle_request_callback_.Run(this, *http_request.get());
131 } 131 }
132 } 132 }
133 133
134 void WriteMore() { 134 void WriteMore() {
135 uint32_t response_bytes_available = 135 uint32_t response_bytes_available =
136 static_cast<uint32_t>(response_.size() - response_offset_); 136 static_cast<uint32_t>(response_.size() - response_offset_);
137 if (response_bytes_available) { 137 if (response_bytes_available) {
138 MojoResult result = WriteDataRaw( 138 MojoResult result = WriteDataRaw(
139 sender_.get(), &response_[response_offset_], 139 sender_.get(), &response_[response_offset_],
140 &response_bytes_available, 0); 140 &response_bytes_available, 0);
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after
228 228
229 // Callback to run once all of the request has been read. 229 // Callback to run once all of the request has been read.
230 const Callback handle_request_callback_; 230 const Callback handle_request_callback_;
231 231
232 // Contains response data to write to the pipe. Initially it is the headers, 232 // 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. 233 // and then when they're written it contains chunks of the body.
234 std::string response_; 234 std::string response_;
235 size_t response_offset_; 235 size_t response_offset_;
236 }; 236 };
237 237
238 HttpResponsePtr CreateResponse(int code, const std::string& data) { 238 void FooHandler(HttpRequestPtr request, Connection* connection) {
239 HttpResponsePtr response = HttpResponse::New(); 239 connection->SendResponse(CreateHttpResponse(200, "Foo"));
240
241 ScopedDataPipeProducerHandle producer_handle;
242 MojoCreateDataPipeOptions options = {sizeof(MojoCreateDataPipeOptions),
243 MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,
244 1,
245 data.size()};
246 MojoResult result = CreateDataPipe(
247 &options, &producer_handle, &response->body);
248 DCHECK_EQ(MOJO_RESULT_OK, result);
249 uint32_t num_bytes = data.size();
250 result = WriteDataRaw(
251 producer_handle.get(), data.c_str(), &num_bytes,
252 MOJO_WRITE_DATA_FLAG_ALL_OR_NONE);
253 DCHECK_EQ(MOJO_RESULT_OK, result);
254 response->content_length = num_bytes;
255 return response.Pass();
256 } 240 }
257 241
258 HttpResponsePtr FooHandler(const HttpRequest& request) { 242 void BarHandler(HttpRequestPtr request, Connection* connection) {
259 return CreateResponse(200, "Foo").Pass(); 243 connection->SendResponse(CreateHttpResponse(200, "Bar"));
260 } 244 }
261 245
262 HttpResponsePtr BarHandler(const HttpRequest& request) { 246 class HttpServerApp;
263 return CreateResponse(200, "Bar").Pass();
264 }
265 247
266 class HttpServerApp : public ApplicationDelegate { 248 class HttpServerServiceImpl : public InterfaceImpl<HttpServerService> {
249 public:
250 HttpServerServiceImpl(ApplicationConnection* connection,
251 HttpServerApp* app)
252 : app_(app) {}
253 virtual ~HttpServerServiceImpl();
254
255 private:
256 // HttpServerService:
257 void AddHandler(const mojo::String& path,
258 const mojo::Callback<void(bool)>& callback) override;
259 void RemoveHandler(const mojo::String& path,
260 const mojo::Callback<void(bool)>& callback) override;
261
262 void OnRequest(HttpRequestPtr request, Connection* connection) {
263 client()->OnHandleRequest(
264 request.Pass(),
265 base::Bind(&HttpServerServiceImpl::OnResponse, base::Unretained(this),
266 connection));
267 }
268
269 void OnResponse(Connection* connection, HttpResponsePtr response) {
270 connection->SendResponse(response.Pass());
271 }
272
273 HttpServerApp* app_;
274 std::vector<std::string> paths_;
275 };
276
277 class HttpServerApp : public ApplicationDelegate,
278 public InterfaceFactory<HttpServerService> {
267 public: 279 public:
268 HttpServerApp() : weak_ptr_factory_(this) {} 280 HttpServerApp() : weak_ptr_factory_(this) {}
269 virtual void Initialize(ApplicationImpl* app) override { 281 virtual void Initialize(ApplicationImpl* app) override {
270 app->ConnectToService("mojo:network_service", &network_service_); 282 app->ConnectToService("mojo:network_service", &network_service_);
271 283
272 AddHandler("/foo", base::Bind(FooHandler)); 284 AddHandler("/foo", base::Bind(FooHandler));
273 AddHandler("/bar", base::Bind(BarHandler)); 285 AddHandler("/bar", base::Bind(BarHandler));
274 286
275 Start(); 287 Start();
276 } 288 }
277 289
278 // Add a handler for the given regex path. 290 // Add a handler for the given regex path.
279 void AddHandler(const std::string& path, 291 bool AddHandler(const std::string& path,
280 const HandleRequestCallback& handler) { 292 const HandleRequestCallback& handler) {
293 for (auto& handler : handlers_) {
294 if (handler.pattern->pattern() == path)
295 return false;
296 }
297
281 handlers_.push_back(Handler(path, handler)); 298 handlers_.push_back(Handler(path, handler));
299 return true;
300 }
301
302 bool RemoveHandler(const std::string& path) {
303 for (auto i = handlers_.begin(); i != handlers_.end(); ++i) {
304 if (i->pattern->pattern() == path) {
305 handlers_.erase(i);
306 return true;
307 }
308 }
309
310 return false;
282 } 311 }
283 312
284 private: 313 private:
314 // ApplicationDelegate:
315 bool ConfigureIncomingConnection(
316 ApplicationConnection* connection) override {
317 connection->AddService(this);
318 return true;
319 }
320
321 // InterfaceFactory<HttpServerService>:
322 void Create(ApplicationConnection* connection,
323 InterfaceRequest<HttpServerService> request) override {
324 mojo::BindToRequest(new HttpServerServiceImpl(connection, this), &request);
325 }
326
285 void OnSocketBound(NetworkErrorPtr err, NetAddressPtr bound_address) { 327 void OnSocketBound(NetworkErrorPtr err, NetAddressPtr bound_address) {
286 if (err->code != 0) { 328 if (err->code != 0) {
287 printf("Bound err = %d\n", err->code); 329 printf("Bound err = %d\n", err->code);
288 return; 330 return;
289 } 331 }
290 332
291 printf("Got address %d.%d.%d.%d:%d\n", 333 printf("Got address %d.%d.%d.%d:%d\n",
292 (int)bound_address->ipv4->addr[0], 334 (int)bound_address->ipv4->addr[0],
293 (int)bound_address->ipv4->addr[1], 335 (int)bound_address->ipv4->addr[1],
294 (int)bound_address->ipv4->addr[2], 336 (int)bound_address->ipv4->addr[2],
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
342 384
343 void Start() { 385 void Start() {
344 NetAddressPtr net_address(NetAddress::New()); 386 NetAddressPtr net_address(NetAddress::New());
345 net_address->family = NET_ADDRESS_FAMILY_IPV4; 387 net_address->family = NET_ADDRESS_FAMILY_IPV4;
346 net_address->ipv4 = NetAddressIPv4::New(); 388 net_address->ipv4 = NetAddressIPv4::New();
347 net_address->ipv4->addr.resize(4); 389 net_address->ipv4->addr.resize(4);
348 net_address->ipv4->addr[0] = 127; 390 net_address->ipv4->addr[0] = 127;
349 net_address->ipv4->addr[1] = 0; 391 net_address->ipv4->addr[1] = 0;
350 net_address->ipv4->addr[2] = 0; 392 net_address->ipv4->addr[2] = 0;
351 net_address->ipv4->addr[3] = 1; 393 net_address->ipv4->addr[3] = 1;
352 net_address->ipv4->port = 0; 394 net_address->ipv4->port = 80;
353 395
354 // Note that we can start using the proxies right away even thought the 396 // 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 397 // callbacks have not been called yet. If a previous step fails, they'll
356 // all fail. 398 // all fail.
357 network_service_->CreateTCPBoundSocket( 399 network_service_->CreateTCPBoundSocket(
358 net_address.Pass(), 400 net_address.Pass(),
359 GetProxy(&bound_socket_), 401 GetProxy(&bound_socket_),
360 base::Bind(&HttpServerApp::OnSocketBound, base::Unretained(this))); 402 base::Bind(&HttpServerApp::OnSocketBound, base::Unretained(this)));
361 bound_socket_->StartListening(GetProxy( 403 bound_socket_->StartListening(GetProxy(
362 &server_socket_), 404 &server_socket_),
363 base::Bind(&HttpServerApp::OnSocketListening, base::Unretained(this))); 405 base::Bind(&HttpServerApp::OnSocketListening, base::Unretained(this)));
364 WaitForNextConnection(); 406 WaitForNextConnection();
365 } 407 }
366 408
367 void HandleRequest(Connection* connection, const HttpRequest& request) { 409 void HandleRequest(Connection* connection, HttpRequestPtr request) {
368 printf("HandleRequest for %s\n", request.relative_url.data());
369 for (auto& handler : handlers_) { 410 for (auto& handler : handlers_) {
370 if (RE2::FullMatch(request.relative_url.data(), *handler.pattern)) { 411 if (RE2::FullMatch(request->relative_url.data(), *handler.pattern)) {
371 connection->SendResponse(handler.callback.Run(request).Pass()); 412 handler.callback.Run(request.Pass(), connection);
372 return; 413 return;
373 } 414 }
374 } 415 }
375 416
376 connection->SendResponse( 417 connection->SendResponse(
377 CreateResponse(404, "No registered handler").Pass()); 418 CreateHttpResponse(404, "No registered handler").Pass());
378 } 419 }
379 420
380 struct Handler { 421 struct Handler {
381 Handler(const std::string& pattern, 422 Handler(const std::string& pattern,
382 const HandleRequestCallback& callback) 423 const HandleRequestCallback& callback)
383 : pattern(new RE2(pattern.c_str())), callback(callback) {} 424 : pattern(new RE2(pattern.c_str())), callback(callback) {}
384 Handler(const Handler& handler) 425 Handler(const Handler& handler)
385 : pattern(new RE2(handler.pattern->pattern())), 426 : pattern(new RE2(handler.pattern->pattern())),
386 callback(handler.callback) {} 427 callback(handler.callback) {}
387 Handler& operator=(const Handler& handler) { 428 Handler& operator=(const Handler& handler) {
(...skipping 13 matching lines...) Expand all
401 TCPBoundSocketPtr bound_socket_; 442 TCPBoundSocketPtr bound_socket_;
402 TCPServerSocketPtr server_socket_; 443 TCPServerSocketPtr server_socket_;
403 444
404 ScopedDataPipeProducerHandle pending_send_handle_; 445 ScopedDataPipeProducerHandle pending_send_handle_;
405 ScopedDataPipeConsumerHandle pending_receive_handle_; 446 ScopedDataPipeConsumerHandle pending_receive_handle_;
406 TCPConnectedSocketPtr pending_connected_socket_; 447 TCPConnectedSocketPtr pending_connected_socket_;
407 448
408 std::vector<Handler> handlers_; 449 std::vector<Handler> handlers_;
409 }; 450 };
410 451
452 HttpServerServiceImpl::~HttpServerServiceImpl() {
453 for (auto& path : paths_)
454 app_->RemoveHandler(path);
455 }
456
457 void HttpServerServiceImpl::AddHandler(
458 const mojo::String& path,
459 const mojo::Callback<void(bool)>& callback) {
460 bool rv = app_->AddHandler(path,
461 base::Bind(&HttpServerServiceImpl::OnRequest,
462 base::Unretained(this)));
463 callback.Run(rv);
464 if (rv)
465 paths_.push_back(path);
466 }
467
468 void HttpServerServiceImpl::RemoveHandler(
469 const mojo::String& path,
470 const mojo::Callback<void(bool)>& callback) {
471 bool rv = app_->RemoveHandler(path);
472 callback.Run(rv);
473 for (auto i = paths_.begin(); i != paths_.end(); ++i) {
474 if (*i == path) {
475 paths_.erase(i);
476 break;
477 }
478 }
479 }
480
411 } // namespace examples 481 } // namespace examples
412 } // namespace mojo 482 } // namespace mojo
413 483
414 MojoResult MojoMain(MojoHandle shell_handle) { 484 MojoResult MojoMain(MojoHandle shell_handle) {
415 mojo::ApplicationRunner runner(new mojo::examples::HttpServerApp); 485 mojo::ApplicationRunner runner(new mojo::examples::HttpServerApp);
416 return runner.Run(shell_handle); 486 return runner.Run(shell_handle);
417 } 487 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698