#include "proxy.h"
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <string.h>
#include "../util/pblock.h"
#include "../proxy/httpclient.h"
static cxstring get_uri_from_clfreq(
const char *clfreq) {
cxstring uri = {
NULL,
0 };
const char *begin =
NULL;
const char *str = clfreq;
for(;*str !=
'\0';str++) {
if(*str <
33) {
if(begin) {
str++;
break;
}
}
else {
if(!begin) {
begin = str;
}
}
}
begin =
NULL;
for(;*str !=
'\0';str++) {
if(*str >
32) {
if(!begin) {
begin = str;
}
}
else {
if(begin) {
break;
}
}
}
if(begin && *str !=
'\0') {
return cx_strn(begin, str-begin);
}
return uri;
}
typedef struct ProxyRequest {
Session *sn;
Request *rq;
pblock *request_header_rewrite;
pblock *response_header_rewrite;
int response_started;
} ProxyRequest;
static int proxy_response_start(HttpClient *client,
int status,
char *message,
void *userdata) {
ProxyRequest *proxy = userdata;
HeaderArray *headers = client->response_headers;
while(headers) {
for(
int i=
0;i<headers->len;i++) {
cxmutstr name = headers->headers[i].name;
cxmutstr value = headers->headers[i].value;
for(
int c=
0;c<name.length;c++) {
name.ptr[c] = tolower(name.ptr[c]);
}
name.ptr[name.length] =
0;
char *rewrite = pblock_findval(name.ptr, proxy->response_header_rewrite);
if(rewrite) {
value = cx_mutstr(rewrite);
if(value.length ==
0) {
continue;
}
}
pblock_nvlinsert(name.ptr, name.length, value.ptr, value.length, proxy->rq->srvhdrs);
}
headers = headers->next;
}
protocol_status(proxy->sn, proxy->rq, status, message);
protocol_start_response(proxy->sn, proxy->rq);
proxy->response_started =
1;
return 0;
}
static void proxy_response_finished(HttpClient *client,
void *userdata) {
ProxyRequest *proxy = userdata;
int ret =
REQ_PROCEED;
if(!proxy->response_started) {
protocol_status(proxy->sn, proxy->rq,
502,
NULL);
ret =
REQ_ABORTED;
}
http_client_free(client);
nsapi_function_return(proxy->sn, proxy->rq, ret);
}
static ssize_t proxy_request_read(HttpClient *client,
void *buf,
size_t nbytes,
void *userdata) {
ProxyRequest *proxy = userdata;
int ret = netbuf_getbytes(proxy->sn->inbuf, buf, nbytes);
if(ret ==
NETBUF_EOF) {
ret =
0;
}
return ret;
}
static ssize_t proxy_response_write(HttpClient *client,
void *buf,
size_t nbytes,
void *userdata) {
ProxyRequest *proxy = userdata;
ssize_t ret = net_write(proxy->sn->csd, buf, nbytes);
return ret;
}
int http_reverse_proxy_service(pblock *param, Session *sn, Request *rq) {
EventHandler *ev = sn->ev;
const char *method = pblock_findkeyval(pb_key_method, rq->reqpb);
const char *clfreq = pblock_findkeyval(pb_key_clf_request, rq->reqpb);
cxstring uri = get_uri_from_clfreq(clfreq);
if(uri.length ==
0) {
return REQ_ABORTED;
}
pblock_removekey(pb_key_content_type, rq->srvhdrs);
ProxyRequest *proxy = pool_malloc(sn->pool,
sizeof(ProxyRequest));
proxy->sn = sn;
proxy->rq = rq;
proxy->request_header_rewrite = pblock_create_pool(sn->pool,
16);
proxy->response_header_rewrite = pblock_create_pool(sn->pool,
16);
proxy->response_started =
0;
pblock_nvinsert(
"host",
"", proxy->request_header_rewrite);
pblock_nvinsert(
"connection",
"", proxy->request_header_rewrite);
pblock_nvinsert(
"transfer-encoding",
"", proxy->request_header_rewrite);
pblock_nvinsert(
"content-length",
"", proxy->request_header_rewrite);
pblock_nvinsert(
"server",
"", proxy->response_header_rewrite);
pblock_nvinsert(
"connection",
"", proxy->response_header_rewrite);
HttpClient *client = http_client_new(ev);
if(!client) {
return REQ_ABORTED;
}
if(http_client_set_method(client, method)) {
http_client_free(client);
return REQ_ABORTED;
}
if(http_client_set_uri_len(client, uri.ptr, uri.length)) {
http_client_free(client);
return REQ_ABORTED;
}
struct sockaddr_in address;
inet_pton(
AF_INET,
"127.0.0.1", &address.sin_addr);
address.sin_family =
AF_INET;
address.sin_port = htons(
8080);
http_client_set_addr(client, (
struct sockaddr*)&address,
sizeof(address));
http_client_add_request_header(client, cx_mutstr(
"host"), cx_mutstr(
"localhost:8080"));
CxIterator i = pblock_iterator(rq->headers);
cx_foreach(pb_entry*, entry, i) {
cxmutstr header_value;
char *rewrite_header = pblock_findval(entry->param->name, proxy->request_header_rewrite);
if(rewrite_header) {
if(!strcmp(entry->param->name,
"transfer-encoding")) {
if(!strcmp(entry->param->value,
"chunked")) {
if(http_client_enable_chunked_transfer_encoding(client)) {
http_client_free(client);
return REQ_ABORTED;
}
continue;
}
}
else if(!strcmp(entry->param->name,
"content-length")) {
long long contentlength;
if(!cx_strtoll(cx_str(entry->param->value), &contentlength,
10)) {
if(http_client_set_content_length(client, contentlength)) {
http_client_free(client);
return REQ_ABORTED;
}
}
else {
protocol_status(sn, rq,
400,
NULL);
http_client_free(client);
return REQ_ABORTED;
}
continue;
}
else {
header_value = cx_mutstr(rewrite_header);
if(header_value.length ==
0) {
continue;
}
}
}
else {
header_value = cx_mutstr(entry->param->value);
}
if(http_client_add_request_header(client, cx_mutstr(entry->param->name), header_value)) {
http_client_free(client);
return REQ_ABORTED;
}
}
client->request_body_read = proxy_request_read;
client->request_body_read_userdata = proxy;
client->response_start = proxy_response_start;
client->response_start_userdata = proxy;
client->response_body_write = proxy_response_write;
client->response_body_write_userdata = proxy;
if(http_client_start(client)) {
http_client_free(client);
return REQ_ABORTED;
}
return REQ_PROCESSING;
}
static CX_TEST(test_safs_proxy_get_uri_from_clfreq) {
CX_TEST_DO {
cxstring ret;
ret = get_uri_from_clfreq(
"GET /uri HTTP/1.1");
CX_TEST_ASSERT(!cx_strcmp(ret,
"/uri"));
ret = get_uri_from_clfreq(
"G / HTTP/1.1");
CX_TEST_ASSERT(!cx_strcmp(ret,
"/"));
ret = get_uri_from_clfreq(
"POST /test%20/path HTTP/1.1");
CX_TEST_ASSERT(!cx_strcmp(ret,
"/test%20/path"));
ret = get_uri_from_clfreq(
" GET /leading_space HTTP/1.1");
CX_TEST_ASSERT(!cx_strcmp(ret,
"/leading_space"));
ret = get_uri_from_clfreq(
" PROPFIND /space2 HTTP/1.1");
CX_TEST_ASSERT(!cx_strcmp(ret,
"/space2"));
ret = get_uri_from_clfreq(
"HEAD /trailing_space HTTP/1.1");
CX_TEST_ASSERT(!cx_strcmp(ret,
"/trailing_space"));
ret = get_uri_from_clfreq(
" GET /space3 HTTP/1.1 ");
CX_TEST_ASSERT(!cx_strcmp(ret,
"/space3"));
ret = get_uri_from_clfreq(
"");
CX_TEST_ASSERT(ret.ptr ==
NULL);
ret = get_uri_from_clfreq(
" ");
CX_TEST_ASSERT(ret.ptr ==
NULL);
ret = get_uri_from_clfreq(
"GET");
CX_TEST_ASSERT(ret.ptr ==
NULL);
ret = get_uri_from_clfreq(
"GET /path");
CX_TEST_ASSERT(ret.ptr ==
NULL);
ret = get_uri_from_clfreq(
" /path2/ ");
CX_TEST_ASSERT(ret.ptr ==
NULL);
}
}
void http_reverse_proxy_add_tests(CxTestSuite *suite) {
cx_test_register(suite, test_safs_proxy_get_uri_from_clfreq);
}