GitHub Viewer
/*
This file is part of libhttpserver
Copyright (C) 2011-2019 Sebastiano Merlino
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
*/
#include "httpserver/http_utils.hpp"
// is not pulled in transitively by
// . Include it directly here so the
// MHD_*-using bodies below still compile.
#include // needed directly; not reachable transitively
// (or its Windows equivalent) must also be
// requested explicitly: it provides struct sockaddr's full definition
// for get_ip_str / get_port / ip_representation::ip_representation.
#if defined(_WIN32) && !defined(__CYGWIN__)
#include // needed directly; not reachable transitively
#include
#include
#include
#include
#include
#else // WIN32 check
#include
#include
#include
#include // needed directly; not reachable transitively
#include
#endif // WIN32 check
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "httpserver/constants.hpp"
#include "httpserver/detail/unescape_helpers.hpp"
#include "httpserver/string_utilities.hpp"
#if defined (__CYGWIN__)
#if !defined (NI_MAXHOST)
#define NI_MAXHOST 1025
#endif // NI_MAXHOST
#ifndef __u_char_defined
typedef unsigned char u_char;
#define __u_char_defined
#endif // __u_char_defined
#endif // CYGWIN
// libmicrohttpd deprecated some definitions with v0.9.74, and introduced new ones
#if MHD_VERSION < 0x00097314
#define MHD_HTTP_CONTENT_TOO_LARGE MHD_HTTP_PAYLOAD_TOO_LARGE
#define MHD_HTTP_UNPROCESSABLE_CONTENT MHD_HTTP_UNPROCESSABLE_ENTITY
#endif
namespace httpserver {
namespace http {
// See also: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
const int http_utils::http_continue = MHD_HTTP_CONTINUE;
const int http_utils::http_switching_protocol = MHD_HTTP_SWITCHING_PROTOCOLS;
const int http_utils::http_processing = MHD_HTTP_PROCESSING;
const int http_utils::http_ok = MHD_HTTP_OK;
const int http_utils::http_created = MHD_HTTP_CREATED;
const int http_utils::http_accepted = MHD_HTTP_ACCEPTED;
const int http_utils::http_non_authoritative_information = MHD_HTTP_NON_AUTHORITATIVE_INFORMATION;
const int http_utils::http_no_content = MHD_HTTP_NO_CONTENT;
const int http_utils::http_reset_content = MHD_HTTP_RESET_CONTENT;
const int http_utils::http_partial_content = MHD_HTTP_PARTIAL_CONTENT;
const int http_utils::http_multi_status = MHD_HTTP_MULTI_STATUS;
const int http_utils::http_multiple_choices = MHD_HTTP_MULTIPLE_CHOICES;
const int http_utils::http_moved_permanently = MHD_HTTP_MOVED_PERMANENTLY;
const int http_utils::http_found = MHD_HTTP_FOUND;
const int http_utils::http_see_other = MHD_HTTP_SEE_OTHER;
const int http_utils::http_not_modified = MHD_HTTP_NOT_MODIFIED;
const int http_utils::http_use_proxy = MHD_HTTP_USE_PROXY;
const int http_utils::http_switch_proxy = MHD_HTTP_SWITCH_PROXY;
const int http_utils::http_temporary_redirect = MHD_HTTP_TEMPORARY_REDIRECT;
const int http_utils::http_bad_request = MHD_HTTP_BAD_REQUEST;
const int http_utils::http_unauthorized = MHD_HTTP_UNAUTHORIZED;
const int http_utils::http_payment_required = MHD_HTTP_PAYMENT_REQUIRED;
const int http_utils::http_forbidden = MHD_HTTP_FORBIDDEN;
const int http_utils::http_not_found = MHD_HTTP_NOT_FOUND;
const int http_utils::http_method_not_allowed = MHD_HTTP_METHOD_NOT_ALLOWED;
const int http_utils::http_method_not_acceptable = MHD_HTTP_NOT_ACCEPTABLE;
const int http_utils::http_proxy_authentication_required = MHD_HTTP_PROXY_AUTHENTICATION_REQUIRED;
const int http_utils::http_request_timeout = MHD_HTTP_REQUEST_TIMEOUT;
const int http_utils::http_conflict = MHD_HTTP_CONFLICT;
const int http_utils::http_gone = MHD_HTTP_GONE;
const int http_utils::http_length_required = MHD_HTTP_LENGTH_REQUIRED;
const int http_utils::http_precondition_failed = MHD_HTTP_PRECONDITION_FAILED;
const int http_utils::http_request_entity_too_large = MHD_HTTP_CONTENT_TOO_LARGE;
const int http_utils::http_request_uri_too_long = MHD_HTTP_URI_TOO_LONG;
const int http_utils::http_unsupported_media_type = MHD_HTTP_UNSUPPORTED_MEDIA_TYPE;
const int http_utils::http_requested_range_not_satisfiable = MHD_HTTP_RANGE_NOT_SATISFIABLE;
const int http_utils::http_expectation_failed = MHD_HTTP_EXPECTATION_FAILED;
const int http_utils::http_unprocessable_entity = MHD_HTTP_UNPROCESSABLE_CONTENT;
const int http_utils::http_locked = MHD_HTTP_LOCKED;
const int http_utils::http_failed_dependency = MHD_HTTP_FAILED_DEPENDENCY;
const int http_utils::http_upgrade_required = MHD_HTTP_UPGRADE_REQUIRED;
const int http_utils::http_retry_with = MHD_HTTP_RETRY_WITH;
const int http_utils::http_internal_server_error = MHD_HTTP_INTERNAL_SERVER_ERROR;
const int http_utils::http_not_implemented = MHD_HTTP_NOT_IMPLEMENTED;
const int http_utils::http_bad_gateway = MHD_HTTP_BAD_GATEWAY;
const int http_utils::http_service_unavailable = MHD_HTTP_SERVICE_UNAVAILABLE;
const int http_utils::http_gateway_timeout = MHD_HTTP_GATEWAY_TIMEOUT;
const int http_utils::http_version_not_supported = MHD_HTTP_HTTP_VERSION_NOT_SUPPORTED;
const int http_utils::http_variant_also_negotiated = MHD_HTTP_VARIANT_ALSO_NEGOTIATES;
const int http_utils::http_insufficient_storage = MHD_HTTP_INSUFFICIENT_STORAGE;
const int http_utils::http_bandwidth_limit_exceeded = MHD_HTTP_BANDWIDTH_LIMIT_EXCEEDED;
const int http_utils::http_not_extended = MHD_HTTP_NOT_EXTENDED;
const int http_utils::shoutcast_response = MHD_ICY_FLAG;
const char* http_utils::http_header_accept = MHD_HTTP_HEADER_ACCEPT;
const char* http_utils::http_header_accept_charset = MHD_HTTP_HEADER_ACCEPT_CHARSET;
const char* http_utils::http_header_accept_encoding = MHD_HTTP_HEADER_ACCEPT_ENCODING;
const char* http_utils::http_header_accept_language = MHD_HTTP_HEADER_ACCEPT_LANGUAGE;
const char* http_utils::http_header_accept_ranges = MHD_HTTP_HEADER_ACCEPT_RANGES;
const char* http_utils::http_header_age = MHD_HTTP_HEADER_AGE;
const char* http_utils::http_header_allow = MHD_HTTP_HEADER_ALLOW;
const char* http_utils::http_header_authorization = MHD_HTTP_HEADER_AUTHORIZATION;
const char* http_utils::http_header_cache_control = MHD_HTTP_HEADER_CACHE_CONTROL;
const char* http_utils::http_header_connection = MHD_HTTP_HEADER_CONNECTION;
const char* http_utils::http_header_content_encoding = MHD_HTTP_HEADER_CONTENT_ENCODING;
const char* http_utils::http_header_content_language = MHD_HTTP_HEADER_CONTENT_LANGUAGE;
const char* http_utils::http_header_content_length = MHD_HTTP_HEADER_CONTENT_LENGTH;
const char* http_utils::http_header_content_location = MHD_HTTP_HEADER_CONTENT_LOCATION;
const char* http_utils::http_header_content_md5 = MHD_HTTP_HEADER_CONTENT_MD5;
const char* http_utils::http_header_content_range = MHD_HTTP_HEADER_CONTENT_RANGE;
const char* http_utils::http_header_content_type = MHD_HTTP_HEADER_CONTENT_TYPE;
const char* http_utils::http_header_date = MHD_HTTP_HEADER_DATE;
const char* http_utils::http_header_etag = MHD_HTTP_HEADER_ETAG;
const char* http_utils::http_header_expect = MHD_HTTP_HEADER_EXPECT;
const char* http_utils::http_header_expires = MHD_HTTP_HEADER_EXPIRES;
const char* http_utils::http_header_from = MHD_HTTP_HEADER_FROM;
const char* http_utils::http_header_host = MHD_HTTP_HEADER_HOST;
const char* http_utils::http_header_if_match = MHD_HTTP_HEADER_IF_MATCH;
const char* http_utils::http_header_if_modified_since = MHD_HTTP_HEADER_IF_MODIFIED_SINCE;
const char* http_utils::http_header_if_none_match = MHD_HTTP_HEADER_IF_NONE_MATCH;
const char* http_utils::http_header_if_range = MHD_HTTP_HEADER_IF_RANGE;
const char* http_utils::http_header_if_unmodified_since = MHD_HTTP_HEADER_IF_UNMODIFIED_SINCE;
const char* http_utils::http_header_last_modified = MHD_HTTP_HEADER_LAST_MODIFIED;
const char* http_utils::http_header_location = MHD_HTTP_HEADER_LOCATION;
const char* http_utils::http_header_max_forwards = MHD_HTTP_HEADER_MAX_FORWARDS;
const char* http_utils::http_header_pragma = MHD_HTTP_HEADER_PRAGMA;
const char* http_utils::http_header_proxy_authenticate = MHD_HTTP_HEADER_PROXY_AUTHENTICATE;
const char* http_utils::http_header_proxy_authentication = MHD_HTTP_HEADER_PROXY_AUTHORIZATION;
const char* http_utils::http_header_range = MHD_HTTP_HEADER_RANGE;
const char* http_utils::http_header_referer = MHD_HTTP_HEADER_REFERER;
const char* http_utils::http_header_retry_after = MHD_HTTP_HEADER_RETRY_AFTER;
const char* http_utils::http_header_server = MHD_HTTP_HEADER_SERVER;
const char* http_utils::http_header_te = MHD_HTTP_HEADER_TE;
const char* http_utils::http_header_trailer = MHD_HTTP_HEADER_TRAILER;
const char* http_utils::http_header_transfer_encoding = MHD_HTTP_HEADER_TRANSFER_ENCODING;
const char* http_utils::http_header_upgrade = MHD_HTTP_HEADER_UPGRADE;
const char* http_utils::http_header_user_agent = MHD_HTTP_HEADER_USER_AGENT;
const char* http_utils::http_header_vary = MHD_HTTP_HEADER_VARY;
const char* http_utils::http_header_via = MHD_HTTP_HEADER_VIA;
const char* http_utils::http_header_warning = MHD_HTTP_HEADER_WARNING;
const char* http_utils::http_header_www_authenticate = MHD_HTTP_HEADER_WWW_AUTHENTICATE;
const char* http_utils::http_version_1_0 = MHD_HTTP_VERSION_1_0;
const char* http_utils::http_version_1_1 = MHD_HTTP_VERSION_1_1;
const char* http_utils::http_method_connect = MHD_HTTP_METHOD_CONNECT;
const char* http_utils::http_method_delete = MHD_HTTP_METHOD_DELETE;
const char* http_utils::http_method_get = MHD_HTTP_METHOD_GET;
const char* http_utils::http_method_head = MHD_HTTP_METHOD_HEAD;
const char* http_utils::http_method_options = MHD_HTTP_METHOD_OPTIONS;
const char* http_utils::http_method_post = MHD_HTTP_METHOD_POST;
const char* http_utils::http_method_put = MHD_HTTP_METHOD_PUT;
const char* http_utils::http_method_trace = MHD_HTTP_METHOD_TRACE;
const char* http_utils::http_method_patch = MHD_HTTP_METHOD_PATCH;
const char* http_utils::http_post_encoding_form_urlencoded = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
const char* http_utils::http_post_encoding_multipart_formdata = MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA;
const char* http_utils::application_octet_stream = "application/octet-stream";
const char* http_utils::text_plain = "text/plain";
const char* http_utils::upload_filename_template = "libhttpserver.XXXXXX";
#if defined(_WIN32)
const char http_utils::path_separator = '\\';
#else // _WIN32
const char http_utils::path_separator = '/';
#endif // _WIN32
std::vector http_utils::tokenize_url(const std::string& str, const char separator) {
return string_utilities::string_split(str, separator);
}
std::string http_utils::standardize_url(const std::string& url) {
if (url.empty()) return url;
std::string result = url;
auto new_end = std::unique(result.begin(), result.end(), [](char a, char b) { return (a == b) && (a == '/'); });
result.erase(new_end, result.end());
if (result.length() > 1 && result.back() == '/') {
result.pop_back();
}
return result;
}
const std::string http_utils::generate_random_upload_filename(const std::string& directory) {
std::string filename = directory + http_utils::path_separator + http_utils::upload_filename_template;
char *template_filename = strdup(filename.c_str());
int fd = 0;
#if defined(_WIN32)
// only function for win32 which creates unique filenames and can handle a given template including a path
// all other functions like tmpnam() always create filenames in the 'temp' directory
if (0 != _mktemp_s(template_filename, filename.size() + 1)) {
free(template_filename);
throw generateFilenameException("Failed to create unique filename");
}
// as no existing file should be overwritten the operation should fail if the file already exists
// fstream or ofstream classes don't feature such an option
// with the function _sopen_s this can be achieved by setting the flag _O_EXCL
if (0 != _sopen_s(&fd, template_filename, _O_CREAT | _O_EXCL | _O_NOINHERIT, _SH_DENYNO, _S_IREAD | _S_IWRITE)) {
free(template_filename);
throw generateFilenameException("Failed to create file");
}
if (fd == -1) {
free(template_filename);
throw generateFilenameException("File descriptor after successful _sopen_s is -1");
}
_close(fd);
#else // _WIN32
fd = mkstemp(template_filename);
if (fd == -1) {
free(template_filename);
throw generateFilenameException("Failed to create unique file");
}
close(fd);
#endif // _WIN32
std::string ret_filename = template_filename;
free(template_filename);
return ret_filename;
}
std::string http_utils::sanitize_upload_filename(const std::string& filename) {
if (filename.empty()) return "";
// Reject filenames containing embedded null bytes. A name like
// "foo.txt\x00.php" would be silently truncated at the null when the
// concatenated path is passed to std::ofstream::open() (which calls
// c_str() and hands a const char* to the OS open() syscall), creating
// a file at the truncated location rather than the full path.
// (CWE-626 / null-byte injection)
if (filename.find('\0') != std::string::npos) return "";
// Find the basename: take everything after the last '/' or '\'
std::string::size_type pos = filename.find_last_of("/\\");
std::string basename = (pos != std::string::npos) ? filename.substr(pos + 1) : filename;
// Reject empty basename, ".", and ".."
if (basename.empty() || basename == "." || basename == "..") {
return "";
}
return basename;
}
// get_ip_str / get_port and the entire ip_representation impl live in
// src/detail/ip_representation.cpp to keep this TU under the project
// per-file LOC ceiling. See FILE_LOC_MAX in scripts/check-file-size.sh.
// hex_digit_value and the core unescape loop live in the shared
// internal header src/httpserver/detail/unescape_helpers.hpp so that
// this TU and src/detail/http_request_impl.cpp share one truth-source.
// http_unescape is a thin wrapper around unescape_buf_raw.
size_t http_unescape(std::string* val) {
if (val->empty()) return 0;
const std::size_t new_size =
httpserver::detail::unescape_buf_raw(val->data(), val->size());
(*val)[new_size] = '\0'; // add 0-terminator (std::string owns the byte)
val->resize(new_size);
return new_size; // = strlen(val)
}
const std::string load_file(const std::string& filename) {
std::ifstream fp(filename.c_str(), std::ios::in | std::ios::binary | std::ios::ate);
if (fp.is_open()) {
std::string content;
fp.seekg(0, fp.end);
content.reserve(fp.tellg());
fp.seekg(0, fp.beg);
content.assign((std::istreambuf_iterator(fp)), std::istreambuf_iterator());
return content;
} else {
throw std::invalid_argument("Unable to open file");
}
}
void dump_header_map(std::ostream& os, const std::string& prefix, const http::header_view_map &map) {
auto it = map.begin();
auto end = map.end();
if (map.size()) {
os