HTTP-Parser2-XS

 view release on metacpan or  search on metacpan

MANIFEST  view on Meta::CPAN

http_parser.c
http_parser.h
lib/HTTP/Parser2/XS.pm
Makefile.PL
MANIFEST			This list of files
parser2.h
ppport.h
README
t/00-use.t
t/01-simple.t
t/02-request-keepalive.t
t/03-request-rv.t
t/04-response-rv.t
t/05-response-keepalive.t
TODO
XS.xs
META.yml                                 Module meta-data (added by MakeMaker)

README  view on Meta::CPAN

        $r->{'_uri'}
            Decoded request uri without query string. A lot like $uri in
            nginx.

        $r->{'_query_string'}
            Query string. Everything after question mark.

        $r->{'_protocol'}
            Protocol and version. Either "HTTP/1.0" or "HTTP/1.1".

        $r->{'_keepalive'}
            Either 1 or 0. Examines connection header and protocol version
            to decide whether or not keep-alive connection is desired. And
            if it is sets "$r->{'_keepalive'}" to 1.

        $r->{'_content_length'}
            Parses content-length header. Stores length as a numeric value
            (SvNV to be precise) or undef if there is no content-length
            header.

    $rv = parse_http_response($buf, $r)
        Parses HTTP response in $buf into the hashref $r. Returns length of
        the header on success, -1 on error and -2 if response isn't complete
        yet, i.e. doesn't have an entire header. Converts each header name

README  view on Meta::CPAN

        $r->{'_protocol'}
            Protocol and version. Either "HTTP/1.0" or "HTTP/1.1".

        $r->{'_status'}
            Response status. For example "200" for "HTTP/1.0 200 OK"
            response.

        $r->{'_message'}
            Status message. For example "OK" for "HTTP/1.0 200 OK" response.

        $r->{'_keepalive'}
            Either 1 or 0. Examines connection header and protocol version
            to decide whether or not keep-alive connection is desired. And
            if it is sets "$r->{'_keepalive'}" to 1.

        $r->{'_content_length'}
            Parses content-length header. Stores length as a numeric value
            (SvNV to be precise) or undef if there is no content-length
            header.

SEE ALSO
    HTTP::Parser::XS

AUTHOR

TODO  view on Meta::CPAN


	- HTTP tests

	- ditch HTTP::Parser::XS code and use nginx-like parser
	  instead;

	- make _keepalive flag more sophisticated, i.e. disable keepalive
	  on POSTs for buggy user agents, like nginx does;

eg/example1.pl  view on Meta::CPAN

#!/usr/bin/perl

use strict;
use warnings;

use HTTP::Parser2::XS;

{
    my $buf = "GET /a%20s?foo=bar HTTP/1.0\x0d\x0a".
              "Host: localhost\x0d\x0a".
              "Connection: keep-alive\x0d\x0a".
              "\x0d\x0a";

    my $r = {};
    my $rv = parse_http_request($buf, $r);

    if ($rv == -1) {
        # bad request or internal error
    } elsif ($rv == -2) {
        # incomplete request, call again when there is more data
        # in the buffer

eg/example1.pl  view on Meta::CPAN

        print << "        END";

        rv           = $rv

        method       = "$r->{'_method'}"
        request_uri  = "$r->{'_request_uri'}"
        uri          = "$r->{'_uri'}"
        query_string = "$r->{'_query_string'}"
        protocol     = "$r->{'_protocol'}"

        keepalive    = $r->{'_keepalive'}
        
        END
    }

};

{
    my $buf = "HTTP/1.0 200 OK\x0d\x0a".
              "Host: localhost\x0d\x0a".
              "Content-Type: text/html\x0d\x0a".

eg/example1.pl  view on Meta::CPAN

        # $rv contains the length of the request header on success

        print << "        END";

        rv           = $rv

        protocol     = "$r->{'_protocol'}"
        status       = "$r->{'_status'}"
        message      = "$r->{'_message'}"

        keepalive    = $r->{'_keepalive'}
        
        END
    }


};

http_parser.c  view on Meta::CPAN

  STRLEN buf_len;
  const char* method;
  size_t method_len;
  const char* path;
  size_t path_len;
  int minor_version;
  struct phr_header headers[MAX_HEADERS];
  size_t num_headers, question_at;
  size_t i;
  int ret;
  int keepalive = 0;
  HV* env;
  SV* last_value;
  char *content_length_str = NULL;
  size_t content_length_strlen = 0;
  char tmp[MAX_HEADER_NAME_LEN + sizeof("HTTP_") - 1];
  
  buf_str = SvPV(buf, buf_len);
  num_headers = MAX_HEADERS;
  ret = phr_parse_request(buf_str, buf_len, &method, &method_len, &path,
			  &path_len, &minor_version, headers, &num_headers, 0);

