OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include <stdio.h> | |
6 #include <arpa/inet.h> | |
7 | |
8 #include "base/bind.h" | |
9 #include "base/memory/weak_ptr.h" | |
10 #include "mojo/common/handle_watcher.h" | |
11 #include "mojo/public/c/system/main.h" | |
12 #include "mojo/public/cpp/application/application_delegate.h" | |
13 #include "mojo/public/cpp/application/application_impl.h" | |
14 #include "mojo/public/cpp/application/application_runner.h" | |
15 #include "mojo/public/cpp/system/data_pipe.h" | |
16 #include "mojo/services/public/interfaces/network/network_service.mojom.h" | |
17 | |
18 namespace mojo { | |
19 namespace examples { | |
20 | |
21 // The canned response that this server replies to each request with. | |
22 const char kResponse[] = | |
23 "HTTP/1.0 200 OK\n" | |
24 "Content-Type: text/html; charset=utf-8\n" | |
25 "\n" | |
26 "Hello, world\n"; | |
27 | |
28 // Represents one connection to a client. This connection will manage its own | |
29 // lifetime and will delete itself when the connection is closed. | |
30 class Connection { | |
31 public: | |
32 Connection(TCPConnectedSocketPtr conn, | |
33 ScopedDataPipeProducerHandle sender, | |
34 ScopedDataPipeConsumerHandle receiver) | |
35 : connection_(conn.Pass()), | |
36 sender_(sender.Pass()), | |
37 receiver_(receiver.Pass()), | |
38 weak_ptr_factory_(this), | |
39 response_(kResponse), | |
40 response_offset_(0) { | |
41 WriteMore(); | |
42 } | |
43 | |
44 ~Connection() { | |
45 } | |
46 | |
47 private: | |
48 void OnSenderReady(MojoResult result) { | |
49 WriteMore(); | |
50 } | |
51 | |
52 void WriteMore() { | |
53 uint32_t num_bytes = | |
54 static_cast<uint32_t>(response_.size() - response_offset_); | |
55 // TODO(brettw) write chunks (ideally capped at some small size to | |
56 // illustrate how it works even in the presence of our relatively small | |
57 // reply) rather than specifying ALL_OR_NONE. See below. | |
58 MojoResult result = WriteDataRaw( | |
59 sender_.get(), &response_[response_offset_], &num_bytes, | |
60 MOJO_WRITE_DATA_FLAG_ALL_OR_NONE); | |
61 if (result == MOJO_RESULT_SHOULD_WAIT) { | |
62 num_bytes = 0; | |
63 } else if (result != MOJO_RESULT_OK) { | |
64 printf("Error writing to pipe.\n"); | |
65 delete this; | |
66 return; | |
67 } | |
68 | |
69 response_offset_ += num_bytes; | |
70 | |
71 if (response_offset_ == response_.size()) { | |
72 // Connection complete. | |
73 delete this; | |
74 return; | |
75 } | |
76 | |
77 // Wait for sender to be writable. | |
78 // TODO(brettw) we won't actually get here since we ALL_OR_NONE was | |
79 // specified above. This call won't work because the message loop is not a | |
80 // Chromium message loop. Instead, we probably need to use | |
81 // Environment::GetDefaultAsyncWaiter to wait on the handles and remove the | |
82 // ALL_OR_NONE flag. | |
83 sender_watcher_.Start( | |
84 sender_.get(), | |
85 MOJO_HANDLE_SIGNAL_WRITABLE, MOJO_DEADLINE_INDEFINITE, | |
86 base::Bind(&Connection::OnSenderReady, weak_ptr_factory_.GetWeakPtr())); | |
87 } | |
88 | |
89 TCPConnectedSocketPtr connection_; | |
90 ScopedDataPipeProducerHandle sender_; | |
91 ScopedDataPipeConsumerHandle receiver_; | |
92 | |
93 base::WeakPtrFactory<Connection> weak_ptr_factory_; | |
94 common::HandleWatcher sender_watcher_; | |
95 | |
96 std::string response_; | |
97 size_t response_offset_; | |
98 }; | |
99 | |
100 class HttpServerApp : public ApplicationDelegate { | |
101 public: | |
102 virtual void Initialize(ApplicationImpl* app) override { | |
103 app->ConnectToService("mojo:network_service", &network_service_); | |
104 Start(); | |
105 } | |
106 | |
107 private: | |
108 void OnSocketBound(NetworkErrorPtr err, NetAddressPtr bound_address) { | |
109 if (err->code != 0) { | |
110 printf("Bound err = %d\n", err->code); | |
111 return; | |
112 } | |
113 | |
114 printf("Got address %d.%d.%d.%d:%d\n", | |
115 (int)bound_address->ipv4->addr[0], | |
116 (int)bound_address->ipv4->addr[1], | |
117 (int)bound_address->ipv4->addr[2], | |
118 (int)bound_address->ipv4->addr[3], | |
119 (int)bound_address->ipv4->port); | |
120 } | |
121 | |
122 void OnSocketListening(NetworkErrorPtr err) { | |
123 if (err->code != 0) { | |
124 printf("Listen err = %d\n", err->code); | |
125 return; | |
126 } | |
127 printf("Waiting for incoming connections...\n"); | |
128 } | |
129 | |
130 void OnConnectionAccepted(NetworkErrorPtr err, NetAddressPtr remote_address) { | |
131 if (err->code != 0) { | |
132 printf("Accepted socket error = %d\n", err->code); | |
133 return; | |
134 } | |
135 | |
136 new Connection(pending_connected_socket_.Pass(), | |
137 pending_send_handle_.Pass(), | |
138 pending_receive_handle_.Pass()); | |
139 | |
140 // Ready for another connection. | |
141 WaitForNextConnection(); | |
142 } | |
143 | |
144 void WaitForNextConnection() { | |
145 // Need two pipes (one for each direction). | |
146 ScopedDataPipeConsumerHandle send_consumer_handle; | |
147 MojoResult result = CreateDataPipe( | |
148 nullptr, &pending_send_handle_, &send_consumer_handle); | |
149 assert(result == MOJO_RESULT_OK); | |
150 | |
151 ScopedDataPipeProducerHandle receive_producer_handle; | |
152 result = CreateDataPipe( | |
153 nullptr, &receive_producer_handle, &pending_receive_handle_); | |
154 assert(result == MOJO_RESULT_OK); | |
155 MOJO_ALLOW_UNUSED_LOCAL(result); | |
156 | |
157 server_socket_->Accept(send_consumer_handle.Pass(), | |
158 receive_producer_handle.Pass(), | |
159 GetProxy(&pending_connected_socket_), | |
160 base::Bind(&HttpServerApp::OnConnectionAccepted, | |
161 base::Unretained(this))); | |
162 } | |
163 | |
164 void Start() { | |
165 NetAddressPtr net_address(NetAddress::New()); | |
166 net_address->family = NET_ADDRESS_FAMILY_IPV4; | |
167 net_address->ipv4 = NetAddressIPv4::New(); | |
168 net_address->ipv4->addr.resize(4); | |
169 net_address->ipv4->addr[0] = 127; | |
170 net_address->ipv4->addr[1] = 0; | |
171 net_address->ipv4->addr[2] = 0; | |
172 net_address->ipv4->addr[3] = 1; | |
173 net_address->ipv4->port = 0; | |
174 | |
175 // Note that we can start using the proxies right away even thought the | |
176 // callbacks have not been called yet. If a previous step fails, they'll | |
177 // all fail. | |
178 network_service_->CreateTCPBoundSocket( | |
179 net_address.Pass(), | |
180 GetProxy(&bound_socket_), | |
181 base::Bind(&HttpServerApp::OnSocketBound, base::Unretained(this))); | |
182 bound_socket_->StartListening(GetProxy( | |
183 &server_socket_), | |
184 base::Bind(&HttpServerApp::OnSocketListening, base::Unretained(this))); | |
185 WaitForNextConnection(); | |
186 } | |
187 | |
188 NetworkServicePtr network_service_; | |
189 TCPBoundSocketPtr bound_socket_; | |
190 TCPServerSocketPtr server_socket_; | |
191 | |
192 ScopedDataPipeProducerHandle pending_send_handle_; | |
193 ScopedDataPipeConsumerHandle pending_receive_handle_; | |
194 TCPConnectedSocketPtr pending_connected_socket_; | |
195 }; | |
196 | |
197 } // namespace examples | |
198 } // namespace mojo | |
199 | |
200 MojoResult MojoMain(MojoHandle shell_handle) { | |
201 mojo::ApplicationRunner runner(new mojo::examples::HttpServerApp); | |
202 return runner.Run(shell_handle); | |
203 } | |
OLD | NEW |