blob: 4b6c0fa60c2a9abb269d0301052b1b8c4285cab6 [file] [log] [blame]
// 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