#include "headers.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
using
namespace
std;
int
skip_spaces(
char
**ptr) {
int
i = 0;
while
(**ptr ==
' '
) {
i++;
(*ptr)++;
}
return
i;
}
int
skip_to_space(
char
**ptr) {
int
i = 0;
while
(**ptr !=
' '
&& **ptr !=
'\0'
) {
i++;
(*ptr)++;
}
return
i;
}
int
skip_to_eol(
char
**ptr) {
int
i = 0;
while
(**ptr !=
'\n'
&& **ptr !=
'\0'
) {
if
(**ptr !=
'\r'
)
i++;
(*ptr)++;
}
if
(**ptr ==
'\n'
)
(*ptr)++;
return
i;
}
int
skip_to_colon(
char
**ptr) {
int
i = 0;
while
(**ptr !=
':'
) {
if
(**ptr ==
'\r'
|| **ptr ==
'\n'
|| **ptr ==
'\0'
)
return
0;
i++;
(*ptr)++;
}
if
(**ptr ==
':'
)
(*ptr)++;
return
i;
}
int
parseVersionNumber(
char
*ptr,
char
**newptr) {
int
i = 0, j = 0;
while
(
isdigit
(ptr[i])) i++;
if
(i == 0 || i > 4 || ptr[i] !=
'.'
)
return
0;
while
(
isdigit
(ptr[i+j+1])) j++;
if
(j == 0 || j > 4)
return
0;
*newptr = (ptr + i + j + 1);
int
ret =
atoi
(ptr) * 1000 +
atoi
(ptr+i+1);
return
ret;
}
HTTPHeaders::HTTPHeaders() {
versionNumber = 0;
statusCode = 0;
headersType = 0;
method = 0;
sv_uri = NULL;
sv_firstLine = NULL;
sv_methodString = NULL;
hdrs = NULL;
hdrtail = NULL;
}
HTTPHeaders::~HTTPHeaders() {
if
(sv_uri) {
SvREFCNT_dec(sv_uri);
}
if
(sv_firstLine) {
SvREFCNT_dec(sv_firstLine);
}
if
(sv_methodString) {
SvREFCNT_dec(sv_methodString);
}
Header *next = NULL;
while
(hdrs) {
next = hdrs->next;
freeHeader(hdrs);
hdrs = next;
}
}
void
HTTPHeaders::freeHeader(Header *hdr) {
Safefree(hdr->key);
SvREFCNT_dec(hdr->sv_value);
Safefree(hdr);
}
int
HTTPHeaders::parseHeaders(SV *headers) {
if
(!SvROK(headers))
return
0;
int
state = 0;
int
len = 0;
char
*initial = SvPV_nolen(SvRV(headers));
char
*pptr, *ptr = initial;
Header *lasthdr = NULL;
while
(*ptr !=
'\0'
) {
if
(state == 0) {
if
(!
strncmp
(ptr,
"HTTP/"
, 5)) {
headersType = H_RESPONSE;
versionNumber = parseVersionNumber(ptr + 5, &ptr);
if
(versionNumber <= 0)
return
0;
skip_spaces(&ptr);
statusCode =
strtol
(ptr, &ptr, 10);
skip_to_eol(&ptr);
len = ptr - initial;
while
(initial[len - 1] ==
'\r'
|| initial[len - 1] ==
'\n'
)
len--;
sv_firstLine = newSVpvn(initial, len);
if
(!sv_firstLine)
return
0;
state = 1;
continue
;
}
headersType = H_REQUEST;
if
(!
strncmp
(ptr,
"GET "
, 4)) {
ptr += 4;
method = M_GET;
}
else
if
(!
strncmp
(ptr,
"POST "
, 5)) {
ptr += 5;
method = M_POST;
}
else
if
(!
strncmp
(ptr,
"HEAD "
, 5)) {
ptr += 5;
method = M_HEAD;
}
else
if
(!
strncmp
(ptr,
"OPTIONS "
, 8)) {
ptr += 8;
method = M_OPTIONS;
}
else
if
(!
strncmp
(ptr,
"PUT "
, 4)) {
ptr += 4;
method = M_PUT;
}
else
if
(!
strncmp
(ptr,
"DELETE "
, 7)) {
ptr += 7;
method = M_DELETE;
}
else
{
pptr = ptr;
len = skip_to_space(&ptr);
if
(len) {
sv_methodString = newSVpvn(pptr, len);
if
(!sv_methodString)
return
0;
}
else
{
return
0;
}
skip_spaces(&ptr);
}
pptr = ptr;
len = skip_to_space(&ptr);
if
(len) {
sv_uri = newSVpvn(pptr, len);
if
(!sv_uri)
return
0;
}
skip_spaces(&ptr);
if
(!
strncmp
(ptr,
"HTTP/"
, 5)) {
versionNumber = parseVersionNumber(ptr + 5, &ptr);
if
(versionNumber <= 0)
return
0;
skip_to_eol(&ptr);
len = ptr - initial;
while
(initial[len - 1] ==
'\r'
|| initial[len - 1] ==
'\n'
)
len--;
sv_firstLine = newSVpvn(initial, len);
if
(!sv_firstLine)
return
0;
}
else
{
return
0;
}
state = 1;
}
else
if
(state == 1) {
if
(*ptr ==
' '
|| *ptr ==
'\t'
) {
if
(!lasthdr)
return
0;
pptr = ptr;
len = skip_to_eol(&ptr);
if
(!len)
return
0;
sv_catpv(lasthdr->sv_value,
"\r\n"
);
sv_catpvn(lasthdr->sv_value, pptr, len);
continue
;
}
if
(*ptr ==
'\r'
|| *ptr ==
'\n'
|| *ptr ==
'\0'
)
return
1;
pptr = ptr;
len = skip_to_colon(&ptr);
if
(!len)
return
0;
skip_spaces(&ptr);
Header *hdr = findHeader(pptr, len);
if
(hdr) {
if
(isRequest() || strncasecmp(hdr->key,
"Set-Cookie"
, len)) {
pptr = ptr;
len = skip_to_eol(&ptr);
sv_catpvn(hdr->sv_value,
", "
, 2);
sv_catpvn(hdr->sv_value, pptr, len);
continue
;
}
}
New(0, hdr, 1, Header);
if
(!hdr)
return
0;
Poison(hdr, 1, Header);
hdr->keylen = len;
hdr->prev = NULL;
hdr->next = NULL;
hdr->key = NULL;
hdr->sv_value = NULL;
hdrtail = hdr;
Newz(0, hdr->key, len + 1,
char
);
if
(!hdr->key)
return
0;
Copy(pptr, hdr->key, len,
char
);
pptr = ptr;
len = skip_to_eol(&ptr);
hdr->sv_value = newSVpvn(pptr, len);
if
(!hdr->sv_value)
return
0;
if
(lasthdr) {
hdr->prev = lasthdr;
lasthdr->next = hdr;
lasthdr = hdr;
}
else
{
hdrs = hdr;
lasthdr = hdr;
}
}
}
return
state;
}
SV *HTTPHeaders::getReconstructed() {
SV *res = newSVpvn(
""
, 0);
if
(!res)
return
&PL_sv_undef;
SvGROW(res, 768);
if
(!sv_firstLine) {
SvREFCNT_dec(res);
return
&PL_sv_undef;
}
sv_catsv(res, sv_firstLine);
sv_catpv(res,
"\r\n"
);
for
(Header *cur = hdrs; cur; cur = cur->next) {
if
(!cur->key) {
SvREFCNT_dec(res);
return
&PL_sv_undef;
}
sv_catpv(res, cur->key);
sv_catpv(res,
": "
);
if
(!cur->sv_value) {
SvREFCNT_dec(res);
return
&PL_sv_undef;
}
sv_catsv(res, cur->sv_value);
sv_catpv(res,
"\r\n"
);
}
sv_catpv(res,
"\r\n"
);
return
res;
}
Header *HTTPHeaders::findHeader(
char
*which,
int
len) {
if
(!which)
return
NULL;
int
wlen = len ? len :
strlen
(which);
if
(!wlen)
return
NULL;
for
(Header *cur = hdrs; cur; cur = cur->next) {
if
(wlen != cur->keylen)
continue
;
if
(!strncasecmp(cur->key, which, wlen))
return
cur;
}
return
NULL;
}
SV *HTTPHeaders::getHeader(
char
*which) {
Header *hdr = findHeader(which);
if
(!hdr)
return
&PL_sv_undef;
SvREFCNT_inc(hdr->sv_value);
return
hdr->sv_value;
}
void
HTTPHeaders::setHeader(
char
*which,
char
*value) {
Header *hdr = findHeader(which);
int
vlen = value ?
strlen
(value) : 0;
if
(vlen) {
if
(!hdr) {
New(0, hdr, 1, Header);
if
(!hdr)
return
;
Poison(hdr, 1, Header);
hdr->key = NULL;
hdr->keylen = 0;
hdr->prev = NULL;
hdr->next = NULL;
hdr->sv_value = NULL;
if
(hdrtail) {
hdrtail->next = hdr;
hdr->prev = hdrtail;
}
if
(!hdrs)
hdrs = hdr;
hdrtail = hdr;
}
if
(hdr->sv_value) {
SvREFCNT_dec(hdr->sv_value);
}
hdr->sv_value = newSVpvn(value, vlen);
if
(!hdr->sv_value)
return
;
if
(hdr->key) {
Safefree(hdr->key);
}
int
wlen =
strlen
(which);
Newz(0, hdr->key, wlen + 1,
char
);
Copy(which, hdr->key, wlen,
char
);
hdr->keylen = wlen;
}
else
{
if
(!hdr)
return
;
if
(hdr->prev) {
hdr->prev->next = hdr->next;
}
else
{
hdrs = hdr->next;
}
if
(hdr->next) {
hdr->next->prev = hdr->prev;
}
else
{
hdrtail = hdr->prev;
}
freeHeader(hdr);
}
}
int
HTTPHeaders::getMethod() {
return
method;
}
SV *HTTPHeaders::getMethodString() {
if
(sv_methodString) {
SvREFCNT_inc(sv_methodString);
return
sv_methodString;
}
else
return
&PL_sv_undef;
}
int
HTTPHeaders::getStatusCode() {
return
statusCode;
}
int
HTTPHeaders::getVersionNumber() {
return
versionNumber;
}
bool
HTTPHeaders::isRequest() {
return
headersType == H_REQUEST;
}
bool
HTTPHeaders::isResponse() {
return
headersType == H_RESPONSE;
}
SV *HTTPHeaders::getURI() {
if
(sv_uri) {
SvREFCNT_inc(sv_uri);
return
sv_uri;
}
else
return
&PL_sv_undef;
}
SV *HTTPHeaders::getHeadersList() {
if
(hdrs) {
AV *header_names = (AV*) sv_2mortal((SV*)newAV());
for
(Header *cur = hdrs; cur; cur = cur->next) {
av_push(header_names, newSVpv(cur->key, cur->keylen));
}
return
newRV((SV*)header_names);
}
else
return
&PL_sv_undef;
}
SV *HTTPHeaders::setURI(
char
*uri) {
int
urilen = uri ?
strlen
(uri) : 0;
SV *temp_uri = newSVpvn(uri, urilen);
if
(!temp_uri)
return
&PL_sv_undef;
const
char
*methodstr;
switch
(method) {
case
M_GET:
methodstr =
"GET"
;
break
;
case
M_POST:
methodstr =
"POST"
;
break
;
case
M_OPTIONS:
methodstr =
"OPTIONS"
;
break
;
case
M_PUT:
methodstr =
"PUT"
;
break
;
case
M_DELETE:
methodstr =
"DELETE"
;
break
;
case
M_HEAD:
methodstr =
"HEAD"
;
break
;
default
:
if
(sv_methodString) {
methodstr = SvPV_nolen(sv_methodString);
break
;
}
else
return
&PL_sv_undef;
}
SV *temp_firstLine;
if
(versionNumber)
temp_firstLine = newSVpvf(
"%s %s HTTP/%d.%d"
,
methodstr, uri,
int
(versionNumber / 1000), versionNumber % 1000);
else
temp_firstLine = newSVpvf(
"%s %s"
,
methodstr, uri);
if
(sv_uri)
SvREFCNT_dec(sv_uri);
sv_uri = temp_uri;
if
(sv_firstLine)
SvREFCNT_dec(sv_firstLine);
sv_firstLine = temp_firstLine;
SvREFCNT_inc(sv_uri);
return
sv_uri;
}
void
HTTPHeaders::setStatusCode(
int
code) {
statusCode = code;
}
void
HTTPHeaders::setCodeText(
int
code,
char
*codetext) {
if
(isRequest())
return
;
if
(statusCode == code)
return
;
if
(!sv_firstLine)
return
;
statusCode = code;
SV *temp = newSVpvf(
"HTTP/%d.%d %d %s"
,
int
(versionNumber / 1000), versionNumber % 1000, code, codetext);
SvREFCNT_dec(sv_firstLine);
sv_firstLine = temp;
}
void
HTTPHeaders::setVersionNumber(
int
version) {
if
(!sv_firstLine)
return
;
SV *temp = newSVpvf(
"HTTP/%d.%d"
,
int
(version / 1000), version % 1000);
char
*initial = SvPV_nolen(sv_firstLine);
char
*ptr = initial;
if
(isResponse()) {
skip_to_space(&ptr);
sv_catpv(temp, ptr);
}
else
{
skip_to_space(&ptr);
skip_spaces(&ptr);
skip_to_space(&ptr);
skip_spaces(&ptr);
SV *temp2 = newSVpvn(initial, ptr - initial);
sv_catsv(temp2, temp);
SvREFCNT_dec(temp);
temp = temp2;
}
SvREFCNT_dec(sv_firstLine);
sv_firstLine = temp;
versionNumber = version;
}