//
// httpclient.h - a pipelined HTTP client
// Copyright (C) 2010 Martin Broadhurst
// www.martinbroadhurst.com
//
#ifndef HTTPCLIENT_H
#define HTTPCLIENT_H
#include <string>
#include <map>
#include <queue>
#include <sstream>
#include <pthread.h>
#include <signal.h>
namespace Pipelined
{
// Base class for exceptions
class Exception : public std::exception
{
public:
Exception(const std::string& what)
: what_(what)
{
}
virtual ~Exception() throw ()
{
}
virtual const char * what () const throw ()
{
return what_.c_str();
}
private:
std::string what_;
};
struct DisconnectedException
{
};
// Abstract base class for streams representing an entity body
class Stream
{
public:
virtual ~Stream()
{
}
virtual ssize_t Read(void* buf, size_t size) = 0;
virtual bool Eof() = 0;
virtual bool Failed() = 0;
};
// Struct representing an HTTP response status line
struct HttpStatus
{
std::string version;
unsigned int code;
std::string message;
HttpStatus(const std::string& start);
};
class HttpClient;
// Abstract base class for HTTP requesters
class HttpRequester
{
public:
virtual ~HttpRequester()
{
}
virtual void Run(HttpClient* client) = 0;
virtual void HandleResponse(const std::string& request, const HttpStatus& status,
const std::multimap<std::string, std::string>& headers, Stream* body) = 0;
virtual void HandleFinish(std::queue<std::string>& requests)
{
}
};
// The pipelined client
class HttpClient
{
public:
HttpClient(const std::string& host,
const std::string& port = "80");
~HttpClient();
void Run(HttpRequester* requester);
void StartRequest(const std::string& method, const std::string& uri);
template <class Value>
void SendHeader(const std::string& name, const Value& value) const
{
std::ostringstream oss;
oss << name << ": " << value << "\n";
ssize_t bytes_written = write(fd_, oss.str().c_str(), oss.str().length());
if (bytes_written == -1) {
throw DisconnectedException();
}
}
void EndHeaders() const
{
ssize_t bytes_written = write(fd_, "\n", 1);
if (bytes_written == -1) {
throw DisconnectedException();
}
}
ssize_t Write(const void* buf, size_t size) const
{
ssize_t bytes_written = write(fd_, buf, size);
if (bytes_written == -1) {
throw DisconnectedException();
}
return bytes_written;
}
void Close()
{
close(fd_);
}
private:
static std::multimap<std::string, std::string> ReadHeaders(HttpClient *client,
size_t& content_length, bool& chunked);
static void* Read(void* pclient);
private: // Not copyable
HttpClient(const HttpClient& other);
HttpClient& operator=(const HttpClient& other);
private:
HttpRequester* requester_;
std::string host_;
std::string port_;
int fd_;
pthread_t reader_;
std::queue<std::string> requests_;
pthread_mutex_t request_lock_;
bool disconnected_;
sighandler_t old_handler_;
};
class HttpClientException : public Exception
{
public:
HttpClientException(const std::string& what)
: Exception(what)
{
}
};
};
#endif // HTTPCLIENT_H