├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── examples ├── esp8266 │ ├── Makefile │ ├── README.md │ └── user │ │ ├── user_config.h │ │ └── user_main.c ├── example.c ├── filesystem-example.c └── server-example.c ├── hardware └── ureq_esp8266.h ├── include ├── ureq_core.h ├── ureq_defines.h ├── ureq_env_dep.h ├── ureq_filesystem.h ├── ureq_fwd.h ├── ureq_http_types.h └── ureq_pages.h ├── packer ├── input │ ├── 2kb.jpg │ ├── 2kb.png │ ├── 4kb.txt │ ├── index.html │ ├── style.css │ ├── test.json │ └── test.xml └── packer.py └── ureq.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Debug files 32 | *.dSYM/ 33 | 34 | server 35 | example 36 | fs 37 | .DS_Store 38 | *.bin 39 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | script: make -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 solusipse 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # TODO: make single makefile 2 | 3 | CC=gcc 4 | CFLAGS+=-O2 -Wall 5 | LIBS= 6 | 7 | ifeq ($(OS),Windows_NT) 8 | LIBS+=-lws2_32 9 | endif 10 | 11 | all: 12 | $(CC) -o example $(CFLAGS) examples/example.c $(LIBS) 13 | $(CC) -o server $(CFLAGS) examples/server-example.c $(LIBS) 14 | $(CC) -o fs $(CFLAGS) examples/filesystem-example.c $(LIBS) 15 | 16 | esp8266: 17 | $(MAKE) -C ./esp8266/ 18 | 19 | clean: 20 | rm -f example 21 | rm -f server 22 | rm -f fs 23 | 24 | .PHONY: all esp8266 clean 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### ureq 2 | 3 | -------- 4 | 5 | ### Status 6 | [![Build Status](https://travis-ci.org/solusipse/ureq.svg?branch=master)](https://travis-ci.org/solusipse/ureq) 7 | [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/solusipse/ureq.svg)](http://isitmaintained.com/project/solusipse/ureq "Average time to resolve an issue") 8 | [![Percentage of issues still open](http://isitmaintained.com/badge/open/solusipse/ureq.svg)](http://isitmaintained.com/project/solusipse/ureq "Percentage of issues still open") 9 | 10 | ------------------------------------------------------------------------------- 11 | 12 | ## Description 13 | 14 | Micro C library for handling HTTP requests on low resource systems. Please note that ureq is still in heavy development and new features are continuously added. Despite this, it behaves very well in stability tests and current user-end interface won't be probably changed. 15 | 16 | ------------------------------------------------------------------------------- 17 | 18 | ## How does it work? 19 | 20 | ureq is a middleware that sits between any kind of tcp server and the core of your application. Use it to **dispatch urls to functions**, like this: `ureq_serve("/", my_function, UREQ_GET);`. It supports **basic http functionality** and can be used anywhere **system resources are low** (it was built for embedded systems, e.g. ESP8266) or where sophisticated http features aren't needed. And yes, **it understands RESTful**. 21 | 22 | ## Basic usage 23 | 24 | As said before, `ureq` is used for dispatching. **Let's add a couple of urls then**: 25 | ``` 26 | ureq_serve("/", s_home, UREQ_GET); 27 | ureq_serve("/post", s_post, UREQ_POST); 28 | ... 29 | ureq_serve("/all", s_all, UREQ_ALL); 30 | 31 | ``` 32 | How does it work? **When there's a request for an url** (`/`, `/all`, `/post`), **with corresponding method** (`UREQ_GET`, `UREQ_POST`, `UREQ_PUT`, `UREQ_DELETE`, `UREQ_ALL`), **ureq calls the corresponding function** (`s_home()`, `s_post()`, ..., `s_all()`). What's `UREQ_ALL` method? It calls a function connected to the url, no matter which type of method was used. 33 | 34 | But wait, how should such function look like? The only requirement is that **it has to return a string** that will be sent to a client. For example: 35 | 36 | ``` 37 | char *some_function() { 38 | return "

Some text!

"; 39 | } 40 | ``` 41 | 42 | You want to **parse GET parameters** or do something with **POST data**? No problem, just define your function with a pointer to `HttpRequest`: 43 | 44 | ``` 45 | char *fancy_function(HttpRequest *r) { 46 | // Do something here, see detailed usage and examples for more info 47 | return "

Some text!

