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 "webservd/request.h"
16 
17 #include <microhttpd.h>
18 
19 #include <base/bind.h>
20 #include <base/files/file.h>
21 #include <base/guid.h>
22 #include <brillo/http/http_request.h>
23 #include <brillo/http/http_utils.h>
24 #include <brillo/mime_utils.h>
25 #include <brillo/streams/file_stream.h>
26 #include <brillo/strings/string_utils.h>
27 #include "webservd/log_manager.h"
28 #include "webservd/protocol_handler.h"
29 #include "webservd/request_handler_interface.h"
30 #include "webservd/server_interface.h"
31 #include "webservd/temp_file_manager.h"
32 
33 namespace webservd {
34 
35 // Helper class to provide static callback methods to microhttpd library,
36 // with the ability to access private methods of Request class.
37 class RequestHelper {
38  public:
PostDataIterator(void * cls,MHD_ValueKind,const char * key,const char * filename,const char * content_type,const char * transfer_encoding,const char * data,uint64_t off,size_t size)39   static int PostDataIterator(void* cls,
40                               MHD_ValueKind /* kind */,
41                               const char* key,
42                               const char* filename,
43                               const char* content_type,
44                               const char* transfer_encoding,
45                               const char* data,
46                               uint64_t off,
47                               size_t size) {
48     auto self = reinterpret_cast<Request*>(cls);
49     return self->ProcessPostData(key, filename, content_type, transfer_encoding,
50                                  data, off, size) ? MHD_YES : MHD_NO;
51   }
52 
ValueCallback(void * cls,MHD_ValueKind kind,const char * key,const char * value)53   static int ValueCallback(void* cls,
54                            MHD_ValueKind kind,
55                            const char* key,
56                            const char* value) {
57     auto self = reinterpret_cast<Request*>(cls);
58     std::string data;
59     if (value)
60       data = value;
61     if (kind == MHD_HEADER_KIND) {
62       self->headers_.emplace_back(brillo::http::GetCanonicalHeaderName(key),
63                                   data);
64     } else if (kind == MHD_COOKIE_KIND) {
65       // TODO(avakulenko): add support for cookies...
66     } else if (kind == MHD_POSTDATA_KIND) {
67       self->post_data_.emplace_back(key, data);
68     } else if (kind == MHD_GET_ARGUMENT_KIND) {
69       self->get_data_.emplace_back(key, data);
70     }
71     return MHD_YES;
72   }
73 };
74 
FileInfo(const std::string & in_field_name,const std::string & in_file_name,const std::string & in_content_type,const std::string & in_transfer_encoding)75 FileInfo::FileInfo(const std::string& in_field_name,
76                    const std::string& in_file_name,
77                    const std::string& in_content_type,
78                    const std::string& in_transfer_encoding)
79     : field_name(in_field_name),
80       file_name(in_file_name),
81       content_type(in_content_type),
82       transfer_encoding(in_transfer_encoding) {
83 }
84 
Request(const std::string & request_handler_id,const std::string & url,const std::string & method,const std::string & version,MHD_Connection * connection,ProtocolHandler * protocol_handler)85 Request::Request(
86     const std::string& request_handler_id,
87     const std::string& url,
88     const std::string& method,
89     const std::string& version,
90     MHD_Connection* connection,
91     ProtocolHandler* protocol_handler)
92     : id_{base::GenerateGUID()},
93       request_handler_id_{request_handler_id},
94       url_{url},
95       method_{method},
96       version_{version},
97       connection_{connection},
98       protocol_handler_{protocol_handler} {
99   // Here we create the data pipe used to transfer the request body from the
100   // web server to the remote request handler.
101   int pipe_fds[2] = {-1, -1};
102   CHECK_EQ(0, pipe(pipe_fds));
103   request_data_pipe_out_ = base::File{pipe_fds[0]};
104   CHECK(request_data_pipe_out_.IsValid());
105   request_data_stream_ = brillo::FileStream::FromFileDescriptor(
106       pipe_fds[1], true, nullptr);
107   CHECK(request_data_stream_);
108 
109   // POST request processor.
110   post_processor_ = MHD_create_post_processor(
111       connection, 1024, &RequestHelper::PostDataIterator, this);
112 }
113 
~Request()114 Request::~Request() {
115   if (post_processor_)
116     MHD_destroy_post_processor(post_processor_);
117   GetTempFileManager()->DeleteRequestTempFiles(id_);
118   protocol_handler_->RemoveRequest(this);
119 }
120 
GetFileData(int file_id)121 base::File Request::GetFileData(int file_id) {
122   base::File file;
123   if (file_id >= 0 && static_cast<size_t>(file_id) < file_info_.size()) {
124     file.Initialize(file_info_[file_id]->temp_file_name,
125                     base::File::FLAG_OPEN | base::File::FLAG_READ);
126   }
127   return file;
128 }
129 
Complete(int32_t status_code,const std::vector<std::tuple<std::string,std::string>> & headers,int64_t in_data_size)130 base::File Request::Complete(
131     int32_t status_code,
132     const std::vector<std::tuple<std::string, std::string>>& headers,
133     int64_t in_data_size) {
134   base::File file;
135   if (response_data_started_)
136     return file;
137 
138   response_status_code_ = status_code;
139   response_headers_.reserve(headers.size());
140   for (const auto& tuple : headers) {
141     response_headers_.emplace_back(std::get<0>(tuple), std::get<1>(tuple));
142   }
143 
144   // Create the pipe for response data.
145   int pipe_fds[2] = {-1, -1};
146   CHECK_EQ(0, pipe(pipe_fds));
147   file = base::File{pipe_fds[1]};
148   CHECK(file.IsValid());
149   response_data_stream_ = brillo::FileStream::FromFileDescriptor(
150       pipe_fds[0], true, nullptr);
151   CHECK(response_data_stream_);
152 
153   response_data_size_ = in_data_size;
154   response_data_started_ = true;
155   const MHD_ConnectionInfo* info =
156       MHD_get_connection_info(connection_, MHD_CONNECTION_INFO_CLIENT_ADDRESS);
157 
158   const sockaddr* client_addr = (info ? info->client_addr : nullptr);
159   LogManager::OnRequestCompleted(base::Time::Now(), client_addr, method_, url_,
160                                  version_, status_code, in_data_size);
161   protocol_handler_->ScheduleWork();
162   return file;
163 }
164 
Complete(int32_t status_code,const std::vector<std::tuple<std::string,std::string>> &,const std::string & mime_type,const std::string & data)165 bool Request::Complete(
166     int32_t status_code,
167     const std::vector<std::tuple<std::string, std::string>>& /* headers */,
168     const std::string& mime_type,
169     const std::string& data) {
170   std::vector<std::tuple<std::string, std::string>> headers_copy;
171   headers_copy.emplace_back(brillo::http::response_header::kContentType,
172                             mime_type);
173   base::File file = Complete(status_code, headers_copy, data.size());
174   bool success = false;
175   if (file.IsValid()) {
176     const int size = data.size();
177     success = (file.WriteAtCurrentPos(data.c_str(), size) == size);
178   }
179   return success;
180 }
181 
GetProtocolHandlerID() const182 const std::string& Request::GetProtocolHandlerID() const {
183   return protocol_handler_->GetID();
184 }
185 
GetBodyDataFileDescriptor() const186 int Request::GetBodyDataFileDescriptor() const {
187   int fd = dup(request_data_pipe_out_.GetPlatformFile());
188   CHECK_GE(fd, 0);
189   return fd;
190 }
191 
BeginRequestData()192 bool Request::BeginRequestData() {
193   MHD_get_connection_values(connection_, MHD_HEADER_KIND,
194                             &RequestHelper::ValueCallback, this);
195   MHD_get_connection_values(connection_, MHD_COOKIE_KIND,
196                             &RequestHelper::ValueCallback, this);
197   MHD_get_connection_values(connection_, MHD_POSTDATA_KIND,
198                             &RequestHelper::ValueCallback, this);
199   MHD_get_connection_values(connection_, MHD_GET_ARGUMENT_KIND,
200                             &RequestHelper::ValueCallback, this);
201   // If we have POST processor, then we are parsing the request ourselves and
202   // we need to dispatch it to the handler only after all the data is parsed.
203   // Otherwise forward the request immediately and let the handler read the
204   // request data as needed.
205   if (!post_processor_)
206     ForwardRequestToHandler();
207   return true;
208 }
209 
AddRequestData(const void * data,size_t * size)210 bool Request::AddRequestData(const void* data, size_t* size) {
211   if (!post_processor_)
212     return AddRawRequestData(data, size);
213   int result =
214       MHD_post_process(post_processor_, static_cast<const char*>(data), *size);
215   *size = 0;
216   return result == MHD_YES;
217 }
218 
EndRequestData()219 void Request::EndRequestData() {
220   if (!request_data_finished_) {
221     if (request_data_stream_)
222       request_data_stream_->CloseBlocking(nullptr);
223     if (!request_forwarded_)
224       ForwardRequestToHandler();
225     request_data_finished_ = true;
226   }
227 
228   if (response_data_started_ && !response_data_finished_) {
229     MHD_Response* resp = MHD_create_response_from_callback(
230         response_data_size_, 4096, &Request::ResponseDataCallback, this,
231         nullptr);
232     CHECK(resp);
233     for (const auto& pair : response_headers_) {
234       MHD_add_response_header(resp, pair.first.c_str(), pair.second.c_str());
235     }
236     CHECK_EQ(MHD_YES,
237              MHD_queue_response(connection_, response_status_code_, resp))
238         << "Failed to queue response";
239     MHD_destroy_response(resp);  // |resp| is ref-counted.
240     response_data_finished_ = true;
241   }
242 }
243 
ForwardRequestToHandler()244 void Request::ForwardRequestToHandler() {
245   request_forwarded_ = true;
246   if (!request_handler_id_.empty()) {
247     // Close all temporary file streams, if any.
248     for (auto& file : file_info_)
249       file->data_stream->CloseBlocking(nullptr);
250 
251     protocol_handler_->AddRequest(this);
252     auto p = protocol_handler_->request_handlers_.find(request_handler_id_);
253     CHECK(p != protocol_handler_->request_handlers_.end());
254     // Send the request over D-Bus and await the response.
255     p->second.handler->HandleRequest(this);
256   } else {
257     // There was no handler found when request was made, respond with
258     // 404 Page Not Found.
259     Complete(brillo::http::status_code::NotFound, {},
260              brillo::mime::text::kPlain, "Not Found");
261   }
262 }
263 
ProcessPostData(const char * key,const char * filename,const char * content_type,const char * transfer_encoding,const char * data,uint64_t off,size_t size)264 bool Request::ProcessPostData(const char* key,
265                               const char* filename,
266                               const char* content_type,
267                               const char* transfer_encoding,
268                               const char* data,
269                               uint64_t off,
270                               size_t size) {
271   if (off > 0)
272     return AppendPostFieldData(key, data, size);
273 
274   return AddPostFieldData(key, filename, content_type, transfer_encoding, data,
275                           size);
276 }
277 
AddRawRequestData(const void * data,size_t * size)278 bool Request::AddRawRequestData(const void* data, size_t* size) {
279   CHECK(*size);
280   CHECK(request_data_stream_) << "Data pipe hasn't been created.";
281 
282   size_t written = 0;
283   if (!request_data_stream_->WriteNonBlocking(data, *size, &written, nullptr))
284     return false;
285 
286   CHECK_LE(written, *size);
287 
288   // If we didn't write all the data requested, we need to let libmicrohttpd do
289   // another write cycle. Schedule a DoWork() action here.
290   if (written != *size)
291     protocol_handler_->ScheduleWork();
292 
293   *size -= written;
294 
295   // If written at least some data, we are good. We will be called again if more
296   // data is available.
297   if (written > 0 || waiting_for_data_)
298     return true;
299 
300   // Nothing has been written. The output pipe is full. Need to stop the data
301   // transfer on the connection and wait till some data is being read from the
302   // pipe by the request handler.
303   MHD_suspend_connection(connection_);
304 
305   // Now, just monitor the pipe and figure out when we can resume sending data
306   // over it.
307   waiting_for_data_ = request_data_stream_->WaitForData(
308       brillo::Stream::AccessMode::WRITE,
309       base::Bind(&Request::OnPipeAvailable, weak_ptr_factory_.GetWeakPtr()),
310       nullptr);
311 
312   if (!waiting_for_data_)
313     MHD_resume_connection(connection_);
314 
315   return waiting_for_data_;
316 }
317 
ResponseDataCallback(void * cls,uint64_t,char * buf,size_t max)318 ssize_t Request::ResponseDataCallback(void *cls, uint64_t /* pos */, char *buf,
319                                       size_t max) {
320   Request* self = static_cast<Request*>(cls);
321   size_t read = 0;
322   bool eos = false;
323   if (!self->response_data_stream_->ReadNonBlocking(buf, max, &read, &eos,
324                                                     nullptr)) {
325     return MHD_CONTENT_READER_END_WITH_ERROR;
326   }
327 
328   if (read > 0 || self->waiting_for_data_)
329     return read;
330 
331   if (eos)
332     return MHD_CONTENT_READER_END_OF_STREAM;
333 
334   // Nothing can be read. The input pipe is empty. Need to stop the data
335   // transfer on the connection and wait till some data is available from the
336   // pipe.
337   MHD_suspend_connection(self->connection_);
338 
339   self->waiting_for_data_ = self->response_data_stream_->WaitForData(
340       brillo::Stream::AccessMode::READ,
341       base::Bind(&Request::OnPipeAvailable,
342                  self->weak_ptr_factory_.GetWeakPtr()),
343       nullptr);
344 
345   if (!self->waiting_for_data_) {
346     MHD_resume_connection(self->connection_);
347     return MHD_CONTENT_READER_END_WITH_ERROR;
348   }
349   return 0;
350 }
351 
OnPipeAvailable(brillo::Stream::AccessMode)352 void Request::OnPipeAvailable(brillo::Stream::AccessMode /* mode */) {
353   MHD_resume_connection(connection_);
354   waiting_for_data_ = false;
355   protocol_handler_->ScheduleWork();
356 }
357 
AddPostFieldData(const char * key,const char * filename,const char * content_type,const char * transfer_encoding,const char * data,size_t size)358 bool Request::AddPostFieldData(const char* key,
359                                const char* filename,
360                                const char* content_type,
361                                const char* transfer_encoding,
362                                const char* data,
363                                size_t size) {
364   if (filename) {
365     std::unique_ptr<FileInfo> file_info{
366         new FileInfo{key, filename, content_type ? content_type : "",
367                      transfer_encoding ? transfer_encoding : ""}};
368     file_info->temp_file_name = GetTempFileManager()->CreateTempFileName(id_);
369     file_info->data_stream = brillo::FileStream::Open(
370         file_info->temp_file_name, brillo::Stream::AccessMode::READ_WRITE,
371         brillo::FileStream::Disposition::CREATE_ALWAYS, nullptr);
372     if (!file_info->data_stream ||
373         !file_info->data_stream->WriteAllBlocking(data, size, nullptr)) {
374       return false;
375     }
376     file_info_.push_back(std::move(file_info));
377     last_posted_data_was_file_ = true;
378     return true;
379   }
380   std::string value{data, size};
381   post_data_.emplace_back(key, value);
382   last_posted_data_was_file_ = false;
383   return true;
384 }
385 
AppendPostFieldData(const char * key,const char * data,size_t size)386 bool Request::AppendPostFieldData(const char* key,
387                                   const char* data,
388                                   size_t size) {
389   if (last_posted_data_was_file_) {
390     CHECK(!file_info_.empty());
391     CHECK(file_info_.back()->field_name == key);
392     FileInfo* file_info = file_info_.back().get();
393     return file_info->data_stream->WriteAllBlocking(data, size, nullptr);
394   }
395 
396   CHECK(!post_data_.empty());
397   CHECK(post_data_.back().first == key);
398   post_data_.back().second.append(data, size);
399   return true;
400 }
401 
GetTempFileManager()402 TempFileManager* Request::GetTempFileManager() {
403   return protocol_handler_->GetServer()->GetTempFileManager();
404 }
405 
406 }  // namespace webservd
407