blob: 544281fbbaf87e1d5fea2d71599cc90d88e96246 [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
34// A dummy callback for async D-Bus errors.
35void IgnoreError(chromeos::Error* error) {}
36
37} // anonymous namespace
38
39const char ProtocolHandler::kHttp[] = "http";
40const char ProtocolHandler::kHttps[] = "https";
41
Alex Vakulenko3d2d5632015-03-02 15:58:32 -080042ProtocolHandler::ProtocolHandler(const std::string& name, Server* server)
43 : name_{name}, server_{server} {
44}
Alex Vakulenko31a63792015-02-03 12:44:57 -080045
46ProtocolHandler::~ProtocolHandler() {
47 // Remove any existing handlers, so the web server knows that we don't
48 // need them anymore.
49
50 // We need to get a copy of the map keys since removing the handlers will
51 // modify the map in the middle of the loop and that's not a good thing.
52 auto handler_ids = chromeos::GetMapKeys(request_handlers_);
53 for (int handler_id : handler_ids) {
54 RemoveHandler(handler_id);
55 }
56}
57
Alex Vakulenko3d2d5632015-03-02 15:58:32 -080058std::string ProtocolHandler::GetName() const {
59 return name_;
Alex Vakulenko31a63792015-02-03 12:44:57 -080060}
61
Alex Vakulenko3d2d5632015-03-02 15:58:32 -080062std::set<uint16_t> ProtocolHandler::GetPorts() const {
63 std::set<uint16_t> ports;
64 for (const auto& pair : proxies_)
65 ports.insert(pair.second->port());
66 return ports;
Alex Vakulenko31a63792015-02-03 12:44:57 -080067}
68
Alex Vakulenko3d2d5632015-03-02 15:58:32 -080069std::set<std::string> ProtocolHandler::GetProtocols() const {
70 std::set<std::string> protocols;
71 for (const auto& pair : proxies_)
72 protocols.insert(pair.second->protocol());
73 return protocols;
Alex Vakulenko31a63792015-02-03 12:44:57 -080074}
75
76chromeos::Blob ProtocolHandler::GetCertificateFingerprint() const {
Alex Vakulenko3d2d5632015-03-02 15:58:32 -080077 chromeos::Blob fingerprint;
78 for (const auto& pair : proxies_) {
79 fingerprint = pair.second->certificate_fingerprint();
80 if (!fingerprint.empty())
81 break;
82 }
83 return fingerprint;
Alex Vakulenko31a63792015-02-03 12:44:57 -080084}
85
86int ProtocolHandler::AddHandler(
87 const std::string& url,
88 const std::string& method,
89 std::unique_ptr<RequestHandlerInterface> handler) {
Alex Vakulenko3d8c9122015-03-13 13:54:00 -070090 request_handlers_.emplace(
91 ++last_handler_id_,
92 HandlerMapEntry{url, method,
93 std::map<ProtocolHandlerProxy*, std::string>{},
94 std::move(handler)});
95 // For each instance of remote protocol handler object sharing the same name,
96 // add the request handler.
Alex Vakulenko3d2d5632015-03-02 15:58:32 -080097 for (const auto& pair : proxies_) {
98 pair.second->AddRequestHandlerAsync(
Alex Vakulenko31a63792015-02-03 12:44:57 -080099 url,
100 method,
101 server_->service_name_,
102 base::Bind(&ProtocolHandler::AddHandlerSuccess,
103 weak_ptr_factory_.GetWeakPtr(),
Alex Vakulenko3d8c9122015-03-13 13:54:00 -0700104 last_handler_id_,
105 pair.second),
Alex Vakulenko31a63792015-02-03 12:44:57 -0800106 base::Bind(&ProtocolHandler::AddHandlerError,
107 weak_ptr_factory_.GetWeakPtr(),
108 last_handler_id_));
109 }
110 return last_handler_id_;
111}
112
113int ProtocolHandler::AddHandlerCallback(
114 const std::string& url,
115 const std::string& method,
116 const base::Callback<RequestHandlerInterface::HandlerSignature>&
117 handler_callback) {
118 std::unique_ptr<RequestHandlerInterface> handler{
119 new RequestHandlerCallback{handler_callback}};
120 return AddHandler(url, method, std::move(handler));
121}
122
123bool ProtocolHandler::RemoveHandler(int handler_id) {
124 auto p = request_handlers_.find(handler_id);
125 if (p == request_handlers_.end())
126 return false;
127
Alex Vakulenko3d8c9122015-03-13 13:54:00 -0700128 for (const auto& pair : p->second.remote_handler_ids) {
129 pair.first->RemoveRequestHandlerAsync(
130 pair.second,
Alex Vakulenko31a63792015-02-03 12:44:57 -0800131 base::Bind(&base::DoNothing),
132 base::Bind(&IgnoreError));
133 }
134
135 request_handlers_.erase(p);
136 return true;
137}
138
Alex Vakulenko3d2d5632015-03-02 15:58:32 -0800139void ProtocolHandler::Connect(ProtocolHandlerProxy* proxy) {
140 proxies_.emplace(proxy->GetObjectPath(), proxy);
Alex Vakulenko31a63792015-02-03 12:44:57 -0800141 for (const auto& pair : request_handlers_) {
Alex Vakulenko3d2d5632015-03-02 15:58:32 -0800142 proxy->AddRequestHandlerAsync(
Alex Vakulenko31a63792015-02-03 12:44:57 -0800143 pair.second.url,
144 pair.second.method,
145 server_->service_name_,
146 base::Bind(&ProtocolHandler::AddHandlerSuccess,
147 weak_ptr_factory_.GetWeakPtr(),
Alex Vakulenko3d8c9122015-03-13 13:54:00 -0700148 pair.first,
149 proxy),
Alex Vakulenko31a63792015-02-03 12:44:57 -0800150 base::Bind(&ProtocolHandler::AddHandlerError,
151 weak_ptr_factory_.GetWeakPtr(),
152 pair.first));
153 }
154}
155
Alex Vakulenko3d2d5632015-03-02 15:58:32 -0800156void ProtocolHandler::Disconnect(const dbus::ObjectPath& object_path) {
157 proxies_.erase(object_path);
158 if (proxies_.empty())
159 remote_handler_id_map_.clear();
Alex Vakulenko3d8c9122015-03-13 13:54:00 -0700160 for (auto& pair : request_handlers_)
161 pair.second.remote_handler_ids.clear();
Alex Vakulenko31a63792015-02-03 12:44:57 -0800162}
163
164void ProtocolHandler::AddHandlerSuccess(int handler_id,
Alex Vakulenko3d8c9122015-03-13 13:54:00 -0700165 ProtocolHandlerProxy* proxy,
Alex Vakulenko31a63792015-02-03 12:44:57 -0800166 const std::string& remote_handler_id) {
167 auto p = request_handlers_.find(handler_id);
168 CHECK(p != request_handlers_.end());
Alex Vakulenko3d8c9122015-03-13 13:54:00 -0700169 p->second.remote_handler_ids.emplace(proxy, remote_handler_id);
Alex Vakulenko31a63792015-02-03 12:44:57 -0800170
171 remote_handler_id_map_.emplace(remote_handler_id, handler_id);
172}
173
174void ProtocolHandler::AddHandlerError(int handler_id, chromeos::Error* error) {
175 // Nothing to do at the moment.
176}
177
Alex Vakulenko3d2d5632015-03-02 15:58:32 -0800178bool ProtocolHandler::ProcessRequest(const std::string& protocol_handler_id,
179 const std::string& remote_handler_id,
Alex Vakulenko31a63792015-02-03 12:44:57 -0800180 const std::string& request_id,
181 std::unique_ptr<Request> request,
182 chromeos::ErrorPtr* error) {
Alex Vakulenko3d2d5632015-03-02 15:58:32 -0800183 request_id_map_.emplace(request_id, protocol_handler_id);
Alex Vakulenko31a63792015-02-03 12:44:57 -0800184 auto id_iter = remote_handler_id_map_.find(remote_handler_id);
185 if (id_iter == remote_handler_id_map_.end()) {
186 chromeos::Error::AddToPrintf(error, FROM_HERE,
187 chromeos::errors::dbus::kDomain,
188 DBUS_ERROR_FAILED,
189 "Unknown request handler '%s'",
190 remote_handler_id.c_str());
191 return false;
192 }
193 auto handler_iter = request_handlers_.find(id_iter->second);
194 if (handler_iter == request_handlers_.end()) {
195 chromeos::Error::AddToPrintf(error, FROM_HERE,
196 chromeos::errors::dbus::kDomain,
197 DBUS_ERROR_FAILED,
198 "Handler # %d is no longer available",
199 id_iter->second);
200 return false;
201 }
202 handler_iter->second.handler->HandleRequest(
Alex Vakulenko83e66cd2015-04-24 14:51:58 -0700203 std::move(request),
204 std::unique_ptr<Response>{new Response{this, request_id}});
Alex Vakulenko31a63792015-02-03 12:44:57 -0800205 return true;
206}
207
208void ProtocolHandler::CompleteRequest(
209 const std::string& request_id,
210 int status_code,
211 const std::multimap<std::string, std::string>& headers,
212 const std::vector<uint8_t>& data) {
Alex Vakulenko3d2d5632015-03-02 15:58:32 -0800213 ProtocolHandlerProxy* proxy = GetRequestProtocolHandlerProxy(request_id);
214 if (!proxy)
Alex Vakulenko31a63792015-02-03 12:44:57 -0800215 return;
Alex Vakulenko31a63792015-02-03 12:44:57 -0800216
217 std::vector<std::tuple<std::string, std::string>> header_list;
218 header_list.reserve(headers.size());
219 for (const auto& pair : headers)
220 header_list.emplace_back(pair.first, pair.second);
221
Alex Vakulenko3d2d5632015-03-02 15:58:32 -0800222 proxy->CompleteRequestAsync(request_id, status_code, header_list, data,
223 base::Bind(&base::DoNothing),
224 base::Bind([](chromeos::Error*) {}));
Alex Vakulenko31a63792015-02-03 12:44:57 -0800225}
226
227void ProtocolHandler::GetFileData(
228 const std::string& request_id,
229 int file_id,
Alex Vakulenko950687b2015-09-18 13:41:58 -0700230 const base::Callback<void(chromeos::StreamPtr)>& success_callback,
Alex Vakulenko31a63792015-02-03 12:44:57 -0800231 const base::Callback<void(chromeos::Error*)>& error_callback) {
Alex Vakulenko3d2d5632015-03-02 15:58:32 -0800232 ProtocolHandlerProxy* proxy = GetRequestProtocolHandlerProxy(request_id);
233 CHECK(proxy);
234
Alex Vakulenko950687b2015-09-18 13:41:58 -0700235 // Store the success/error callback in a shared object so it can be referenced
236 // by the two wrapper callbacks. Since the original callbacks MAY contain
237 // move-only types, copying the base::Callback object is generally unsafe and
238 // may destroy the source object of the copy (despite the fact that it is
239 // constant). So, here we move both callbacks to |Callbacks| structure and
240 // use a shared pointer to it in both success and error callback wrappers.
241 struct Callbacks {
242 base::Callback<void(chromeos::StreamPtr)> on_success;
243 base::Callback<void(chromeos::Error*)> on_error;
244 };
245 auto callbacks = std::make_shared<Callbacks>();
246 callbacks->on_success = success_callback;
247 callbacks->on_error = error_callback;
248
249 auto on_success = [callbacks](const dbus::FileDescriptor& fd) {
250 chromeos::ErrorPtr error;
251 // Unfortunately there is no way to take ownership of the file descriptor
252 // since |fd| is a const reference, so duplicate the descriptor.
253 int dupfd = dup(fd.value());
254 auto stream = chromeos::FileStream::FromFileDescriptor(dupfd, true, &error);
255 if (!stream)
256 return callbacks->on_error.Run(error.get());
257 callbacks->on_success.Run(std::move(stream));
258 };
259 auto on_error = [callbacks](chromeos::Error* error) {
260 callbacks->on_error.Run(error);
261 };
262
263 proxy->GetRequestFileDataAsync(request_id, file_id, base::Bind(on_success),
264 base::Bind(on_error));
Alex Vakulenko31a63792015-02-03 12:44:57 -0800265}
266
Alex Vakulenko3d2d5632015-03-02 15:58:32 -0800267ProtocolHandler::ProtocolHandlerProxy*
268ProtocolHandler::GetRequestProtocolHandlerProxy(
269 const std::string& request_id) const {
270 auto iter = request_id_map_.find(request_id);
271 if (iter == request_id_map_.end()) {
272 LOG(ERROR) << "Can't find pending request with ID " << request_id;
273 return nullptr;
274 }
275 std::string handler_id = iter->second;
276 auto find_proxy_by_id = [handler_id](decltype(*proxies_.begin()) pair) {
277 return pair.second->id() == handler_id;
278 };
279 auto proxy_iter = std::find_if(proxies_.begin(), proxies_.end(),
280 find_proxy_by_id);
281 if (proxy_iter == proxies_.end()) {
282 LOG(WARNING) << "Completing a request after the handler proxy is removed";
283 return nullptr;
284 }
285 return proxy_iter->second;
286}
287
Alex Vakulenko31a63792015-02-03 12:44:57 -0800288
289} // namespace libwebserv