Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Revision 1:119c4f7144c8, committed 2018-07-24
- 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
--- /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;
+}
--- /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 */
--- /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;
+}
--- /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
--- /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;
+ }
+}
+
--- /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
--- /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;
+}
--- /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
--- /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
--- /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