#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(
sbuf_t *out,
pool_handle_t *pool, Request *rq) {
sbuf_write(out,
"HTTP/1.1 ",
9);
char *status_code_str = pool_malloc(pool,
8);
int sc_len = snprintf(status_code_str,
8,
"%d ", rq->status_num);
sbuf_write(out, status_code_str, sc_len);
char *scmsg = pblock_findkeyval(pb_key_status, rq->srvhdrs);
if(scmsg ==
NULL) {
scmsg =
"OK";
}
sbuf_write(out, scmsg, strlen(scmsg));
sbuf_write(out,
"\r\n",
2);
}
void add_http_response_header(
sbuf_t *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) {
sbuf_put(out, name[
0]-
32);
if(name_len >
1) {
sbuf_write(out, name+
1, name_len-
1);
}
}
else {
sbuf_write(out, name, name_len);
}
sbuf_write(out,
": ",
2);
sbuf_write(out, value, strlen(value));
sbuf_write(out,
"\r\n",
2);
p = p->next;
}
}
}
int http_start_response(Session *sn, Request *rq) {
Connection *conn = ((NSAPISession*)sn)->connection;
if(rq->status_num == -
1) {
protocol_status(sn, rq,
200,
"OK");
}
sbuf_t *out = sbuf_new(
512);
add_http_status_line(out, 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);
sbuf_write(out,
"Date: ",
6);
sbuf_write(out, date, strlen(date));
sbuf_write(out,
"\r\n",
2);
sbuf_write(out,
"Server: webserver\r\n",
19);
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 *stream = (HttpStream*)sn->csd;
stream->chunked_enc =
1;
rq->rq_attr.chunked =
1;
}
add_http_response_header(out, rq);
if(rq->rq_attr.keep_alive) {
sbuf_write(out,
"Connection: keep-alive\r\n",
24);
pblock_kvinsert(pb_key_connection,
"keep-alive",
10, rq->srvhdrs);
}
else {
sbuf_write(out,
"Connection: close\r\n",
19);
pblock_kvinsert(pb_key_connection,
"close",
5, rq->srvhdrs);
}
sbuf_write(out,
"\r\n",
2);
conn->write(conn, out->ptr, out->length);
sbuf_free(out);
rq->senthdrs =
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;
}