├── .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 | [](https://travis-ci.org/solusipse/ureq)
7 | [](http://isitmaintained.com/project/solusipse/ureq "Average time to resolve an issue")
8 | [](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 | 
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 | "";
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 | "";
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 = "
"
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 |
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 |
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 | JohnDoe
4 |
5 |
6 | AnnaSmith
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 |
--------------------------------------------------------------------------------