11
11
// - the copyright holder be liable for any direct, indirect, incidental or -
12
12
// - special damages arising in any way out of the use of this software. -
13
13
// ---------------------------------------------------------------------------
14
// - copyright (c) 1999-2012 amaury darsch -
14
// - copyright (c) 1999-2015 amaury darsch -
15
15
// ---------------------------------------------------------------------------
17
17
#include "Regex.hpp"
18
18
#include "Vector.hpp"
19
19
#include "Strvec.hpp"
20
#include "Boolean.hpp"
20
21
#include "Utility.hpp"
21
22
#include "Runnable.hpp"
22
23
#include "QuarkZone.hpp"
55
56
static const String HTTP_CONN_ATTR = "Connection";
56
57
// the http connection value
57
58
static const String HTTP_CONN_XDEF = "close";
59
// the cookie attribute
60
static const String HTTP_COOK_ATTR = "Cookie";
58
61
// the form url encoded type
59
62
static const String HTTP_CTYP_FORM = "application/x-www-form-urlencoded";
60
// the maximum pos content-length with form
61
static const long HTTP_CLEN_MSIZ = 8192;
63
// the multipart/form data type
64
static const String HTTP_CTYP_PART = "multipart/form-data";
63
66
// this procedure returns the default request uri by method
64
67
static String http_rmth_ruri (const String rmth) {
98
101
// this procedure parse a query string if any
99
102
static String http_parse_query (const String& rmth, const String& ruri,
100
const Plist& head, InputStream& is) {
103
const Plist& head, const Buffer* rbuf) {
101
104
// prepare result
103
106
// eventually normalize the request uri
114
117
// process the post method
115
118
if (rmth == HTTP_RMTH_POST) {
119
// check for valid buffer
120
if (rbuf == nilp) return result;
116
121
// check if the content type is application/x-www-form-urlencoded
117
122
if (head.exists (HTTP_CTYP_ATTR) == false) return result;
118
123
if (head.getpval (HTTP_CTYP_ATTR) != HTTP_CTYP_FORM) return result;
119
// check if we have a content length
120
if (head.exists (HTTP_CLEN_ATTR) == false) return result;
124
// use the buffer as the content string
125
result = rbuf->tostring ();
130
// this procedure read the content into a buffer
131
static Buffer* http_read_content (const Plist& head, InputStream& is) {
132
Buffer* result = nilp;
134
// get the content length
135
if (head.exists (HTTP_CLEN_ATTR) == false) return nilp;
121
136
long clen = head.tolong (HTTP_CLEN_ATTR);
122
if (clen > HTTP_CLEN_MSIZ) {
123
throw Exception ("http-error", "content length is too large");
125
// read the string as the content length
127
if (is.copy (rbuf, clen) != clen) {
128
throw Exception ("http-error", "cannot extract port query string");
138
if (clen <= 0) return nilp;
139
// allocate a buffer by size
140
char* data = new char[clen];
141
// copy the stream into the buffer
142
if (is.copy (data, clen) != clen) {
144
throw Exception ("http-error",
145
"inconsistent content length in request");
147
result = new Buffer (clen, clen, data);
156
// this procedure check if a content-type is multipart and get the boundary
157
static String http_get_multipart_boundary (const Plist& head) {
158
// check for a content type
159
if (head.exists (HTTP_CTYP_ATTR) == false) return "";
160
// get the content type
161
String cval = head.getpval (HTTP_CTYP_ATTR);
162
Strvec pvec = Strvec::split (cval, ";", false);
163
if (pvec.length () != 2) return "";
164
String part = pvec.get(0).strip ();
165
if (part != HTTP_CTYP_PART) return "";
166
Strvec bvec = Strvec::split (pvec.get(1).strip (), "=", false);
167
if (bvec.length () != 2) return "";
168
if (bvec.get(0).strip () != "boundary") return "";
169
return bvec.get(1).strip ();
136
172
// -------------------------------------------------------------------------
156
194
// create a http request by uri
158
196
HttpRequest::HttpRequest (const Uri& uri) : HttpProto (HTTP_CLIENT) {
159
198
// reset protocol
161
200
// set the request data
162
201
d_rmth = HTTP_RMTH_XDEF;
163
202
d_ruri = http_uri_ruri (uri);
164
203
// set the default header
165
hset (HTTP_HOST_ATTR, uri.getauth ());
166
hset (HTTP_CONN_ATTR, HTTP_CONN_XDEF);
204
HeaderPart::hset (HTTP_HOST_ATTR, uri.getauth ());
205
HeaderPart::hset (HTTP_CONN_ATTR, HTTP_CONN_XDEF);
169
208
// create a http request by method and uri name
171
210
HttpRequest::HttpRequest (const String& rmth,
172
211
const String& ruri) : HttpProto (HTTP_CLIENT) {
173
213
// reset protocol
175
215
// set the request data
182
222
HttpRequest::HttpRequest (const String& rmth,
183
223
const Uri& uri) : HttpProto (HTTP_CLIENT) {
184
225
// reset protocol
186
227
// set the request data
188
229
d_ruri = http_uri_ruri (uri);
189
230
// set the default header
190
hset (HTTP_HOST_ATTR, uri.getauth ());
191
hset (HTTP_CONN_ATTR, HTTP_CONN_XDEF);
231
HeaderPart::hset (HTTP_HOST_ATTR, uri.getauth ());
232
HeaderPart::hset (HTTP_CONN_ATTR, HTTP_CONN_XDEF);
194
235
HttpRequest::HttpRequest (InputStream& is) : HttpProto (HTTP_SERVER) {
284
334
// read the header
285
335
if (rlen == 3) hparse (is);
286
// read the query string if any
287
d_rqry = http_parse_query (d_rmth, d_ruri, d_head, is);
336
// fill the request buffer
337
p_rbuf = http_read_content (d_head, is);
338
Object::iref (p_rbuf);
288
339
// unlock and return
390
441
String HttpRequest::getrqry (void) const {
393
String result = d_rqry;
444
String result = http_parse_query (d_rmth, d_ruri, d_head, p_rbuf);
454
// check if a cookie is defined
456
bool HttpRequest::iscookie (void) const {
459
bool result = d_head.exists (HTTP_COOK_ATTR);
468
// get a cookie object from the header
470
String HttpRequest::getcookie (void) const {
473
// check for a cookie string
474
if (d_head.exists (HTTP_COOK_ATTR) == false) {
475
throw Exception ("http-error", "cannot find cookie in request header");
477
// get the cookie header string
478
String result = d_head.getpval (HTTP_COOK_ATTR);
487
// get the request buffer if any
489
Buffer* HttpRequest::getrbuf (void) const {
492
Buffer* result = p_rbuf;
501
MultiPart* HttpRequest::getmpart (void) {
504
// check for a boundary string
505
String bnds = http_get_multipart_boundary (d_head);
506
// get the multipart content
507
MultiPart* result = nilp;
508
if ((bnds.isnil () == false) && (p_rbuf != nilp)) {
509
result = new MultiPart (bnds, *p_rbuf);
413
530
static const long QUARK_SETRURI = zone.intern ("set-uri");
414
531
static const long QUARK_GETRURI = zone.intern ("get-uri");
415
532
static const long QUARK_GETRQRY = zone.intern ("get-query");
533
static const long QUARK_ISCOOKP = zone.intern ("cookie-p");
534
static const long QUARK_GETCOOK = zone.intern ("get-cookie");
535
static const long QUARK_GETRBUF = zone.intern ("get-buffer");
536
static const long QUARK_GETMPRT = zone.intern ("get-multipart-content");
417
538
// create a new object in a generic way
479
600
// dispatch 0 argument
481
if (quark == QUARK_GETRMTH) return new String (getrmth ());
482
if (quark == QUARK_GETRURI) return new String (getruri ());
483
if (quark == QUARK_GETRQRY) return new String (getrqry ());
602
if (quark == QUARK_GETRMTH) return new String (getrmth ());
603
if (quark == QUARK_GETRURI) return new String (getruri ());
604
if (quark == QUARK_GETRQRY) return new String (getrqry ());
605
if (quark == QUARK_ISCOOKP) return new Boolean (iscookie ());
606
if (quark == QUARK_GETCOOK) return new String (getcookie ());
607
if (quark == QUARK_GETMPRT) return getmpart ();
608
if (quark == QUARK_GETRBUF) {
611
Object* result = getrbuf ();
485
621
// dispatch 1 argument