lwip-1.4.1 (partial)

Dependents:   IGLOO_board

http-server/http_req.c

Committer:
ua1arn
Date:
2018-07-24
Revision:
1:119c4f7144c8

File content as of revision 1:119c4f7144c8:

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 by Sergey Fetisov <fsenok@gmail.com>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/*
 * version: 1.0 demo (7.02.2015)
 * brief:   part of tiny http ipv4 server using lwip (pcb)
 */

#include "http_req.h"

#ifdef __cplusplus
extern "C" {
#endif

const char MIME_TEXT_HTML[] =   "text/html";
const char MIME_TEXT_JS[] =     "text/javascript";
const char MIME_TEXT_PLAIN[] =  "text/plain";
const char MIME_TEXT_XML[] =    "text/xml";
const char MIME_TEXT_CSS[] =    "text/css";
const char MIME_IMAGE_GIF[] =   "image/gif";
const char MIME_IMAGE_JPEG[] =  "image/jpeg";
const char MIME_IMAGE_PJPEG[] = "image/pjpeg";
const char MIME_IMAGE_PNG[] =   "image/png";
const char MIME_IMAGE_SVG[] =   "image/svg+xml";
const char MIME_IMAGE_TIFF[] =  "image/tiff";
const char MIME_IMAGE_ICON[] =  "image/vnd.microsoft.icon";
const char MIME_IMAGE_WBMP[] =  "image/vnd.wap.wbmp";

static const char *METHODS_STR[] =
{
	"",
	"GET",
	"POST",
	"HEAD",
	"PUT",
	"CONNECT",
	"OPTIONS",
	"DELETE",
	"TRACE",
	"PATCH"
};

static const char *CONN_TYPE_STR[] =
{
	"", "close", "keep-alive"
};

void http_reqb_init(http_reqb_t *reqb, void *buff, int size)
{
	memset(reqb, 0, sizeof(http_reqb_t));
	reqb->buff = buff;
	reqb->bsize = size;
}

int http_reqb_avail(const http_reqb_t *reqb)
{
	return reqb->bsize - reqb->size;
}

static chatset_t req_ident =
{
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, // '%', '-', '.', '/'
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // '0'...'9'
	0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 'A'...'O'
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 'P'...'Z', '_'
	0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 'a'...'z'
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 'p'...'z'
};

static http_mt_t str_to_method(const char *str)
{
	http_mt_t res;
	for (res = METHOD_GET; res <= METHOD_PATCH; res++)
		if (strcmp(METHODS_STR[res], str) == 0)
			return res;
	return METHOD_NONE;
}

static http_ct_t str_to_ct(const char *str)
{
	if (strcmp(str, CONN_TYPE_STR[CT_CLOSE]) == 0) return CT_CLOSE;
	if (strcmp(str, CONN_TYPE_STR[CT_KEEP_ALIVE]) == 0) return CT_KEEP_ALIVE;
	return CT_NONE;
}

void http_req_set_param(http_req_t *req, char *name, char *val)
{
	if (strcmp(name, "Host") == 0)
		req->host = val;
	else
	if (strcmp(name, "User-Agent") == 0)
		req->user_agent = val;
	else
	if (strcmp(name, "Content-Type") == 0)
		req->mime = val;
	else
	if (strcmp(name, "Content-Length") == 0)
		req->cont_len = strtol(val, NULL, 10);
	else
	if (strcmp(name, "Accept") == 0)
		req->accept = val;
	else
	if (strcmp(name, "Accept-Language") == 0)
		req->accept_lang = val;
	else
	if (strcmp(name, "Accept-Encoding") == 0)
		req->accept_enc = val;
	else
	if (strcmp(name, "Cookie") == 0)
		req->cookie = val;
	else
	if (strcmp(name, "Connection") == 0)
		req->conn_type = str_to_ct(val);
	else
	if (strcmp(name, "Keep-Alive") == 0)
		req->keep_alive = strtol(val, NULL, 10);
}

void http_reqb_push(http_reqb_t *rb, const void *data, int size)
{
	parser_t p;
	int indeces[4];

	if (rb->size + size >= rb->bsize)
	{
		rb->state = REQB_REQ_TO_BIG;
		return;
	}
	if (rb->state != REQB_UNFINISHED) return;

	memcpy(&rb->buff[rb->size], data, size);
	rb->size += size;

	parser_init(&p, rb->buff, rb->size);
	parser_skip_n(&p, rb->parsing.pos);

	while (!parser_eof(&p))
	{
		int total;
		switch (rb->parsing.state)
		{
		case 0:
			parser_skip_ws(&p);
			total = p.total;
			parser_skip_before(&p, ' ');
			if (parser_eof(&p)) break;
			rb->buff[p.total] = 0;
			rb->req.method = str_to_method(&rb->buff[total]);
			parser_skip_n(&p, 1);
			rb->parsing.pos = p.total;
			rb->parsing.state = 1;
			break;
		case 1:
			parser_skip_ws(&p);
			total = p.total;
			parser_skip(&p, req_ident);
			if (parser_eof(&p)) break;
			switch (parser_curr(&p))
			{
			case '?': rb->parsing.state = 2; break;
			case ' ': rb->parsing.state = 4; break;
			default:  rb->parsing.state = 11; break;
			}
			if (rb->parsing.state >= 10) break; // error
			rb->req.uri = &rb->buff[total];
			rb->buff[p.total] = 0;
			parser_skip_n(&p, 1);
			rb->parsing.pos = p.total;
			break;
		case 2:
			total = p.total;
			parser_skip(&p, req_ident);
			if (parser_eof(&p)) break;
			switch (parser_curr(&p))
			{
			case '&': rb->parsing.state = 2; break;
			case '=': rb->parsing.state = 3; break;
			case ' ': rb->parsing.state = 4; break;
			default:  rb->parsing.state = 12; break;
			}
			if (rb->parsing.state >= 10) break; // error
			rb->req.num_params++;
			if (rb->req.num_params <= HTTP_REQ_MAX_PARAMS)
			{
				rb->req.params[rb->req.num_params - 1] = &rb->buff[total];
				rb->req.values[rb->req.num_params - 1] = NULL;
			}
			rb->buff[p.total] = 0;
			parser_skip_n(&p, 1);
			rb->parsing.pos = p.total;
			break;
		case 3:
			total = p.total;
			parser_skip(&p, req_ident);
			if (parser_eof(&p)) break;
			switch (parser_curr(&p))
			{
			case '&': rb->parsing.state = 2; break;
			case ' ': rb->parsing.state = 4; break;
			default:  rb->parsing.state = 13; break;
			}
			if (rb->parsing.state >= 10) break; // error
			if (rb->req.num_params <= HTTP_REQ_MAX_PARAMS)
				rb->req.values[rb->req.num_params - 1] = &rb->buff[total];
			rb->buff[p.total] = 0;
			parser_skip_n(&p, 1);
			rb->parsing.pos = p.total;
			break;
		case 4:
			parser_skip_ws(&p);
			total = p.total;
			if (!parser_skip_line(&p)) break;
			rb->req.ver = &rb->buff[total];
			rb->buff[p.total - 2] = 0;
			rb->parsing.pos = p.total;
			rb->parsing.state = 5;
			break;
		case 5:
			parser_skip_ws_in_line(&p);
			if (parser_skip_char(&p, '\r') || parser_skip_char(&p, '\n'))
			{
				// DONE
				rb->state = REQB_FINISHED;
				return;
			}
			// 0     1   2            3
			// Accept:   text/html\r\n...
			parser_skip_ws_in_line(&p);
			indeces[0] = p.total;
			parser_skip(&p, req_ident);
			if (parser_eof(&p)) break;
			if (parser_curr(&p) != ':')
			{
				rb->parsing.state = 15;
				break;
			}
			indeces[1] = p.total;
			parser_skip_n(&p, 1);
			parser_skip_ws_in_line(&p);
			indeces[2] = p.total;
			if (!parser_skip_line(&p)) break;
			indeces[3] = p.total;
			rb->buff[indeces[1]] = 0;
			rb->buff[indeces[3] - 2] = 0;
			http_req_set_param(&rb->req,
				&rb->buff[indeces[0]],
				&rb->buff[indeces[2]]);
			rb->parsing.pos = p.total;
			break;
		default:
			rb->state = REQB_SYNT_ERROR;
			return;
		}
	}
}

int http_resp_len(const http_resp_t *resp)
{
	char temp[1];
	return http_resp_str(resp, temp, 1);
}

int http_resp_str(const http_resp_t *resp, char *str, int size)
{
	return snprintf(str, size,
		"HTTP/1.1 %d\r\n"
		"Server: %s\r\n"
//		"Content-Type: text/html\r\n"
		"Content-Type: %s\r\n"
		"Content-Length: %d\r\n"
		"Connection: %s\r\n"
		"\r\n",
		resp->code,
		resp->server,
		resp->mime,
		resp->cont_len,
		CONN_TYPE_STR[resp->conn_type]);
}

#ifdef __cplusplus
}
#endif