/** * Digital Voice Modem - Host Software * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * @package DVM / Host Software * */ // // Based on code from the CRUD project. (https://github.com/venediktov/CRUD) // Licensed under the BPL-1.0 License (https://opensource.org/license/bsl1-0-html) // /* * Copyright (c) 2003-2013 Christopher M. Kohlhoff * Copyright (C) 2023 by Bryan Biedenkapp N2PLL * * Permission is hereby granted, free of charge, to any person or organization * obtaining a copy of the software and accompanying documentation covered by * this license (the “Software”) to use, reproduce, display, distribute, execute, * and transmit the Software, and to prepare derivative works of the Software, and * to permit third-parties to whom the Software is furnished to do so, all subject * to the following: * * The copyright notices in the Software and this entire statement, including the * above license grant, this restriction and the following disclaimer, must be included * in all copies of the Software, in whole or in part, and all derivative works of the * Software, unless such copies or derivative works are solely in the form of * machine-executable object code generated by a source language processor. * * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE * DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "Defines.h" #include "network/rest/http/HTTPRequestHandler.h" #include "network/rest/http/HTTPRequest.h" #include "network/rest/http/HTTPReply.h" using namespace rest::server; #include #include #include // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- /// /// Initializes a new instance of the HTTPRequestHandler class. /// /// HTTPRequestHandler::HTTPRequestHandler(const std::string& docRoot) : m_docRoot(docRoot) { /* stub */ } /// /// Handle a request and produce a reply. /// void HTTPRequestHandler::handleRequest(const HTTPRequest& request, HTTPReply& reply) { // decode url to path std::string requestPath; if (!urlDecode(request.uri, requestPath)) { reply = HTTPReply::stockReply(HTTPReply::BAD_REQUEST); return; } // request path must be absolute and not contain "..". if (requestPath.empty() || requestPath[0] != '/' || requestPath.find("..") != std::string::npos) { reply = HTTPReply::stockReply(HTTPReply::BAD_REQUEST); return; } // if path ends in slash (i.e. is a directory) then add "index.html" if (requestPath[requestPath.size() - 1] == '/') { requestPath += "index.html"; } // determine the file extension std::size_t lastSlashPos = requestPath.find_last_of("/"); std::size_t lastDotPos = requestPath.find_last_of("."); std::string extension; if (lastDotPos != std::string::npos && lastDotPos > lastSlashPos) { extension = requestPath.substr(lastDotPos + 1); } // open the file to send back std::string fullPath = m_docRoot + requestPath; std::ifstream is(fullPath.c_str(), std::ios::in | std::ios::binary); if (!is) { reply = HTTPReply::stockReply(HTTPReply::NOT_FOUND); return; } // fill out the reply to be sent to the client reply.status = HTTPReply::OK; char buf[512]; while (is.read(buf, sizeof(buf)).gcount() > 0) reply.content.append(buf, is.gcount()); reply.headers.resize(2); reply.headers[0].name = "Content-Length"; reply.headers[0].value = std::to_string(reply.content.size()); reply.headers[1].name = "Content-Type"; reply.headers[1].value = "application/octet-stream"; } /// /// Perform URL-decoding on a string. Returns false if the encoding was invalid. /// /// /// bool HTTPRequestHandler::urlDecode(const std::string& in, std::string& out) { out.clear(); out.reserve(in.size()); for (std::size_t i = 0; i < in.size(); ++i) { if (in[i] == '%') { if (i + 3 <= in.size()) { int value = 0; std::istringstream is(in.substr(i + 1, 2)); if (is >> std::hex >> value) { out += static_cast(value); i += 2; } else { return false; } } else { return false; } } else if (in[i] == '+') { out += ' '; } else { out += in[i]; } } return true; }