Class: Unicorn::HttpParser
- Inherits:
-
Object
- Object
- Unicorn::HttpParser
- Defined in:
- ext/unicorn_http/unicorn_http.c
Constant Summary collapse
- CHUNK_MAX =
The maximum size a single chunk when using chunked transfer encoding. This is only a theoretical maximum used to detect errors in clients, it is highly unlikely to encounter clients that send more than several kilobytes at once.
OFFT2NUM(UH_OFF_T_MAX)
- LENGTH_MAX =
The maximum size of the body as specified by Content-Length. This is only a theoretical maximum, the actual limit is subject to the limits of the file system used for
Dir.tmpdir
. OFFT2NUM(UH_OFF_T_MAX)
Instance Method Summary collapse
-
#body_eof? ⇒ Boolean
Detects if we’re done filtering the body or not.
-
#content_length ⇒ nil, Integer
Returns the number of bytes left to run through HttpParser#filter_body.
-
#filter_body(buf, data) ⇒ Object
Takes a String of
data
, will modify data if dechunking is done. -
#headers(req, data) ⇒ nil
Takes a Hash and a String of data, parses the String of data filling in the Hash returning the Hash if parsing is finished, nil otherwise When returning the req Hash, it may modify data to point to where body processing should begin.
-
#headers? ⇒ Boolean
This should be used to detect if a request has headers (and if the response will have headers as well).
-
#new ⇒ Object
constructor
Creates a new parser.
-
#keepalive? ⇒ Boolean
This should be used to detect if a request can really handle keepalives and pipelining.
-
#reset ⇒ nil
Resets the parser to it’s initial state so that you can reuse it rather than making new ones.
-
#headers(req, data) ⇒ nil
Takes a Hash and a String of data, parses the String of data filling in the Hash returning the Hash if parsing is finished, nil otherwise When returning the req Hash, it may modify data to point to where body processing should begin.
Constructor Details
#new ⇒ Object
Creates a new parser.
3195 3196 3197 3198 3199 3200 |
# File 'ext/unicorn_http/unicorn_http.c', line 3195
static VALUE HttpParser_init(VALUE self)
{
http_parser_init(data_get(self));
return self;
}
|
Instance Method Details
#body_eof? ⇒ Boolean
Detects if we’re done filtering the body or not. This can be used to detect when to stop calling HttpParser#filter_body.
3306 3307 3308 3309 3310 3311 3312 3313 3314 |
# File 'ext/unicorn_http/unicorn_http.c', line 3306
static VALUE HttpParser_body_eof(VALUE self)
{
struct http_parser *hp = data_get(self);
if (HP_FL_TEST(hp, CHUNKED))
return chunked_eof(hp) ? Qtrue : Qfalse;
return hp->len.content == 0 ? Qtrue : Qfalse;
}
|
#content_length ⇒ nil, Integer
Returns the number of bytes left to run through HttpParser#filter_body. This will initially be the value of the “Content-Length” HTTP header after header parsing is complete and will decrease in value as HttpParser#filter_body is called for each chunk. This should return zero for requests with no body.
This will return nil on “Transfer-Encoding: chunked” requests.
3244 3245 3246 3247 3248 3249 |
# File 'ext/unicorn_http/unicorn_http.c', line 3244
static VALUE HttpParser_content_length(VALUE self)
{
struct http_parser *hp = data_get(self);
return HP_FL_TEST(hp, CHUNKED) ? Qnil : OFFT2NUM(hp->len.content);
}
|
#filter_body(buf, data) ⇒ Object
Takes a String of data
, will modify data if dechunking is done. Returns nil
if there is more data left to process. Returns data
if body processing is complete. When returning data
, it may modify data
so the start of the string points to where the body ended so that trailer processing can begin.
Raises HttpParserError if there are dechunking errors. Basically this is a glorified memcpy(3) that copies data
into buf
while filtering it through the dechunker.
3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 |
# File 'ext/unicorn_http/unicorn_http.c', line 3363
static VALUE HttpParser_filter_body(VALUE self, VALUE buf, VALUE data)
{
struct http_parser *hp = data_get(self);
char *dptr;
long dlen;
rb_str_update(data);
dptr = RSTRING_PTR(data);
dlen = RSTRING_LEN(data);
StringValue(buf);
rb_str_resize(buf, dlen); /* we can never copy more than dlen bytes */
OBJ_TAINT(buf); /* keep weirdo $SAFE users happy */
if (HP_FL_TEST(hp, CHUNKED)) {
if (!chunked_eof(hp)) {
hp->s.dest_offset = 0;
http_parser_execute(hp, buf, dptr, dlen);
if (hp->cs == http_parser_error)
rb_raise(eHttpParserError, "Invalid HTTP format, parsing fails.");
assert(hp->s.dest_offset <= hp->start.offset &&
"destination buffer overflow");
advance_str(data, hp->start.offset);
rb_str_set_len(buf, hp->s.dest_offset);
if (RSTRING_LEN(buf) == 0 && chunked_eof(hp)) {
assert(hp->len.chunk == 0 && "chunk at EOF but more to parse");
} else {
data = Qnil;
}
}
} else {
/* no need to enter the Ragel machine for unchunked transfers */
assert(hp->len.content >= 0 && "negative Content-Length");
if (hp->len.content > 0) {
long nr = MIN(dlen, hp->len.content);
memcpy(RSTRING_PTR(buf), dptr, nr);
hp->len.content -= nr;
if (hp->len.content == 0)
hp->cs = http_parser_first_final;
advance_str(data, nr);
rb_str_set_len(buf, nr);
data = Qnil;
}
}
hp->start.offset = 0; /* for trailer parsing */
return data;
}
|
#headers(req, data) ⇒ nil
Takes a Hash and a String of data, parses the String of data filling in the Hash returning the Hash if parsing is finished, nil otherwise When returning the req Hash, it may modify data to point to where body processing should begin.
Raises HttpParserError if there are parsing errors.
3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 |
# File 'ext/unicorn_http/unicorn_http.c', line 3271
static VALUE HttpParser_headers(VALUE self, VALUE req, VALUE data)
{
struct http_parser *hp = data_get(self);
rb_str_update(data);
http_parser_execute(hp, req, RSTRING_PTR(data), RSTRING_LEN(data));
VALIDATE_MAX_LENGTH(hp->start.offset, HEADER);
if (hp->cs == http_parser_first_final ||
hp->cs == http_parser_en_ChunkedBody) {
advance_str(data, hp->start.offset + 1);
hp->start.offset = 0;
return req;
}
if (hp->cs == http_parser_error)
rb_raise(eHttpParserError, "Invalid HTTP format, parsing fails.");
return Qnil;
}
|
#headers? ⇒ Boolean
This should be used to detect if a request has headers (and if the response will have headers as well). HTTP/0.9 requests should return false, all subsequent HTTP versions will return true
3342 3343 3344 3345 3346 3347 |
# File 'ext/unicorn_http/unicorn_http.c', line 3342
static VALUE HttpParser_has_headers(VALUE self)
{
struct http_parser *hp = data_get(self);
return HP_FL_TEST(hp, HASHEADER) ? Qtrue : Qfalse;
}
|
#keepalive? ⇒ Boolean
This should be used to detect if a request can really handle keepalives and pipelining. Currently, the rules are:
-
MUST be a GET or HEAD request
-
MUST be HTTP/1.1
or
HTTP/1.0 with “Connection: keep-alive” -
MUST NOT have “Connection: close” set
3327 3328 3329 3330 3331 3332 |
# File 'ext/unicorn_http/unicorn_http.c', line 3327
static VALUE HttpParser_keepalive(VALUE self)
{
struct http_parser *hp = data_get(self);
return HP_FL_ALL(hp, KEEPALIVE) ? Qtrue : Qfalse;
}
|
#reset ⇒ nil
Resets the parser to it’s initial state so that you can reuse it rather than making new ones.
3209 3210 3211 3212 3213 3214 |
# File 'ext/unicorn_http/unicorn_http.c', line 3209
static VALUE HttpParser_reset(VALUE self)
{
http_parser_init(data_get(self));
return Qnil;
}
|
#headers(req, data) ⇒ nil
Takes a Hash and a String of data, parses the String of data filling in the Hash returning the Hash if parsing is finished, nil otherwise When returning the req Hash, it may modify data to point to where body processing should begin.
Raises HttpParserError if there are parsing errors.
3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 |
# File 'ext/unicorn_http/unicorn_http.c', line 3271
static VALUE HttpParser_headers(VALUE self, VALUE req, VALUE data)
{
struct http_parser *hp = data_get(self);
rb_str_update(data);
http_parser_execute(hp, req, RSTRING_PTR(data), RSTRING_LEN(data));
VALIDATE_MAX_LENGTH(hp->start.offset, HEADER);
if (hp->cs == http_parser_first_final ||
hp->cs == http_parser_en_ChunkedBody) {
advance_str(data, hp->start.offset + 1);
hp->start.offset = 0;
return req;
}
if (hp->cs == http_parser_error)
rb_raise(eHttpParserError, "Invalid HTTP format, parsing fails.");
return Qnil;
}
|