http_parser.c  view on Meta::CPAN

  if (question_at != path_len)
    ++question_at;
  hv_store(env, "_query_string", sizeof("_query_string") - 1,
	   newSVpvn(path + question_at, path_len - question_at), 0);

  sprintf(tmp, "HTTP/1.%d", minor_version);
  hv_store(env, "_protocol", sizeof("_protocol") - 1,
           newSVpv(tmp, 0), 0);

  if (minor_version == 1) {
    keepalive = 1;
  }

  last_value = NULL;
  for (i = 0; i < num_headers; ++i) {
    if (headers[i].name != NULL) {
      const char* name;
      size_t name_len;
      SV** slot;

      AV *av;      /* for arrayref */

http_parser.c  view on Meta::CPAN

        }
        for (s = headers[i].name, n = headers[i].name_len, d = tmp;
	     n != 0;
	     s++, --n, d++)
          *d = tol(*s);
        name = tmp;
        name_len = headers[i].name_len;

	if (str_eq(name, name_len, "connection", sizeof("connection") - 1)) {
	  if (str_lc_eq(headers[i].value, 
			 headers[i].value_len > (sizeof("keep-alive") - 1) 
			    ? (sizeof("keep-alive") - 1) 
			    : headers[i].value_len,
			 "keep-alive", sizeof("keep-alive") - 1)) {
	    keepalive = 1;
	  } else {
	    keepalive = 0;
	  }
	}

	if (str_eq(name, name_len, "content-length", 
		    sizeof("content-length") - 1)) {
	  content_length_str = (char *) headers[i].value;
	  content_length_strlen = headers[i].value_len;
	}
      }
      slot = hv_fetch(env, name, name_len, 1);

http_parser.c  view on Meta::CPAN

      }

      last_value = sv;
    } else {
      /* continuing lines of a mulitiline header */
      sv_catpvn(last_value, headers[i].value, headers[i].value_len);
    }
  }

  if (ret > 0) {
    hv_store(env, "_keepalive", sizeof("_keepalive") - 1,
             newSViv(keepalive), 0);
    hv_store(env, "_content_length", sizeof("_content_length") - 1,
             str_svnv(content_length_str, content_length_strlen), 0);
  }

failed:

  return ret;
}


http_parser.c  view on Meta::CPAN

  const char* msg;
  size_t msg_len;
  struct phr_header headers[MAX_HEADERS];
  size_t num_headers = MAX_HEADERS;
  STRLEN buf_len;
  const char *buf_str;
  size_t last_len = 0;
  size_t i;
  char name[MAX_HEADER_NAME_LEN]; /* temp buffer for normalized names */
  int ret;
  int keepalive = 0;
  HV* env;
  SV* last_value;
  char *content_length_str = NULL;
  size_t content_length_strlen = 0;
  char tmp[MAX_HEADER_NAME_LEN + sizeof("HTTP_") - 1];

  buf_str = SvPV(buf, buf_len);
  num_headers = MAX_HEADERS;

  ret = phr_parse_response(buf_str, buf_len, &minor_version, &status, &msg, 

http_parser.c  view on Meta::CPAN


  sprintf(tmp, "HTTP/1.%d", minor_version);
  hv_store(env, "_protocol", sizeof("_protocol") - 1,
           newSVpv(tmp, 0), 0);

  sprintf(tmp, "%d", status);
  hv_store(env, "_status", sizeof("_status") - 1,
           newSVpv(tmp, 0), 0);

  if (minor_version == 1) {
    keepalive = 1;
  }

  last_value = NULL;
  for (i = 0; i < num_headers; ++i) {
    if (headers[i].name != NULL) {
      const char* name;
      size_t name_len;
      SV** slot;

      AV *av;      /* for arrayref */

http_parser.c  view on Meta::CPAN

        }
        for (s = headers[i].name, n = headers[i].name_len, d = tmp;
	     n != 0;
	     s++, --n, d++)
          *d = tol(*s);
        name = tmp;
        name_len = headers[i].name_len;

	if (str_eq(name, name_len, "connection", sizeof("connection") - 1)) {
	  if (str_lc_eq(headers[i].value, 
			 headers[i].value_len > (sizeof("keep-alive") - 1) 
			    ? (sizeof("keep-alive") - 1) 
			    : headers[i].value_len,
			 "keep-alive", sizeof("keep-alive") - 1)) {
	    keepalive = 1;
	  } else {
	    keepalive = 0;
	  }
	}

	if (str_eq(name, name_len, "content-length", 
		    sizeof("content-length") - 1)) {
	  content_length_str = (char *) headers[i].value;
	  content_length_strlen = headers[i].value_len;
	}

      }

http_parser.c  view on Meta::CPAN

      }

      last_value = sv;
    } else {
      /* continuing lines of a mulitiline header */
      sv_catpvn(last_value, headers[i].value, headers[i].value_len);
    }
  }

  if (ret > 0) {
    hv_store(env, "_keepalive", sizeof("_keepalive") - 1,
             newSViv(keepalive), 0);
    hv_store(env, "_content_length", sizeof("_content_length") - 1,
             str_svnv(content_length_str, content_length_strlen), 0);
  }