"; 48 | } 49 | ``` 50 | 51 | Keep in mind that adding struct argument to the function's definition is **not obligatory**. 52 | 53 | **We connected some urls to functions**, what's next? 54 | 55 | We have to **create a structure** that holds all the data and initialize it. And we have to do it per every request. Simply put such line in your program: 56 | ``` 57 | HttpRequest r = ureq_init(request); 58 | ``` 59 | `request` in that case is everything you got from the client. 60 | 61 | Now let the magic happen. Put such loop in your code: 62 | ``` 63 | while(ureq_run(&r)) send(r.response.data, r.len); 64 | ``` 65 | and do all the sending job inside it (`send` is our sending function in that case, remember to replace it with one used by your server backend, `r.response.data` is the response generated by ureq, and `req.len` is its length. 66 | 67 | If you wish, do something else with `HttpRequest` now. When you're ready, call 68 | ``` 69 | ureq_close(&r); 70 | ``` 71 | To perform a **cleanup**. See `examples/example.c` for basic server-less request parser. See `examples/example-server.c` for basic unix http server. 72 | 73 | **That's all.** 74 | 75 | ------------------------------------------------------------------------------- 76 | 77 | Let's summarize: 78 | 79 | ``` 80 | #include "ureq.h" 81 | 82 | char *s_home() { 83 | return "home"; 84 | } 85 | 86 | int main() { 87 | ureq_serve("/", s_home, UREQ_GET); 88 | 89 | char request[] = "GET / HTTP/1.1\n" 90 | "Host: 127.0.0.1:80\n"; 91 | 92 | HttpRequest r = ureq_init(request); 93 | while(ureq_run(&r)) printf("%s\n", r.response.data); 94 | 95 | ureq_close(&r); 96 | ureq_finish(); 97 | 98 | return 0; 99 | } 100 | ``` 101 | 102 | ------------------------------------------------------------------------------- 103 | 104 | ## Detailed usage 105 | This part of `README` needs to be improved, please treat it as an early draft. 106 | 107 | To take **precise control of server's response**, modify **HttpRequest** struct's fields **inside page connected to framework via `ureq_serve`**. Reading the [interface](./ureq.h) file and the [type-definitions](./include/ureq_http_types.h) file may provide many useful information. 108 | 109 | Let's take a look at `UreqResponse` struct, which is initialized in every `HttpRequest` struct: 110 | 111 | ``` 112 | typedef struct ureq_response_t { 113 | int code; 114 | char *mime; 115 | char *header; 116 | char *data; 117 | char *file; 118 | } UreqResponse; 119 | ``` 120 | 121 | Except `*data`, these parameters can be set from your page functions. Some examples: 122 | 123 | #### Set response code 124 | ``` 125 | r->response.code = 302; 126 | ``` 127 | 128 | #### Set custom response header 129 | ``` 130 | r->response.header = "Location: /"; 131 | ``` 132 | 133 | #### Set response mime-type: 134 | ``` 135 | r->reponse.mime = "text/plain"; 136 | ``` 137 | 138 | ## Load test 139 | With the help of Reddit and Hacker News users, `ureq` was benchmarked on the [`ESP8266`](http://i.imgur.com/3fyAXN0.jpg). 140 | 141 | ![charts](https://solusipse.net/varia/esp8266/esp8266stats.png) 142 | 143 | For more data, go to: http://ureq.solusipse.net. 144 | 145 | ## Roadmap 146 | - minimal templating system 147 | - file sending on unix systems 148 | - todos from comments in files 149 | 150 | ## License 151 | See the [LICENSE](LICENSE) file for license rights and limitations (MIT). 152 | -------------------------------------------------------------------------------- /examples/esp8266/Makefile: -------------------------------------------------------------------------------- 1 | # That's an example Makefile for esp8266 projects 2 | # It was taken from: https://github.com/esp8266/source-code-examples 3 | # You probably has to change some paths in this file: 4 | # - XTENSA_TOOLS_ROOT 5 | # - SDK_BASE 6 | 7 | # Output directors to store intermediate compiled files 8 | # relative to the project directory 9 | BUILD_BASE = build 10 | FW_BASE = firmware 11 | 12 | # base directory for the compiler 13 | XTENSA_TOOLS_ROOT ?= /Volumes/case-sensitive/esp-open-sdk/xtensa-lx106-elf/bin 14 | 15 | # base directory of the ESP8266 SDK package, absolute 16 | SDK_BASE ?= /Volumes/case-sensitive/esp-open-sdk/esp_iot_sdk_v1.2.0/ 17 | 18 | # esptool.py path and port 19 | ESPTOOL ?= esptool.py 20 | ESPPORT ?= /dev/ttyUSB0 21 | 22 | # name for the target project 23 | TARGET = app 24 | 25 | # which modules (subdirectories) of the project to include in compiling 26 | MODULES = driver user 27 | EXTRA_INCDIR = include $(SDK_BASE)/../include 28 | 29 | # libraries used in this project, mainly provided by the SDK 30 | LIBS = c gcc hal pp phy net80211 lwip wpa main 31 | 32 | # compiler flags using during compilation of source files 33 | CFLAGS = -Os -g -O2 -Wpointer-arith -Wundef -Werror -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH 34 | 35 | # linker flags used to generate the main object file 36 | LDFLAGS = -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static 37 | 38 | # linker script used for the above linkier step 39 | LD_SCRIPT = eagle.app.v6.ld 40 | 41 | # various paths from the SDK used in this project 42 | SDK_LIBDIR = lib 43 | SDK_LDDIR = ld 44 | SDK_INCDIR = include include/json 45 | 46 | # we create two different files for uploading into the flash 47 | # these are the names and options to generate them 48 | FW_FILE_1_ADDR = 0x00000 49 | FW_FILE_2_ADDR = 0x40000 50 | 51 | # select which tools to use as compiler, librarian and linker 52 | CC := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc 53 | AR := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-ar 54 | LD := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc 55 | 56 | 57 | 58 | #### 59 | #### no user configurable options below here 60 | #### 61 | SRC_DIR := $(MODULES) 62 | BUILD_DIR := $(addprefix $(BUILD_BASE)/,$(MODULES)) 63 | 64 | SDK_LIBDIR := $(addprefix $(SDK_BASE)/,$(SDK_LIBDIR)) 65 | SDK_INCDIR := $(addprefix -I$(SDK_BASE)/,$(SDK_INCDIR)) 66 | 67 | SRC := $(foreach sdir,$(SRC_DIR),$(wildcard $(sdir)/*.c)) 68 | OBJ := $(patsubst %.c,$(BUILD_BASE)/%.o,$(SRC)) 69 | LIBS := $(addprefix -l,$(LIBS)) 70 | APP_AR := $(addprefix $(BUILD_BASE)/,$(TARGET)_app.a) 71 | TARGET_OUT := $(addprefix $(BUILD_BASE)/,$(TARGET).out) 72 | 73 | LD_SCRIPT := $(addprefix -T$(SDK_BASE)/$(SDK_LDDIR)/,$(LD_SCRIPT)) 74 | 75 | INCDIR := $(addprefix -I,$(SRC_DIR)) 76 | EXTRA_INCDIR := $(addprefix -I,$(EXTRA_INCDIR)) 77 | MODULE_INCDIR := $(addsuffix /include,$(INCDIR)) 78 | 79 | FW_FILE_1 := $(addprefix $(FW_BASE)/,$(FW_FILE_1_ADDR).bin) 80 | FW_FILE_2 := $(addprefix $(FW_BASE)/,$(FW_FILE_2_ADDR).bin) 81 | 82 | V ?= $(VERBOSE) 83 | ifeq ("$(V)","1") 84 | Q := 85 | vecho := @true 86 | else 87 | Q := @ 88 | vecho := @echo 89 | endif 90 | 91 | vpath %.c $(SRC_DIR) 92 | 93 | define compile-objects 94 | $1/%.o: %.c 95 | $(vecho) "CC $$<" 96 | $(Q) $(CC) $(INCDIR) $(MODULE_INCDIR) $(EXTRA_INCDIR) $(SDK_INCDIR) $(CFLAGS) -c $$< -o $$@ 97 | endef 98 | 99 | .PHONY: all checkdirs flash clean 100 | 101 | all: checkdirs $(TARGET_OUT) $(FW_FILE_1) $(FW_FILE_2) 102 | 103 | $(FW_BASE)/%.bin: $(TARGET_OUT) | $(FW_BASE) 104 | $(vecho) "FW $(FW_BASE)/" 105 | $(Q) $(ESPTOOL) elf2image -o $(FW_BASE)/ $(TARGET_OUT) 106 | 107 | $(TARGET_OUT): $(APP_AR) 108 | $(vecho) "LD $@" 109 | $(Q) $(LD) -L$(SDK_LIBDIR) $(LD_SCRIPT) $(LDFLAGS) -Wl,--start-group $(LIBS) $(APP_AR) -Wl,--end-group -o $@ 110 | 111 | $(APP_AR): $(OBJ) 112 | $(vecho) "AR $@" 113 | $(Q) $(AR) cru $@ $^ 114 | 115 | checkdirs: $(BUILD_DIR) $(FW_BASE) 116 | 117 | $(BUILD_DIR): 118 | $(Q) mkdir -p $@ 119 | 120 | $(FW_BASE): 121 | $(Q) mkdir -p $@ 122 | 123 | flash: $(FW_FILE_1) $(FW_FILE_2) 124 | $(ESPTOOL) --port $(ESPPORT) write_flash $(FW_FILE_1_ADDR) $(FW_FILE_1) $(FW_FILE_2_ADDR) $(FW_FILE_2) 125 | 126 | clean: 127 | $(Q) rm -rf $(FW_BASE) $(BUILD_BASE) 128 | 129 | $(foreach bdir,$(BUILD_DIR),$(eval $(call compile-objects,$(bdir)))) 130 | -------------------------------------------------------------------------------- /examples/esp8266/README.md: -------------------------------------------------------------------------------- 1 | ## ESP8266 2 | 3 | That's an example http server implementation for ESP8266. Type `make` to compile. 4 | 5 | You have to install [ESP Open SDK](https://github.com/pfalcon/esp-open-sdk) first. Remember to export path, i.e.: 6 | 7 | ``` 8 | export PATH=/Volumes/case-sensitive/esp-open-sdk/xtensa-lx106-elf/bin/:$PATH 9 | ``` 10 | 11 | -------------------------------------------------------------------------------- /examples/esp8266/user/user_config.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solusipse/ureq/a413e8c3c2b935f6fbfd3ee76623e4342e335cfa/examples/esp8266/user/user_config.h -------------------------------------------------------------------------------- /examples/esp8266/user/user_main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define UREQ_ESP8266 13 | #define UREQ_USE_FILESYSTEM 14 | 15 | #include "../../../ureq.h" 16 | 17 | // 6 works best 18 | // this value was obtained experimentally 19 | #define MAX_CONNS 6 20 | 21 | struct HttpConnection { 22 | HttpRequest r; 23 | int id; 24 | }; 25 | 26 | struct HttpConnection conns[MAX_CONNS]; 27 | 28 | //const char ssid[32] = "ssid"; 29 | //const char password[32] = "password"; 30 | 31 | bool gpioStatus = false; 32 | 33 | // Data structure for the configuration of your wireless network. 34 | // Will contain ssid and password for your network. 35 | struct station_config stationConf; 36 | 37 | /* 38 | void ICACHE_FLASH_ATTR setGpio( int status ) { 39 | if (status == 0) 40 | gpio_output_set(BIT0, 0, BIT0, 0); 41 | else 42 | gpio_output_set(0, BIT0, BIT0, 0); 43 | } 44 | 45 | void ICACHE_FLASH_ATTR reverseGpio() { 46 | if (GPIO_REG_READ(GPIO_OUT_ADDRESS) & BIT0) { 47 | //Set GPIO2 to LOW 48 | gpio_output_set(0, BIT0, BIT0, 0); 49 | } else { 50 | //Set GPIO2 to HIGH 51 | gpio_output_set(BIT0, 0, BIT0, 0); 52 | } 53 | } 54 | */ 55 | 56 | void ICACHE_FLASH_ATTR ssRecvCb(void *arg, char *data, unsigned short len) { 57 | char buf[64]; 58 | int l; 59 | struct espconn *pespconn = (struct espconn *)arg; 60 | 61 | if (strlen(data) >= 1024) { 62 | // This is esp8266 specific, it just has too little memory 63 | // to answer all big requests with 413 64 | espconn_disconnect(pespconn); 65 | return; 66 | } 67 | 68 | HttpRequest r = ureq_init(data); 69 | 70 | ureq_run(&r); 71 | 72 | int i; 73 | for(i=0; i< MAX_CONNS-1; i++) { 74 | if (conns[i].id == 0) 75 | break; 76 | if (i == MAX_CONNS) { 77 | conns[0].id == 0; 78 | i = 0; 79 | break; 80 | } 81 | } 82 | 83 | conns[i].r = r; 84 | conns[i].id = pespconn->proto.tcp->remote_port; 85 | 86 | espconn_sent(pespconn, r.response.data, r.len); 87 | os_printf("Request %s from %d.%d.%d.%d. Status: %d. ID: %d.\n", 88 | r.url, 89 | pespconn->proto.tcp->remote_ip[0], 90 | pespconn->proto.tcp->remote_ip[1], 91 | pespconn->proto.tcp->remote_ip[2], 92 | pespconn->proto.tcp->remote_ip[3], 93 | r.response.code, 94 | conns[i].id); 95 | } 96 | 97 | struct HttpConnection *getHttpConnection(void *arg) { 98 | struct espconn *c = (struct espconn *)arg; 99 | int i; 100 | for(i=0; iproto.tcp->remote_port) 102 | return &conns[i]; 103 | } 104 | return NULL; 105 | } 106 | 107 | void ICACHE_FLASH_ATTR ssSentCb(void *arg) { 108 | struct espconn *pespconn = (struct espconn *)arg; 109 | struct HttpConnection *c = getHttpConnection(pespconn); 110 | 111 | if (c->r.complete == 1) { 112 | espconn_disconnect(pespconn); 113 | return; 114 | } 115 | 116 | ureq_run(&c->r); 117 | espconn_sent(pespconn, c->r.response.data, c->r.len); 118 | if (c->r.len < 1024) { 119 | espconn_disconnect(pespconn); 120 | } 121 | 122 | } 123 | 124 | void ICACHE_FLASH_ATTR ssDiscCb(void *arg) { 125 | struct espconn *pespconn = (struct espconn *)arg; 126 | struct HttpConnection *c = getHttpConnection(pespconn); 127 | if (!c) return; 128 | ureq_close(&c->r); 129 | c->id = 0; 130 | } 131 | 132 | void ICACHE_FLASH_ATTR ssConnCb(void *arg) { 133 | struct espconn *pespconn = (struct espconn *)arg; 134 | 135 | //printf("HEAP SIZE: %d\n", system_get_free_heap_size()); 136 | 137 | if (pespconn->link_cnt > MAX_CONNS) { 138 | espconn_disconnect(pespconn); 139 | os_printf("Too many connections at once.\n"); 140 | return; 141 | } 142 | 143 | espconn_regist_recvcb(pespconn, ssRecvCb); 144 | espconn_regist_sentcb(pespconn, ssSentCb); 145 | espconn_regist_disconcb(pespconn, ssDiscCb); 146 | } 147 | 148 | void ICACHE_FLASH_ATTR ssServerInit() { 149 | 150 | struct espconn * pSimpleServer; 151 | 152 | pSimpleServer = (struct espconn *)os_zalloc(sizeof(struct espconn)); 153 | ets_memset( pSimpleServer, 0, sizeof( struct espconn ) ); 154 | 155 | espconn_create( pSimpleServer ); 156 | pSimpleServer->type = ESPCONN_TCP; 157 | pSimpleServer->state = ESPCONN_NONE; 158 | pSimpleServer->proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp)); 159 | pSimpleServer->proto.tcp->local_port = 80; 160 | 161 | //espconn_set_opt(pSimpleServer, ESPCONN_REUSEADDR); 162 | 163 | espconn_regist_connectcb(pSimpleServer, ssConnCb); 164 | espconn_accept(pSimpleServer); 165 | espconn_regist_time(pSimpleServer, 0, 0); 166 | } 167 | 168 | void ICACHE_FLASH_ATTR wifiInit() { 169 | // TODO: wifi configuration via http 170 | //os_memcpy(&stationConf.ssid, ssid, 32); 171 | //os_memcpy(&stationConf.password, password, 32); 172 | // STATION_MODE, SOFTAP_MODE or STATIONAP_MODE. 173 | wifi_set_opmode( STATION_MODE ); 174 | //wifi_set_opmode( STATION_MODE ); 175 | 176 | wifi_station_set_config(&stationConf); 177 | } 178 | 179 | char *s_home() { 180 | return "Hello World!"; 181 | } 182 | 183 | char *s_get(HttpRequest *r) { 184 | /* This one shows how to handle GET parameters. 185 | * Please note, that ureq_get_param_value uses 186 | * common buffer for all operations, so store 187 | * copy data from it before calling it again */ 188 | char *arg; 189 | 190 | strcpy(r->buffer, "data: "); 191 | arg = ureq_get_param_value(r, "data"); 192 | strcat(r->buffer, arg); 193 | 194 | strcat(r->buffer, "
data2: "); 195 | arg = ureq_get_param_value(r, "data2"); 196 | strcat(r->buffer, arg); 197 | 198 | return r->buffer; 199 | } 200 | 201 | char *s_post(HttpRequest *r) { 202 | if ( strcmp(UREQ_POST, r->type) != 0 ) 203 | return "Try requesting this page with POST method!
" 204 | "Feel free to use this form:
" 205 | "
" 206 | "Data:
" 207 | "Data2:
" 208 | "" 209 | "
"; 210 | 211 | char *arg; 212 | 213 | strcpy(r->buffer, "data: "); 214 | arg = ureq_post_param_value(r, "data"); 215 | strcat(r->buffer, arg); 216 | 217 | strcat(r->buffer, "
data2: "); 218 | arg = ureq_post_param_value(r, "data2"); 219 | strcat(r->buffer, arg); 220 | 221 | return r->buffer; 222 | } 223 | 224 | char *s_redirect(HttpRequest *r) { 225 | r->response.code = 302; 226 | r->response.header = "Location: /redirected"; 227 | return ""; 228 | } 229 | 230 | char *s_redirected() { 231 | return "Redirected from /redirect"; 232 | } 233 | 234 | void user_init(void) { 235 | // Uart init 236 | uart_div_modify(0, UART_CLK_FREQ / 115200); 237 | os_printf("\nStarting...\n"); 238 | 239 | int i; 240 | for(i=0; iresponse.mime = "text/plain"; 47 | return "home"; 48 | } 49 | 50 | char *s_header(HttpRequest *r) { 51 | /* This will redirect client to /test */ 52 | r->response.code = 302; 53 | r->response.header = "Location: /test"; 54 | return ""; 55 | } 56 | 57 | char *s_param(HttpRequest *r) { 58 | /* This one shows how to handle GET parameters. 59 | * Please note, that ureq_get_param_value uses 60 | * common buffer for all operations, so store 61 | * copy data from it before calling it again */ 62 | char *arg = NULL; 63 | 64 | strcpy(r->buffer, "data: "); 65 | arg = ureq_get_param_value(r, "data"); 66 | strcat(r->buffer, arg); 67 | 68 | strcat(r->buffer, "
data2: "); 69 | arg = ureq_get_param_value(r, "data2"); 70 | strcat(r->buffer, arg); 71 | 72 | return r->buffer; 73 | } 74 | 75 | char *s_all() { 76 | /* No mater which method client used, if he requested this one, 77 | * he'll always get it */ 78 | return "All requests welcomed"; 79 | } 80 | 81 | char *s_post(HttpRequest *r) { 82 | printf("PARAMS: %s\n", r->params); 83 | printf("BODY: %s\n", r->body); 84 | 85 | /* 86 | char *arg = ureq_get_param_value(r->params, "test2"); 87 | 88 | if ( strcmp( arg, "2" ) == 0 ) { 89 | printf("POST OK!\n"); 90 | } 91 | 92 | //free(arg); 93 | */ 94 | 95 | return "OK"; 96 | } 97 | 98 | char *s_buf() { 99 | return "test"; 100 | } 101 | 102 | char *s_404() { 103 | return "Custom 404 page!"; 104 | } 105 | 106 | /* --------------------------------^ PAGES ^-------------------------------- */ 107 | 108 | int main() { 109 | 110 | /* 111 | Before doing anything, set some urls to be dispatched when connection 112 | will come. First, set url, then corresponding function, then method 113 | that will be connected to that url. 114 | */ 115 | ureq_serve("/", s_home, UREQ_GET); 116 | ureq_serve("/header", s_header, UREQ_GET); 117 | ureq_serve("/param", s_param, UREQ_GET); 118 | ureq_serve("/all", s_all, UREQ_ALL); 119 | ureq_serve("/post", s_post, UREQ_POST); 120 | ureq_serve("/buffer", s_buf, UREQ_GET); 121 | 122 | //ureq_serve("404", s_404, ALL); 123 | 124 | char request[] = "GET / HTTP/1.1\r\n" 125 | "Host: 127.0.0.1:80\r\n"; 126 | 127 | HttpRequest r = ureq_init(request); 128 | 129 | while(ureq_run(&r)) printf("%s\n", r.response.data); 130 | 131 | ureq_close(&r); 132 | ureq_finish(); 133 | 134 | return 0; 135 | } 136 | -------------------------------------------------------------------------------- /examples/filesystem-example.c: -------------------------------------------------------------------------------- 1 | /* 2 | https://github.com/solusipse/ureq 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2015 solusipse 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #include "../ureq.h" 28 | 29 | int main() { 30 | 31 | //ureq_fs_open(); 32 | 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /examples/server-example.c: -------------------------------------------------------------------------------- 1 | /* 2 | https://github.com/solusipse/ureq 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2015 solusipse 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /* 28 | SCROLL DOWN FOR ACTUAL EXAMPLE! 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #ifndef _WIN32 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #else 42 | #include 43 | #undef close 44 | #define close(s) closesocket(s) 45 | #undef write 46 | #define write(s,b,n) send(s,b,n,0) 47 | #undef read 48 | #define read(s,b,n) recv(s,b,n,0) 49 | #define bzero(p,n) memset(p,0,n) 50 | #endif 51 | #include 52 | 53 | #include "../ureq.h" 54 | 55 | #define BUFSIZE 1024 56 | #define QUEUE_SIZE 100 57 | #define PORT 9999 58 | 59 | void server(char *buffer, int socket); 60 | int create_socket(); 61 | int main_b(); 62 | void error(); 63 | struct sockaddr_in set_address(struct sockaddr_in serveraddr); 64 | void bind_to_port(int listen_socket, struct sockaddr_in serveraddr); 65 | 66 | 67 | void get_client_address(struct sockaddr_in client_address) { 68 | struct hostent *hostp = NULL; 69 | char *hostaddrp = NULL; 70 | hostp = gethostbyaddr((const char *)&client_address.sin_addr.s_addr, sizeof(client_address.sin_addr.s_addr), AF_INET); 71 | if (hostp == NULL) error(); 72 | hostaddrp = inet_ntoa(client_address.sin_addr); 73 | if (hostaddrp == NULL) error(); 74 | printf("Client: %s (%s)\n", hostaddrp, hostp->h_name); 75 | } 76 | 77 | void perform_connection(int connection_socket, int listen_socket, struct sockaddr_in client_address, socklen_t address_lenght) { 78 | char buffer[BUFSIZE]; 79 | connection_socket = accept(listen_socket, (struct sockaddr *) &client_address, &address_lenght); 80 | if (connection_socket < 0) error(); 81 | bzero(buffer, BUFSIZE); 82 | read(connection_socket, buffer, BUFSIZE); 83 | server(buffer, connection_socket); 84 | close(connection_socket); 85 | //exit(0); 86 | } 87 | 88 | int main(int argc, char **argv) { 89 | #ifdef _WIN32 90 | WSADATA wsaData; 91 | WSAStartup(MAKEWORD(2,0), &wsaData); 92 | #endif 93 | main_b(); 94 | int listen_socket, connection_socket = 0, address_lenght, optval = 1; 95 | struct sockaddr_in server_address, client_address; 96 | listen_socket = create_socket(); 97 | setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval , sizeof(int)); 98 | server_address = set_address(server_address); 99 | bind_to_port(listen_socket, server_address); 100 | address_lenght = sizeof(client_address); 101 | while (1) perform_connection(connection_socket, listen_socket, client_address, address_lenght); 102 | } 103 | 104 | int create_socket() { 105 | int lsocket = socket(AF_INET, SOCK_STREAM, 0); 106 | if (lsocket < 0) 107 | error(); 108 | return lsocket; 109 | } 110 | 111 | void error() { 112 | perror("ERROR"); 113 | exit(1); 114 | } 115 | 116 | struct sockaddr_in set_address(struct sockaddr_in server_address) { 117 | bzero((char *) &server_address, sizeof(server_address)); 118 | server_address.sin_family = AF_INET; 119 | server_address.sin_addr.s_addr = htonl(INADDR_ANY); 120 | server_address.sin_port = htons((unsigned short)PORT); 121 | return server_address; 122 | } 123 | 124 | void bind_to_port(int listen_socket, struct sockaddr_in server_address) { 125 | if (bind(listen_socket, (struct sockaddr *) &server_address, sizeof(server_address)) < 0) 126 | error(); 127 | if (listen(listen_socket, QUEUE_SIZE) < 0) 128 | error(); 129 | } 130 | 131 | 132 | /* 133 | ACTUAL EXAMPLE STARTS HERE 134 | see example.c for more explainations 135 | ------------------------------------------------------------------------------- 136 | Above that line is just simple and dirty tcp server. 137 | Interesting part begins here. 138 | */ 139 | 140 | char *s_home(HttpRequest *r) { 141 | return "

