blob: 81f7a5ce9bfba9455c57c6356c9efd00b7b92dd3 [file] [log] [blame]
Daniel Erat35f65872015-08-17 20:59:29 -06001// Copyright 2015 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
Alex Vakulenko31a63792015-02-03 12:44:57 -080014
Daniel Eratf719c982015-08-14 17:33:16 -060015#include "libwebserv/protocol_handler.h"
Alex Vakulenko31a63792015-02-03 12:44:57 -080016
17#include <tuple>
18
19#include <base/logging.h>
20#include <chromeos/map_utils.h>
Alex Vakulenko950687b2015-09-18 13:41:58 -070021#include <chromeos/streams/file_stream.h>
Alex Vakulenko31a63792015-02-03 12:44:57 -080022
Christopher Wiley34bc9df2015-08-19 15:03:47 -070023#include "dbus_bindings/org.chromium.WebServer.RequestHandler.h"
Daniel Eratf719c982015-08-14 17:33:16 -060024#include "libwebserv/request.h"
25#include "libwebserv/request_handler_callback.h"
26#include "libwebserv/response.h"
27#include "libwebserv/server.h"
Alex Vakulenko31a63792015-02-03 12:44:57 -080028#include "webservd/dbus-proxies.h"
Alex Vakulenko31a63792015-02-03 12:44:57 -080029
30namespace libwebserv {
31
32namespace {
33
Alex Vakulenko330a10f2015-09-23 16:51:02 -070034// Dummy callbacks for async D-Bus/Stream errors.
35void IgnoreDBusError(chromeos::Error* error) {}
36void IgnoreStreamError(const chromeos::Error* error) {}
37
38// Structure to hold the data needed for asynchronous copying of two streams.
39struct StreamCopyData {
40 chromeos::StreamPtr src_stream;
41 chromeos::StreamPtr dest_stream;
42 std::vector<uint8_t> buffer;
43};
44
45// Forward-declaration.
46void PerformRead(std::unique_ptr<StreamCopyData> data);
47
48// Async callback which writes data to the destination stream after read.
49void PerformWrite(std::unique_ptr<StreamCopyData> data, size_t size) {
50 if (size == 0) // We are all done.
51 return;
52 data->dest_stream->WriteAllAsync(
53 data->buffer.data(), size, base::Bind(&PerformRead, base::Passed(&data)),
54 base::Bind(&IgnoreStreamError), nullptr);
55}
56
57// Reads the data from the source stream into a buffer and invokes PerformWrite
58// when done.
59void PerformRead(std::unique_ptr<StreamCopyData> data) {
60 data->src_stream->ReadAsync(data->buffer.data(), data->buffer.size(),
61 base::Bind(&PerformWrite, base::Passed(&data)),
62 base::Bind(&IgnoreStreamError), nullptr);
63}
64
65// Copies the data from |src_stream| to the destination stream represented
66// by a file descriptor |fd|.
67void WriteResponseData(chromeos::StreamPtr src_stream,
68 const dbus::FileDescriptor& fd) {
69 std::unique_ptr<StreamCopyData> data{new StreamCopyData};
70 int dupfd = dup(fd.value());
71 data->src_stream = std::move(src_stream);
72 data->dest_stream =
73 chromeos::FileStream::FromFileDescriptor(dupfd, true, nullptr);
74 data->buffer.resize(4096); // Read buffer of 4 KiB.
75 CHECK(data->src_stream);
76 CHECK(data->dest_stream);
77 PerformRead(std::move(data));
78}
Alex Vakulenko31a63792015-02-03 12:44:57 -080079
80} // anonymous namespace
81
82const char ProtocolHandler::kHttp[] = "http";
83const char ProtocolHandler::kHttps[] = "https";
84
Alex Vakulenko3d2d5632015-03-02 15:58:32 -080085ProtocolHandler::ProtocolHandler(const std::string& name, Server* server)
86 : name_{name}, server_{server} {
87}
Alex Vakulenko31a63792015-02-03 12:44:57 -080088
89ProtocolHandler::~ProtocolHandler() {
90 // Remove any existing handlers, so the web server knows that we don't
91 // need them anymore.
92
93 // We need to get a copy of the map keys since removing the handlers will
94 // modify the map in the middle of the loop and that's not a good thing.
95 auto handler_ids = chromeos::GetMapKeys(request_handlers_);
96 for (int handler_id : handler_ids) {
97 RemoveHandler(handler_id);
98 }
99}
100
Alex Vakulenko3d2d5632015-03-02 15:58:32 -0800101std::string ProtocolHandler::GetName() const {
102 return name_;
Alex Vakulenko31a63792015-02-03 12:44:57 -0800103}
104
Alex Vakulenko3d2d5632015-03-02 15:58:32 -0800105std::set<uint16_t> ProtocolHandler::GetPorts() const {
106 std::set<uint16_t> ports;
107 for (const auto& pair : proxies_)
108 ports.insert(pair.second->port());
109 return ports;
Alex Vakulenko31a63792015-02-03 12:44:57 -0800110}
111
Alex Vakulenko3d2d5632015-03-02 15:58:32 -0800112std::set<std::string> ProtocolHandler::GetProtocols() const {
113 std::set<std::string> protocols;
114 for (const auto& pair : proxies_)
115 protocols.insert(pair.second->protocol());
116 return protocols;
Alex Vakulenko31a63792015-02-03 12:44:57 -0800117}
118
119chromeos::Blob ProtocolHandler::GetCertificateFingerprint() const {
Alex Vakulenko3d2d5632015-03-02 15:58:32 -0800120 chromeos::Blob fingerprint;
121 for (const auto& pair : proxies_) {
122 fingerprint = pair.second->certificate_fingerprint();
123 if (!fingerprint.empty())
124 break;
125 }
126 return fingerprint;
Alex Vakulenko31a63792015-02-03 12:44:57 -0800127}
128
129int ProtocolHandler::AddHandler(
130 const std::string& url,
131 const std::string& method,
132 std::unique_ptr<RequestHandlerInterface> handler) {
Alex Vakulenko3d8c9122015-03-13 13:54:00 -0700133 request_handlers_.emplace(
134 ++last_handler_id_,
135 HandlerMapEntry{url, method,
136 std::map<ProtocolHandlerProxy*, std::string>{},
137 std::move(handler)});
138 // For each instance of remote protocol handler object sharing the same name,
139 // add the request handler.
Alex Vakulenko3d2d5632015-03-02 15:58:32 -0800140 for (const auto& pair : proxies_) {
141 pair.second->AddRequestHandlerAsync(
Alex Vakulenko31a63792015-02-03 12:44:57 -0800142 url,
143 method,
144 server_->service_name_,
145 base::Bind(&ProtocolHandler::AddHandlerSuccess,
146 weak_ptr_factory_.GetWeakPtr(),
Alex Vakulenko3d8c9122015-03-13 13:54:00 -0700147 last_handler_id_,
148 pair.second),
Alex Vakulenko31a63792015-02-03 12:44:57 -0800149 base::Bind(&ProtocolHandler::AddHandlerError,
150 weak_ptr_factory_.GetWeakPtr(),
151 last_handler_id_));
152 }
153 return last_handler_id_;
154}
155
156int ProtocolHandler::AddHandlerCallback(
157 const std::string& url,
158 const std::string& method,
159 const base::Callback<RequestHandlerInterface::HandlerSignature>&
160 handler_callback) {
161 std::unique_ptr<RequestHandlerInterface> handler{
162 new RequestHandlerCallback{handler_callback}};
163 return AddHandler(url, method, std::move(handler));
164}
165
166bool ProtocolHandler::RemoveHandler(int handler_id) {
167 auto p = request_handlers_.find(handler_id);
168 if (p == request_handlers_.end())
169 return false;
170
Alex Vakulenko3d8c9122015-03-13 13:54:00 -0700171 for (const auto& pair : p->second.remote_handler_ids) {
172 pair.first->RemoveRequestHandlerAsync(
173 pair.second,
Alex Vakulenko31a63792015-02-03 12:44:57 -0800174 base::Bind(&base::DoNothing),
Alex Vakulenko330a10f2015-09-23 16:51:02 -0700175 base::Bind(&IgnoreDBusError));
Alex Vakulenko31a63792015-02-03 12:44:57 -0800176 }
177
178 request_handlers_.erase(p);
179 return true;
180}
181
Alex Vakulenko3d2d5632015-03-02 15:58:32 -0800182void ProtocolHandler::Connect(ProtocolHandlerProxy* proxy) {
183 proxies_.emplace(proxy->GetObjectPath(), proxy);
Alex Vakulenko31a63792015-02-03 12:44:57 -0800184 for (const auto& pair : request_handlers_) {
Alex Vakulenko3d2d5632015-03-02 15:58:32 -0800185 proxy->AddRequestHandlerAsync(
Alex Vakulenko31a63792015-02-03 12:44:57 -0800186 pair.second.url,
187 pair.second.method,
188 server_->service_name_,
189 base::Bind(&ProtocolHandler::AddHandlerSuccess,
190 weak_ptr_factory_.GetWeakPtr(),
Alex Vakulenko3d8c9122015-03-13 13:54:00 -0700191 pair.first,
192 proxy),
Alex Vakulenko31a63792015-02-03 12:44:57 -0800193 base::Bind(&ProtocolHandler::AddHandlerError,
194 weak_ptr_factory_.GetWeakPtr(),
195 pair.first));
196 }
197}
198
Alex Vakulenko3d2d5632015-03-02 15:58:32 -0800199void ProtocolHandler::Disconnect(const dbus::ObjectPath& object_path) {
200 proxies_.erase(object_path);
201 if (proxies_.empty())
202 remote_handler_id_map_.clear();
Alex Vakulenko3d8c9122015-03-13 13:54:00 -0700203 for (auto& pair : request_handlers_)
204 pair.second.remote_handler_ids.clear();
Alex Vakulenko31a63792015-02-03 12:44:57 -0800205}
206
207void ProtocolHandler::AddHandlerSuccess(int handler_id,
Alex Vakulenko3d8c9122015-03-13 13:54:00 -0700208 ProtocolHandlerProxy* proxy,
Alex Vakulenko31a63792015-02-03 12:44:57 -0800209 const std::string& remote_handler_id) {
210 auto p = request_handlers_.find(handler_id);
211 CHECK(p != request_handlers_.end());
Alex Vakulenko3d8c9122015-03-13 13:54:00 -0700212 p->second.remote_handler_ids.emplace(proxy, remote_handler_id);
Alex Vakulenko31a63792015-02-03 12:44:57 -0800213
214 remote_handler_id_map_.emplace(remote_handler_id, handler_id);
215}
216
217void ProtocolHandler::AddHandlerError(int handler_id, chromeos::Error* error) {
218 // Nothing to do at the moment.
219}
220
Alex Vakulenko3d2d5632015-03-02 15:58:32 -0800221bool ProtocolHandler::ProcessRequest(const std::string& protocol_handler_id,
222 const std::string& remote_handler_id,
Alex Vakulenko31a63792015-02-03 12:44:57 -0800223 const std::string& request_id,
224 std::unique_ptr<Request> request,
225 chromeos::ErrorPtr* error) {
Alex Vakulenko3d2d5632015-03-02 15:58:32 -0800226 request_id_map_.emplace(request_id, protocol_handler_id);
Alex Vakulenko31a63792015-02-03 12:44:57 -0800227 auto id_iter = remote_handler_id_map_.find(remote_handler_id);
228 if (id_iter == remote_handler_id_map_.end()) {
229 chromeos::Error::AddToPrintf(error, FROM_HERE,
230 chromeos::errors::dbus::kDomain,
231 DBUS_ERROR_FAILED,
232 "Unknown request handler '%s'",
233 remote_handler_id.c_str());
234 return false;
235 }
236 auto handler_iter = request_handlers_.find(id_iter->second);
237 if (handler_iter == request_handlers_.end()) {
238 chromeos::Error::AddToPrintf(error, FROM_HERE,
239 chromeos::errors::dbus::kDomain,
240 DBUS_ERROR_FAILED,
241 "Handler # %d is no longer available",
242 id_iter->second);
243 return false;
244 }
245 handler_iter->second.handler->HandleRequest(
Alex Vakulenko83e66cd2015-04-24 14:51:58 -0700246 std::move(request),
247 std::unique_ptr<Response>{new Response{this, request_id}});
Alex Vakulenko31a63792015-02-03 12:44:57 -0800248 return true;
249}
250
251void ProtocolHandler::CompleteRequest(
252 const std::string& request_id,
253 int status_code,
254 const std::multimap<std::string, std::string>& headers,
Alex Vakulenko330a10f2015-09-23 16:51:02 -0700255 chromeos::StreamPtr data_stream) {
Alex Vakulenko3d2d5632015-03-02 15:58:32 -0800256 ProtocolHandlerProxy* proxy = GetRequestProtocolHandlerProxy(request_id);
257 if (!proxy)
Alex Vakulenko31a63792015-02-03 12:44:57 -0800258 return;
Alex Vakulenko31a63792015-02-03 12:44:57 -0800259
260 std::vector<std::tuple<std::string, std::string>> header_list;
261 header_list.reserve(headers.size());
262 for (const auto& pair : headers)
263 header_list.emplace_back(pair.first, pair.second);
264
Alex Vakulenko330a10f2015-09-23 16:51:02 -0700265 int64_t data_size = -1;
266 if (data_stream->CanGetSize())
267 data_size = data_stream->GetRemainingSize();
268 proxy->CompleteRequestAsync(
269 request_id, status_code, header_list, data_size,
270 base::Bind(&WriteResponseData, base::Passed(&data_stream)),
271 base::Bind(&IgnoreDBusError));
Alex Vakulenko31a63792015-02-03 12:44:57 -0800272}
273
274void ProtocolHandler::GetFileData(
275 const std::string& request_id,
276 int file_id,
Alex Vakulenko950687b2015-09-18 13:41:58 -0700277 const base::Callback<void(chromeos::StreamPtr)>& success_callback,
Alex Vakulenko31a63792015-02-03 12:44:57 -0800278 const base::Callback<void(chromeos::Error*)>& error_callback) {
Alex Vakulenko3d2d5632015-03-02 15:58:32 -0800279 ProtocolHandlerProxy* proxy = GetRequestProtocolHandlerProxy(request_id);
280 CHECK(proxy);
281
Alex Vakulenko950687b2015-09-18 13:41:58 -0700282 // Store the success/error callback in a shared object so it can be referenced
283 // by the two wrapper callbacks. Since the original callbacks MAY contain
284 // move-only types, copying the base::Callback object is generally unsafe and
285 // may destroy the source object of the copy (despite the fact that it is
286 // constant). So, here we move both callbacks to |Callbacks| structure and
287 // use a shared pointer to it in both success and error callback wrappers.
288 struct Callbacks {
289 base::Callback<void(chromeos::StreamPtr)> on_success;
290 base::Callback<void(chromeos::Error*)> on_error;
291 };
292 auto callbacks = std::make_shared<Callbacks>();
293 callbacks->on_success = success_callback;
294 callbacks->on_error = error_callback;
295
296 auto on_success = [callbacks](const dbus::FileDescriptor& fd) {
297 chromeos::ErrorPtr error;
298 // Unfortunately there is no way to take ownership of the file descriptor
299 // since |fd| is a const reference, so duplicate the descriptor.
300 int dupfd = dup(fd.value());
301 auto stream = chromeos::FileStream::FromFileDescriptor(dupfd, true, &error);
302 if (!stream)
303 return callbacks->on_error.Run(error.get());
304 callbacks->on_success.Run(std::move(stream));
305 };
306 auto on_error = [callbacks](chromeos::Error* error) {
307 callbacks->on_error.Run(error);
308 };
309
310 proxy->GetRequestFileDataAsync(request_id, file_id, base::Bind(on_success),
311 base::Bind(on_error));
Alex Vakulenko31a63792015-02-03 12:44:57 -0800312}
313
Alex Vakulenko3d2d5632015-03-02 15:58:32 -0800314ProtocolHandler::ProtocolHandlerProxy*
315ProtocolHandler::GetRequestProtocolHandlerProxy(
316 const std::string& request_id) const {
317 auto iter = request_id_map_.find(request_id);
318 if (iter == request_id_map_.end()) {
319 LOG(ERROR) << "Can't find pending request with ID " << request_id;
320 return nullptr;
321 }
322 std::string handler_id = iter->second;
323 auto find_proxy_by_id = [handler_id](decltype(*proxies_.begin()) pair) {
324 return pair.second->id() == handler_id;
325 };
326 auto proxy_iter = std::find_if(proxies_.begin(), proxies_.end(),
327 find_proxy_by_id);
328 if (proxy_iter == proxies_.end()) {
329 LOG(WARNING) << "Completing a request after the handler proxy is removed";
330 return nullptr;
331 }
332 return proxy_iter->second;
333}
334
Alex Vakulenko31a63792015-02-03 12:44:57 -0800335
336} // namespace libwebserv