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 33namespace webservd { 34 35// Helper class to provide static callback methods to microhttpd library, 36// with the ability to access private methods of Request class. 37class RequestHelper { 38 public: 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 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 75FileInfo::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 85Request::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 114Request::~Request() { 115 if (post_processor_) 116 MHD_destroy_post_processor(post_processor_); 117 GetTempFileManager()->DeleteRequestTempFiles(id_); 118 protocol_handler_->RemoveRequest(this); 119} 120 121base::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 130base::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 165bool 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 182const std::string& Request::GetProtocolHandlerID() const { 183 return protocol_handler_->GetID(); 184} 185 186int Request::GetBodyDataFileDescriptor() const { 187 int fd = dup(request_data_pipe_out_.GetPlatformFile()); 188 CHECK_GE(fd, 0); 189 return fd; 190} 191 192bool 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 210bool 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 219void 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 244void 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 264bool 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 278bool 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 318ssize_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 352void Request::OnPipeAvailable(brillo::Stream::AccessMode /* mode */) { 353 MHD_resume_connection(connection_); 354 waiting_for_data_ = false; 355 protocol_handler_->ScheduleWork(); 356} 357 358bool 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 386bool 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 402TempFileManager* Request::GetTempFileManager() { 403 return protocol_handler_->GetServer()->GetTempFileManager(); 404} 405 406} // namespace webservd 407