Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 52 additions & 19 deletions include/urdl/detail/http_read_stream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <algorithm>
#include <ostream>
#include <iterator>
#include <map>
#include "urdl/http.hpp"
#include "urdl/option_set.hpp"
#include "urdl/url.hpp"
Expand Down Expand Up @@ -86,6 +87,8 @@ class http_read_stream
= options_.get_option<urdl::http::request_content_type>().value();
std::string user_agent
= options_.get_option<urdl::http::user_agent>().value();
std::map<std::string, std::string> headers
= options_.get_option<urdl::http::request_headers>().value();

// Form the request. We specify the "Connection: close" header so that the
// server will close the socket after transmitting the response. This will
Expand All @@ -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<std::string, std::string>::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.
Expand Down Expand Up @@ -236,6 +253,8 @@ class http_read_stream
= options_.get_option<urdl::http::request_content_type>().value();
std::string user_agent
= options_.get_option<urdl::http::user_agent>().value();
std::map<std::string, std::string> headers
= options_.get_option<urdl::http::request_headers>().value();

// Form the request. We specify the "Connection: close" header so that
// the server will close the socket after transmitting the response.
Expand All @@ -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<std::string, std::string>::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;
}

Expand Down
69 changes: 69 additions & 0 deletions include/urdl/http.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#define URDL_HTTP_HPP

#include <string>
#include <map>
#include <boost/system/error_code.hpp>
#include "urdl/detail/config.hpp"

Expand Down Expand Up @@ -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<std::string, std::string> 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 <urdl/http.hpp> @n
* @e Namespace: @c urdl::http
*/
class request_headers {
public:
/// Constructs an object of class @c request_headers.
/**
* @par Remarks
* Postcondition: <tt>value() == Empty map</tt>.
*/
request_headers()
{
}

/// Constructs an object of class @c request_headers.
/**
* @param v The desired header names and values for the option.
*
* @par Remarks
* Postcondition: <tt>value() == v</tt>
*/
explicit request_headers(const std::map<std::string, std::string>& v)
: value_(v)
{
}

/// Gets the value of the option.
/**
* @returns The value of the option.
*/
std::map<std::string, std::string> value() const
{
return value_;
}

/// Sets the value of the option.
/**
* @param v The desired header names and values for the option.
*
* @par Remarks
* Postcondition: <tt>value() == v</tt>
*/
void value(const std::map<std::string, std::string>& v)
{
value_ = v;
}

private:
std::map<std::string, std::string> value_;
};

/// Option to specify content to accompany an HTTP request.
/**
* @par Remarks
Expand Down