1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
|
/*
* HappyHTTP - a simple HTTP library
* Version 0.1
*
* Copyright (c) 2006 Ben Campbell
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
*/
#ifndef HAPPYHTTP_H
#define HAPPYHTTP_H
#include <string>
#include <map>
#include <vector>
#include <deque>
// forward decl
struct in_addr;
namespace happyhttp
{
class Response;
// Helper Functions
void BailOnSocketError( const char* context );
struct in_addr *atoaddr( const char* address);
typedef void (*ResponseBegin_CB)( const Response* r, void* userdata );
typedef void (*ResponseData_CB)( const Response* r, void* userdata, const unsigned char* data, int numbytes );
typedef void (*ResponseComplete_CB)( const Response* r, void* userdata );
// HTTP status codes
enum {
// 1xx informational
CONTINUE = 100,
SWITCHING_PROTOCOLS = 101,
PROCESSING = 102,
// 2xx successful
OK = 200,
CREATED = 201,
ACCEPTED = 202,
NON_AUTHORITATIVE_INFORMATION = 203,
NO_CONTENT = 204,
RESET_CONTENT = 205,
PARTIAL_CONTENT = 206,
MULTI_STATUS = 207,
IM_USED = 226,
// 3xx redirection
MULTIPLE_CHOICES = 300,
MOVED_PERMANENTLY = 301,
FOUND = 302,
SEE_OTHER = 303,
NOT_MODIFIED = 304,
USE_PROXY = 305,
TEMPORARY_REDIRECT = 307,
// 4xx client error
BAD_REQUEST = 400,
UNAUTHORIZED = 401,
PAYMENT_REQUIRED = 402,
FORBIDDEN = 403,
NOT_FOUND = 404,
METHOD_NOT_ALLOWED = 405,
NOT_ACCEPTABLE = 406,
PROXY_AUTHENTICATION_REQUIRED = 407,
REQUEST_TIMEOUT = 408,
CONFLICT = 409,
GONE = 410,
LENGTH_REQUIRED = 411,
PRECONDITION_FAILED = 412,
REQUEST_ENTITY_TOO_LARGE = 413,
REQUEST_URI_TOO_LONG = 414,
UNSUPPORTED_MEDIA_TYPE = 415,
REQUESTED_RANGE_NOT_SATISFIABLE = 416,
EXPECTATION_FAILED = 417,
UNPROCESSABLE_ENTITY = 422,
LOCKED = 423,
FAILED_DEPENDENCY = 424,
UPGRADE_REQUIRED = 426,
// 5xx server error
INTERNAL_SERVER_ERROR = 500,
NOT_IMPLEMENTED = 501,
BAD_GATEWAY = 502,
SERVICE_UNAVAILABLE = 503,
GATEWAY_TIMEOUT = 504,
HTTP_VERSION_NOT_SUPPORTED = 505,
INSUFFICIENT_STORAGE = 507,
NOT_EXTENDED = 510,
};
// Exception class
class Wobbly
{
public:
Wobbly( const char* fmt, ... );
const char* what() const
{ return m_Message; }
protected:
enum { MAXLEN=256 };
char m_Message[ MAXLEN ];
};
//-------------------------------------------------
// Connection
//
// Handles the socket connection, issuing of requests and managing
// responses.
// ------------------------------------------------
class Connection
{
friend class Response;
public:
// doesn't connect immediately
Connection( const char* host, int port );
~Connection();
// Set up the response handling callbacks. These will be invoked during
// calls to pump().
// begincb - called when the responses headers have been received
// datacb - called repeatedly to handle body data
// completecb - response is completed
// userdata is passed as a param to all callbacks.
void setcallbacks(
ResponseBegin_CB begincb,
ResponseData_CB datacb,
ResponseComplete_CB completecb,
void* userdata );
// Don't need to call connect() explicitly as issuing a request will
// call it automatically if needed.
// But it could block (for name lookup etc), so you might prefer to
// call it in advance.
void connect();
// close connection, discarding any pending requests.
void close();
// Update the connection (non-blocking)
// Just keep calling this regularly to service outstanding requests.
void pump();
// any requests still outstanding?
bool outstanding() const
{ return !m_Outstanding.empty(); }
// ---------------------------
// high-level request interface
// ---------------------------
// method is "GET", "POST" etc...
// url is only path part: eg "/index.html"
// headers is array of name/value pairs, terminated by a null-ptr
// body & bodysize specify body data of request (eg values for a form)
void request( const char* method, const char* url, const char* headers[]=0,
const unsigned char* body=0, int bodysize=0 );
// ---------------------------
// low-level request interface
// ---------------------------
// begin request
// method is "GET", "POST" etc...
// url is only path part: eg "/index.html"
void putrequest( const char* method, const char* url );
// Add a header to the request (call after putrequest() )
void putheader( const char* header, const char* value );
void putheader( const char* header, int numericvalue ); // alternate version
// Finished adding headers, issue the request.
void endheaders();
// send body data if any.
// To be called after endheaders()
void send( const unsigned char* buf, int numbytes );
protected:
// some bits of implementation exposed to Response class
// callbacks
ResponseBegin_CB m_ResponseBeginCB;
ResponseData_CB m_ResponseDataCB;
ResponseComplete_CB m_ResponseCompleteCB;
void* m_UserData;
private:
enum { IDLE, REQ_STARTED, REQ_SENT } m_State;
std::string m_Host;
int m_Port;
int m_Sock;
std::vector< std::string > m_Buffer; // lines of request
std::deque< Response* > m_Outstanding; // responses for outstanding requests
};
//-------------------------------------------------
// Response
//
// Handles parsing of response data.
// ------------------------------------------------
class Response
{
friend class Connection;
public:
// retrieve a header (returns 0 if not present)
const char* getheader( const char* name ) const;
bool completed() const
{ return m_State == COMPLETE; }
// get the HTTP status code
int getstatus() const;
// get the HTTP response reason string
const char* getreason() const;
// true if connection is expected to close after this response.
bool willclose() const
{ return m_WillClose; }
protected:
// interface used by Connection
// only Connection creates Responses.
Response( const char* method, Connection& conn );
// pump some data in for processing.
// Returns the number of bytes used.
// Will always return 0 when response is complete.
int pump( const unsigned char* data, int datasize );
// tell response that connection has closed
void notifyconnectionclosed();
private:
enum {
STATUSLINE, // start here. status line is first line of response.
HEADERS, // reading in header lines
BODY, // waiting for some body data (all or a chunk)
CHUNKLEN, // expecting a chunk length indicator (in hex)
CHUNKEND, // got the chunk, now expecting a trailing blank line
TRAILERS, // reading trailers after body.
COMPLETE, // response is complete!
} m_State;
Connection& m_Connection; // to access callback ptrs
std::string m_Method; // req method: "GET", "POST" etc...
// status line
std::string m_VersionString; // HTTP-Version
int m_Version; // 10: HTTP/1.0 11: HTTP/1.x (where x>=1)
int m_Status; // Status-Code
std::string m_Reason; // Reason-Phrase
// header/value pairs
std::map<std::string,std::string> m_Headers;
int m_BytesRead; // body bytes read so far
bool m_Chunked; // response is chunked?
int m_ChunkLeft; // bytes left in current chunk
int m_Length; // -1 if unknown
bool m_WillClose; // connection will close at response end?
std::string m_LineBuf; // line accumulation for states that want it
std::string m_HeaderAccum; // accumulation buffer for headers
void FlushHeader();
void ProcessStatusLine( std::string const& line );
void ProcessHeaderLine( std::string const& line );
void ProcessTrailerLine( std::string const& line );
void ProcessChunkLenLine( std::string const& line );
int ProcessDataChunked( const unsigned char* data, int count );
int ProcessDataNonChunked( const unsigned char* data, int count );
void BeginBody();
bool CheckClose();
void Finish();
};
} // end namespace happyhttp
#endif // HAPPYHTTP_H
|