Yet Another HTTP Library
yahttp
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
reqresp.hpp
Go to the documentation of this file.
1 #ifdef HAVE_CXX11
2 #include <functional>
3 #define HAVE_CPP_FUNC_PTR
4 namespace funcptr = std;
5 #else
6 #ifdef HAVE_BOOST
7 #include <boost/function.hpp>
8 namespace funcptr = boost;
9 #define HAVE_CPP_FUNC_PTR
10 #endif
11 #endif
12 
13 #include <fstream>
14 #include <cctype>
15 
16 #ifndef WIN32
17 #include <cstdio>
18 #include <unistd.h>
19 #endif
20 
21 #include <algorithm>
22 
23 #ifndef YAHTTP_MAX_REQUEST_SIZE
24 #define YAHTTP_MAX_REQUEST_SIZE 2097152
25 #endif
26 
27 #ifndef YAHTTP_MAX_RESPONSE_SIZE
28 #define YAHTTP_MAX_RESPONSE_SIZE 2097152
29 #endif
30 
31 #define YAHTTP_TYPE_REQUEST 1
32 #define YAHTTP_TYPE_RESPONSE 2
33 
34 namespace YaHTTP {
35  typedef std::map<std::string,Cookie,ASCIICINullSafeComparator> strcookie_map_t; //<! String to Cookie map
36 
37  typedef enum {
40  } postformat_t; //<! Enumeration of possible post encodings, url encoding or multipart
41 
43  class HTTPBase {
44  public:
47  public:
49 
50  size_t operator()(const HTTPBase *doc, std::ostream& os, bool chunked) const {
51  if (chunked) {
52  std::string::size_type i,cl;
53  for(i=0;i<doc->body.length();i+=1024) {
54  cl = std::min(static_cast<std::string::size_type>(1024), doc->body.length()-i); // for less than 1k blocks
55  os << std::hex << cl << std::dec << "\r\n";
56  os << doc->body.substr(i, cl) << "\r\n";
57  }
58  os << 0 << "\r\n\r\n"; // last chunk
59  } else {
60  os << doc->body;
61  }
62  return doc->body.length();
63  }; //<! writes body to ostream and returns length
64  };
65  /* Simple sendfile renderer which streams file to ostream */
67  public:
68  SendFileRender(const std::string& path) {
69  this->path = path;
70  };
71 
72  size_t operator()(const HTTPBase *doc __attribute__((unused)), std::ostream& os, bool chunked) const {
73  char buf[4096];
74  size_t n,k;
75 #ifdef HAVE_CXX11
76  std::ifstream ifs(path, std::ifstream::binary);
77 #else
78  std::ifstream ifs(path.c_str(), std::ifstream::binary);
79 #endif
80  n = 0;
81 
82  std::cerr << "Sending file " << path << std::endl;
83  while(ifs && ifs.good()) {
84  ifs.read(buf, sizeof buf);
85  n += (k = ifs.gcount());
86  if (k) {
87  if (chunked) os << std::hex << k << std::dec << "\r\n";
88  os.write(buf, k);
89  if (chunked) os << "\r\n";
90  }
91  }
92  if (chunked) os << 0 << "\r\n\r\n";
93  return n;
94  }; //<! writes file to ostream and returns length
95 
96  std::string path; //<! File to send
97  };
98 
100  initialize();
101  };
102 
103  virtual void initialize() {
104  kind = 0;
105  status = 0;
106 #ifdef HAVE_CPP_FUNC_PTR
108 #endif
111  url = "";
112  method = "";
113  statusText = "";
114  jar.clear();
115  headers.clear();
116  parameters.clear();
117  getvars.clear();
118  postvars.clear();
119  body = "";
120  routeName = "";
121  version = 11; // default to version 1.1
122  }
123 protected:
124  HTTPBase(const HTTPBase& rhs) {
125  this->url = rhs.url; this->kind = rhs.kind;
126  this->status = rhs.status; this->statusText = rhs.statusText;
127  this->method = rhs.method; this->headers = rhs.headers;
128  this->jar = rhs.jar; this->postvars = rhs.postvars;
129  this->parameters = rhs.parameters; this->getvars = rhs.getvars;
130  this->body = rhs.body; this->max_request_size = rhs.max_request_size;
131  this->max_response_size = rhs.max_response_size; this->version = rhs.version;
132 #ifdef HAVE_CPP_FUNC_PTR
133  this->renderer = rhs.renderer;
134 #endif
135  };
136  HTTPBase& operator=(const HTTPBase& rhs) {
137  this->url = rhs.url; this->kind = rhs.kind;
138  this->status = rhs.status; this->statusText = rhs.statusText;
139  this->method = rhs.method; this->headers = rhs.headers;
140  this->jar = rhs.jar; this->postvars = rhs.postvars;
141  this->parameters = rhs.parameters; this->getvars = rhs.getvars;
142  this->body = rhs.body; this->max_request_size = rhs.max_request_size;
143  this->max_response_size = rhs.max_response_size; this->version = rhs.version;
144 #ifdef HAVE_CPP_FUNC_PTR
145  this->renderer = rhs.renderer;
146 #endif
147  return *this;
148  };
149 public:
150  URL url; //<! URL of this request/response
151  int kind; //<! Type of object (1 = request, 2 = response)
152  int status; //<! status code
153  int version; //<! http version 9 = 0.9, 10 = 1.0, 11 = 1.1
154  std::string statusText; //<! textual representation of status code
155  std::string method; //<! http verb
156  strstr_map_t headers; //<! map of header(s)
157  CookieJar jar; //<! cookies
158  strstr_map_t postvars; //<! map of POST variables (from POST body)
159  strstr_map_t getvars; //<! map of GET variables (from URL)
160 // these two are for Router
161  strstr_map_t parameters; //<! map of route parameters (only if you use YaHTTP::Router)
162  std::string routeName; //<! name of the current route (only if you use YaHTTP::Router)
163 
164  std::string body; //<! the actual content
165 
166  ssize_t max_request_size; //<! maximum size of request
167  ssize_t max_response_size; //<! maximum size of response
168 
169 #ifdef HAVE_CPP_FUNC_PTR
170  funcptr::function<size_t(const HTTPBase*,std::ostream&,bool)> renderer; //<! rendering function
171 #endif
172  void write(std::ostream& os) const; //<! writes request to the given output stream
173 
174  strstr_map_t& GET() { return getvars; }; //<! acccessor for getvars
175  strstr_map_t& POST() { return postvars; }; //<! accessor for postvars
176  strcookie_map_t& COOKIES() { return jar.cookies; }; //<! accessor for cookies
177 
178  std::string versionStr(int version) const {
179  switch(version) {
180  case 9: return "0.9";
181  case 10: return "1.0";
182  case 11: return "1.1";
183  default: throw YaHTTP::Error("Unsupported version");
184  }
185  };
186 
187  std::string str() const {
188  std::ostringstream oss;
189  write(oss);
190  return oss.str();
191  }; //<! return string representation of this object
192  };
193 
195  class Response: public HTTPBase {
196  public:
198  Response(const HTTPBase& rhs): HTTPBase(rhs) {
199  this->kind = YAHTTP_TYPE_RESPONSE;
200  };
201  Response& operator=(const HTTPBase& rhs) {
202  HTTPBase::operator=(rhs);
203  this->kind = YAHTTP_TYPE_RESPONSE;
204  return *this;
205  };
206  void initialize() {
208  this->kind = YAHTTP_TYPE_RESPONSE;
209  }
210  friend std::ostream& operator<<(std::ostream& os, const Response &resp);
211  friend std::istream& operator>>(std::istream& is, Response &resp);
212  };
213 
214  /* Request class, represents a HTTP Request document */
215  class Request: public HTTPBase {
216  public:
217  Request() { initialize(); };
218  Request(const HTTPBase& rhs): HTTPBase(rhs) {
219  this->kind = YAHTTP_TYPE_REQUEST;
220  };
221  Request& operator=(const HTTPBase& rhs) {
222  HTTPBase::operator=(rhs);
223  this->kind = YAHTTP_TYPE_REQUEST;
224  return *this;
225  };
226  void initialize() {
228  this->kind = YAHTTP_TYPE_REQUEST;
229  }
230  void setup(const std::string& method, const std::string& url) {
231  this->url.parse(url);
232  this->headers["host"] = this->url.host;
233  this->method = method;
234  std::transform(this->method.begin(), this->method.end(), this->method.begin(), ::toupper);
235  this->headers["user-agent"] = "YaHTTP v1.0";
236  }; //<! Set some initial things for a request
237 
239  std::ostringstream postbuf;
240  if (format == urlencoded) {
241  for(strstr_map_t::const_iterator i = POST().begin(); i != POST().end(); i++) {
242  postbuf << Utility::encodeURL(i->first, false) << "=" << Utility::encodeURL(i->second, false) << "&";
243  }
244  // remove last bit
245  if (postbuf.str().length()>0)
246  body = postbuf.str().substr(0, postbuf.str().length()-1);
247  else
248  body = "";
249  headers["content-type"] = "application/x-www-form-urlencoded; charset=utf-8";
250  } else if (format == multipart) {
251  headers["content-type"] = "multipart/form-data; boundary=YaHTTP-12ca543";
252  for(strstr_map_t::const_iterator i = POST().begin(); i != POST().end(); i++) {
253  postbuf << "--YaHTTP-12ca543\r\nContent-Disposition: form-data; name=\"" << Utility::encodeURL(i->first, false) << "; charset=UTF-8\r\n\r\n"
254  << Utility::encodeURL(i->second, false) << "\r\n";
255  }
256  }
257 
258  postbuf.str("");
259  postbuf << body.length();
260  // set method and change headers
261  method = "POST";
262  headers["content-length"] = postbuf.str();
263  }; //<! convert all postvars into string and stuff it into body
264 
265  friend std::ostream& operator<<(std::ostream& os, const Request &resp);
266  friend std::istream& operator>>(std::istream& is, Request &resp);
267  };
268 
270  template <class T>
271  class AsyncLoader {
272  public:
273  T* target; //<! target to populate
274  int state; //<! reader state
275  size_t pos; //<! reader position
276 
277  std::string buffer; //<! read buffer
278  bool chunked; //<! whether we are parsing chunked data
279  int chunk_size; //<! expected size of next chunk
280  std::ostringstream bodybuf; //<! buffer for body
281  size_t maxbody; //<! maximum size of body
282  size_t minbody; //<! minimum size of body
283  bool hasBody; //<! are we expecting body
284 
285  void keyValuePair(const std::string &keyvalue, std::string &key, std::string &value); //<! key value pair parser helper
286 
287  void initialize(T* target) {
288  chunked = false; chunk_size = 0;
289  bodybuf.str(""); maxbody = 0;
290  pos = 0; state = 0; this->target = target;
291  hasBody = false;
292  buffer = "";
293  this->target->initialize();
294  }; //<! Initialize the parser for target and clear state
295  int feed(const std::string& somedata); //<! Feed data to the parser
296  bool ready() {
297  return (chunked == true && state == 3) || // if it's chunked we get end of data indication
298  (chunked == false && state > 1 &&
299  (!hasBody ||
300  (bodybuf.str().size() <= maxbody &&
301  bodybuf.str().size() >= minbody)
302  )
303  );
304  }; //<! whether we have received enough data
305  void finalize() {
306  bodybuf.flush();
307  if (ready()) {
308  strstr_map_t::iterator pos = target->headers.find("content-type");
309  if (pos != target->headers.end() && Utility::iequals(pos->second, "application/x-www-form-urlencoded", 32)) {
310  target->postvars = Utility::parseUrlParameters(bodybuf.str());
311  }
312  target->body = bodybuf.str();
313  }
314  bodybuf.str("");
315  this->target = NULL;
316  }; //<! finalize and release target
317  };
318 
320  class AsyncResponseLoader: public AsyncLoader<Response> {
321  };
322 
324  class AsyncRequestLoader: public AsyncLoader<Request> {
325  };
326 
327 };
#define YAHTTP_MAX_REQUEST_SIZE
Definition: reqresp.hpp:24
std::string path
Definition: reqresp.hpp:94
URL url
Definition: reqresp.hpp:148
std::map< std::string, Cookie, ASCIICINullSafeComparator > cookies
Definition: cookie.hpp:49
void write(std::ostream &os) const
Definition: reqresp.cpp:167
Response(const HTTPBase &rhs)
Definition: reqresp.hpp:198
std::map< std::string, std::string, ASCIICINullSafeComparator > strstr_map_t
Definition: utility.hpp:22
Definition: reqresp.hpp:271
strstr_map_t headers
Definition: reqresp.hpp:156
std::string versionStr(int version) const
Definition: reqresp.hpp:178
SendBodyRender()
Definition: reqresp.hpp:48
int kind
Definition: reqresp.hpp:151
std::string str() const
Definition: reqresp.hpp:187
void initialize()
Definition: reqresp.hpp:206
Definition: exception.hpp:8
static std::string encodeURL(const std::string &component, bool asUrl=true)
Definition: utility.hpp:233
Definition: reqresp.hpp:66
Definition: reqresp.hpp:320
ssize_t max_response_size
Definition: reqresp.hpp:167
#define YAHTTP_MAX_RESPONSE_SIZE
Definition: reqresp.hpp:28
strstr_map_t postvars
Definition: reqresp.hpp:158
#define YAHTTP_TYPE_RESPONSE
Definition: reqresp.hpp:32
Definition: reqresp.hpp:324
friend std::istream & operator>>(std::istream &is, Response &resp)
Definition: reqresp.cpp:248
Definition: reqresp.hpp:195
Request()
Definition: reqresp.hpp:217
void clear()
Definition: cookie.hpp:56
SendFileRender(const std::string &path)
Definition: reqresp.hpp:68
T * target
Definition: reqresp.hpp:273
strstr_map_t parameters
Definition: reqresp.hpp:161
std::ostringstream bodybuf
Definition: reqresp.hpp:280
std::string body
Definition: reqresp.hpp:164
strcookie_map_t & COOKIES()
Definition: reqresp.hpp:176
strstr_map_t & GET()
Definition: reqresp.hpp:174
friend std::ostream & operator<<(std::ostream &os, const Request &resp)
Definition: reqresp.cpp:266
int state
Definition: reqresp.hpp:274
int feed(const std::string &somedata)
Definition: reqresp.cpp:5
Response()
Definition: reqresp.hpp:197
void initialize()
Definition: reqresp.hpp:226
Definition: cookie.hpp:47
bool ready()
Definition: reqresp.hpp:296
int status
Definition: reqresp.hpp:152
strstr_map_t & POST()
Definition: reqresp.hpp:175
void initialize(T *target)
Definition: reqresp.hpp:287
int chunk_size
Definition: reqresp.hpp:279
size_t minbody
Definition: reqresp.hpp:282
strstr_map_t getvars
Definition: reqresp.hpp:159
std::string statusText
Definition: reqresp.hpp:154
size_t operator()(const HTTPBase *doc __attribute__((unused)), std::ostream &os, bool chunked) const
Definition: reqresp.hpp:72
Definition: reqresp.hpp:43
Response & operator=(const HTTPBase &rhs)
Definition: reqresp.hpp:201
Definition: reqresp.hpp:215
postformat_t
Definition: reqresp.hpp:37
#define YAHTTP_TYPE_REQUEST
Definition: reqresp.hpp:31
std::map< std::string, Cookie, ASCIICINullSafeComparator > strcookie_map_t
Definition: reqresp.hpp:35
size_t maxbody
Definition: reqresp.hpp:281
HTTPBase(const HTTPBase &rhs)
Definition: reqresp.hpp:124
void finalize()
Definition: reqresp.hpp:305
void setup(const std::string &method, const std::string &url)
Definition: reqresp.hpp:230
void preparePost(postformat_t format=urlencoded)
Definition: reqresp.hpp:238
HTTPBase()
Definition: reqresp.hpp:99
friend std::istream & operator>>(std::istream &is, Request &resp)
Definition: reqresp.cpp:271
size_t operator()(const HTTPBase *doc, std::ostream &os, bool chunked) const
Definition: reqresp.hpp:50
Definition: reqresp.hpp:39
virtual void initialize()
Definition: reqresp.hpp:103
friend std::ostream & operator<<(std::ostream &os, const Response &resp)
Definition: reqresp.cpp:243
std::string buffer
Definition: reqresp.hpp:277
Definition: reqresp.hpp:38
void keyValuePair(const std::string &keyvalue, std::string &key, std::string &value)
funcptr::function< size_t(const HTTPBase *, std::ostream &, bool)> renderer
Definition: reqresp.hpp:170
HTTPBase & operator=(const HTTPBase &rhs)
Definition: reqresp.hpp:136
static strstr_map_t parseUrlParameters(std::string parameters)
Definition: utility.hpp:349
bool chunked
Definition: reqresp.hpp:278
bool hasBody
Definition: reqresp.hpp:283
Definition: reqresp.hpp:46
std::string method
Definition: reqresp.hpp:155
Request(const HTTPBase &rhs)
Definition: reqresp.hpp:218
CookieJar jar
Definition: reqresp.hpp:157
ssize_t max_request_size
Definition: reqresp.hpp:166
size_t pos
Definition: reqresp.hpp:275
static bool iequals(const std::string &a, const std::string &b, size_t length)
Definition: utility.hpp:388
std::string routeName
Definition: reqresp.hpp:162
int version
Definition: reqresp.hpp:153
Request & operator=(const HTTPBase &rhs)
Definition: reqresp.hpp:221
Definition: url.hpp:14