lwip-1.4.1 (partial)

Dependents:   IGLOO_board

Files at this revision

API Documentation at this revision

Comitter:
ua1arn
Date:
Tue Jul 24 17:36:01 2018 +0000
Parent:
0:c2ca3c5ded62
Commit message:
lwip 1.4.1 with necessary servers

Changed in this revision

dhcp-server/dhserver.c Show annotated file Show diff for this revision Revisions of this file
dhcp-server/dhserver.h Show annotated file Show diff for this revision Revisions of this file
dns-server/dnserver.c Show annotated file Show diff for this revision Revisions of this file
dns-server/dnserver.h Show annotated file Show diff for this revision Revisions of this file
http-server/cparser.c Show annotated file Show diff for this revision Revisions of this file
http-server/cparser.h Show annotated file Show diff for this revision Revisions of this file
http-server/htserv.c Show annotated file Show diff for this revision Revisions of this file
http-server/htserv.h Show annotated file Show diff for this revision Revisions of this file
http-server/http_req.c Show annotated file Show diff for this revision Revisions of this file
http-server/http_req.h Show annotated file Show diff for this revision Revisions of this file
diff -r c2ca3c5ded62 -r 119c4f7144c8 dhcp-server/dhserver.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dhcp-server/dhserver.c	Tue Jul 24 17:36:01 2018 +0000
@@ -0,0 +1,332 @@
+/*
+ * 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.
+ */
+
+#include "dhserver.h"
+
+/* DHCP message type */
+#define DHCP_DISCOVER       1
+#define DHCP_OFFER          2
+#define DHCP_REQUEST        3
+#define DHCP_DECLINE        4
+#define DHCP_ACK            5
+#define DHCP_NAK            6
+#define DHCP_RELEASE        7
+#define DHCP_INFORM         8
+
+/* DHCP options */
+enum DHCP_OPTIONS
+{
+	DHCP_PAD                    = 0,
+	DHCP_SUBNETMASK             = 1,
+	DHCP_ROUTER                 = 3,
+	DHCP_DNSSERVER              = 6,
+	DHCP_HOSTNAME               = 12,
+	DHCP_DNSDOMAIN              = 15,
+	DHCP_MTU                    = 26,
+	DHCP_BROADCAST              = 28,
+	DHCP_PERFORMROUTERDISC      = 31,
+	DHCP_STATICROUTE            = 33,
+	DHCP_NISDOMAIN              = 40,
+	DHCP_NISSERVER              = 41,
+	DHCP_NTPSERVER              = 42,
+	DHCP_VENDOR                 = 43,
+	DHCP_IPADDRESS              = 50,
+	DHCP_LEASETIME              = 51,
+	DHCP_OPTIONSOVERLOADED      = 52,
+	DHCP_MESSAGETYPE            = 53,
+	DHCP_SERVERID               = 54,
+	DHCP_PARAMETERREQUESTLIST   = 55,
+	DHCP_MESSAGE                = 56,
+	DHCP_MAXMESSAGESIZE         = 57,
+	DHCP_RENEWALTIME            = 58,
+	DHCP_REBINDTIME             = 59,
+	DHCP_CLASSID                = 60,
+	DHCP_CLIENTID               = 61,
+	DHCP_USERCLASS              = 77,  /* RFC 3004 */
+	DHCP_FQDN                   = 81,
+	DHCP_DNSSEARCH              = 119, /* RFC 3397 */
+	DHCP_CSR                    = 121, /* RFC 3442 */
+	DHCP_MSCSR                  = 249, /* MS code for RFC 3442 */
+	DHCP_END                    = 255
+};
+
+typedef struct
+{
+    uint8_t  dp_op;           /* packet opcode type */
+    uint8_t  dp_htype;        /* hardware addr type */
+    uint8_t  dp_hlen;         /* hardware addr length */
+    uint8_t  dp_hops;         /* gateway hops */
+    uint32_t dp_xid;          /* transaction ID */
+    uint16_t dp_secs;         /* seconds since boot began */
+    uint16_t dp_flags;
+    uint8_t  dp_ciaddr[4];    /* client IP address */
+    uint8_t  dp_yiaddr[4];    /* 'your' IP address */
+    uint8_t  dp_siaddr[4];    /* server IP address */
+    uint8_t  dp_giaddr[4];    /* gateway IP address */
+    uint8_t  dp_chaddr[16];   /* client hardware address */
+    uint8_t  dp_legacy[192];
+    uint8_t  dp_magic[4];     
+    uint8_t  dp_options[275]; /* options area */
+} DHCP_TYPE;
+
+DHCP_TYPE dhcp_data;
+static struct udp_pcb *pcb = NULL;
+static dhcp_config_t *config = NULL;
+
+char magic_cookie[] = {0x63,0x82,0x53,0x63};
+
+static dhcp_entry_t *entry_by_ip(uint32_t ip)
+{
+	int i;
+	for (i = 0; i < config->num_entry; i++)
+		if (*(uint32_t *)config->entries[i].addr == ip)
+			return &config->entries[i];
+	return NULL;
+}
+
+static dhcp_entry_t *entry_by_mac(uint8_t *mac)
+{
+	int i;
+	for (i = 0; i < config->num_entry; i++)
+		if (memcmp(config->entries[i].mac, mac, 6) == 0)
+			return &config->entries[i];
+	return NULL;
+}
+
+static __inline bool is_vacant(dhcp_entry_t *entry)
+{
+	return memcmp("\0\0\0\0\0", entry->mac, 6) == 0;
+}
+
+static dhcp_entry_t *vacant_address()
+{
+	int i;
+	for (i = 0; i < config->num_entry; i++)
+		if (is_vacant(config->entries + i))
+			return config->entries + 1;
+	return NULL;
+}
+
+static __inline void free_entry(dhcp_entry_t *entry)
+{
+	memset(entry->mac, 0, 6);
+}
+
+uint8_t *find_dhcp_option(uint8_t *attrs, int size, uint8_t attr)
+{
+	int i = 0;
+	while ((i + 1) < size)
+	{
+		int next = i + attrs[i + 1] + 2;
+		if (next > size) return NULL;
+		if (attrs[i] == attr)
+			return attrs + i;
+		i = next;
+	}
+	return NULL;
+}
+
+int fill_options(void *dest,
+	uint8_t msg_type,
+	const char *domain,
+	uint32_t dns,
+	int lease_time,
+	uint32_t serverid,
+	uint32_t router,
+	uint32_t subnet)
+{
+	uint8_t *ptr = (uint8_t *)dest;
+	/* ACK message type */
+	*ptr++ = 53;
+	*ptr++ = 1;
+	*ptr++ = msg_type;
+
+	/* dhcp server identifier */
+	*ptr++ = DHCP_SERVERID;
+	*ptr++ = 4;
+	*(uint32_t *)ptr = serverid;
+	ptr += 4;
+
+	/* lease time */
+	*ptr++ = DHCP_LEASETIME;
+	*ptr++ = 4;
+	*ptr++ = (lease_time >> 24) & 0xFF;
+	*ptr++ = (lease_time >> 16) & 0xFF;
+	*ptr++ = (lease_time >> 8) & 0xFF;
+	*ptr++ = (lease_time >> 0) & 0xFF;
+
+	/* subnet mask */
+	*ptr++ = DHCP_SUBNETMASK;
+	*ptr++ = 4;
+	*(uint32_t *)ptr = subnet;
+	ptr += 4;
+
+	/* router */
+	if (router != 0)
+	{
+		*ptr++ = DHCP_ROUTER;
+		*ptr++ = 4;
+		*(uint32_t *)ptr = router;
+		ptr += 4;
+	}
+
+	/* domain name */
+	if (domain != NULL)
+	{
+		int len = strlen(domain);
+		*ptr++ = DHCP_DNSDOMAIN;
+		*ptr++ = len;
+		memcpy(ptr, domain, len);
+		ptr += len;
+	}
+
+	/* domain name server (DNS) */
+	if (dns != 0)
+	{
+		*ptr++ = DHCP_DNSSERVER;
+		*ptr++ = 4;
+		*(uint32_t *)ptr = dns;
+		ptr += 4;
+	}
+
+	/* end */
+	*ptr++ = DHCP_END;
+	return ptr - (uint8_t *)dest;
+}
+
+static void udp_recv_proc(void *arg, struct udp_pcb *upcb, struct pbuf *p, struct ip_addr *addr, u16_t port)
+{
+	uint8_t *ptr;
+	dhcp_entry_t *entry;
+	struct pbuf *pp;
+
+	int n = p->len;
+	if (n > sizeof(dhcp_data)) n = sizeof(dhcp_data);
+	memcpy(&dhcp_data, p->payload, n);
+	switch (dhcp_data.dp_options[2])
+	{
+		case DHCP_DISCOVER:
+			entry = entry_by_mac(dhcp_data.dp_chaddr);
+			if (entry == NULL) entry = vacant_address();
+			if (entry == NULL) break;
+
+			dhcp_data.dp_op = 2; /* reply */
+			dhcp_data.dp_secs = 0;
+			dhcp_data.dp_flags = 0;
+			*(uint32_t *)dhcp_data.dp_yiaddr = *(uint32_t *)entry->addr;
+			memcpy(dhcp_data.dp_magic, magic_cookie, 4);
+
+			memset(dhcp_data.dp_options, 0, sizeof(dhcp_data.dp_options));
+
+			fill_options(dhcp_data.dp_options,
+				DHCP_OFFER,
+				config->domain,
+				*(uint32_t *)config->dns,
+				entry->lease, 
+				*(uint32_t *)config->addr,
+				0,
+				*(uint32_t *)entry->subnet);
+
+			pp = pbuf_alloc(PBUF_TRANSPORT, sizeof(dhcp_data), PBUF_POOL);
+			if (pp == NULL) break;
+			memcpy(pp->payload, &dhcp_data, sizeof(dhcp_data));
+			udp_sendto(upcb, pp, IP_ADDR_BROADCAST, port);
+			pbuf_free(pp);
+			break;
+
+		case DHCP_REQUEST:
+			/* 1. find requested ipaddr in option list */
+			ptr = find_dhcp_option(dhcp_data.dp_options, sizeof(dhcp_data.dp_options), DHCP_IPADDRESS);
+			if (ptr == NULL) break;
+			if (ptr[1] != 4) break;
+			ptr += 2;
+
+			/* 2. does hw-address registered? */
+			entry = entry_by_mac(dhcp_data.dp_chaddr);
+			if (entry != NULL) free_entry(entry);
+
+			/* 3. find requested ipaddr */
+			entry = entry_by_ip(*(uint32_t *)ptr);
+			if (entry == NULL) break;
+			if (!is_vacant(entry)) break;
+
+			/* 4. fill struct fields */
+			memcpy(dhcp_data.dp_yiaddr, ptr, 4);
+			dhcp_data.dp_op = 2; /* reply */
+			dhcp_data.dp_secs = 0;
+			dhcp_data.dp_flags = 0;
+			memcpy(dhcp_data.dp_magic, magic_cookie, 4);
+
+			/* 5. fill options */
+			memset(dhcp_data.dp_options, 0, sizeof(dhcp_data.dp_options));
+
+			fill_options(dhcp_data.dp_options,
+				DHCP_ACK,
+				config->domain,
+				*(uint32_t *)config->dns,
+				entry->lease, 
+				*(uint32_t *)config->addr,
+				0,
+				*(uint32_t *)entry->subnet);
+
+			/* 6. send ACK */
+			pp = pbuf_alloc(PBUF_TRANSPORT, sizeof(dhcp_data), PBUF_POOL);
+			if (pp == NULL) break;
+			memcpy(entry->mac, dhcp_data.dp_chaddr, 6);
+			memcpy(pp->payload, &dhcp_data, sizeof(dhcp_data));
+			udp_sendto(upcb, pp, IP_ADDR_BROADCAST, port);
+			pbuf_free(pp);
+			break;
+
+		default:
+				break;
+	}
+	pbuf_free(p);
+}
+
+err_t dhserv_init(dhcp_config_t *c)
+{
+	err_t err;
+	udp_init();
+	dhserv_free();
+	pcb = udp_new();
+	if (pcb == NULL)
+		return ERR_MEM;
+	err = udp_bind(pcb, IP_ADDR_ANY, c->port);
+	if (err != ERR_OK)
+	{
+		dhserv_free();
+		return err;
+	}
+	udp_recv(pcb, udp_recv_proc, NULL);
+	config = c;
+	return ERR_OK;
+}
+
+void dhserv_free(void)
+{
+	if (pcb == NULL) return;
+	udp_remove(pcb);
+	pcb = NULL;
+}
diff -r c2ca3c5ded62 -r 119c4f7144c8 dhcp-server/dhserver.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dhcp-server/dhserver.h	Tue Jul 24 17:36:01 2018 +0000
@@ -0,0 +1,63 @@
+/*
+ * 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:   tiny dhcp ipv4 server using lwip (pcb)
+ * ref:     https://lists.gnu.org/archive/html/lwip-users/2012-12/msg00016.html
+ */
+
+#ifndef DHSERVER_H
+#define DHSERVER_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include "lwip/err.h"
+#include "lwip/udp.h"
+#include "netif/etharp.h"
+
+typedef struct dhcp_entry
+{
+	uint8_t  mac[6];
+	uint8_t  addr[4];
+	uint8_t  subnet[4];
+	uint32_t lease;
+} dhcp_entry_t;
+
+typedef struct dhcp_config
+{
+	uint8_t       addr[4];
+	uint16_t      port;
+	uint8_t       dns[4];
+	const char   *domain;
+	int           num_entry;
+	dhcp_entry_t *entries;
+} dhcp_config_t;
+
+err_t dhserv_init(dhcp_config_t *config);
+void dhserv_free(void);
+
+#endif /* DHSERVER_H */
diff -r c2ca3c5ded62 -r 119c4f7144c8 dns-server/dnserver.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dns-server/dnserver.c	Tue Jul 24 17:36:01 2018 +0000
@@ -0,0 +1,200 @@
+/*
+ * 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:   tiny dns ipv4 server using lwip (pcb)
+ */
+
+#include "dnserver.h"
+
+#define DNS_MAX_HOST_NAME_LEN 128
+
+static struct udp_pcb *pcb = NULL;
+dns_query_proc_t query_proc = NULL;
+
+#pragma pack(push, 1)
+typedef struct
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+	uint8_t rd: 1,     /* Recursion Desired */
+	        tc: 1,     /* Truncation Flag */
+	        aa: 1,     /* Authoritative Answer Flag */
+	        opcode: 4, /* Operation code */
+	        qr: 1;     /* Query/Response Flag */
+	uint8_t rcode: 4,  /* Response Code */
+	        z: 3,      /* Zero */
+	        ra: 1;     /* Recursion Available */
+#else
+	uint8_t qr: 1,     /* Query/Response Flag */
+	        opcode: 4, /* Operation code */
+	        aa: 1,     /* Authoritative Answer Flag */
+	        tc: 1,     /* Truncation Flag */
+	        rd: 1;     /* Recursion Desired */
+	uint8_t ra: 1,     /* Recursion Available */
+	        z: 3,      /* Zero */
+	        rcode: 4;  /* Response Code */
+#endif
+} dns_header_flags_t;
+
+typedef struct
+{
+	uint16_t id;
+	dns_header_flags_t flags;
+	uint16_t n_record[4];
+} dns_header_t;
+
+typedef struct dns_answer
+{
+	uint16_t name;
+	uint16_t type;
+	uint16_t Class;
+	uint32_t ttl;
+	uint16_t len;
+	uint32_t addr;
+} dns_answer_t;
+#pragma pack(pop)
+
+typedef struct dns_query
+{
+	char name[DNS_MAX_HOST_NAME_LEN];
+	uint16_t type;
+	uint16_t Class;
+} dns_query_t;
+
+static int parse_next_query(void *data, int size, dns_query_t *query)
+{
+	int len;
+	int lables;
+	uint8_t *ptr;
+
+	len = 0;
+	lables = 0;
+	ptr = (uint8_t *)data;
+
+	while (true)
+	{
+		uint8_t lable_len;
+		if (size <= 0) return -1;
+		lable_len = *ptr++;
+		size--;
+		if (lable_len == 0) break;
+		if (lables > 0)
+		{
+			if (len == DNS_MAX_HOST_NAME_LEN) return -2;
+			query->name[len++] = '.';
+		}
+		if (lable_len > size) return -1;
+		if (len + lable_len >= DNS_MAX_HOST_NAME_LEN) return -2;
+		memcpy(&query->name[len], ptr, lable_len);
+		len += lable_len;
+		ptr += lable_len;
+		size -= lable_len;
+		lables++;
+	}
+
+	if (size < 4) return -1;
+	query->name[len] = 0;
+	query->type = *(uint16_t *)ptr;
+	ptr += 2;
+	query->Class = *(uint16_t *)ptr;
+	ptr += 2;
+	return ptr - (uint8_t *)data;
+}
+
+static void udp_recv_proc(void *arg, struct udp_pcb *upcb, struct pbuf *p, struct ip_addr *addr, u16_t port)
+{
+	int len;
+	dns_header_t *header;
+	static dns_query_t query;
+	struct pbuf *out;
+	ip_addr_t host_addr;
+	dns_answer_t *answer;
+
+	if (p->len <= sizeof(dns_header_t)) goto error;
+	header = (dns_header_t *)p->payload;
+	if (header->flags.qr != 0) goto error;
+	if (ntohs(header->n_record[0]) != 1) goto error;
+
+	len = parse_next_query(header + 1, p->len - sizeof(dns_header_t), &query);
+	if (len < 0) goto error;
+	if (!query_proc(query.name, &host_addr)) goto refuse;
+
+	len += sizeof(dns_header_t);
+	out = pbuf_alloc(PBUF_TRANSPORT, len + 16, PBUF_POOL);
+	if (out == NULL) goto error;
+
+	memcpy(out->payload, p->payload, len);
+	header = (dns_header_t *)out->payload;
+	header->flags.qr = 1;
+	header->n_record[1] = htons(1);
+	answer = (struct dns_answer *)((uint8_t *)out->payload + len);
+	answer->name = htons(0xC00C);
+	answer->type = htons(1);
+	answer->Class = htons(1);
+	answer->ttl = htonl(32);
+	answer->len = htons(4);
+	answer->addr = host_addr.addr;
+	
+	udp_sendto(upcb, out, addr, port);
+	pbuf_free(out);
+	pbuf_free(p);
+	return;
+
+refuse:
+	header->flags.qr = 1;
+	header->flags.rcode = 5;
+	udp_sendto(upcb, p, addr, port);
+	pbuf_free(p);
+	return;
+
+error:
+	pbuf_free(p);
+}
+
+err_t dnserv_init(ip_addr_t *bind, uint16_t port, dns_query_proc_t qp)
+{
+	err_t err;
+	udp_init();
+	dnserv_free();
+	pcb = udp_new();
+	if (pcb == NULL)
+		return ERR_MEM;
+	err = udp_bind(pcb, bind, port);
+	if (err != ERR_OK)
+	{
+		dnserv_free();
+		return err;
+	}
+	udp_recv(pcb, udp_recv_proc, NULL);
+	query_proc = qp;
+	return ERR_OK;
+}
+
+void dnserv_free()
+{
+	if (pcb == NULL) return;
+	udp_remove(pcb);
+	pcb = NULL;
+}
diff -r c2ca3c5ded62 -r 119c4f7144c8 dns-server/dnserver.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dns-server/dnserver.h	Tue Jul 24 17:36:01 2018 +0000
@@ -0,0 +1,47 @@
+/*
+ * 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:   tiny dns ipv4 server using lwip (pcb)
+ */
+
+#ifndef DNSERVER
+#define DNSERVER
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include "lwip/def.h"
+#include "lwip/err.h"
+#include "lwip/udp.h"
+#include "netif/etharp.h"
+
+typedef bool (*dns_query_proc_t)(const char *name, ip_addr_t *addr);
+
+err_t dnserv_init(ip_addr_t *bind, uint16_t port, dns_query_proc_t query_proc);
+void  dnserv_free(void);
+
+#endif
diff -r c2ca3c5ded62 -r 119c4f7144c8 http-server/cparser.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/http-server/cparser.c	Tue Jul 24 17:36:01 2018 +0000
@@ -0,0 +1,519 @@
+/*
+ * 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)
+ */
+
+#include "cparser.h"
+
+void parser_init(parser_t *p, const char *str, int len)
+{
+	p->buff = NULL;
+	p->buff_size = 0;
+	p->user_data = NULL;
+	p->read_cb = NULL;
+	p->total = 0;
+	p->line = 0;
+	p->col = 0;
+	p->priv.curr = str;
+	p->priv.stop = str + len;
+}
+
+void parser_init_s(parser_t *p, char *stream_buff, size_t size, parser_read_t cb)
+{
+	p->buff = stream_buff;
+	p->buff_size = size;
+	p->user_data = NULL;
+	p->read_cb = cb;
+	p->total = 0;
+	p->line = 0;
+	p->col = 0;
+	p->priv.curr = NULL;
+	p->priv.stop = NULL;
+}
+
+bool parser_provide_space(parser_t *p, int size)
+{
+	int allow;
+	allow = p->priv.stop - p->priv.curr;
+	if (allow >= size) return true;
+	if (p->buff == NULL || p->buff_size < size) return false;
+	memmove(p->buff, p->priv.stop - allow, allow);
+	size = p->read_cb(p, p->buff + allow, p->buff_size - allow);
+	p->priv.curr = p->buff;
+	p->priv.stop = p->buff + allow + size;
+	return true;
+}
+
+bool parser_is_next(parser_t *p, const char *str, int len)
+{
+	if (!parser_provide_space(p, len)) return false;
+	return memcmp(p->priv.curr, str, len) == 0;
+}
+
+static bool query_data(parser_t *p)
+{
+	size_t size = p->read_cb(p, p->buff, p->buff_size);
+	p->priv.curr = p->buff;
+	p->priv.stop = p->buff + size;
+	return size > 0;
+}
+
+#define INC_POS                 \
+	p->total++;                 \
+	p->col++;                   \
+	if (*p->priv.curr == '\n')  \
+	{                           \
+		p->line++;              \
+		p->col = 0;             \
+	}                           \
+	p->priv.curr++;
+
+#define CHECK_STATE()                                       \
+	eof = false;                                            \
+	if (p->priv.curr == p->priv.stop)                       \
+		eof = p->buff == NULL ? true : !query_data(p);
+
+char parser_curr(parser_t *p)
+{
+	bool eof;
+	CHECK_STATE();
+	return eof ? 0 : *p->priv.curr;
+}
+
+bool parser_eof(parser_t *p)
+{
+	bool eof;
+	CHECK_STATE();
+	return eof;
+}
+
+int parser_read(parser_t *p, const chatset_t chatset, char *dest, int dest_size)
+{
+	bool eof;
+	register int c;
+	int res = 0;
+	while (true)
+	{
+		CHECK_STATE();
+		if (eof) break;
+		c = (unsigned)*p->priv.curr;
+		if (!chatset[c]) break;
+		if (res < dest_size - 1)
+			dest[res] = c;
+		res++;
+		INC_POS;
+	}
+	if (res >= dest_size)
+		dest[dest_size - 1] = 0; else
+		dest[res] = 0;
+	return res;
+}
+
+int parser_read_before2(parser_t *p, char *dest, int dest_size, char c1, char c2)
+{
+	bool eof;
+	register int cc;
+	int res = 0;
+	while (true)
+	{
+		CHECK_STATE();
+		if (eof) break;
+		cc = *p->priv.curr;
+		if (cc == c1 || cc == c2) break;
+		if (res < dest_size)
+			dest[res] = cc;
+		res++;
+		INC_POS;
+	}
+	if (res >= dest_size)
+		dest[dest_size - 1] = 0; else
+		dest[res] = 0;
+	return res;
+}
+
+int parser_read_digits(parser_t *p, char *dest, int dest_size, bool hex)
+{
+	bool eof;
+	register int c;
+	int res = 0;
+	while (true)
+	{
+		bool done;
+		CHECK_STATE();
+		if (eof) break;
+		c = (unsigned)*p->priv.curr;
+		done = false;
+		done |= c >= '0' && c <= '9';
+		done |= hex && (c >= 'a' && c <= 'f');
+		done |= hex && (c >= 'A' && c <= 'F');
+		if (!done) break;
+		if (res < dest_size - 1)
+			dest[res] = c;
+		res++;
+		INC_POS;
+	}
+	if (res >= dest_size)
+		dest[dest_size - 1] = 0; else
+		dest[res] = 0;
+	return res;
+}
+
+int parser_read_value(parser_t *p, char *dest, int dest_size, int fmt)
+{
+// state:     0    1   2    3   4   5   6     7
+//         1234567
+//         1234567          e       2
+//         1234567          e   -   2
+//             345 . 1234   e   +   4
+//               0 . 1234   e   +   4
+//               0 x                   ABCD
+//               0 h                   ABCD
+//               0 b                         0110
+
+	bool eof;
+	register int c;
+	int res;
+	int state;
+	char last;
+
+	res = 0;
+	state = 0;
+
+	while (true)
+	{
+		CHECK_STATE();
+		if (eof) break;
+		c = *p->priv.curr;
+
+		if (state == 0 || state == 2 || state == 5)
+		{
+			if (c >= '0' && c <= '9')
+			{
+				if (res < dest_size) dest[res] = c;
+				last = c;
+				INC_POS;
+				res++;
+				continue;
+			}
+			if (state == 0 && c == '.' && (fmt & VAL_FMT_FLOAT))
+			{
+				if (res < dest_size) dest[res] = c;
+				INC_POS;
+				res++;
+				state = 2;
+				continue;
+			}
+			if ((state == 0 || state == 2) && (c == 'e' || c == 'E') && (fmt & VAL_FMT_FLOAT))
+			{
+				if (res < dest_size) dest[res] = c;
+				INC_POS;
+				res++;
+				state = 4;
+				continue;
+			}
+			if (state == 0 && last == '0' && res == 1)
+			{
+				if (((fmt & VAL_FMT_0H) && (c == 'H' || c == 'h')) || ((c == 'x' || c == 'X') && (fmt & VAL_FMT_0X)))
+				{
+					if (res < dest_size) dest[res] = c;
+					INC_POS;
+					res++;
+					state = 6;
+					continue;
+				}
+				if ((fmt & VAL_FMT_0B) && (c == 'b' || c == 'B'))
+				{
+					if (res < dest_size) dest[res] = c;
+					INC_POS;
+					res++;
+					state = 7;
+					continue;
+				}
+			}
+			if ((fmt & VAL_FMT_SIGN) && res == 0 && (c == '+' || c == '-'))
+			{
+				if (res < dest_size) dest[res] = c;
+				INC_POS;
+				res++;
+				continue;
+			}
+		}
+		if (state == 4)
+		{
+			if (c == '+' || c == '-')
+			{
+				if (res < dest_size) dest[res] = c;
+				INC_POS;
+				res++;
+			}
+			state = 5;
+			continue;
+		}
+		if (state == 6)
+		{
+			if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))
+			{
+				if (res < dest_size) dest[res] = c;
+				INC_POS;
+				res++;
+				continue;
+			}
+		}
+		if (state == 7)
+		{
+			if (c == '0' || c == '1')
+			{
+				if (res < dest_size) dest[res] = c;
+				INC_POS;
+				res++;
+				continue;
+			}
+		}
+		break;
+	}
+	if (res >= dest_size)
+		dest[dest_size - 1] = 0; else
+		dest[res] = 0;
+	return res;
+}
+
+#define is_ident_char(c)      \
+	(c == '_' ||              \
+	(c >= '0' && c <= '9') || \
+	(c >= 'a' && c <= 'z') || \
+	(c >= 'a' && c <= 'z'))
+
+int parser_read_string(parser_t *p, char *dest, int dest_size, int fmt)
+{
+// state:     0     1    2      3         4     5
+//               string
+//            '  string                         '
+//            "  string                         "
+//            '  str     \                      '
+//            '  str     \    rntbf\/"          '
+//            '  str     \    u                 '
+//            '  str     \    u                 '
+//            '  str     \    u         12AB    '
+	bool eof;
+	register int c;
+	int res;
+	int state;
+	char first;
+
+	res = 0;
+	state = 0;
+	first = 0;
+
+	while (true)
+	{
+		CHECK_STATE();
+		if (eof) break;
+		c = *p->priv.curr;
+
+		if (state == 0 && (((fmt & STR_FMT_QUOTE) && c == '"') || ((fmt & STR_FMT_APOST) && c == '\'')))
+		{
+			first = c;
+			state = 1;
+			INC_POS;
+			continue;
+		}
+
+		if (state == 0 && (fmt & STR_FMT_NAKED))
+		{
+			state = 1;
+			continue;
+		}
+
+		if (state == 1 && c == first)
+		{
+			INC_POS;
+			break;
+		}
+
+		if (state == 1 && first == 0 && is_ident_char(c)) // reading naked
+		{
+			if (res < dest_size) dest[res] = c;
+			INC_POS;
+			res++;
+			continue;
+		}
+
+		if (state == 1 && (fmt & STR_FMT_ESC) && c == '\\')
+		{
+			state = 3;
+			INC_POS;
+			continue;
+		}
+
+		if (state == 1 && first != 0)
+		{
+			if (res < dest_size) dest[res] = c;
+			INC_POS;
+			res++;
+			continue;
+		}
+
+		if (state == 3 && (fmt & STR_FMT_ESCU) && c == 'u')
+		{
+			state = 4;
+			INC_POS;
+			continue;
+		}
+
+		if (state == 3)
+		{
+			char e;
+			switch (c)
+			{
+			case 'r': e = '\r'; break;
+			case 'n': e = '\n'; break;
+			case 't': e = '\t'; break;
+			case 'b': e = '\b'; break;
+			case 'f': e = '\f'; break;
+			case '\\': e = '\\'; break;
+			case '/': e = '/'; break;
+			case '"': e = '"'; break;
+			default:
+				if (res < dest_size) dest[res] = '\\';
+				res++;
+				e = c;
+				break;
+			}
+			state = 1;
+			if (res < dest_size) dest[res] = e;
+			INC_POS;
+			res++;
+			continue;
+		}
+
+		if (state == 4)
+		{
+		}
+
+		break;
+	}
+	if (res >= dest_size)
+		dest[dest_size - 1] = 0; else
+		dest[res] = 0;
+	return res;
+}
+
+int parser_skip(parser_t *p, const chatset_t chatset)
+{
+	bool eof;
+	register int c;
+	int res = 0;
+	while (true)
+	{
+		CHECK_STATE();
+		if (eof) break;
+		c = (unsigned)*p->priv.curr;
+		if (!chatset[(uint8_t)c]) break;
+		res++;
+		INC_POS;
+	}
+	return res;
+}
+
+int parser_skip_n(parser_t *p, int n)
+{
+	bool eof;
+	int i;
+	for (i = 0; i < n; i++)
+	{
+		CHECK_STATE();
+		if (eof) return i;
+		INC_POS;
+	}
+	return n;
+}
+
+bool parser_skip_char(parser_t *p, char c)
+{
+	bool eof;
+	CHECK_STATE();
+	if (*p->priv.curr != c || eof) return false;
+	INC_POS;
+	return true;
+}
+
+bool parser_skip_ws(parser_t *p)
+{
+	bool eof;
+	bool res = false;
+	while (true)
+	{
+		CHECK_STATE();
+		if (eof) return res;
+		if (*p->priv.curr > 0x20) return res;
+		res = true;
+		INC_POS;
+	}
+}
+
+bool parser_skip_ws_in_line(parser_t *p)
+{
+	bool eof;
+	bool res = false;
+	while (true)
+	{
+		CHECK_STATE();
+		if (eof) return res;
+		if (*p->priv.curr > 0x20 || *p->priv.curr == '\r' || *p->priv.curr == '\n')
+			return res;
+		res = true;
+		INC_POS;
+	}
+}
+
+bool parser_skip_line(parser_t *p)
+{
+	bool eof;
+	while (true)
+	{
+		CHECK_STATE();
+		if (eof) return false;
+		if (*p->priv.curr == '\n')
+		{
+			INC_POS;
+			return true;
+		}
+		INC_POS;
+	}
+}
+
+bool parser_skip_before(parser_t *p, char c)
+{
+	bool eof;
+	while (true)
+	{
+		CHECK_STATE();
+		if (eof) return false;
+		if (*p->priv.curr == c) return true;
+		INC_POS;
+	}
+}
+
diff -r c2ca3c5ded62 -r 119c4f7144c8 http-server/cparser.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/http-server/cparser.h	Tue Jul 24 17:36:01 2018 +0000
@@ -0,0 +1,107 @@
+/*
+ * 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)
+ */
+
+#ifndef CPARSER_H
+#define CPARSER_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct parser parser_t;
+
+typedef size_t (*parser_read_t)(parser_t *parser, char *buffer, size_t size);
+typedef char chatset_t[256];
+
+typedef struct parser
+{
+    char          *buff;
+    size_t         buff_size;
+    void          *user_data;
+    parser_read_t  read_cb;
+    size_t         total;
+    size_t         line;
+    size_t         col;
+    struct
+    {
+        const char *curr;
+        const char *stop;
+    } priv;
+} parser_t;
+
+
+#define VAL_FMT_0X    1    // supports 0x12AB
+#define VAL_FMT_0H    2    // supports 0h12AB
+#define VAL_FMT_0B    4    // supports 0b01101001
+#define VAL_FMT_FLOAT 8    // supports 3.1415
+#define VAL_FMT_EXP   16   // supports 0.31415e+1
+#define VAL_FMT_SIGN  32   // supports +-3.1415
+#define VAL_FMT_ALL   0xFF
+
+#define STR_FMT_QUOTE 1     // supports "string"
+#define STR_FMT_APOST 2     // supports 'string'
+#define STR_FMT_NAKED 4     // supports  string
+#define STR_FMT_ESC   8     // supports \ escaping (bfnrt'"/\)
+#define STR_FMT_ESCU  16    // supports unicode escaping \u12AB (like json)
+#define STR_FMT_ESCW  32    // supports web escaping %2A
+#define STR_FMT_ALL   0xFF
+
+void parser_init(parser_t *p, const char *str, int len);
+void parser_init_s(parser_t *p, char *stream_buff, size_t size, parser_read_t cb);
+bool parser_is_next(parser_t *p, const char *str, int len);
+char parser_curr(parser_t *p);
+bool parser_eof(parser_t *p);
+
+int  parser_read(parser_t *p, const chatset_t chatset, char *dest, int dest_size);
+int  parser_read_before2(parser_t *p, char *dest, int dest_size, char c1, char c2);
+int  parser_read_digits(parser_t *p, char *dest, int dest_size, bool hex);
+int  parser_read_value(parser_t *p, char *dest, int dest_size, int fmt);
+int  parser_read_string(parser_t *p, char *dest, int dest_size, int fmt);
+#define parser_read_before(p, dest, dest_size, c) \
+	parser_read_before2(p, dest, dest_size, c, c);
+#define parser_read_before_line_end(p, dest, dest_size) \
+	parser_read_before2(p, dest, dest_size, '\r', '\n');
+
+int  parser_skip(parser_t *p, const chatset_t chatset);
+int  parser_skip_n(parser_t *p, int n);
+bool parser_skip_char(parser_t *p, char c);
+bool parser_skip_ws(parser_t *p);
+bool parser_skip_ws_in_line(parser_t *p);
+bool parser_skip_line(parser_t *p);
+bool parser_skip_before(parser_t *p, char c);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -r c2ca3c5ded62 -r 119c4f7144c8 http-server/htserv.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/http-server/htserv.c	Tue Jul 24 17:36:01 2018 +0000
@@ -0,0 +1,405 @@
+/*
+ * 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:   tiny http ipv4 server using lwip (pcb)
+ */
+
+#include "htserv.h"
+
+static struct tcp_pcb *http_pcb = NULL;
+
+htserv_on_req_t htserv_on_req = NULL;
+htserv_on_con_t htserv_on_con = NULL;
+htserv_on_err_t htserv_on_err = NULL;
+
+typedef enum htcon_state_priv
+{
+	CSP_ACTIVE,  // as CON_ACTIVE
+	CSP_CLOSING, // as CON_CLOSING
+	CSP_CLOSED,  // as CON_CLOSED
+	CSP_RD_REQ,
+	CSP_WR_RESP,
+	CSP_NONE,
+} htcon_state_priv_t;
+
+typedef struct htcon_priv
+{
+	htcon_t            htcon;      // user connection struct
+	char buff[HTTP_CON_BUFF_SIZE]; // request/response buffer
+	htcon_state_priv_t state;      // connection state
+	struct tcp_pcb    *pcb;        // connection pcb
+	http_reqb_t        reqb;       // request buffer
+	http_resp_t        resp;       // responce
+	// writing managment
+	char              *wbuff;      // writing buffer
+	int                wsize;      // writing buffer size
+	int                writed;     // writed to buffer
+	int                wsended;    // sended by tcp
+	int                wacksize;   // acknowledged
+} htcon_priv_t;
+
+htcon_priv_t htcons[HTTP_SERVER_MAX_CON];
+
+void htserv_io(void);
+
+void http_prepare_resp(http_resp_t *resp, int code)
+{
+	resp->code = code;
+	resp->server = HTTP_SERVER_NAME;
+	resp->cont_lang = HTTP_DEF_CONT_LANG;
+	resp->mime = MIME_TEXT_HTML;
+	resp->cont_len = 0;
+	resp->conn_type = CT_CLOSE;
+}
+
+void htcon_req_finished(htcon_priv_t *con)
+{
+	// 1. check request
+	if (con->reqb.req.method != METHOD_GET)
+	{
+		//http_prepare_resp(&con->resp, 405); // Method Not Allowed
+		//con->state = CSP_WR_RESP;
+		con->state = CSP_CLOSING;
+		htserv_io();
+		return;
+	}
+	// 2. ask user
+	if (htserv_on_req == NULL)
+	{
+		con->state = CSP_CLOSING;
+		htserv_io();
+		return;
+	}
+
+	http_prepare_resp(&con->resp, 200);
+
+	if (htserv_on_req(&con->reqb.req, &con->resp, &con->htcon.arg))
+	{
+		int len;
+		con->wbuff = con->buff + con->reqb.size;
+		con->wsize = HTTP_CON_BUFF_SIZE - con->reqb.size;
+		len = http_resp_str(&con->resp, con->wbuff, con->wsize);
+		if (len >= con->wsize)
+		{
+			if (htserv_on_err != NULL)
+				htserv_on_err(HTTP_RESP_NOMEM);
+			con->state = CSP_CLOSING;
+		}
+		else
+		{
+			con->state = CSP_WR_RESP;
+			con->wacksize = 0;
+			con->writed = len;
+			con->wsended = 0;
+		}
+	}
+	else
+		con->state = CSP_CLOSING;
+
+	htserv_io();
+}
+
+void htcon_received(htcon_priv_t *con, const char *data, int size)
+{
+	htserv_err_t err;
+	err = HTTP_NO_ERROR;
+
+	if (con->state == CSP_RD_REQ)
+	{
+		http_reqb_push(&con->reqb, data, size);
+		switch (con->reqb.state)
+		{
+		case REQB_UNFINISHED:
+			return;
+		case REQB_FINISHED:
+			htcon_req_finished(con);
+			return;
+		case REQB_REQ_TO_BIG:
+			err = HTTP_BIG_REQUEST;
+			break;
+		case REQB_SYNT_ERROR:
+			err = HTTP_REQ_SYNT_ERR;
+			break;
+		}
+	}
+	if (err != HTTP_NO_ERROR)
+	{
+		if (htserv_on_err != NULL)
+			htserv_on_err(err);
+		con->htcon.state = CON_CLOSING;
+		con->state = CSP_CLOSING;
+		htserv_io();
+	}
+}
+
+err_t htcon_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) 
+{
+	htcon_priv_t *con;
+
+	con = (htcon_priv_t *)arg;
+
+	if (err == ERR_OK && p != NULL)
+	{
+		tcp_recved(pcb, p->tot_len);
+		if (con != NULL)
+			htcon_received(con, p->payload, p->tot_len);
+	}
+
+	if (err == ERR_OK && p == NULL)
+	{
+		if (con != NULL)
+			if (con->state != CSP_CLOSED)
+			{
+				con->state = CSP_CLOSING;
+				con->htcon.state = CON_CLOSING;
+			}
+	}
+
+	if (p != NULL)
+		pbuf_free(p);
+
+	return ERR_OK;
+}
+
+static int find_free_con(void)
+{
+	int i;
+	for (i = 0; i < HTTP_SERVER_MAX_CON; i++)
+		if (htcons[i].state == CSP_NONE) return i;
+	return -1;
+}
+
+err_t htcon_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
+{
+	htcon_priv_t *con;
+	con = (htcon_priv_t *)arg;
+	if (con == NULL) return ERR_OK;
+	con->wacksize += len;
+//	if (con->state == CSP_ACTIVE)
+//		con->htcon.writed += len;
+	return ERR_OK;
+}
+
+void htcon_err(void *arg, err_t err)
+{
+	htcon_priv_t *con;
+	con = (htcon_priv_t *)arg;
+	if (con == NULL) return;
+	if (htserv_on_err != NULL)
+		htserv_on_err(HTTP_TRANSP_ERR);
+	con->htcon.state = CON_CLOSING;
+	con->state = CSP_CLOSING;
+}
+
+err_t htserv_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
+{
+	int index;
+	htcon_priv_t *con;
+
+	LWIP_UNUSED_ARG(arg);
+	LWIP_UNUSED_ARG(err);
+
+	index = find_free_con();
+	if (index < 0) return ERR_MEM;
+  con = &htcons[index];
+
+	con->pcb = newpcb;
+	http_reqb_init(&con->reqb, con->buff, HTTP_REQ_MAX_SIZE);
+	con->wbuff = NULL;
+	con->wsize = 0;
+	con->wsended = 0;
+	con->wacksize = 0;
+	con->state = CSP_RD_REQ;
+	
+	con->htcon.req = &con->reqb.req;
+	con->htcon.resp = &con->resp;
+	con->htcon.writed = 0;
+	con->htcon.arg = NULL;
+
+
+	tcp_setprio(newpcb, TCP_PRIO_MIN);
+	tcp_arg(newpcb, con);
+	tcp_recv(newpcb, htcon_recv);
+	tcp_err(newpcb, htcon_err);
+	tcp_sent(newpcb, htcon_sent);
+	tcp_poll(newpcb, NULL, 4); // No polling here
+
+	return ERR_OK;
+}
+
+void htserv_io(void) // state machine
+{
+	for (int i = 0; i < HTTP_SERVER_MAX_CON; i++)
+	{
+		htcon_priv_t *con;
+		con = &htcons[i];
+		if (con->state == CSP_NONE || con->state == CSP_CLOSED)
+			continue;
+
+		// have data to send?
+		if (con->state != CSP_CLOSING && con->writed > con->wsended)
+		{
+			int count;
+			err_t err;
+			count = con->writed - con->wsended;
+			if (count > tcp_sndbuf(con->pcb))
+				count = tcp_sndbuf(con->pcb);
+			err = tcp_write(con->pcb, con->wbuff + con->wsended, count, TCP_WRITE_FLAG_COPY);
+			if (err == ERR_OK)
+			{
+				con->wsended += count;
+				tcp_output(con->pcb);
+			}
+		}
+
+		// data was successfully sended?
+		if (con->state != CSP_CLOSING && con->wacksize >= con->wsended && con->wsended > 0)
+		{
+			con->writed = 0;
+			con->wsended = 0;
+			con->wacksize = 0;
+			// is it http-response?
+			if (con->state == CSP_WR_RESP)
+			{
+				con->htcon.state = CON_ACTIVE;
+				con->state = CSP_ACTIVE;
+				if (htserv_on_con != NULL)
+					htserv_on_con(i);
+			}
+			else
+			// is it document?
+			if (con->state == CSP_ACTIVE && con->resp.conn_type != CT_KEEP_ALIVE)
+				if (con->htcon.writed >= con->resp.cont_len)
+				{
+					con->htcon.state = CON_CLOSING;
+					con->state = CSP_CLOSING;
+				}
+		}
+
+		// closing connection?
+		if (con->state == CSP_CLOSING)
+			// if (tcp_close(con->pcb) == ERR_OK)
+			{
+				con->htcon.state = CON_CLOSED;
+				con->state = CSP_CLOSED;
+				con->pcb = NULL;
+			}
+	}
+}
+
+err_t htserv_init(uint16_t port)
+{
+	int i;
+	err_t err;
+	tcp_init();
+	htserv_free();
+	if (http_pcb != NULL)
+		return ERR_USE;
+	http_pcb = tcp_new();
+	if (http_pcb == NULL) return ERR_MEM;
+	err = tcp_bind(http_pcb, IP_ADDR_ANY, port);
+	if (err != ERR_OK)
+	{
+		htserv_free();
+		return err;
+	}
+	for (i = 0; i < HTTP_SERVER_MAX_CON; i++)
+		htcons[i].state = CSP_NONE;
+	return ERR_OK;
+}
+
+void htserv_free(void)
+{
+	if (http_pcb == NULL) return;
+	if (tcp_close(http_pcb) == ERR_OK)
+		http_pcb = NULL;
+}
+
+void htserv_tmr(uint32_t time_ms)                                               //TODO
+{
+  http_pcb = tcp_listen(http_pcb);
+  if (http_pcb != NULL)
+  {
+    tcp_accept(http_pcb, htserv_accept);
+  }
+  htserv_io();
+}
+
+const htcon_t *htcon(int index)
+{
+	if (index < 0 || index >= HTTP_SERVER_MAX_CON)
+		return NULL;
+	if (htcons[index].state > CSP_CLOSED) return NULL;
+	return &htcons[index].htcon;
+}
+
+void htcon_close(int index)
+{
+	htcon_priv_t *con;
+	con = (htcon_priv_t *)htcon(index);
+	if (con == NULL) return;
+	if (con->state == CSP_CLOSED) return;
+	con->htcon.state = CON_CLOSING;
+	con->state = CSP_CLOSING;
+	htserv_io();
+}
+
+void htcon_free(int index)
+{
+	htcon_priv_t *con;
+	con = (htcon_priv_t *)htcon(index);
+	if (con == NULL) return;
+	if (con->state != CSP_CLOSED) return;
+	htcons[index].state = CSP_NONE;
+}
+
+int htcon_write_avail(int index)
+{
+	htcon_priv_t *con;
+	con = (htcon_priv_t *)htcon(index);
+	if (con == NULL) return 0;
+	if (con->state != CSP_ACTIVE) return 0;
+	return con->wsize - con->writed;
+}
+
+int htcon_write(int index, const char *data, int size)
+{
+	int res;
+	htcon_priv_t *con;
+	con = (htcon_priv_t *)htcon(index);
+	if (con == NULL) return 0;
+	if (con->state != CSP_ACTIVE) return 0;
+	res = con->resp.cont_len - con->htcon.writed;
+	if (size < res) res = size;
+	if (con->wsize - con->writed < res)
+		res = con->wsize - con->writed;
+	if (res <= 0) return 0;
+	memcpy(con->wbuff + con->writed, data, res);
+	con->writed += res;
+	con->htcon.writed += res;
+	htserv_io();
+	return res;
+}
diff -r c2ca3c5ded62 -r 119c4f7144c8 http-server/htserv.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/http-server/htserv.h	Tue Jul 24 17:36:01 2018 +0000
@@ -0,0 +1,93 @@
+/*
+ * 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:   tiny http ipv4 server using lwip (pcb)
+ */
+
+#ifndef HTSERV_H
+#define HTSERV_H
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "lwip/netif.h"
+#include "lwip/pbuf.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/tcp.h"
+#include "http_req.h"
+
+// server configuration
+
+#define HTTP_SERVER_NAME    "lrndis" // http server name
+#define HTTP_SERVER_MAX_CON 3        // max connections number at one time
+#define HTTP_CON_BUFF_SIZE  2048     // size of con buffer, used for request/response data storing
+#define HTTP_REQ_MAX_SIZE   1024     // max part of con buffer for a request storing
+#define HTTP_DEF_CONT_LANG  "en"     // content language which will be sent to client by default
+
+// http server types
+
+typedef enum htserv_err
+{
+	HTTP_NO_ERROR,
+	HTTP_REQ_SYNT_ERR, // http request synt error
+	HTTP_BIG_REQUEST,  // http request size more then HTTP_CON_BUFF
+	HTTP_RESP_NOMEM,   // http response size more then HTTP_CON_BUFF
+	HTTP_TRANSP_ERR    // http transport error (client disconnected?)
+} htserv_err_t;
+
+typedef bool (*htserv_on_req_t)(const http_req_t *req, http_resp_t *resp, void **arg);
+typedef void (*htserv_on_con_t)(int index);
+typedef void (*htserv_on_err_t)(htserv_err_t err);
+
+typedef enum htcon_state
+{
+	CON_ACTIVE,
+	CON_CLOSING,
+	CON_CLOSED
+} htcon_state_t;
+
+typedef struct htcon
+{
+	void              *arg;
+	http_req_t        *req;
+	http_resp_t       *resp;
+	htcon_state_t      state;
+	int                writed;
+} htcon_t;
+
+extern htserv_on_req_t htserv_on_req;
+extern htserv_on_con_t htserv_on_con;
+extern htserv_on_err_t htserv_on_err;
+
+err_t          htserv_init(uint16_t port);
+void           htserv_free(void);
+void           htserv_tmr(uint32_t time_ms);
+const htcon_t *htcon(int index);
+void           htcon_close(int con);
+void           htcon_free(int con);
+int            htcon_write_avail(int con);
+int            htcon_write(int con, const char *data, int size);
+
+#endif
diff -r c2ca3c5ded62 -r 119c4f7144c8 http-server/http_req.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/http-server/http_req.c	Tue Jul 24 17:36:01 2018 +0000
@@ -0,0 +1,305 @@
+/*
+ * 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
diff -r c2ca3c5ded62 -r 119c4f7144c8 http-server/http_req.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/http-server/http_req.h	Tue Jul 24 17:36:01 2018 +0000
@@ -0,0 +1,148 @@
+/*
+ * 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)
+ */
+
+#ifndef HTTP_REQ_H
+#define HTTP_REQ_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "cparser.h"
+
+//#define HTTP_REQ_MAX_SIZE   512
+#define HTTP_REQ_MAX_PARAMS 32
+
+typedef enum http_method
+{
+	METHOD_NONE,
+	METHOD_GET,
+	METHOD_POST,
+	METHOD_HEAD,
+	METHOD_PUT,
+	METHOD_CONNECT,
+	METHOD_OPTIONS,
+	METHOD_DELETE,
+	METHOD_TRACE,
+	METHOD_PATCH
+} http_mt_t;
+
+typedef enum http_conn_type
+{
+	CT_NONE,
+	CT_CLOSE,
+	CT_KEEP_ALIVE
+} http_ct_t;
+
+extern const char MIME_TEXT_HTML[];   // text/html
+extern const char MIME_TEXT_HTML[];   // text/javascript
+extern const char MIME_TEXT_PLAIN[];  // text/plain
+extern const char MIME_TEXT_XML[];    // text/xml
+extern const char MIME_TEXT_CSS[];    // text/css
+extern const char MIME_IMAGE_GIF[];   // image/gif
+extern const char MIME_IMAGE_JPEG[];  // image/jpeg
+extern const char MIME_IMAGE_PJPEG[]; // image/pjpeg
+extern const char MIME_IMAGE_PNG[];   // image/png
+extern const char MIME_IMAGE_SVG[];   // image/svg+xml
+extern const char MIME_IMAGE_TIFF[];  // image/tiff
+extern const char MIME_IMAGE_ICON[];  // image/vnd.microsoft.icon
+extern const char MIME_IMAGE_WBMP[];  // image/vnd.wap.wbmp
+
+typedef struct http_req
+{
+	http_mt_t  method;                      // GET
+	char      *uri;                         //      /path/resource
+	int        num_params;                  //                      2
+	char      *params[HTTP_REQ_MAX_PARAMS]; //                      param1, param2
+	char      *values[HTTP_REQ_MAX_PARAMS]; //                      value1, value2
+	char      *ver;                         //                                      HTTP/1.1
+	char      *host;                        // Host:            [wikipedia.org]
+	char      *user_agent;                  // User-Agent:      [Mozilla/5.0]
+	char      *mime;                        // Content-Type:    [multipart/form-data; boundary="Asrf456BGe4h"]
+	int        cont_len;                    // Content-Length:  [123]
+	char      *accept;                      // Accept:          [text/html]
+	char      *accept_lang;                 // Accept-Language: [en-US;q=0.5,en;q=0.3]
+	char      *accept_enc;                  // Accept-Encoding: [gzip, deflate]
+	char      *cookie;                      // Cookie:          [Cookie data]
+	http_ct_t  conn_type;                   // Connection:      [keep-alive]
+	int        keep_alive;                  // Keep-Alive:      [300]
+} http_req_t;
+
+typedef struct http_resp
+{
+	int         code;                   // HTTP/1.1 [200] OK
+	const char *server;                 // Server: [lrndis]
+	const char *cont_lang;              // Content-Language: [ru]
+	const char *mime;                   // Content-Type: [text/html; charset=utf-8]
+	int         cont_len;               // Content-Length: [1234]
+	http_ct_t   conn_type;              // Connection: [close]
+} http_resp_t;
+
+// HTTP REQUEST BUFFER
+
+typedef enum http_reqb_state
+{
+	REQB_UNFINISHED,
+	REQB_FINISHED,
+	REQB_SYNT_ERROR,
+	REQB_REQ_TO_BIG
+} http_reqb_state_t;
+
+typedef struct http_reqb
+{
+	http_req_t req;
+	http_reqb_state_t state;
+	//char buff[HTTP_REQ_MAX_SIZE];
+	char *buff;
+	int bsize;
+	int size;
+	struct
+	{
+		int pos;
+		int state;
+	} parsing;
+} http_reqb_t;
+
+void http_reqb_init(http_reqb_t *reqb, void *buff, int size);
+int  http_reqb_avail(const http_reqb_t *reqb);
+void http_reqb_push(http_reqb_t *rb, const void *data, int size);
+int  http_resp_len(const http_resp_t *resp);
+int  http_resp_str(const http_resp_t *resp, char *str, int size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif