• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // 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 
32 namespace libwebserv {
33 
34 namespace {
35 
36 // Dummy callback for async D-Bus errors.
IgnoreDBusError(brillo::Error *)37 void IgnoreDBusError(brillo::Error* /* error */) {}
38 
39 // Copies the data from |src_stream| to the destination stream represented
40 // by a file descriptor |fd|.
WriteResponseData(brillo::StreamPtr src_stream,const dbus::FileDescriptor & fd)41 void 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 
DBusProtocolHandler(const std::string & name,DBusServer * server)59 DBusProtocolHandler::DBusProtocolHandler(const std::string& name,
60                                          DBusServer* server)
61     : name_{name}, server_{server} {
62 }
63 
~DBusProtocolHandler()64 DBusProtocolHandler::~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 }
IsConnected() const75 bool DBusProtocolHandler::IsConnected() const {
76   return !proxies_.empty();
77 }
78 
GetName() const79 std::string DBusProtocolHandler::GetName() const {
80   return name_;
81 }
82 
GetPorts() const83 std::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 
GetProtocols() const90 std::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 
GetCertificateFingerprint() const97 brillo::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 
AddHandler(const std::string & url,const std::string & method,std::unique_ptr<RequestHandlerInterface> handler)107 int 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 
AddHandlerCallback(const std::string & url,const std::string & method,const base::Callback<RequestHandlerInterface::HandlerSignature> & handler_callback)134 int 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 
RemoveHandler(int handler_id)144 bool 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 
Connect(ProtocolHandlerProxyInterface * proxy)160 void 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 
Disconnect(const dbus::ObjectPath & object_path)177 void 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 
AddHandlerSuccess(int handler_id,ProtocolHandlerProxyInterface * proxy,const std::string & remote_handler_id)185 void 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 
AddHandlerError(int,brillo::Error *)196 void DBusProtocolHandler::AddHandlerError(int /* handler_id */,
197                                           brillo::Error* /* error */) {
198   // Nothing to do at the moment.
199 }
200 
ProcessRequest(const std::string & protocol_handler_id,const std::string & remote_handler_id,const std::string & request_id,std::unique_ptr<Request> request,brillo::ErrorPtr * error)201 bool 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 
CompleteRequest(const std::string & request_id,int status_code,const std::multimap<std::string,std::string> & headers,brillo::StreamPtr data_stream)231 void 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 
GetFileData(const std::string & request_id,int file_id,const base::Callback<void (brillo::StreamPtr)> & success_callback,const base::Callback<void (brillo::Error *)> & error_callback)255 void 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 
296 DBusProtocolHandler::ProtocolHandlerProxyInterface*
GetRequestProtocolHandlerProxy(const std::string & request_id) const297 DBusProtocolHandler::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
319