From 90f081f9cf602fa1b75b74fad58977dd40111949 Mon Sep 17 00:00:00 2001 From: Masakazu Kitajo Date: Thu, 14 Jul 2016 10:30:42 -0700 Subject: [PATCH] Add request_headers option --- include/urdl/detail/http_read_stream.hpp | 71 +++++++++++++++++------- include/urdl/http.hpp | 69 +++++++++++++++++++++++ 2 files changed, 121 insertions(+), 19 deletions(-) diff --git a/include/urdl/detail/http_read_stream.hpp b/include/urdl/detail/http_read_stream.hpp index 720bec7..bf9974c 100644 --- a/include/urdl/detail/http_read_stream.hpp +++ b/include/urdl/detail/http_read_stream.hpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "urdl/http.hpp" #include "urdl/option_set.hpp" #include "urdl/url.hpp" @@ -86,6 +87,8 @@ class http_read_stream = options_.get_option().value(); std::string user_agent = options_.get_option().value(); + std::map headers + = options_.get_option().value(); // Form the request. We specify the "Connection: close" header so that the // server will close the socket after transmitting the response. This will @@ -94,23 +97,37 @@ class http_read_stream request_stream << request_method << " "; request_stream << u.to_string(url::path_component | url::query_component); request_stream << " HTTP/1.0\r\n"; - request_stream << "Host: "; - request_stream << u.to_string(url::host_component | url::port_component); - request_stream << "\r\n"; - request_stream << "Accept: */*\r\n"; + if (headers.find("Host") == headers.end()) { + request_stream << "Host: "; + request_stream << u.to_string(url::host_component | url::port_component); + request_stream << "\r\n"; + } + if (headers.find("Accept") == headers.end()) { + request_stream << "Accept: */*\r\n"; + } + if (request_content.length()) { - request_stream << "Content-Length: "; - request_stream << request_content.length() << "\r\n"; - if (request_content_type.length()) + if (headers.find("Content-Length") == headers.end()) { + request_stream << "Content-Length: "; + request_stream << request_content.length() << "\r\n"; + } + if (request_content_type.length() && headers.find("Content-Type") == headers.end()) { request_stream << "Content-Type: "; request_stream << request_content_type << "\r\n"; } } - if (user_agent.length()) + if (user_agent.length() && headers.find("User-Agent") == headers.end()) request_stream << "User-Agent: " << user_agent << "\r\n"; - request_stream << "Connection: close\r\n\r\n"; + if (headers.find("Connection") == headers.end()) + request_stream << "Connection: close\r\n"; + + for (std::map::const_iterator itr = headers.begin(); itr != headers.end(); ++itr) { + request_stream << itr->first << ": " << itr->second << "\r\n"; + } + request_stream << "\r\n"; + request_stream << request_content; // Send the request. @@ -236,6 +253,8 @@ class http_read_stream = options_.get_option().value(); std::string user_agent = options_.get_option().value(); + std::map headers + = options_.get_option().value(); // Form the request. We specify the "Connection: close" header so that // the server will close the socket after transmitting the response. @@ -246,24 +265,38 @@ class http_read_stream request_stream << url_.to_string( url::path_component | url::query_component); request_stream << " HTTP/1.0\r\n"; - request_stream << "Host: "; - request_stream << url_.to_string( - url::host_component | url::port_component); - request_stream << "\r\n"; - request_stream << "Accept: */*\r\n"; + if (headers.find("Host") == headers.end()) { + request_stream << "Host: "; + request_stream << url_.to_string( + url::host_component | url::port_component); + request_stream << "\r\n"; + } + if (headers.find("Accept") == headers.end()) { + request_stream << "Accept: */*\r\n"; + } + if (request_content.length()) { - request_stream << "Content-Length: "; - request_stream << request_content.length() << "\r\n"; - if (request_content_type.length()) + if (headers.find("Content-Length") == headers.end()) { + request_stream << "Content-Length: "; + request_stream << request_content.length() << "\r\n"; + } + if (request_content_type.length() && headers.find("Content-Type") == headers.end()) { request_stream << "Content-Type: "; request_stream << request_content_type << "\r\n"; } } - if (user_agent.length()) + if (user_agent.length() && headers.find("User-Agent") == headers.end()) request_stream << "User-Agent: " << user_agent << "\r\n"; - request_stream << "Connection: close\r\n\r\n"; + if (headers.find("Connection") == headers.end()) + request_stream << "Connection: close\r\n"; + + for (std::map::const_iterator itr = headers.begin(); itr != headers.end(); ++itr) { + request_stream << itr->first << ": " << itr->second << "\r\n"; + } + request_stream << "\r\n"; + request_stream << request_content; } diff --git a/include/urdl/http.hpp b/include/urdl/http.hpp index 4442b84..f6480b6 100644 --- a/include/urdl/http.hpp +++ b/include/urdl/http.hpp @@ -12,6 +12,7 @@ #define URDL_HTTP_HPP #include +#include #include #include "urdl/detail/config.hpp" @@ -104,6 +105,74 @@ class request_method std::string value_; }; +/// Option to send arbitrary HTTP headers. +/** + * @par Remarks + * The default is no additional headers. + * + * @par Example + * To add a header to the HTTP request using an object of class @c std::map: + * @code + * std::map headers = { + * {"X-Foo", "bar"} + * }; + * urdl::istream is; + * is.set_option(urdl::http::request_heders(headers)); + * is.open("http://host/path"); + * @endcode + * + * @par Requirements + * @e Header: @c @n + * @e Namespace: @c urdl::http + */ +class request_headers { +public: + /// Constructs an object of class @c request_headers. + /** + * @par Remarks + * Postcondition: value() == Empty map. + */ + request_headers() + { + } + + /// Constructs an object of class @c request_headers. + /** + * @param v The desired header names and values for the option. + * + * @par Remarks + * Postcondition: value() == v + */ + explicit request_headers(const std::map& v) + : value_(v) + { + } + + /// Gets the value of the option. + /** + * @returns The value of the option. + */ + std::map value() const + { + return value_; + } + + /// Sets the value of the option. + /** + * @param v The desired header names and values for the option. + * + * @par Remarks + * Postcondition: value() == v + */ + void value(const std::map& v) + { + value_ = v; + } + +private: + std::map value_; +}; + /// Option to specify content to accompany an HTTP request. /** * @par Remarks