Chapter 66. A simple HTTP client example

Index

Sending requests
Uploading files
Processing HTTP responses
Persistent HTTP connections
Cookies
HTTP forms
Limiting form size
Encoding an HTTP form
HTTP authentication challenges
#include <x/http/useragent.H>
#include <iterator>
#include <iostream>
#include <cstdlib>

void http_example()
{
	x::http::useragent::base::https_enable();

	x::http::useragent ua(x::http::useragent::create(x::http::noverifycert));

	x::http::useragent::base::response
		resp=ua->request(x::http::GET, "https://localhost");

	std::cout << resp->message.getStatusCode()
		  << " " << resp->message.getReasonPhrase() << std::endl;

	for (auto hdr: resp->message)
	{
		std::cout << hdr.first << "="
			  << hdr.second.value() << std::endl;
	}

	if (resp->has_content())
	{
		std::copy(resp->begin(), resp->end(),
			  std::ostreambuf_iterator<char>(std::cout.rdbuf()));
		std::cout << std::flush;
	}

	if (resp->message.getStatusCodeClass() != x::http::resp_success)
		exit(1);

}

int main()
{
	http_example();
	return 0;
}

This is an example of implementing a basic HTTP client. x::http::useragent and x::http::useragentptr are a reference and a nullable pointer reference to a reference-counted object that implements a generic HTTP user agent client.

A user agent object manages connections with HTTP servers. An application should invoke x::http::useragent::base::https_enable() if the application uses HTTPS. This is strictly necessary only when employing static linking. Linking with -lcxxtls dynamically automatically enables HTTPS; but using static linkage is convenient when debugging, and that requires x::http::useragent::base::https_enable().

The create() method takes three optional parameters that specify configuration options for the user agent object. The first parameter is a set of flags (use 0 to specify none of the following):

x::http::noverifypeer

For HTTPS connections, do not verify that the server's certificate's name matches the server's name.

x::http::noverifycert

For HTTPS connections, do not verify that the server's certificate is signed by a trusted certificate authority, and do not verify that the server's certificate's name matches the server's name.

The list of trusted certificate authorities is set by the x::gnutls::calist property.

When enabled the same user agent object manages both HTTP and HTTPS connections. An application can create multiple user agent objects, with different options. The reference-counted user agent object is thread safe. A single user agent object may be used, simultaneously, by multiple threads.

HTTP 1.1 servers can use the same connection for multiple requests. The user agent object handles one request at a time, and saves open HTTP 1.1 connections internally. A subsequent request for a URI with the same server tries to use the existing connection, if it is still open. The second optional parameter to create() sets the maximum number of simultaneous connections saved by a user agent object, and defaults to the x::http::useragent::pool::maxconn property. The third optional parameter, that defaults to the x::http::useragent::pool::maxhostconn property, sets the maximum number of saved connections to the same server.

Note

These properties do not set the maximum number of simultaneous requests, but the maximum number of unused connections saved by the user agent objects. When the number of unused connections exceed these maximums, the oldest connections get closed, as many as needed to bring them down below the limits.

Sending requests

The user agent's request() method is heavily overloaded, and takes the following parameters:

  1. A terminator file descriptor object. 

    x::fd timeoutfd;
    
    // ...
    
    auto resp=ua->request(timeoutfd, x::http::GET, "https://localhost");

    request() sends a request and waits until the server responds. request() does not return until it receives a response. The x::http::client::response_timeout property defaults to fifteen minutes. If the server does not reply, the request times out and a timeout response object gets returned.

    The first parameter to request() can optionally be a terminator file descriptor. If it becomes readable, the request gets aborted before the timeout expires.

    Note

    If specified, the timeout property does not get overridden, but rather supplemented. The request times out either when the interval specified by x::http::client::response_timeout elapses, or the terminator file descriptor becomes readable, whichever occurs first.

  2. An HTTP request method.  One of the HTTP request types, usually x::HTTP::GET or x::HTTP::POST.

  3. A URI The next parameter to request() is a x::uriimpl that specifies the requested URI being requested.

    The x::uriimpl is constructable from a literal string. To use international domain names, it is necessary to construct it explicitly:

    auto resp=ua->request(timeoutfd, x::http::GET,
                          x::uriimpl("http://привет.example.com", x::locale::base::utf8()));
  4. A reference to an x::requestimpl This is an alternative for a method and a URI. The following two examples are generally equivalent to each other.

    1. auto resp=ua->request(x::http::GET, "http://localhost");
    2. x::http::requestimpl req;
      
      req.setMethod(x::http::GET);
      req.setURI("http://localhost");
      
      auto resp=ua->request(req);

    Note

    request() receives a x::http::requestimpl by reference, and may adjust its contents in order to format the request according to the connection parameters.

  5. Additional HTTP headers. 

    auto resp=ua->request(x::http::GET, "http://localhost",
                         "Accept", "text/plain",
                         "Accept-Language", "en");

    An optional list of name/value tuples follows the request method and the URI. If specified, the HTTP request includes these as custom headers (a name/value tuple list may also follow if they are replaced by a x::http::requestimpl, which has the effect of modifying the x::http::requestimpl object).

    This example adds Accept: text/plain and Accept-Language: en to the request. See RFC 2616 for a list of headers that can be added.

  6. An HTTP form or content.  The last parameter to request() is optional. It specifies any content that's part of the request. There are two ways to specify the content.

    auto resp=ua->request(x::http::POST, "http://localhost/cgi-bin/req.pl",
    	              x::http::form::parameters::create("username",
                                                            "alfred",
                                                            "password",
                                                            "rosebud"));

    An x::http::form::parameters parameter specifies HTTP form parameters as the contents of the request. The above example specifies two parameters, username=alfred and password=rosebud. For a x::http::GET request, this sets the query string part of the URI, otherwise the request's Content-Type header gets set to application/x-www-form-urlencoded and the request's content consists of the form's parameters, appropriately encoded.

    std::vector<char> buf;
    
    // ...
    
    auto resp=ua->request(x::http::POST, "http://localhost/cgi-bin/upload.pl",
                          "Content-Type", "text/plain",
                          std::make_pair(buf.begin(), buf.end()));

    Non-form request content takes the form of a std::pair with a beginning and an ending iterator that define an input sequence that forms the content of the request.

    Note

    The iterators must be, at a minimum, forward iterators. Input iterators are not sufficient. Random access iterators are preferred. Content defined by non-random access iterators may have to be iterated over more than once, depending on the connection parameters with the HTTP server.

Note

Requests should specify a complete, absolute URI. Do not set a Host header in the request, giving the hostname part of the URI. Just specify an absolute URI and request() takes care of formatting the request.