Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 } |
| OLD | NEW |