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 )