#include "protocol.h"
#include "session.h"
#include "httplistener.h"
#include "request.h"
#include "../util/pblock.h"
#include "../util/pool.h"
#include "../util/io.h"
#include "../util/util.h"
#include "../util/strbuf.h"
#define HTTP_SCHEME "http"
#define HTTPS_SCHEME "https"
void protocol_status(Session *sn, Request *rq,
int n,
const char *m) {
rq->status_num = n;
const char *msg = m ? m : protocol_status_message(n);
pb_param *pp = pblock_removekey(pb_key_status, rq->srvhdrs);
if (pp !=
NULL) {
param_free(pp);
}
pp = pblock_key_param_create(rq->srvhdrs, pb_key_status, msg, strlen(msg));
pblock_kpinsert(pb_key_status, pp, rq->srvhdrs);
}
NSAPI_PUBLIC const char * protocol_status_message (
int code)
{
WS_ASSERT(code >
0);
const char *r;
switch (code)
{
case PROTOCOL_CONTINUE :
r =
"Continue";
break;
case PROTOCOL_SWITCHING:
r =
"Switching Protocols";
break;
case PROTOCOL_OK:
r =
"OK";
break;
case PROTOCOL_CREATED:
r =
"Created";
break;
case PROTOCOL_ACCEPTED:
r =
"Accepted";
break;
case PROTOCOL_NONAUTHORITATIVE:
r =
"Non-Authoritative Information";
break;
case PROTOCOL_NO_CONTENT:
r =
"No Content";
break;
case PROTOCOL_RESET_CONTENT:
r =
"Reset Content";
break;
case PROTOCOL_PARTIAL_CONTENT:
r =
"Partial Content";
break;
case PROTOCOL_MULTI_STATUS:
r =
"Multi Status";
break;
case PROTOCOL_MULTIPLE_CHOICES:
r =
"Multiple Choices";
break;
case PROTOCOL_MOVED_PERMANENTLY:
r =
"Moved Permanently";
break;
case PROTOCOL_REDIRECT:
r =
"Moved Temporarily";
break;
case PROTOCOL_SEE_OTHER:
r =
"See Other";
break;
case PROTOCOL_NOT_MODIFIED:
r =
"Use local copy";
break;
case PROTOCOL_USE_PROXY:
r =
"Use Proxy";
break;
case PROTOCOL_TEMPORARY_REDIRECT:
r =
"Temporary Redirect";
break;
case PROTOCOL_BAD_REQUEST:
r =
"Bad request";
break;
case PROTOCOL_UNAUTHORIZED:
r =
"Unauthorized";
break;
case PROTOCOL_PAYMENT_REQUIRED:
r =
"Payment Required";
break;
case PROTOCOL_FORBIDDEN:
r =
"Forbidden";
break;
case PROTOCOL_NOT_FOUND:
r =
"Not found";
break;
case PROTOCOL_METHOD_NOT_ALLOWED:
r =
"Method Not Allowed";
break;
case PROTOCOL_NOT_ACCEPTABLE:
r =
"Not Acceptable";
break;
case PROTOCOL_PROXY_UNAUTHORIZED:
r =
"Proxy Authentication Required";
break;
case PROTOCOL_REQUEST_TIMEOUT:
r =
"Request Timeout";
break;
case PROTOCOL_CONFLICT:
r =
"Conflict";
break;
case PROTOCOL_GONE:
r =
"Gone";
break;
case PROTOCOL_LENGTH_REQUIRED:
r =
"Length Required";
break;
case PROTOCOL_PRECONDITION_FAIL:
r =
"Precondition Failed";
break;
case PROTOCOL_ENTITY_TOO_LARGE:
r =
"Request Entity Too Large";
break;
case PROTOCOL_URI_TOO_LARGE:
r =
"Request-URI Too Large";
break;
case PROTOCOL_UNSUPPORTED_MEDIA_TYPE:
r =
"Unsupported Media Type";
break;
case PROTOCOL_REQUESTED_RANGE_NOT_SATISFIABLE:
r =
"Requested range not satisfiable";
break;
case PROTOCOL_EXPECTATION_FAILED:
r =
"Expectation Failed";
break;
case PROTOCOL_LOCKED:
r =
"Locked";
break;
case PROTOCOL_FAILED_DEPENDENCY:
r =
"Failed Dependency";
break;
case PROTOCOL_SERVER_ERROR:
r =
"Server Error";
break;
case PROTOCOL_NOT_IMPLEMENTED:
r =
"Not Implemented";
break;
case PROTOCOL_BAD_GATEWAY:
r =
"Bad Gateway";
break;
case PROTOCOL_SERVICE_UNAVAILABLE:
r =
"Service Unavailable";
break;
case PROTOCOL_GATEWAY_TIMEOUT:
r =
"Gateway Timeout";
break;
case PROTOCOL_VERSION_NOT_SUPPORTED:
r =
"HTTP Version Not Supported";
break;
case PROTOCOL_INSUFFICIENT_STORAGE:
r =
"Insufficient Storage";
break;
default:
switch (code /
100)
{
case 1:
r =
"Information";
break;
case 2:
r =
"Success";
break;
case 3:
r =
"Redirect";
break;
case 4:
r =
"Client error";
break;
case 5:
r =
"Server error";
break;
default:
r =
"Unknown reason";
break;
}
break;
}
return r;
}
void add_http_status_line(CxBuffer *out,
pool_handle_t *pool, Request *rq) {
cxBufferWrite(
"HTTP/1.1 ",
1,
9, out);
char status_code_str[
8];
int sc_len = snprintf(status_code_str,
8,
"%d ", rq->status_num);
cxBufferWrite(status_code_str,
1, sc_len, out);
char *scmsg = pblock_findkeyval(pb_key_status, rq->srvhdrs);
if(scmsg ==
NULL) {
scmsg =
"OK";
}
cxBufferWrite(scmsg,
1, strlen(scmsg), out);
cxBufferWrite(
"\r\n",
1,
2, out);
}
void add_http_response_header(CxBuffer *out, Request *rq) {
pblock *h = rq->srvhdrs;
pb_entry *p;
for(
int i=
0;i<h->hsize;i++) {
p = h->ht[i];
while(p !=
NULL) {
const pb_key *key =
PARAM_KEY(p->param);
if (key == pb_key_status || key == pb_key_server || key == pb_key_date) {
p = p->next;
continue;
}
char *name = p->param->name;
char *value = p->param->value;
size_t name_len = strlen(name);
if(name[
0] >
90) {
cxBufferPut(out, name[
0]-
32);
if(name_len >
1) {
cxBufferWrite(name+
1,
1, name_len-
1, out);
}
}
else {
cxBufferWrite(name,
1, name_len, out);
}
cxBufferWrite(
": ",
1,
2, out);
cxBufferWrite(value,
1, strlen(value), out);
cxBufferWrite(
"\r\n",
1,
2, out);
p = p->next;
}
}
}
struct HttpResponseWriter {
Session *sn;
Request *rq;
CxBuffer buf;
};
HttpResponseWriter *http_create_response(Session *sn, Request *rq) {
HttpResponseWriter *writer = pool_malloc(sn->pool,
sizeof(HttpResponseWriter));
if(!writer) {
return NULL;
}
if(cxBufferInit(&writer->buf,
NULL,
512, pool_allocator(sn->pool),
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS)) {
pool_free(sn->pool, writer);
return NULL;
}
writer->sn = sn;
writer->rq = rq;
if(rq->status_num == -
1) {
protocol_status(sn, rq,
200,
"OK");
}
add_http_status_line(&writer->buf, sn->pool, rq);
struct tm mtms;
struct tm *mtm = system_gmtime(&rq->req_start, &mtms);
char date[
HTTP_DATE_LEN +
1];
strftime(date,
HTTP_DATE_LEN,
HTTP_DATE_FMT, mtm);
cxBufferWrite(
"Date: ",
1,
6, &writer->buf);
cxBufferWrite(date,
1, strlen(date), &writer->buf);
cxBufferWrite(
"\r\n",
1,
2, &writer->buf);
cxBufferWrite(
"Server: webserver\r\n",
1,
19, &writer->buf);
char *ctlen = pblock_findkeyval(pb_key_content_length, rq->srvhdrs);
char *enc = pblock_findkeyval(pb_key_transfer_encoding, rq->srvhdrs);
if(ctlen && enc) {
pblock_removekey(pb_key_transfer_encoding, rq->srvhdrs);
}
if(!ctlen) {
if(!enc) {
pblock_kvinsert(
pb_key_transfer_encoding,
"chunked",
7,
rq->srvhdrs);
}
else if(strcmp(enc,
"chunked")) {
pblock_removekey(pb_key_transfer_encoding, rq->srvhdrs);
pblock_kvinsert(
pb_key_transfer_encoding,
"chunked",
7,
rq->srvhdrs);
}
httpstream_enable_chunked_write(sn->csd);
rq->rq_attr.chunked =
1;
}
add_http_response_header(&writer->buf, rq);
if(rq->rq_attr.keep_alive) {
cxBufferWrite(
"Connection: keep-alive\r\n",
1,
24, &writer->buf);
pblock_kvinsert(pb_key_connection,
"keep-alive",
10, rq->srvhdrs);
}
else {
cxBufferWrite(
"Connection: close\r\n",
1,
19, &writer->buf);
pblock_kvinsert(pb_key_connection,
"close",
5, rq->srvhdrs);
}
cxBufferWrite(
"\r\n",
1,
2,& writer->buf);
writer->buf.pos =
0;
return writer;
}
int http_send_response(HttpResponseWriter *writer) {
Connection *conn = ((NSAPISession*)writer->sn)->connection;
CxBuffer *buf = &writer->buf;
int ret =
0;
while(buf->pos < buf->size) {
int w = conn->write(conn, buf->space + buf->pos, buf->size - buf->pos);
if(w <=
0) {
if(conn->ssl) {
if(conn->ssl_error ==
SSL_ERROR_WANT_WRITE) {
return 1;
}
}
else {
if(errno ==
EWOULDBLOCK) {
return 1;
}
}
ret = -
1;
break;
}
buf->pos += w;
}
if(ret ==
0) {
writer->rq->senthdrs =
1;
}
cxBufferDestroy(buf);
pool_free(writer->sn->pool, writer);
return ret;
}
int http_start_response(Session *sn, Request *rq) {
HttpResponseWriter *writer = http_create_response(sn, rq);
if(!writer) {
return 1;
}
return http_send_response(writer);
}
int http_send_continue(Session *sn) {
NSAPISession *s = (NSAPISession*)sn;
cxstring msg =
CX_STR(
"HTTP/1.1 100 Continue\r\n\r\n");
int w = s->connection->write(s->connection, msg.ptr, msg.length);
if(w != msg.length) {
return 1;
}
return 0;
}
int request_header(
char *name,
char **value, Session *sn, Request *rq) {
const pb_key *key = pblock_key(name);
pb_param *pp = pblock_findkey(key, rq->headers);
if(pp !=
NULL) {
*value = pp->value;
return REQ_PROCEED;
}
else {
*value =
NULL;
return REQ_NOACTION;
}
}
char *http_uri2url(
const char *prefix,
const char *suffix) {
return NULL;
}
char *http_uri2url_dynamic(
const char *prefix,
const char *suffix,
Session *sn, Request *rq)
{
return NULL;
}
void http_get_scheme_host_port(
Session *sn,
Request *rq,
char **scheme,
char **host,
uint16_t *port)
{
Connection *con = ((NSAPISession*)sn)->connection;
if(con->ssl) {
*scheme =
HTTPS_SCHEME;
}
else {
*scheme =
HTTP_SCHEME;
}
NSAPIRequest *request = (NSAPIRequest*)rq;
if(request->host) {
*host = request->host;
}
else {
*host =
"localhost";
}
*port = request->port;
}