done:

  return ret;
}


lib/HTTP/Parser2/XS.pm  view on Meta::CPAN

Decoded request uri without query string. A lot like C<$uri> in nginx.

=item $r->{'_query_string'}

Query string. Everything after question mark.

=item $r->{'_protocol'}

Protocol and version. Either "HTTP/1.0" or "HTTP/1.1". 

=item $r->{'_keepalive'}

Either C<1> or C<0>. Examines connection header and protocol version to 
decide whether or not keep-alive connection is desired. And if it is 
sets C<< $r->{'_keepalive'} >> to C<1>. 

=item $r->{'_content_length'}

Parses content-length header. Stores length as a numeric value 
(SvNV to be precise) or undef if there is no content-length header.

=back

=item $rv = parse_http_response($buf, $r)

lib/HTTP/Parser2/XS.pm  view on Meta::CPAN

Protocol and version. Either "HTTP/1.0" or "HTTP/1.1". 

=item $r->{'_status'}

Response status. For example "200" for "HTTP/1.0 200 OK" response.

=item $r->{'_message'}

Status message. For example "OK" for "HTTP/1.0 200 OK" response.

=item $r->{'_keepalive'}

Either C<1> or C<0>. Examines connection header and protocol version to 
decide whether or not keep-alive connection is desired. And if it is 
sets C<< $r->{'_keepalive'} >> to C<1>. 

=item $r->{'_content_length'}

Parses content-length header. Stores length as a numeric value 
(SvNV to be precise) or undef if there is no content-length header.

=back

=back

t/01-simple.t  view on Meta::CPAN


use Test::More tests => 9;

BEGIN { 
    use_ok('HTTP::Parser2::XS') 
};