home

"; 142 | } 143 | 144 | char *s_test() { 145 | return "test"; 146 | } 147 | 148 | char *s_param(HttpRequest *r) { 149 | /* This one shows how to handle GET parameters. 150 | * Please note, that ureq_get_param_value uses 151 | * common buffer for all operations, so store 152 | * copy data from it before calling it again */ 153 | char *arg = NULL; 154 | 155 | strcpy(r->buffer, "data: "); 156 | arg = ureq_get_param_value(r, "data"); 157 | strcat(r->buffer, arg); 158 | 159 | strcat(r->buffer, "
data2: "); 160 | arg = ureq_get_param_value(r, "data2"); 161 | strcat(r->buffer, arg); 162 | 163 | return r->buffer; 164 | } 165 | 166 | char *s_post(HttpRequest *r) { 167 | if ( strcmp(UREQ_POST, r->type) != 0 ) 168 | return "Try requesting this page with POST method!
" 169 | "Feel free to use this form:
" 170 | "
" 171 | "Data:
" 172 | "Data2:
" 173 | "" 174 | "
"; 175 | 176 | char *arg = NULL; 177 | 178 | strcpy(r->buffer, "data: "); 179 | arg = ureq_post_param_value(r, "data"); 180 | strcat(r->buffer, arg); 181 | 182 | strcat(r->buffer, "
data2: "); 183 | arg = ureq_post_param_value(r, "data2"); 184 | strcat(r->buffer, arg); 185 | 186 | return r->buffer; 187 | } 188 | 189 | char *s_redirect_to_test(HttpRequest *r) { 190 | r->response.code = 302; 191 | r->response.header = "Location: /test"; 192 | return ""; 193 | } 194 | 195 | char *s_template(HttpRequest *r) { 196 | r->response.file = r->buffer; 197 | ureq_template(r, "first", "Yes"); 198 | ureq_template(r, "second", "working"); 199 | return "Does it work? {{first}}. Another keyword: {{second}}."; 200 | } 201 | 202 | char *s_exit() { 203 | // Using it only during development for memory leakage testing 204 | exit(1); 205 | return ""; 206 | } 207 | 208 | int main_b() { 209 | /* 210 | That's a part of example main function. Declare urls to be served 211 | before receiving any requests. 212 | */ 213 | 214 | ureq_serve("/", s_home, UREQ_GET); 215 | ureq_serve("/test", s_test, UREQ_GET); 216 | ureq_serve("/post", s_post, UREQ_ALL); 217 | ureq_serve("/param", s_param, UREQ_GET); 218 | ureq_serve("/redirect", s_redirect_to_test, UREQ_GET); 219 | ureq_serve("/template", s_template, UREQ_GET); 220 | 221 | ureq_serve("/exit", s_exit, UREQ_GET); 222 | 223 | return 0; 224 | } 225 | 226 | void server(char *buffer, int socket) { 227 | 228 | clock_t start = clock(); 229 | HttpRequest req = ureq_init(buffer); 230 | 231 | while(ureq_run(&req)) { 232 | write(socket, req.response.data, req.len); 233 | } 234 | 235 | clock_t end = clock(); 236 | float seconds = (float)(end - start) / CLOCKS_PER_SEC; 237 | 238 | printf("Requested: %s (%s). Response: %d. It took: %f s.\n", \ 239 | req.url, req.type, req.response.code, seconds); 240 | 241 | ureq_close(&req); 242 | } 243 | -------------------------------------------------------------------------------- /hardware/ureq_esp8266.h: -------------------------------------------------------------------------------- 1 | /* 2 | https://github.com/solusipse/ureq 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2015 solusipse 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #ifndef UREQ_ESP8266_H 28 | #define UREQ_ESP8266_H 29 | 30 | #include 31 | #include 32 | 33 | #define realloc ureq_realloc 34 | #define malloc ureq_malloc 35 | #define free ureq_free 36 | 37 | #define printf(...) os_printf(__VA_ARGS__) 38 | #define sprintf(...) os_sprintf(__VA_ARGS__) 39 | 40 | #undef MAX_REQUEST_SIZE 41 | #define MAX_REQUEST_SIZE 768 42 | 43 | char *ureq_malloc(size_t l) { 44 | return (char *) os_malloc(l); 45 | } 46 | 47 | void ureq_free(void *p) { 48 | if (p != NULL) { 49 | os_free(p); 50 | p = NULL; 51 | } 52 | } 53 | 54 | #endif /* UREQ_ESP8266_H */ 55 | -------------------------------------------------------------------------------- /include/ureq_core.h: -------------------------------------------------------------------------------- 1 | /* 2 | https://github.com/solusipse/ureq 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2015-2016 solusipse 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #ifndef UREQ_CORE_H 28 | #define UREQ_CORE_H 29 | 30 | #include "ureq_fwd.h" 31 | #include "ureq_pages.h" 32 | #include "ureq_env_dep.h" 33 | 34 | #ifdef UREQ_ESP8266 35 | #define UREQ_STATIC_LIST 36 | #endif 37 | 38 | #ifdef UREQ_STATIC_LIST 39 | static UreqPage pages[16]; 40 | static int pageCount = 0; 41 | #else 42 | static UreqPage *pages = NULL; 43 | static int pageCount = 0; 44 | #endif 45 | 46 | #ifdef UREQ_ESP8266 47 | #include "../hardware/ureq_esp8266.h" 48 | #endif 49 | 50 | #ifdef UREQ_USE_FILESYSTEM 51 | #include "ureq_filesystem.h" 52 | #endif 53 | 54 | const char *ureq_methods[] = { 55 | UREQ_GET, 56 | UREQ_POST, 57 | UREQ_ALL, 58 | UREQ_PUT, 59 | UREQ_DELETE, 60 | NULL 61 | }; 62 | 63 | const UreqMime ureq_mime_types[] = { 64 | /* Text mimes */ 65 | {"html", "text/html"}, 66 | {"htm", "text/html"}, 67 | {"js", "text/javascript"}, 68 | {"txt", "text/plain"}, 69 | {"css", "text/css"}, 70 | {"xml", "text/xml"}, 71 | 72 | /* Image mimes */ 73 | {"bmp", "image/bmp"}, 74 | {"gif", "image/gif"}, 75 | {"png", "image/png"}, 76 | {"jpg", "image/jpeg"}, 77 | {"jpeg", "image/jpeg"}, 78 | 79 | /* Application mimes */ 80 | {"json", "application/json"}, 81 | 82 | /* 83 | The default mime-type is text/html (urls without extensions) 84 | Use it for files with unknown extensions 85 | */ 86 | {NULL, "text/html"} 87 | }; 88 | 89 | static int ureq_get_header(char *h, const char *r) { 90 | const char *p = strstr(r, UREQ_EOL); 91 | if (!p) return 0; 92 | if ((p-r) < UREQ_HTTP_REQ_LEN) return 0; 93 | strncpy(h, r, (p-r)); 94 | return 1; 95 | } 96 | 97 | static int ureq_check_method_validity(const char *m) { 98 | int i; 99 | for(i = 0; ureq_methods[i] != NULL; ++i) 100 | if (strcmp(ureq_methods[i], m) == 0) 101 | return 1; 102 | return 0; 103 | } 104 | 105 | static int ureq_parse_header(HttpRequest *req, const char *r) { 106 | 107 | const size_t r_len = strlen(r) + 1; 108 | 109 | char *header = malloc(r_len); 110 | char *b = NULL; 111 | char *bk = NULL; 112 | 113 | if (!ureq_get_header(header, r)) { 114 | free(header); 115 | return 0; 116 | } 117 | 118 | b = strtok_r(header, " ", &bk); 119 | if (!ureq_check_method_validity(b)) { 120 | free(header); 121 | return 0; 122 | } 123 | req->type = malloc(strlen(b) + 1); 124 | strcpy(req->type, b); 125 | 126 | b = strtok_r(NULL, " ", &bk); 127 | if (!b) { 128 | free(header); 129 | return 0; 130 | } 131 | req->url = malloc(strlen(b) + 1); 132 | strcpy(req->url, b); 133 | 134 | b = strtok_r(NULL, " ", &bk); 135 | if (!b) { 136 | free(header); 137 | return 0; 138 | } 139 | if (strncmp(b, "HTTP/1.", 7)) { 140 | free(header); 141 | return 0; 142 | } 143 | req->version = malloc(strlen(b) + 1); 144 | strcpy(req->version, b); 145 | free(header); 146 | 147 | req->message = malloc(r_len); 148 | strcpy(req->message, r); 149 | 150 | req->body = NULL; 151 | req->params = NULL; 152 | req->response.data = NULL; 153 | req->response.mime = NULL; 154 | req->response.file = NULL; 155 | req->response.code = 0; 156 | req->response.header = NULL; 157 | 158 | return 1; 159 | } 160 | 161 | void ureq_serve(char *url, char *(*func)(HttpRequest*), char *method) { 162 | UreqPage page = {url, method, func}; 163 | 164 | #ifdef UREQ_STATIC_LIST 165 | if (pageCount >= 16) return; 166 | pages[pageCount++] = page; 167 | #else 168 | pages = (UreqPage*) realloc(pages, ++pageCount * sizeof(UreqPage)); 169 | pages[pageCount-1] = page; 170 | #endif 171 | } 172 | 173 | HttpRequest ureq_init(const char *ur) { 174 | HttpRequest r = UREQ_HTTP_REQ_INIT; 175 | 176 | if (strlen(ur) > UREQ_BUFFER_SIZE) { 177 | r.response.code = 413; 178 | return r; 179 | } 180 | 181 | if (!ureq_parse_header(&r, ur)) { 182 | r.response.code = 400; 183 | return r; 184 | } 185 | 186 | r.valid = 1; 187 | 188 | return r; 189 | } 190 | 191 | static int ureq_first_run(HttpRequest *req) { 192 | req->complete = -2; 193 | 194 | int i; 195 | 196 | char *plain_url = malloc(strlen(req->url) + 1); 197 | ureq_remove_parameters(plain_url, req->url); 198 | 199 | /* Check for special 404 response */ 200 | for (i = 0; i < pageCount; ++i) { 201 | if (req->page404) break; 202 | 203 | if (!strcmp(pages[i].url, "404")) 204 | req->page404 = pages[i].func; 205 | } 206 | 207 | /* 208 | This loop iterates through the pages list and compares 209 | urls and methods to requested ones. If there's a match, 210 | it calls a corresponding function and saves the HTTP 211 | response to 212 | */ 213 | for (i = 0; i < pageCount; ++i) { 214 | /* If there's no match between this, skip to next iteration. */ 215 | if (strcmp(plain_url, pages[i].url)) continue; 216 | 217 | /* 218 | If request type is ALL, corresponding function is always called, 219 | no matter which method type was used. 220 | */ 221 | if (strcmp(UREQ_ALL, pages[i].method) != 0) { 222 | /* 223 | If there's no match between an url and method, skip 224 | to next iteration. 225 | */ 226 | if (strcmp(req->type, pages[i].method)) continue; 227 | } 228 | 229 | /* Save get parameters to r->params */ 230 | ureq_get_query(req); 231 | 232 | /* If method was POST, save body to r->message */ 233 | if (!strcmp(UREQ_POST, req->type)) 234 | ureq_set_post_data(req); 235 | 236 | /* 237 | Run page function but don't save data from it 238 | at first run (use it now only to set some things). 239 | If user returns blank string, don't send data again 240 | (complete code 2) 241 | */ 242 | if (!strlen(pages[i].func(req))) 243 | req->complete = 2; 244 | 245 | /* Save pointer to page's func for later use */ 246 | req->func = pages[i].func; 247 | 248 | /* 249 | If user decided to bind an url to file, set mimetype 250 | here and break. 251 | */ 252 | if (req->response.file) { 253 | if (req->response.file != req->buffer) { 254 | if (!req->response.mime) 255 | req->response.mime = ureq_set_mimetype(req->response.file); 256 | break; 257 | } 258 | } 259 | 260 | if (req->response.code == 0) 261 | req->response.code = 200; 262 | 263 | /* Return only header at first run */ 264 | req->response.data = ureq_generate_response_header(req); 265 | req->len = strlen(req->response.data); 266 | 267 | return req->complete; 268 | } 269 | #ifdef UREQ_USE_FILESYSTEM 270 | return ureq_fs_first_run(req); 271 | #else 272 | return ureq_set_404_response(req); 273 | #endif 274 | } 275 | 276 | static void ureq_render_template(HttpRequest *r) { 277 | // Abort when it's not template file 278 | if (r->tmp_len <= 0) return; 279 | // Abort when no keywords inside file 280 | if (!strstr(r->buffer, "}}")) return; 281 | 282 | /* 283 | This piece of code is being rewritten right now and is not 284 | fully functional yet. Please, use carefully. 285 | 286 | Some situation handlers have to be implemented, e.g.: 287 | - keyword at the seam 288 | - buffer overflow caused by: rendered page size > template size >= UREQ_BUFFER_SIZE 289 | - no keywords in template file 290 | */ 291 | 292 | char bbb[UREQ_BUFFER_SIZE]; 293 | char *p = r->_buffer, *q; 294 | int pos = 0, i; 295 | 296 | memcpy(r->_buffer, r->buffer, UREQ_BUFFER_SIZE); 297 | memcpy(bbb, r->buffer, UREQ_BUFFER_SIZE); 298 | memset(r->buffer, 0, UREQ_BUFFER_SIZE); 299 | 300 | while ((p = strstr(p, "{{"))) { 301 | bbb[p-r->_buffer] = 0; 302 | if((q = strstr(p, "}}"))) { 303 | p[q-p] = 0; 304 | p += 2; 305 | memmove(bbb, bbb+pos, pos); 306 | 307 | pos += strlen(bbb) + 4 + strlen(p); 308 | 309 | // bbb contains text before every encountered {{ 310 | // p contains keyword inside {{ and }} 311 | // q+2 contains text after last }} 312 | strcat(r->buffer, bbb); 313 | for (i=0; i < r->tmp_len; i++) { 314 | if (strcmp(p, r->templates[i].destination) == 0) { 315 | strcat(r->buffer, r->templates[i].value); 316 | } 317 | } 318 | p = q; 319 | } 320 | p++; 321 | } 322 | if (q) strcat(r->buffer, q+2); 323 | } 324 | 325 | static int ureq_next_run(HttpRequest *req) { 326 | if (req->complete == -2) { 327 | free(req->response.data); 328 | } 329 | 330 | UreqResponse respcpy = req->response; 331 | req->complete--; 332 | 333 | if (req->big_file) { 334 | #if defined UREQ_USE_FILESYSTEM 335 | if (req->file.size > UREQ_BUFFER_SIZE) { 336 | respcpy.data = ureq_fs_read(req->file.address, UREQ_BUFFER_SIZE, req->buffer); 337 | req->file.address += UREQ_BUFFER_SIZE; 338 | req->file.size -= UREQ_BUFFER_SIZE; 339 | req->len = UREQ_BUFFER_SIZE; 340 | ureq_render_template(req); 341 | req->complete -= 1; 342 | } else { 343 | req->len = req->file.size; 344 | respcpy.data = ureq_fs_read(req->file.address, req->file.size, req->buffer); 345 | ureq_render_template(req); 346 | req->complete = 1; 347 | } 348 | #else 349 | // TODO: buffer read from func 350 | memset(req->buffer, 0, UREQ_BUFFER_SIZE); 351 | respcpy.data = req->func(req); 352 | req->len = strlen(respcpy.data); 353 | #endif 354 | } else { 355 | memset(req->buffer, 0, UREQ_BUFFER_SIZE); 356 | respcpy.data = req->func(req); 357 | req->len = strlen(respcpy.data); 358 | if (req->response.file == req->buffer) { 359 | strcpy(req->buffer, respcpy.data); 360 | respcpy.data = req->buffer; 361 | } 362 | ureq_render_template(req); 363 | req->complete = 1; 364 | } 365 | req->response = respcpy; 366 | return req->complete; 367 | 368 | } 369 | 370 | static int ureq_set_404_response(HttpRequest *r) { 371 | char *page; 372 | r->response.code = 404; 373 | 374 | if (r->page404) 375 | page = r->page404(r); 376 | else 377 | page = (char *) UREQ_HTML_PAGE_404; 378 | 379 | ureq_generate_response(r, page); 380 | return r->complete = 1; 381 | } 382 | 383 | static char *ureq_get_error_page(HttpRequest *r) { 384 | const char *desc = ureq_get_code_description(r->response.code); 385 | sprintf(r->buffer, "%s%d %s%s%d %s%s", \ 386 | UREQ_HTML_HEADER, r->response.code, desc, \ 387 | UREQ_HTML_BODY, r->response.code, desc, \ 388 | UREQ_HTML_FOOTER); 389 | return r->buffer; 390 | } 391 | 392 | static int ureq_set_error_response(HttpRequest *r) { 393 | if (!r->response.code) r->response.code = 400; 394 | r->response.mime = "text/html"; 395 | r->response.header = ""; 396 | ureq_generate_response(r, ureq_get_error_page(r)); 397 | return r->complete = 1; 398 | } 399 | 400 | int ureq_run(HttpRequest *req) { 401 | /* 402 | Code meanings: 403 | 1: Everything went smooth 404 | 405 | 2: User provided blank string, don't send data 406 | for the second time (and free header) 407 | 408 | <-1: Still running, on -2 free header which is 409 | dynamically alloced 410 | */ 411 | if (req->complete == 1) 412 | return 0; 413 | 414 | if (req->complete == 2) { 415 | free(req->response.data); 416 | return 0; 417 | } 418 | 419 | /* 420 | If code equals to -1, it's the very first run, 421 | parameters are set there and header is sent. 422 | 423 | Data (if any), will be sent in next run(s). 424 | */ 425 | if (req->complete == -1) { 426 | /* If request was invalid, set everything to null */ 427 | if (!req->valid) return ureq_set_error_response(req); 428 | return ureq_first_run(req); 429 | } 430 | 431 | if ((req->complete < -1) && (req->response.code != 404)) 432 | return ureq_next_run(req); 433 | 434 | return 0; 435 | } 436 | 437 | static void ureq_generate_response(HttpRequest *r, char *html) { 438 | char *header = ureq_generate_response_header(r); 439 | r->response.data = malloc(strlen(header) + strlen(html) + 3); 440 | 441 | strcpy(r->response.data, header); 442 | strcat(r->response.data, html); 443 | strncat(r->response.data, UREQ_EOL, UREQ_EOL_LEN); 444 | 445 | r->len = strlen(r->response.data); 446 | 447 | free(header); 448 | } 449 | 450 | static const char *ureq_get_code_description(const int c) { 451 | switch (c) { 452 | case 200: return "OK"; 453 | case 302: return "Found"; 454 | case 400: return "Bad Request"; 455 | case 401: return "Unauthorized"; 456 | case 403: return "Forbidden"; 457 | case 404: return "Not Found"; 458 | case 408: return "Request Timeout"; 459 | case 413: return "Request-URI Too Long"; 460 | case 500: return "Internal Error"; 461 | case 503: return "Service Temporarily Overloaded"; 462 | default: return "Not Implemented"; 463 | } 464 | } 465 | 466 | static char *ureq_set_mimetype(const char *r) { 467 | const char *e = strrchr(r, '.'); 468 | if (!e) return "text/html"; 469 | e += 1; 470 | 471 | int i; 472 | for (i = 0; ureq_mime_types[i].ext != NULL; ++i) { 473 | if (!strcmp(ureq_mime_types[i].ext, e)) { 474 | return (char*) ureq_mime_types[i].mime; 475 | } 476 | } 477 | 478 | return "text/html"; 479 | } 480 | 481 | static char *ureq_generate_response_header(HttpRequest *r) { 482 | /* Set default mime type if blank */ 483 | if (!r->response.mime) { 484 | if (r->response.code == 200 || r->response.code == 404) { 485 | r->response.mime = ureq_set_mimetype(r->url); 486 | } else { 487 | r->response.mime = ""; 488 | } 489 | } 490 | 491 | char *br = malloc(strlen(r->response.mime) + 15); 492 | strcpy(br, "Content-Type: "); 493 | strcat(br, r->response.mime); 494 | 495 | if (r->response.header) { 496 | size_t h_len = strlen(r->response.header) + 1; 497 | char *bb = malloc(h_len); 498 | strcpy(bb, r->response.header); 499 | r->response.header = malloc(strlen(br) + h_len + UREQ_EOL_LEN + 1); 500 | strcpy(r->response.header, br); 501 | strcat(r->response.header, UREQ_EOL); 502 | strcat(r->response.header, bb); 503 | free(bb); 504 | } else { 505 | r->response.header = malloc(strlen(br) + UREQ_EOL_LEN + 1); 506 | strcpy(r->response.header, br); 507 | strcat(r->response.header, UREQ_EOL); 508 | } 509 | 510 | free(br); 511 | 512 | const char *desc = ureq_get_code_description(r->response.code); 513 | 514 | size_t hlen = strlen(UREQ_HTTP_V) + 4 /*response code*/ + strlen(desc) + \ 515 | strlen(r->response.header) + 8/*spaces,specialchars*/; 516 | 517 | char *h = malloc(hlen + 1); 518 | sprintf(h, "%s %d %s\r\n%s\r\n", UREQ_HTTP_V, r->response.code, desc, r->response.header); 519 | 520 | return h; 521 | } 522 | 523 | static void ureq_set_post_data(HttpRequest *r) { 524 | char *n = strstr(r->message, "\r\n\r\n"); 525 | if (!n) return; 526 | r->body = n + 4; 527 | } 528 | 529 | static void ureq_param_to_value(char *data, char *buffer, const char *arg) { 530 | char *bk, *buf; 531 | for (buf = strtok_r(data, "&", &bk); buf != NULL; buf = strtok_r(NULL, "&", &bk)) { 532 | 533 | if (strstr(buf, arg) == NULL) continue; 534 | 535 | char *sptr; 536 | buf = strtok_r(buf, "=", &sptr); 537 | 538 | if (strcmp(buf, arg) == 0) { 539 | strcpy(buffer, sptr); 540 | return; 541 | } 542 | } 543 | *buffer = '\0'; 544 | } 545 | 546 | char *ureq_get_param_value(HttpRequest *r, const char *arg) { 547 | if (!r->params) return "\0"; 548 | char *data = malloc(strlen(r->params) + 1); 549 | strcpy(data, r->params); 550 | ureq_param_to_value(data, r->_buffer, arg); 551 | free(data); 552 | return r->_buffer; 553 | } 554 | 555 | char *ureq_post_param_value(HttpRequest *r, const char *arg) { 556 | if (!r->body) return "\0"; 557 | char *data = malloc(strlen(r->body) + 1); 558 | strcpy(data, r->body); 559 | ureq_param_to_value(data, r->_buffer, arg); 560 | free(data); 561 | return r->_buffer; 562 | } 563 | 564 | static void ureq_remove_parameters(char *b, const char *u) { 565 | char *bk; 566 | strcpy(b, u); 567 | b = strtok_r(b, "?", &bk); 568 | } 569 | 570 | static void ureq_get_query(HttpRequest *r) { 571 | char *q = strchr(r->url, '?'); 572 | if (!q) return; 573 | r->params = q + 1; 574 | } 575 | 576 | void ureq_template(HttpRequest *r, char *d, char *v) { 577 | if (r->complete != -2) return; 578 | UreqTemplate t = {d, v}; 579 | r->templates[r->tmp_len++] = t; 580 | } 581 | 582 | void ureq_close(HttpRequest *req) { 583 | if (req->type) free(req->type); 584 | if (req->url) free(req->url); 585 | if (req->version) free(req->version); 586 | if (req->message) free(req->message); 587 | 588 | if (!req->valid || req->response.code == 404) 589 | free(req->response.data); 590 | 591 | if (req->response.header) 592 | if (strlen(req->response.header) > 1) 593 | free(req->response.header); 594 | } 595 | 596 | void ureq_finish() { 597 | #ifndef UREQ_STATIC_LIST 598 | free(pages); 599 | #endif 600 | } 601 | 602 | #endif /* UREQ_CORE_H */ 603 | -------------------------------------------------------------------------------- /include/ureq_defines.h: -------------------------------------------------------------------------------- 1 | /* 2 | https://github.com/solusipse/ureq 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2015-2016 solusipse 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #ifndef UREQ_DEFINES_H 28 | #define UREQ_DEFINES_H 29 | 30 | /* HTTP METHODS */ 31 | #define UREQ_GET "GET" 32 | #define UREQ_POST "POST" 33 | #define UREQ_ALL "ALL" 34 | #define UREQ_PUT "PUT" 35 | #define UREQ_DELETE "DELETE" 36 | 37 | /* HTTP VERSION */ 38 | #define UREQ_HTTP_V "HTTP/1.1" 39 | 40 | /* Minimum length of a request message */ 41 | #define UREQ_HTTP_REQ_LEN 14 42 | 43 | /* Message End-of-Line constants */ 44 | #define UREQ_EOL "\r\n" 45 | #define UREQ_EOL_LEN 2 46 | 47 | /* ESP8266 filesystem base address */ 48 | #define UREQ_FS_START 0x12000 49 | 50 | /* 51 | This may be redefined on your device 52 | Check corresponding file in hardware directory 53 | */ 54 | #define UREQ_BUFFER_SIZE 1024 55 | 56 | /* 57 | This macro is used to intialize an HttpRequest 58 | object 59 | */ 60 | #define UREQ_HTTP_REQ_INIT { \ 61 | NULL, /* type */ \ 62 | NULL, /* url */ \ 63 | NULL, /* version */ \ 64 | NULL, /* message */ \ 65 | NULL, /* param */ \ 66 | NULL, /* body */ \ 67 | {}, /* response */ \ 68 | {}, /* templates */ \ 69 | 0, /* tmp_len */ \ 70 | -1, /* complete */ \ 71 | 0, /* big_file */ \ 72 | 0, /* len */ \ 73 | {}, /* file */ \ 74 | {}, /* buffer */ \ 75 | {}, /* _buffer */ \ 76 | 0, /* valid */ \ 77 | NULL, /* func */ \ 78 | NULL /* page404 */ \ 79 | } 80 | 81 | #endif /* UREQ_DEFINES_H */ 82 | -------------------------------------------------------------------------------- /include/ureq_env_dep.h: -------------------------------------------------------------------------------- 1 | /* 2 | https://github.com/solusipse/ureq 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2015-2016 solusipse 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #ifndef UREQ_ENVIRONMENT_DEPENDENCIES_H 28 | #define UREQ_ENVIRONMENT_DEPENDENCIES_H 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #endif /* UREQ_ENVIRONMENT_DEPENDENCIES_H */ 36 | -------------------------------------------------------------------------------- /include/ureq_filesystem.h: -------------------------------------------------------------------------------- 1 | /* 2 | https://github.com/solusipse/ureq 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2015-2016 solusipse 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #ifndef UREQ_FILESYSTEM_H 28 | #define UREQ_FILESYSTEM_H 29 | 30 | /* 31 | This function comes from dht-esp8266 project 32 | It was written by Jeroen Domburg 33 | 34 | Slightly modified for this project 35 | */ 36 | static void memcpy_aligned(char *dst, char *src, const int len) { 37 | int x, w, b; 38 | for (x = 0; x < len; ++x, ++dst, ++src) { 39 | b = ((int)src & 3); 40 | w = *((int *)(src - b)); 41 | switch (b) { 42 | case 0 : *dst=(w>>0); break; 43 | case 1 : *dst=(w>>8); break; 44 | case 2 : *dst=(w>>16); break; 45 | case 3 : *dst=(w>>24); break; 46 | } 47 | } 48 | } 49 | 50 | static char *ureq_fs_read(const int a, const int s, char *buf) { 51 | char *pos = (char*)(UREQ_FS_START + 0x40200000); 52 | pos += a; 53 | memset(buf, 0, 1024); 54 | memcpy_aligned(buf, pos, s); 55 | return buf; 56 | } 57 | 58 | static UreqFile ureq_fs_open(const char *rf) { 59 | char *pos = (char*)(UREQ_FS_START + 0x40200000); 60 | char name[16]; 61 | int32_t size; 62 | int32_t address; 63 | 64 | UreqFile f = {0, 0}; 65 | 66 | /* Get number of files */ 67 | int32_t amount; 68 | os_memcpy(&amount, pos, sizeof(amount)); 69 | 70 | /* Move to the filesystem header */ 71 | pos += sizeof(int32_t); 72 | 73 | int i; 74 | for (i = 0; i < amount; ++i) { 75 | memset(name, 0, sizeof(name)); 76 | os_memcpy(name, pos, sizeof(name)); 77 | 78 | if (strcmp(name, rf) == 0) { 79 | /* Requested file was found */ 80 | pos += sizeof(char) * 16; 81 | os_memcpy(&size, pos, sizeof(int32_t)); 82 | pos += sizeof(int32_t); 83 | os_memcpy(&address, pos, sizeof(int32_t)); 84 | f.size = size; 85 | f.address = address; 86 | return f; 87 | } else { 88 | /* Move to next file */ 89 | pos += sizeof(char) * 16; /* filename */ 90 | pos += sizeof(int32_t); /* size */ 91 | pos += sizeof(int32_t); /* address */ 92 | } 93 | } 94 | return f; 95 | } 96 | 97 | static int ureq_fs_first_run(HttpRequest *r) { 98 | /* 99 | If there's no function bound to /, then try 100 | to read index.html 101 | */ 102 | UreqFile f; 103 | 104 | if (r->response.file) { 105 | f = ureq_fs_open(r->response.file); 106 | } else { 107 | if (strcmp(r->url, "/") == 0) 108 | f = ureq_fs_open("index.html"); 109 | else 110 | f = ureq_fs_open(r->url + 1); 111 | } 112 | 113 | if (f.address == 0) { 114 | /* File was not found */ 115 | return ureq_set_404_response(r); 116 | } 117 | 118 | if (r->response.code == 0) 119 | r->response.code = 200; 120 | 121 | r->file = f; 122 | r->big_file = 1; 123 | r->complete = -2; 124 | r->response.data = ureq_generate_response_header(r); 125 | r->len = strlen(r->response.data); 126 | return r->complete; 127 | } 128 | 129 | #endif /* UREQ_FILESYSTEM_H */ 130 | -------------------------------------------------------------------------------- /include/ureq_fwd.h: -------------------------------------------------------------------------------- 1 | /* 2 | https://github.com/solusipse/ureq 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2015-2016 solusipse 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #ifndef UREQ_FWD_DECLS_H 28 | #define UREQ_FWD_DECLS_H 29 | 30 | void ureq_serve (char*, char *(*)(HttpRequest*), char*); 31 | HttpRequest ureq_init (const char*); 32 | void ureq_close (HttpRequest*); 33 | void ureq_finish (); 34 | void ureq_template (HttpRequest*, char*, char*); 35 | int ureq_run (HttpRequest*); 36 | 37 | static int ureq_get_header (char*, const char*); 38 | static int ureq_parse_header (HttpRequest*, const char*); 39 | static void ureq_remove_parameters (char*, const char*); 40 | static void ureq_get_query (HttpRequest*); 41 | static void ureq_generate_response (HttpRequest*, char*); 42 | static void ureq_set_post_data (HttpRequest*); 43 | static char *ureq_set_mimetype (const char*); 44 | static char *ureq_generate_response_header (HttpRequest*); 45 | static const char *ureq_get_code_description (const int); 46 | static int ureq_set_error_response (HttpRequest*); 47 | static int ureq_set_404_response (HttpRequest*); 48 | 49 | #endif /* UREQ_FWD_DECLS_H */ 50 | -------------------------------------------------------------------------------- /include/ureq_http_types.h: -------------------------------------------------------------------------------- 1 | /* 2 | https://github.com/solusipse/ureq 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2015-2016 solusipse 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #ifndef UREQ_HTTP_TYPES_H 28 | #define UREQ_HTTP_TYPES_H 29 | 30 | /* 31 | */ 32 | typedef struct ureq_mime_t { 33 | const char *ext; 34 | const char *mime; 35 | } UreqMime; 36 | 37 | /* 38 | */ 39 | typedef struct ureq_file_t { 40 | int size; 41 | int address; 42 | } UreqFile; 43 | 44 | /* 45 | */ 46 | typedef struct ureq_response_t { 47 | int code; 48 | char *mime; 49 | char *header; 50 | char *data; 51 | char *file; 52 | } UreqResponse; 53 | 54 | /* 55 | */ 56 | typedef struct ureq_template_t { 57 | char *destination; 58 | char *value; 59 | } UreqTemplate; 60 | 61 | /* 62 | */ 63 | typedef struct ureq_http_request_t { 64 | char *type; 65 | char *url; 66 | char *version; 67 | char *message; 68 | char *params; 69 | char *body; 70 | 71 | UreqResponse response; 72 | 73 | UreqTemplate templates[16]; 74 | int tmp_len; 75 | 76 | int complete; 77 | int big_file; 78 | int len; 79 | 80 | UreqFile file; 81 | 82 | char buffer[UREQ_BUFFER_SIZE]; /* Buffer for user operations */ 83 | char _buffer[UREQ_BUFFER_SIZE]; /* Buffer for backend operations */ 84 | 85 | int valid; 86 | 87 | /* Type Methods */ 88 | char *(*func) (struct ureq_http_request_t*); 89 | char *(*page404)(struct ureq_http_request_t*); 90 | } HttpRequest; 91 | 92 | /* 93 | */ 94 | typedef struct ureq_page_t { 95 | char *url; 96 | char *method; 97 | 98 | /* Type Methods */ 99 | char *(*func)(); 100 | } UreqPage; 101 | 102 | #endif /* UREQ_HTTP_TYPES_H */ 103 | -------------------------------------------------------------------------------- /include/ureq_pages.h: -------------------------------------------------------------------------------- 1 | /* 2 | https://github.com/solusipse/ureq 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2015-2016 solusipse 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #ifndef UREQ_PAGES_H 28 | #define UREQ_PAGES_H 29 | 30 | const char* UREQ_HTML_HEADER = ""; 31 | 32 | const char* UREQ_HTML_BODY = "

