blob: dc6e87bf4ace08984f1a43074af2c73b6eab0a83 [file] [log] [blame]
Christopher Wiley33207262015-12-15 18:33:50 -08001// 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.
14
15#include "libwebserv/dbus_protocol_handler.h"
16
17#include <tuple>
18
19#include <base/logging.h>
20#include <brillo/map_utils.h>
21#include <brillo/streams/file_stream.h>
22#include <brillo/streams/stream_utils.h>
23
24#include "dbus_bindings/org.chromium.WebServer.RequestHandler.h"
25#include "libwebserv/dbus_server.h"
26#include "libwebserv/protocol_handler.h"
27#include "libwebserv/request.h"
28#include "libwebserv/request_handler_callback.h"
29#include "libwebserv/response_impl.h"
30#include "webservd/dbus-proxies.h"
31
32namespace libwebserv {
33
34namespace {
35
36// Dummy callback for async D-Bus errors.
Christopher Wiley016df0a2016-01-04 08:41:41 -080037void IgnoreDBusError(brillo::Error* /* error */) {}
Christopher Wiley33207262015-12-15 18:33:50 -080038
39// Copies the data from |src_stream| to the destination stream represented
40// by a file descriptor |fd|.
41void WriteResponseData(brillo::StreamPtr src_stream,
42 const dbus::FileDescriptor& fd) {
43 int dupfd = dup(fd.value());
44 auto dest_stream =
45 brillo::FileStream::FromFileDescriptor(dupfd, true, nullptr);
46 CHECK(dest_stream);
47 // Dummy callbacks for success/error of data-copy operation. We ignore both
48 // notifications here.
49 auto on_success = [](brillo::StreamPtr, brillo::StreamPtr, uint64_t) {};
50 auto on_error = [](brillo::StreamPtr, brillo::StreamPtr,
51 const brillo::Error*) {};
52 brillo::stream_utils::CopyData(
53 std::move(src_stream), std::move(dest_stream), base::Bind(on_success),
54 base::Bind(on_error));
55}
56
57} // anonymous namespace
58
59DBusProtocolHandler::DBusProtocolHandler(const std::string& name,
60 DBusServer* server)
61 : name_{name}, server_{server} {
62}
63
64DBusProtocolHandler::~DBusProtocolHandler() {
65 // Remove any existing handlers, so the web server knows that we don't
66 // need them anymore.
67
68 // We need to get a copy of the map keys since removing the handlers will
69 // modify the map in the middle of the loop and that's not a good thing.
70 auto handler_ids = brillo::GetMapKeys(request_handlers_);
71 for (int handler_id : handler_ids) {
72 RemoveHandler(handler_id);
73 }
74}
75bool DBusProtocolHandler::IsConnected() const {
76 return !proxies_.empty();
77}
78
79std::string DBusProtocolHandler::GetName() const {
80 return name_;
81}
82
83std::set<uint16_t> DBusProtocolHandler::GetPorts() const {
84 std::set<uint16_t> ports;
85 for (const auto& pair : proxies_)
86 ports.insert(pair.second->port());
87 return ports;
88}
89
90std::set<std::string> DBusProtocolHandler::GetProtocols() const {
91 std::set<std::string> protocols;
92 for (const auto& pair : proxies_)
93 protocols.insert(pair.second->protocol());
94 return protocols;
95}
96
97brillo::Blob DBusProtocolHandler::GetCertificateFingerprint() const {
98 brillo::Blob fingerprint;
99 for (const auto& pair : proxies_) {
100 fingerprint = pair.second->certificate_fingerprint();
101 if (!fingerprint.empty())
102 break;
103 }
104 return fingerprint;
105}
106
107int DBusProtocolHandler::AddHandler(
108 const std::string& url,
109 const std::string& method,
110 std::unique_ptr<RequestHandlerInterface> handler) {
111 request_handlers_.emplace(
112 ++last_handler_id_,
113 HandlerMapEntry{url, method,
114 std::map<ProtocolHandlerProxyInterface*, std::string>{},
115 std::move(handler)});
116 // For each instance of remote protocol handler object sharing the same name,
117 // add the request handler.
118 for (const auto& pair : proxies_) {
119 pair.second->AddRequestHandlerAsync(
120 url,
121 method,
122 server_->service_name_,
123 base::Bind(&DBusProtocolHandler::AddHandlerSuccess,
124 weak_ptr_factory_.GetWeakPtr(),
125 last_handler_id_,
126 pair.second),
127 base::Bind(&DBusProtocolHandler::AddHandlerError,
128 weak_ptr_factory_.GetWeakPtr(),
129 last_handler_id_));
130 }
131 return last_handler_id_;
132}
133
134int DBusProtocolHandler::AddHandlerCallback(
135 const std::string& url,
136 const std::string& method,
137 const base::Callback<RequestHandlerInterface::HandlerSignature>&
138 handler_callback) {
139 std::unique_ptr<RequestHandlerInterface> handler{
140 new RequestHandlerCallback{handler_callback}};
141 return AddHandler(url, method, std::move(handler));
142}
143
144bool DBusProtocolHandler::RemoveHandler(int handler_id) {
145 auto p = request_handlers_.find(handler_id);
146 if (p == request_handlers_.end())
147 return false;
148
149 for (const auto& pair : p->second.remote_handler_ids) {
150 pair.first->RemoveRequestHandlerAsync(
151 pair.second,
152 base::Bind(&base::DoNothing),
153 base::Bind(&IgnoreDBusError));
154 }
155
156 request_handlers_.erase(p);
157 return true;
158}
159
160void DBusProtocolHandler::Connect(ProtocolHandlerProxyInterface* proxy) {
161 proxies_.emplace(proxy->GetObjectPath(), proxy);
162 for (const auto& pair : request_handlers_) {
163 proxy->AddRequestHandlerAsync(
164 pair.second.url,
165 pair.second.method,
166 server_->service_name_,
167 base::Bind(&DBusProtocolHandler::AddHandlerSuccess,
168 weak_ptr_factory_.GetWeakPtr(),
169 pair.first,
170 proxy),
171 base::Bind(&DBusProtocolHandler::AddHandlerError,
172 weak_ptr_factory_.GetWeakPtr(),
173 pair.first));
174 }
175}
176
177void DBusProtocolHandler::Disconnect(const dbus::ObjectPath& object_path) {
178 proxies_.erase(object_path);
179 if (proxies_.empty())
180 remote_handler_id_map_.clear();
181 for (auto& pair : request_handlers_)
182 pair.second.remote_handler_ids.clear();
183}
184
185void DBusProtocolHandler::AddHandlerSuccess(
186 int handler_id,
187 ProtocolHandlerProxyInterface* proxy,
188 const std::string& remote_handler_id) {
189 auto p = request_handlers_.find(handler_id);
190 CHECK(p != request_handlers_.end());
191 p->second.remote_handler_ids.emplace(proxy, remote_handler_id);
192
193 remote_handler_id_map_.emplace(remote_handler_id, handler_id);
194}
195
Christopher Wiley016df0a2016-01-04 08:41:41 -0800196void DBusProtocolHandler::AddHandlerError(int /* handler_id */,
197 brillo::Error* /* error */) {
Christopher Wiley33207262015-12-15 18:33:50 -0800198 // Nothing to do at the moment.
199}
200
201bool DBusProtocolHandler::ProcessRequest(const std::string& protocol_handler_id,
202 const std::string& remote_handler_id,
203 const std::string& request_id,
204 std::unique_ptr<Request> request,
205 brillo::ErrorPtr* error) {
206 request_id_map_.emplace(request_id, protocol_handler_id);
207 auto id_iter = remote_handler_id_map_.find(remote_handler_id);
208 if (id_iter == remote_handler_id_map_.end()) {
209 brillo::Error::AddToPrintf(error, FROM_HERE,
210 brillo::errors::dbus::kDomain,
211 DBUS_ERROR_FAILED,
212 "Unknown request handler '%s'",
213 remote_handler_id.c_str());
214 return false;
215 }
216 auto handler_iter = request_handlers_.find(id_iter->second);
217 if (handler_iter == request_handlers_.end()) {
218 brillo::Error::AddToPrintf(error, FROM_HERE,
219 brillo::errors::dbus::kDomain,
220 DBUS_ERROR_FAILED,
221 "Handler # %d is no longer available",
222 id_iter->second);
223 return false;
224 }
225 handler_iter->second.handler->HandleRequest(
226 std::move(request),
227 std::unique_ptr<Response>{new ResponseImpl{this, request_id}});
228 return true;
229}
230
231void DBusProtocolHandler::CompleteRequest(
232 const std::string& request_id,
233 int status_code,
234 const std::multimap<std::string, std::string>& headers,
235 brillo::StreamPtr data_stream) {
236 ProtocolHandlerProxyInterface* proxy =
237 GetRequestProtocolHandlerProxy(request_id);
238 if (!proxy)
239 return;
240
241 std::vector<std::tuple<std::string, std::string>> header_list;
242 header_list.reserve(headers.size());
243 for (const auto& pair : headers)
244 header_list.emplace_back(pair.first, pair.second);
245
246 int64_t data_size = -1;
247 if (data_stream->CanGetSize())
248 data_size = data_stream->GetRemainingSize();
249 proxy->CompleteRequestAsync(
250 request_id, status_code, header_list, data_size,
251 base::Bind(&WriteResponseData, base::Passed(&data_stream)),
252 base::Bind(&IgnoreDBusError));
253}
254
255void DBusProtocolHandler::GetFileData(
256 const std::string& request_id,
257 int file_id,
258 const base::Callback<void(brillo::StreamPtr)>& success_callback,
259 const base::Callback<void(brillo::Error*)>& error_callback) {
260 ProtocolHandlerProxyInterface* proxy =
261 GetRequestProtocolHandlerProxy(request_id);
262 CHECK(proxy);
263
264 // Store the success/error callback in a shared object so it can be referenced
265 // by the two wrapper callbacks. Since the original callbacks MAY contain
266 // move-only types, copying the base::Callback object is generally unsafe and
267 // may destroy the source object of the copy (despite the fact that it is
268 // constant). So, here we move both callbacks to |Callbacks| structure and
269 // use a shared pointer to it in both success and error callback wrappers.
270 struct Callbacks {
271 base::Callback<void(brillo::StreamPtr)> on_success;
272 base::Callback<void(brillo::Error*)> on_error;
273 };
274 auto callbacks = std::make_shared<Callbacks>();
275 callbacks->on_success = success_callback;
276 callbacks->on_error = error_callback;
277
278 auto on_success = [callbacks](const dbus::FileDescriptor& fd) {
279 brillo::ErrorPtr error;
280 // Unfortunately there is no way to take ownership of the file descriptor
281 // since |fd| is a const reference, so duplicate the descriptor.
282 int dupfd = dup(fd.value());
283 auto stream = brillo::FileStream::FromFileDescriptor(dupfd, true, &error);
284 if (!stream)
285 return callbacks->on_error.Run(error.get());
286 callbacks->on_success.Run(std::move(stream));
287 };
288 auto on_error = [callbacks](brillo::Error* error) {
289 callbacks->on_error.Run(error);
290 };
291
292 proxy->GetRequestFileDataAsync(request_id, file_id, base::Bind(on_success),
293 base::Bind(on_error));
294}
295
296DBusProtocolHandler::ProtocolHandlerProxyInterface*
297DBusProtocolHandler::GetRequestProtocolHandlerProxy(
298 const std::string& request_id) const {
299 auto iter = request_id_map_.find(request_id);
300 if (iter == request_id_map_.end()) {
301 LOG(ERROR) << "Can't find pending request with ID " << request_id;
302 return nullptr;
303 }
304 std::string handler_id = iter->second;
305 auto find_proxy_by_id = [handler_id](decltype(*proxies_.begin()) pair) {
306 return pair.second->id() == handler_id;
307 };
308 auto proxy_iter = std::find_if(proxies_.begin(), proxies_.end(),
309 find_proxy_by_id);
310 if (proxy_iter == proxies_.end()) {
311 LOG(WARNING) << "Completing a request after the handler proxy is removed";
312 return nullptr;
313 }
314 return proxy_iter->second;
315}
316
317
318} // namespace libwebserv