{
    my $buf = "GET /a%20s HTTP/1.0\x0d\x0a".
              "Host: localhost\x0d\x0a".
              "Connection: keep-alive\x0d\x0a".
              "Content-Length: 16000000000\x0d\x0a".
              "\x0d\x0a";
    my $r = {};
    parse_http_request($buf, $r);

    ok exists $r->{'host'}, "host header exists";
    ok $r->{'host'}->[0] eq 'localhost', "correct host";
    ok $r->{'_uri'} eq '/a s', "correct _uri (url_decoded)";
    ok exists $r->{'content-length'}, "has content length header";
    ok $r->{'_content_length'}, "has _content_length";

t/02-request-keepalive.t  view on Meta::CPAN


use Test::More tests => 6;

BEGIN { 
    use_ok('HTTP::Parser2::XS') 
};

{
    my $buf = "GET / HTTP/1.0\x0d\x0a".
              "Host: localhost\x0d\x0a".
              "Connection: keep-alive\x0d\x0a".
              "\x0d\x0a";
    my $r = {};
    parse_http_request($buf, $r);

    ok $r->{'_keepalive'} == 1, "keepalive (connection: keep-alive)";
};

{
    my $buf = "GET / HTTP/1.0\x0d\x0a".
              "Host: localhost\x0d\x0a".
              "Connection: Keep-Alive, asdf\x0d\x0a".
              "\x0d\x0a";
    my $r = {};
    parse_http_request($buf, $r);

    ok $r->{'_keepalive'} == 1, "keepalive (connection: Keep-Alive, asdf)";
};

{
    my $buf = "GET / HTTP/1.1\x0d\x0a".
              "Host: localhost\x0d\x0a".
              "\x0d\x0a";
    my $r = {};
    parse_http_request($buf, $r);

    ok $r->{'_keepalive'} == 1, "keepalive (HTTP/1.1)";
};



{
    my $buf = "GET / HTTP/1.1\x0d\x0a".
              "Host: localhost\x0d\x0a".
              "Connection: close, qwerrrr\x0d\x0a".
              "\x0d\x0a";
    my $r = {};
    parse_http_request($buf, $r);

    ok $r->{'_keepalive'} == 0, "keepalive (HTTP/1.1, connection: close)";
};

{
    my $buf = "GET / HTTP/1.0\x0d\x0a".
              "Host: localhost\x0d\x0a".
              "Connection: close, asfdasdf\x0d\x0a".
              "\x0d\x0a";
    my $r = {};
    parse_http_request($buf, $r);

    ok $r->{'_keepalive'} == 0, "keepalive (HTTP/1.0, connection: close)";
};


t/03-request-rv.t  view on Meta::CPAN


use Test::More tests => 5;

BEGIN { 
    use_ok('HTTP::Parser2::XS') 
};

{
    my $buf = "GET / HTTP/1.0\x0d\x0a".
              "Host: localhost\x0d\x0a".
              "Connection: keep-alive\x0d\x0a".
              "\x0d\x0a";
    my $r = {};
    my $rv = parse_http_request($buf, $r);

    ok $rv > 0, "rv > 0, complete request";
};

{
    my $buf = "GET / HTTP/1.0\x0d\x0a".
              "Host: local";

t/03-request-rv.t  view on Meta::CPAN

              "\x0d\x0a";
    my $r = {};
    my $rv = parse_http_request($buf, $r);

    ok $rv == -1, "rv == -1, bad request" or diag "rv = $rv";
};

{
    my $buf = "GET / HTTP/1.0\x0d\x0a".
              "Host: localhost\x0d\x0a".
              "Connection".("x" x 1024).": keep-alive\x0d\x0a".
              "\x0d\x0a";    my $r = {};
    my $rv = parse_http_request($buf, $r);

    ok $rv == -1, "rv == -1, too long header" or diag "rv = $rv";
};


t/04-response-rv.t  view on Meta::CPAN

use warnings;

use Test::More tests => 5;

BEGIN { 
    use_ok('HTTP::Parser2::XS') 
};

{
    my $buf = "HTTP/1.0 200 OK\x0d\x0a".
              "Connection: keep-alive\x0d\x0a".
              "\x0d\x0a";
    my $r = {};
    my $rv = parse_http_response($buf, $r);

    ok $rv > 0, "rv > 0, complete response";
};

{
    my $buf = "HTTP/1.0 200 OK\x0d\x0a".
              "Host: local";

t/04-response-rv.t  view on Meta::CPAN

    my $buf = "BOOMBOOMZCZXXZXCCZX\x0d\x0a".
              "\x0d\x0a";
    my $r = {};
    my $rv = parse_http_response($buf, $r);

    ok $rv == -1, "rv == -1, bad response" or diag "rv = $rv";
};

{
    my $buf = "HTTP/1.0 200 OK\x0d\x0a".
              "Connection".("x" x 1024).": keep-alive\x0d\x0a".
              "\x0d\x0a";
    my $r = {};
    my $rv = parse_http_response($buf, $r);

    ok $rv == -1, "rv == -1, too long header" or diag "rv = $rv";
};


t/05-response-keepalive.t  view on Meta::CPAN


use Test::More tests => 6;

BEGIN { 
    use_ok('HTTP::Parser2::XS') 
};

{
    my $buf = "HTTP/1.0 200 OK\x0d\x0a".
              "Host: localhost\x0d\x0a".
              "Connection: keep-alive\x0d\x0a".
              "\x0d\x0a";
    my $r = {};
    parse_http_response($buf, $r);

    ok $r->{'_keepalive'} == 1, "keepalive (connection: keep-alive)";
};

{
    my $buf = "HTTP/1.0 200 OK\x0d\x0a".
              "Host: localhost\x0d\x0a".
              "Connection: Keep-Alive, asdf\x0d\x0a".
              "\x0d\x0a";
    my $r = {};
    parse_http_response($buf, $r);

    ok $r->{'_keepalive'} == 1, "keepalive (connection: Keep-Alive, asdf)";
};

{
    my $buf = "HTTP/1.1 200 OK\x0d\x0a".
              "Host: localhost\x0d\x0a".
              "\x0d\x0a";
    my $r = {};
    parse_http_response($buf, $r);

    ok $r->{'_keepalive'} == 1, "keepalive (HTTP/1.1)";
};



{
    my $buf = "HTTP/1.1 200 OK\x0d\x0a".
              "Host: localhost\x0d\x0a".
              "Connection: close, qwerrrr\x0d\x0a".
              "\x0d\x0a";
    my $r = {};
    parse_http_response($buf, $r);

    ok $r->{'_keepalive'} == 0, "keepalive (HTTP/1.1, connection: close)";
};

{
    my $buf = "HTTP/1.0 200 OK\x0d\x0a".
              "Host: localhost\x0d\x0a".
              "Connection: close, asfdasdf\x0d\x0a".
              "\x0d\x0a";
    my $r = {};
    parse_http_response($buf, $r);

    ok $r->{'_keepalive'} == 0, "keepalive (HTTP/1.0, connection: close)";
};




( run in 2.153 seconds using v1.01-cache-2.11-cpan-71847e10f99 )