"; 33 | 34 | const char* UREQ_HTML_FOOTER = "


ureq
"; 35 | 36 | const char* UREQ_HTML_PAGE_404 = "" 37 | "404 Not Found" 38 | "

404 Not Found


ureq
" 39 | ""; 40 | 41 | #endif /* UREQ_PAGES_H */ 42 | -------------------------------------------------------------------------------- /packer/input/2kb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solusipse/ureq/a413e8c3c2b935f6fbfd3ee76623e4342e335cfa/packer/input/2kb.jpg -------------------------------------------------------------------------------- /packer/input/2kb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solusipse/ureq/a413e8c3c2b935f6fbfd3ee76623e4342e335cfa/packer/input/2kb.png -------------------------------------------------------------------------------- /packer/input/4kb.txt: -------------------------------------------------------------------------------- 1 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc laoreet risus vel erat luctus scelerisque. Quisque semper egestas dolor nec scelerisque. Sed ultricies quam mauris, ac molestie nisl fermentum ac. Sed arcu augue, semper et neque et, varius auctor lectus. In hac habitasse platea dictumst. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Vestibulum commodo orci sit amet lectus tristique tempus. Maecenas faucibus feugiat massa. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed non est viverra, tempor est nec, consectetur lorem. Aenean laoreet, velit eu feugiat volutpat, nisi lorem lacinia eros, eu porta purus lacus non purus. Quisque aliquet ornare lacus, at convallis nibh mattis nec. Aliquam gravida felis et erat convallis, eget eleifend justo finibus. Sed ullamcorper, diam eget blandit volutpat, magna tortor commodo nisi, vitae ultrices enim elit ac enim. Vivamus ex ipsum, maximus et euismod a, hendrerit eget odio. Aliquam erat volutpat. 2 | 3 | Suspendisse pretium eros vitae suscipit ultricies. Aliquam erat volutpat. Proin vitae lacus eget tellus pretium ultricies. Nulla ultricies efficitur quam at sagittis. Mauris eget fringilla est, in tempus libero. Quisque dignissim justo nec feugiat venenatis. Sed lobortis orci a ante blandit sagittis. Duis vestibulum dolor eget ex tincidunt vehicula. Aliquam aliquet orci odio, eget malesuada turpis mollis vel. Praesent vestibulum fermentum ultrices. Ut a dui diam. Nullam urna leo, fermentum quis tincidunt vel, consequat vel nisl. Vestibulum quis velit quam. 4 | 5 | Ut ultrices sodales vehicula. Phasellus sodales massa sit amet eros porttitor, in sagittis libero malesuada. Vivamus hendrerit sagittis sem, id efficitur elit vehicula eget. Phasellus egestas neque non purus finibus faucibus. Integer pharetra sed neque sed luctus. Nullam pretium ultricies faucibus. Vivamus enim diam, lacinia in diam ac, vestibulum tincidunt tellus. Suspendisse a sapien at mauris lacinia aliquet at tempus nunc. Nullam vel ligula mi. Duis faucibus orci id massa rutrum pretium sed pretium risus. Quisque ac elit in felis eleifend efficitur nec at diam. 6 | 7 | Integer tempor quam quis commodo ullamcorper. Sed faucibus felis et orci hendrerit, ut rhoncus felis gravida. Duis non tellus et ante tristique volutpat eget vitae ligula. Aliquam in tincidunt nisi. Ut a mattis urna. Phasellus viverra hendrerit sem. Donec in mauris ut massa vestibulum varius. Etiam in ipsum rutrum, laoreet orci vitae, dignissim ex. Integer semper eros lorem, et dictum dui finibus sed. Duis dictum iaculis sollicitudin. 8 | 9 | Morbi nec urna non nunc laoreet maximus ut a risus. Curabitur non erat nibh. Phasellus ultrices leo quis magna lobortis eleifend. Nam fermentum mi eget turpis sodales commodo. Curabitur bibendum consequat mauris, sit amet accumsan velit sollicitudin a. In eu lacus non nulla malesuada facilisis. Quisque sollicitudin ipsum vel dolor varius interdum. Vestibulum faucibus justo erat. Sed dapibus vitae eros ut dapibus. Duis ullamcorper tellus id mollis faucibus. Etiam iaculis nibh lorem, in scelerisque massa tempus quis. Aliquam iaculis hendrerit metus, a scelerisque ligula blandit at. Nullam nisl risus, porttitor sed fermentum eget, porttitor vitae diam. Integer gravida in lacus a faucibus. Duis ultricies magna ut scelerisque imperdiet. 10 | 11 | Aliquam ante purus, consequat quis sagittis ut, consectetur sit amet nibh. Phasellus vestibulum suscipit ante quis efficitur. Curabitur faucibus nibh metus, ac ornare felis mattis quis. Vestibulum nulla est, convallis sed tempus id, tempor sed erat. Nunc vehicula nunc justo, nec pellentesque ante varius in. Vivamus elementum sed neque at posuere. In eget lectus vehicula, consectetur enim in, rutrum mauris. Pellentesque fermentum vel nisi vitae fringilla. Sed porta dapibus neque non fermentum. Sed rhoncus, quam at rhoncus vestibulum, sapien mauris rhoncus mauris, nec placerat ipsum ex non mauris. Integer pulvinar iaculis ligula metus. -------------------------------------------------------------------------------- /packer/input/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ureq - esp8266 6 | 7 | 8 | 9 |
10 |

