| // Copyright 2019 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "cups_proxy/mhd_util.h" |
| |
| #include <microhttpd.h> |
| |
| #include "cups_proxy/mhd_http_request.h" |
| |
| namespace cups_proxy { |
| |
| namespace { |
| |
| // Adds a HTTP header to the MHDHttpRequest. |
| // |
| // This is called for each header of the request. |
| // |cls| is the MHDHttpRequest argument passed from MHD_get_connection_values. |
| int AddHeader(void* cls, |
| enum MHD_ValueKind kind, |
| const char* key, |
| const char* value) { |
| DCHECK(kind == MHD_HEADER_KIND); |
| auto* request = static_cast<MHDHttpRequest*>(cls); |
| request->AddHeader(key, value); |
| return MHD_YES; |
| } |
| |
| // Handles incoming HTTP request. |
| // |
| // This function would be called multiple times by MHD with the same |con_cls| |
| // for a single HTTP request. |
| // |
| // For a POST request, the first call would have |con_cls| storing NULL, |url|, |
| // |method|, |version| from the HTTP header, and empty |upload_data|. |
| // Then POST data would be incrementally available by calling this function |
| // multiple times (with data in |upload_data| and a non-zero |
| // |*upload_data_size|). |
| // A final call with |*upload_data_size| zero indicates the end of the request. |
| int AccessHandler(void* cls, |
| struct MHD_Connection* connection, |
| const char* url, |
| const char* method, |
| const char* version, |
| const char* upload_data, |
| size_t* upload_data_size, |
| void** con_cls) { |
| auto* request = static_cast<MHDHttpRequest*>(*con_cls); |
| if (request == nullptr) { |
| request = new MHDHttpRequest(); |
| request->SetStatusLine(method, url, version); |
| MHD_get_connection_values(connection, MHD_HEADER_KIND, &AddHeader, request); |
| *con_cls = request; |
| return MHD_YES; |
| } |
| |
| if (*upload_data_size != 0) { |
| request->PushToBody(base::StringPiece(upload_data, *upload_data_size)); |
| *upload_data_size = 0; |
| return MHD_YES; |
| } |
| |
| request->Finalize(); |
| auto* mojo_handler = static_cast<MojoHandler*>(cls); |
| IppResponse response = mojo_handler->ProxyRequestSync(*request); |
| |
| ScopedMHDResponse mhd_resp(MHD_create_response_from_buffer( |
| response.body.size(), response.body.data(), MHD_RESPMEM_MUST_COPY)); |
| if (!mhd_resp) { |
| return MHD_NO; |
| } |
| |
| for (auto& header : response.headers) { |
| int ret = MHD_add_response_header(mhd_resp.get(), header->key.c_str(), |
| header->value.c_str()); |
| if (ret != MHD_YES) { |
| return ret; |
| } |
| } |
| |
| int ret = |
| MHD_queue_response(connection, response.http_status_code, mhd_resp.get()); |
| return ret; |
| } |
| |
| // Cleanup the allocated MHDHttpRequest object. |
| // |
| // This function is called when the HTTP request is completed. |
| void CleanupRequest(void* cls, |
| struct MHD_Connection* connection, |
| void** con_cls, |
| enum MHD_RequestTerminationCode toe) { |
| auto* request = static_cast<MHDHttpRequest*>(*con_cls); |
| delete request; |
| } |
| |
| } // namespace |
| |
| ScopedMHDDaemon StartMHDDaemon(base::ScopedFD fd, MojoHandler* mojo_handler) { |
| const struct MHD_OptionItem kOps[] = { |
| {MHD_OPTION_LISTEN_SOCKET, fd.release(), NULL}, |
| {MHD_OPTION_NOTIFY_COMPLETED, reinterpret_cast<intptr_t>(&CleanupRequest), |
| NULL}, |
| {MHD_OPTION_END, 0, NULL} // MUST stay last option |
| }; |
| |
| return ScopedMHDDaemon( |
| MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, 0, NULL, NULL, &AccessHandler, |
| mojo_handler, MHD_OPTION_ARRAY, kOps, MHD_OPTION_END)); |
| } |
| |
| } // namespace cups_proxy |