// // ApacheConnector.cpp // // Copyright (c) 2006-2011, Applied Informatics Software Engineering GmbH. // and Contributors. // // SPDX-License-Identifier: BSL-1.0 // #include "ApacheConnector.h" #include "ApacheApplication.h" #include "ApacheServerRequest.h" #include "ApacheServerResponse.h" #include "ApacheRequestHandlerFactory.h" #include "Poco/Net/HTTPRequestHandler.h" #include #include "httpd.h" #include "http_connection.h" #include "http_config.h" #include "http_core.h" #include "http_protocol.h" #include "http_log.h" #include "apr.h" #include "apr_lib.h" #include "apr_strings.h" #include "apr_buckets.h" #include "apr_file_info.h" #include "apr_hash.h" #define APR_WANT_STRFUNC #include "apr_want.h" #include "http_request.h" #include "util_filter.h" using Poco::Net::HTTPServerRequest; using Poco::Net::HTTPServerResponse; using Poco::Net::HTTPRequestHandler; using Poco::Net::HTTPResponse; extern "C" module AP_MODULE_DECLARE_DATA poco_module; ApacheRequestRec::ApacheRequestRec(request_rec* pRec): _pRec(pRec) { } bool ApacheRequestRec::haveRequestBody() { return ap_should_client_block(_pRec) != 0; } int ApacheRequestRec::readRequest(char* buffer, int length) { return ap_get_client_block(_pRec, buffer, length); } void ApacheRequestRec::writeResponse(const char* buffer, int length) { ap_rwrite(buffer, length, _pRec); } void ApacheRequestRec::redirect(const std::string& uri, int status) { apr_table_set(_pRec->headers_out, "Location", uri.c_str()); _pRec->connection->keepalive = AP_CONN_CLOSE; _pRec->status = status; ap_set_keepalive(_pRec); ap_send_error_response(_pRec, 0); } void ApacheRequestRec::sendErrorResponse(int status) { _pRec->connection->keepalive = AP_CONN_CLOSE; _pRec->status = status; ap_set_keepalive(_pRec); ap_send_error_response(_pRec, 0); } void ApacheRequestRec::addHeader(const std::string& key, const std::string& value) { const apr_array_header_t *arr = apr_table_elts(_pRec->headers_out); apr_table_add(const_cast(reinterpret_cast(arr)), key.c_str(), value.c_str()); } void ApacheRequestRec::setContentType(const std::string& mediaType) { ap_set_content_type(_pRec, mediaType.c_str()); } int ApacheRequestRec::sendFile(const std::string& path, unsigned int fileSize, const std::string& mediaType) { apr_file_t *thefile = 0; apr_finfo_t finfo; apr_size_t nBytes; // setting content-type ap_set_content_type(_pRec, mediaType.c_str()); // opening file if (apr_file_open(&thefile, path.c_str(), APR_READ, APR_UREAD | APR_GREAD, _pRec->pool) == APR_SUCCESS) { // getting fileinfo apr_file_info_get(&finfo, APR_FINFO_NORM, thefile); // setting last-updated & co ap_update_mtime(_pRec, finfo.mtime); ap_set_last_modified(_pRec); ap_set_content_length(_pRec, fileSize); // sending file ap_send_fd(thefile, _pRec, 0, fileSize, &nBytes); // well done return 0; } // file not opened successfully -> produce an exception in C++ code return 1; } bool ApacheRequestRec::secure() { return DEFAULT_HTTPS_PORT == ap_default_port(_pRec) && ap_http_scheme(_pRec) == "https"; } void ApacheRequestRec::setStatus(int status) { _pRec->status = status; } void ApacheRequestRec::copyHeaders(ApacheServerRequest& request) { const apr_array_header_t* arr = apr_table_elts(_pRec->headers_in); const apr_table_entry_t* elts = (const apr_table_entry_t *)arr->elts; request.setMethod(_pRec->method); request.setURI(_pRec->unparsed_uri); // iterating over raw-headers and printing them for (int i = 0; i < arr->nelts; i++) { request.add(elts[i].key, elts[i].val); } } void ApacheConnector::log(const char* file, int line, int level, int status, const char *text) { // ap_log_error() has undergone significant changes in Apache 2.4. // Validate Apache version for using a proper ap_log_error() version. #if AP_SERVER_MAJORVERSION_NUMBER == 2 && AP_SERVER_MINORVERSION_NUMBER < 4 ap_log_error(file, line, level, 0, NULL, "%s", text); #else ap_log_error(file, line, level, 0, 0, 0, text); #endif } extern "C" int ApacheConnector_handler(request_rec *r) { ApacheRequestRec rec(r); ApacheApplication& app(ApacheApplication::instance()); try { // ensure application is ready app.setup(); // if the ApacheRequestHandler declines handling - we stop // request handling here and let other modules do their job! if (!app.factory().mustHandle(r->uri)) return DECLINED; apr_status_t rv; if ((rv = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK))) return rv; // The properties conn_rec->remote_ip and conn_rec->remote_addr have undergone significant changes in Apache 2.4. // Validate Apache version for using conn_rec->remote_ip and conn_rec->remote_addr proper versions. #if AP_SERVER_MAJORVERSION_NUMBER == 2 && AP_SERVER_MINORVERSION_NUMBER < 4 const char* clientIp = r->connection->remote_ip; apr_port_t clientPort = r->connection->remote_addr->port; #else const char* clientIp = r->connection->client_ip; apr_port_t clientPort = r->connection->client_addr->port; #endif std::unique_ptr pRequest(new ApacheServerRequest( &rec, r->connection->local_ip, r->connection->local_addr->port, clientIp, clientPort)); std::unique_ptr pResponse(new ApacheServerResponse(pRequest.get())); // add header information to request rec.copyHeaders(*pRequest); try { std::unique_ptr pHandler(app.factory().createRequestHandler(*pRequest)); if (pHandler.get()) { pHandler->handleRequest(*pRequest, *pResponse); } else { pResponse->sendErrorResponse(HTTP_NOT_IMPLEMENTED); } } catch (...) { pResponse->sendErrorResponse(HTTP_INTERNAL_SERVER_ERROR); throw; } } catch (Poco::Exception& exc) { ApacheConnector::log(__FILE__, __LINE__, ApacheConnector::PRIO_ERROR, 0, exc.displayText().c_str()); } catch (...) { ApacheConnector::log(__FILE__, __LINE__, ApacheConnector::PRIO_ERROR, 0, "Unknown exception"); } return OK; } extern "C" void ApacheConnector_register_hooks(apr_pool_t *p) { ap_hook_handler(ApacheConnector_handler, NULL, NULL, APR_HOOK_MIDDLE); } extern "C" const char* ApacheConnector_uris(cmd_parms *cmd, void *in_dconf, const char *in_str) { try { ApacheApplication::instance().factory().handleURIs(in_str); } catch (Poco::Exception& exc) { ApacheConnector::log(__FILE__, __LINE__, ApacheConnector::PRIO_ERROR, 0, exc.displayText().c_str()); } catch (...) { ApacheConnector::log(__FILE__, __LINE__, ApacheConnector::PRIO_ERROR, 0, "Unknown exception"); } return 0; } extern "C" const char* ApacheConnector_config(cmd_parms *cmd, void *in_dconf, const char *in_str) { try { ApacheApplication::instance().loadConfiguration(in_str); } catch (Poco::Exception& exc) { ApacheConnector::log(__FILE__, __LINE__, ApacheConnector::PRIO_ERROR, 0, exc.displayText().c_str()); } catch (...) { ApacheConnector::log(__FILE__, __LINE__, ApacheConnector::PRIO_ERROR, 0, "Unknown exception"); } return 0; } extern "C" const command_rec ApacheConnector_cmds[] = { AP_INIT_RAW_ARGS( "AddPocoRequestHandler", reinterpret_cast(ApacheConnector_uris), NULL, RSRC_CONF, "POCO RequestHandlerFactory class name followed by shared library path followed by a list of ' ' separated URIs that must be handled by this module."), AP_INIT_RAW_ARGS( "AddPocoConfig", reinterpret_cast(ApacheConnector_config), NULL, RSRC_CONF, "Path of the POCO configuration file."), { NULL } }; module AP_MODULE_DECLARE_DATA poco_module = { STANDARD20_MODULE_STUFF, NULL, NULL, NULL, NULL, ApacheConnector_cmds, ApacheConnector_register_hooks };