Hello there.

11 |

12 | This page is entirely served from ESP8266 chip thanks to ureq - micro-framework for handling HTTP requests on low resource systems. 13 | For more details, see repository page on Github. 14 |

15 |

What can it do?

16 |

17 | Before we start, just keep in mind that ESP8266 has 64KB DRAM, 96KB SRAM, 32-bit RISC CPU running at 80Mhz and it has to handle also a wireless traffic. Despite this it's still able to dispatch more than 50 reqs/s on Hello World. And serious optimization hasn't started yet. 18 |

19 |

And what can it do now?

20 |

21 | This set of pages was placed on ESP8266 to test ureq and chip capabilities. Is it able to sucesfully serve pages under small traffic? Will it handle moderate traffic? I'd like you to help me checking that out. And actually, you're already doing this right now. Thank You! 22 |

23 |

24 | There's the list of example requests 25 |

26 |

27 |

41 |

42 |

Some statistics

43 |

44 | CHECK number of requests till now. This page is about 4KB. It takes two requests to load it (/index.html, /style.css). 45 |

46 |

P.S. Normally you probably shouldn't make your ESP8266 accessible from outside world. This library wasn't built for this. But if it can hold moderate traffic from multiple strangers, it's for sure suitable for your closed-network applications. 47 |

48 |

