HTTP-Parser2-XS

 view release on metacpan or  search on metacpan

http_parser.c  view on Meta::CPAN

{
  const char *x, *y;
  if (len1 != len2)
    return 0;
  for (x = str1, y = str2; len1 != 0; --len1, ++x, ++y)
    if (tol(*x) != *y)
      return 0;
  return 1;
}

static SV* 
str_svnv(const char *buf, size_t len)
{
    SV          *sv;
    const char  *c;
    size_t       i;

    if (buf == NULL || len == 0 || len > 15) {
	return &PL_sv_undef;
    }

    sv = newSVnv(0);
    if (sv == NULL) {
	return &PL_sv_undef;
    }

    c = buf;
    for (i = 0; i < len; i++, c++) {
	if (*c >= '0' && *c <= '9') {
	    SvNV_set(sv, SvNVX(sv) * 10 + (*c - '0'));
	} else if (SvNVX(sv) > 0) {
	    break;
	} else {
	    goto failed;
	}
    }

    return sv;

failed:

    SvREFCNT_dec(sv);
    return &PL_sv_undef;
}


int
parse_http_request(SV* buf, SV* envref) 
{
  const char* buf_str;
  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);
  if (ret < 0)
    goto failed;
  
  env = (HV*)SvRV(envref);
 
  hv_store(env, "_method", sizeof("_method") - 1,
           newSVpvn(method, method_len), 0);
  hv_store(env, "_request_uri", sizeof("_request_uri") - 1,
	   newSVpvn(path, path_len), 0);

  path_len = find_ch(path, path_len, '#'); /* backported from 
					      HTTP-Parser-XS-0.14 */
  question_at = find_ch(path, path_len, '?');
  if (store_url_decoded(env, "_uri", sizeof("_uri") - 1, path,
			question_at)
      != 0) {
    hv_clear(env);
    ret = -1;
    goto failed;
  }
  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 */
      SV *rv, *sv;

      if (*(headers[i].name) == '_') {
	ret = -1;
	goto failed;
      } else {
	const char* s;
	char* d;
	size_t n;
        if (sizeof(tmp) < headers[i].name_len) {
	  hv_clear(env);
          ret = -1;
          goto failed;
        }
        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);
      if ( !slot ) {
        warn("failed to create hash entry");
	ret = -1;
	goto failed;
      }

      if (SvOK(*slot)) {
	av = (AV *) SvRV(*slot);
        sv = newSV(128);

	SvPOK_only(sv);

	SvGROW(sv, headers[i].value_len + 1);
        sv_setpvn(sv, headers[i].value, headers[i].value_len);

	av_push(av, sv);
      } else {
        av = newAV();
        rv = sv_2mortal(newRV_noinc((SV *) av));
        sv = newSV(128);

	SvPOK_only(sv);

	SvGROW(sv, headers[i].value_len + 1);
        sv_setpvn(sv, headers[i].value, headers[i].value_len);

	av_push(av, sv);
	SvSetSV(*slot, rv);
      }

      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;
}



int
parse_http_response(SV* buf, SV* envref) 
{
  int minor_version, status;
  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, 
			   &msg_len, headers, &num_headers, last_len);
  if (ret < 0)
    goto done;
  
  env = (HV*)SvRV(envref);
 
  hv_store(env, "_message", sizeof("_message") - 1,
           newSVpvn(msg, msg_len), 0);

  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 */
      SV *rv, *sv;

      if (*(headers[i].name) == '_') {
	ret = -1;
	goto done;
      } else {
	const char* s;
	char* d;
	size_t n;
        if (sizeof(tmp) < headers[i].name_len) {
	  hv_clear(env);
          ret = -1;
          goto done;
        }
        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);
      if ( !slot ) {
        warn("failed to create hash entry");
	ret = -2;
	goto done;
      }

      if (SvOK(*slot)) {
	av = (AV *) SvRV(*slot);
        sv = newSV(128);

	SvPOK_only(sv);

	SvGROW(sv, headers[i].value_len + 1);
        sv_setpvn(sv, headers[i].value, headers[i].value_len);

	av_push(av, sv);
      } else {
        av = newAV();
        rv = sv_2mortal(newRV_noinc((SV *) av));
        sv = newSV(128);

	SvPOK_only(sv);

	SvGROW(sv, headers[i].value_len + 1);
        sv_setpvn(sv, headers[i].value, headers[i].value_len);

	av_push(av, sv);
	SvSetSV(*slot, rv);
      }

      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;
}





( run in 0.493 second using v1.01-cache-2.11-cpan-71847e10f99 )