200 OK

49 |
50 | Fork me on GitHub 51 | 52 | -------------------------------------------------------------------------------- /packer/input/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #edebe8; 3 | color: #0F0E0F; 4 | font-family: sans-serif; 5 | } 6 | 7 | h1:before, h1:after, 8 | h2:before, h2:after, 9 | h3:before, h3:after { 10 | content: " - "; 11 | } 12 | 13 | h1 { 14 | color: #20457C; 15 | } 16 | 17 | h2 { 18 | color: #F8AC35; 19 | } 20 | 21 | h3 { 22 | color: #F8AC35; 23 | } 24 | 25 | b { 26 | color: #657E84; 27 | } 28 | 29 | a { 30 | color: #f17f51; 31 | text-decoration: none; 32 | font-weight: 600; 33 | } 34 | 35 | a:hover { 36 | color: #FF6652; 37 | } 38 | 39 | p { 40 | text-align: justify; 41 | } 42 | 43 | ul { 44 | list-style-type: hebrew; 45 | text-align: left; 46 | display: inline-block; 47 | } 48 | 49 | .m { 50 | text-align: center; 51 | margin: 0px auto; 52 | height: 200px; 53 | width: 50%; 54 | } 55 | 56 | @media (max-width: 1600px) { 57 | .m { 58 | width: 60% 59 | } 60 | } 61 | 62 | @media (max-width: 1280px) { 63 | .m { 64 | width: 80% 65 | } 66 | } -------------------------------------------------------------------------------- /packer/input/test.json: -------------------------------------------------------------------------------- 1 | {"employees":[ 2 | {"firstName":"John", "lastName":"Doe"}, 3 | {"firstName":"Anna", "lastName":"Smith"}, 4 | ]} -------------------------------------------------------------------------------- /packer/input/test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | John Doe 4 | 5 | 6 | Anna Smith 7 | 8 | -------------------------------------------------------------------------------- /packer/packer.py: -------------------------------------------------------------------------------- 1 | # Use this script to upload output image to device 2 | # Currently only esp8266 is supported 3 | 4 | ''' 5 | typedef struct UreqFilesystem { 6 | char filename[16]; 7 | int size; 8 | int address; 9 | } UreqFilesystem; 10 | 11 | #ifdef ESP8266 12 | #define UREQ_FS_START 0x12000 13 | #endif 14 | ''' 15 | 16 | source_dir = "input" 17 | 18 | import os, numpy, sys, argparse 19 | from numpy import int32 20 | 21 | parser = argparse.ArgumentParser() 22 | parser.add_argument("--input", help="input directory") 23 | parser.add_argument("--port", help="serial port of device") 24 | args = parser.parse_args() 25 | 26 | files = [] 27 | output = "output.image" 28 | 29 | if args.input: 30 | source_dir = args.input 31 | else: 32 | if not os.path.isdir(source_dir): 33 | print("Please provide input directory or use default one (./input/). " 34 | "Run with -h parameter for more info.") 35 | exit() 36 | 37 | for f in os.listdir(source_dir): 38 | if f.startswith("."): 39 | continue 40 | 41 | fl = [] 42 | 43 | fl.append( "{:<16}".format(f + '\0') ) 44 | 45 | with open(source_dir + "/" + f, 'r') as fh: 46 | fl.append( int32( os.fstat(fh.fileno()).st_size ) ) 47 | fl.append( 0 ) 48 | fl.append( fh.read() ) 49 | 50 | files.append(fl) 51 | 52 | # 4 is size of int32_t containing number of files 53 | header_size = 4 54 | 55 | # Estimate header size 56 | for h in files: 57 | # 1 char 2 ints 58 | header_size += (1 * 16) + (4 * 2) 59 | 60 | address = header_size 61 | 62 | for i, h in enumerate(files): 63 | if i > 0: 64 | address += files[i-1][1] 65 | h[2] = int32( address ) 66 | 67 | print("Adding files:") 68 | 69 | with open(output, "w") as fh: 70 | # number of files 71 | int32( len(files) ).tofile(fh) 72 | for p in files: 73 | # filename 74 | fh.write( p[0] ) 75 | # size 76 | p[1].tofile(fh) 77 | # address 78 | p[2].tofile(fh) 79 | print("At address %s, with size %s, added file %s" % (p[2], p[1], p[0]) ) 80 | 81 | print("Header successfully saved to file!") 82 | 83 | with open(output, "a") as fh: 84 | for p in files: 85 | # contents 86 | fh.write( p[3] ) 87 | 88 | print("Contents successfully saved to file") 89 | 90 | print("Image is ready! Saved to %s." % output) 91 | 92 | if args.port: 93 | print("Uploading to device!") 94 | os.system("esptool.py --port " + args.port + " write_flash 0x12000 " + output) 95 | -------------------------------------------------------------------------------- /ureq.h: -------------------------------------------------------------------------------- 1 | /* 2 | * https://github.com/solusipse/ureq 3 | * 4 | * The MIT License (MIT) 5 | * 6 | * Copyright (c) 2015-2016 solusipse 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | #ifndef UREQ_H 28 | #define UREQ_H 29 | 30 | #include "include/ureq_defines.h" 31 | #include "include/ureq_http_types.h" 32 | 33 | /** 34 | * @brief: Register a function into the system that maps to a url with 35 | * the corresponding http method 36 | * 37 | * @url: URL to associate with the specified function 38 | * @func: Function to register into the system that maps to the url 39 | * @method: HTTP method 40 | */ 41 | void ureq_serve(char *url, char *(*func)(HttpRequest*), char *method); 42 | 43 | /** 44 | * @brief: Retrieve an object from the system containing information 45 | * about the request 46 | * 47 | * @req: Character input stream containing the request 48 | * 49 | * @return: An object representing an HTTP request message 50 | */ 51 | HttpRequest ureq_init(const char *req); 52 | 53 | /** 54 | * @brief: Process the request 55 | * 56 | * @req: Object containing information about the request message 57 | * 58 | * @see: ureq_init 59 | */ 60 | int ureq_run(HttpRequest *req); 61 | 62 | /** 63 | * @brief: Clean up system resources associated with the object 64 | * 65 | * @req: Processed HTTP request object 66 | * 67 | * @see: ureq_run 68 | */ 69 | void ureq_close(HttpRequest *req); 70 | 71 | /** 72 | * @brief: Shutdown the system to allow cleanup 73 | */ 74 | void ureq_finish(); 75 | 76 | /** 77 | * @brief: 78 | * 79 | * @req: 80 | * 81 | * @dst: 82 | * 83 | * @cnt: 84 | */ 85 | void ureq_template(HttpRequest *req, char *dst, char *cnt); 86 | 87 | #include "include/ureq_core.h" 88 | 89 | #endif 90 | --------------------------------------------------------------------------------