source: uKadecot/trunk/uip/apps/webserver/httpd.c@ 156

Last change on this file since 156 was 156, checked in by coas-nagasima, 8 years ago

クエリー式もリダイレクトアドレスに含めるよう修正

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
  • Property svn:mime-type set to text/x-chdr; charset=SHIFT_JIS
File size: 15.6 KB
Line 
1/**
2 * \addtogroup apps
3 * @{
4 */
5
6/**
7 * \defgroup httpd Web server
8 * @{
9 * The uIP web server is a very simplistic implementation of an HTTP
10 * server. It can serve web pages and files from a read-only ROM
11 * filesystem, and provides a very small scripting language.
12
13 */
14
15/**
16 * \file
17 * Web server
18 * \author
19 * Adam Dunkels <adam@sics.se>
20 */
21
22
23/*
24 * Copyright (c) 2004, Adam Dunkels.
25 * All rights reserved.
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions
29 * are met:
30 * 1. Redistributions of source code must retain the above copyright
31 * notice, this list of conditions and the following disclaimer.
32 * 2. Redistributions in binary form must reproduce the above copyright
33 * notice, this list of conditions and the following disclaimer in the
34 * documentation and/or other materials provided with the distribution.
35 * 3. Neither the name of the Institute nor the names of its contributors
36 * may be used to endorse or promote products derived from this software
37 * without specific prior written permission.
38 *
39 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
40 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
42 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
43 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
44 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
45 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
46 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
47 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
48 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
49 * SUCH DAMAGE.
50 *
51 * This file is part of the uIP TCP/IP stack.
52 *
53 * Author: Adam Dunkels <adam@sics.se>
54 *
55 * $Id: httpd.c 156 2016-02-05 00:06:37Z coas-nagasima $
56 */
57
58#include "uip.h"
59#include "httpd.h"
60#include "httpd-fs.h"
61#include "http-strings.h"
62#include "base64.h"
63#include "sha1.h"
64#include "kadecot_names.h"
65#include "uip_adpt.h"
66
67#include <string.h>
68
69#define STATE_WAITING 0
70#define STATE_OUTPUT 1
71#define STATE_WS_OUTPUT 2
72
73#define ISO_nl 0x0a
74#define ISO_space 0x20
75#define ISO_bang 0x21
76#define ISO_percent 0x25
77#define ISO_period 0x2e
78#define ISO_slash 0x2f
79#define ISO_colon 0x3a
80
81#ifndef _MSC_VER
82#ifndef strcpy_s
83#define strcpy_s(s1, s1m, s2) strcpy(s1, s2)
84#endif
85
86#ifndef strncpy_s
87#define strncpy_s(dst, dsz, src, sz) strncpy(dst, src, sz)
88#endif
89#endif
90
91#define MAX(a, b) ((a > b) ? a : b)
92
93union temp_type_t {
94 char binary[sizeof(http_content_type_binary)];
95 char html[sizeof(http_content_type_html)];
96 char css[sizeof(http_content_type_css)];
97 char js[sizeof(http_content_type_js)];
98 char json[sizeof(http_content_type_json)];
99 char png[sizeof(http_content_type_png)];
100 char gif[sizeof(http_content_type_gif)];
101 char jpg[sizeof(http_content_type_jpg)];
102 char svg[sizeof(http_content_type_svg)];
103 char plain[sizeof(http_content_type_plain)];
104};
105
106struct temp_buf_t{
107 char headers[MAX(sizeof(http_header_404), sizeof(http_header_200))
108 + sizeof(http_content_encoding_gzip)
109 + sizeof(http_location) + sizeof(((struct httpd_state *)0)->filename) + 2
110 + sizeof(union temp_type_t)
111 ];
112 char ws_headers[sizeof(http_header_101)
113 + sizeof(http_upgrade) + sizeof(((struct httpd_state *)0)->message.upgrade) + sizeof(http_crnl)
114 + sizeof(http_connection) + sizeof(((struct httpd_state *)0)->message.connection) + sizeof(http_crnl)
115 + sizeof(http_sec_websocket_accept) + sizeof(((struct httpd_state *)0)->message.response_key) + sizeof(http_crnl)
116 + sizeof(http_sec_websocket_protocol) + sizeof(((struct httpd_state *)0)->message.sec_websocket_protocol) + sizeof(http_crnl)
117 + sizeof(http_crnl)
118 ];
119};
120static char temp_buf[sizeof(struct temp_buf_t)];
121
122int httpd_strnicmp(const char *s1, const char *s2, size_t n)
123{
124 int i;
125 char c1, c2;
126
127 for(i = 0; i < n; i++, s1++, s2++){
128 c1 = *s1;
129 c2 = *s2;
130 if(c1 == '\0' && c2 == '\0')
131 return 0;
132
133 if(c1 >= 'a' && c1 <= 'z')
134 c1 += 'A' - 'a';
135
136 if(c2 >= 'a' && c2 <= 'z')
137 c2 += 'A' - 'a';
138
139 if(c1 < c2)
140 return -1;
141
142 if(c1 > c2)
143 return 1;
144 }
145
146 return 0;
147}
148
149struct websocket *websocket_getws(ID wbsid)
150{
151 struct uip_conn *conn = uip_getconn(wbsid);
152 if (conn == NULL)
153 return NULL;
154
155 return &conn->appstate.websocket;
156}
157
158/*---------------------------------------------------------------------------*/
159static unsigned short
160generate_part_of_file(void *state)
161{
162 struct httpd_state *s = (struct httpd_state *)state;
163 int len;
164
165 if (s->file.len > uip_mss()) {
166 len = uip_mss();
167 } else {
168 len = s->file.len;
169 }
170 s->len = httpd_fs_read(&s->file, uip_appdata, len);
171
172 return s->len;
173}
174/*---------------------------------------------------------------------------*/
175static
176PT_THREAD(send_file(struct httpd_state *s))
177{
178 PSOCK_BEGIN(&s->sout);
179
180 do {
181 PSOCK_GENERATOR_SEND(&s->sout, generate_part_of_file, s);
182 s->file.len -= s->len;
183 s->file.pos += s->len;
184 } while (s->file.len > 0);
185
186 PSOCK_END(&s->sout);
187}
188/*---------------------------------------------------------------------------*/
189static
190PT_THREAD(send_headers(struct httpd_state *s, const char *statushdr))
191{
192 char *pos = temp_buf;
193 int len;
194 char *ptr;
195
196 PSOCK_BEGIN(&s->sout);
197
198 len = strlen(statushdr);
199 memcpy(pos, statushdr, len); pos += len;
200
201 if (s->drv == 0) {
202 len = strlen(http_content_encoding_gzip);
203 memcpy(pos, http_content_encoding_gzip, len); pos += len;
204 }
205
206 if (s->file.redirect) {
207 len = strlen(http_location);
208 memcpy(pos, http_location, len); pos += len;
209 if (s->drv == 1) {
210 len = 2;
211 memcpy(pos, "/~", len); pos += len;
212 }
213 len = strlen(s->filename);
214 memcpy(pos, s->filename, len); pos += len;
215 if (s->query != NULL) {
216 pos[0] = '?'; pos++;
217 len = strlen(s->query);
218 memcpy(pos, s->query, len); pos += len;
219 }
220 len = 2;
221 memcpy(pos, "\r\n", len); pos += len;
222 }
223
224 ptr = strrchr(s->filename, ISO_period);
225 if (ptr == NULL) {
226 len = strlen(http_content_type_binary);
227 memcpy(pos, http_content_type_binary, len); pos += len;
228 } else if (strncmp(http_html, ptr, 5) == 0 ||
229 strncmp(http_shtml, ptr, 6) == 0) {
230 len = strlen(http_content_type_html);
231 memcpy(pos, http_content_type_html, len); pos += len;
232 } else if (strncmp(http_css, ptr, 4) == 0) {
233 len = strlen(http_content_type_css);
234 memcpy(pos, http_content_type_css, len); pos += len;
235 } else if (strncmp(http_js, ptr, 3) == 0) {
236 len = strlen(http_content_type_js);
237 memcpy(pos, http_content_type_js, len); pos += len;
238 } else if (strncmp(http_json, ptr, 5) == 0) {
239 len = strlen(http_content_type_json);
240 memcpy(pos, http_content_type_json, len); pos += len;
241 } else if (strncmp(http_png, ptr, 4) == 0) {
242 len = strlen(http_content_type_png);
243 memcpy(pos, http_content_type_png, len); pos += len;
244 } else if (strncmp(http_gif, ptr, 4) == 0) {
245 len = strlen(http_content_type_gif);
246 memcpy(pos, http_content_type_gif, len); pos += len;
247 } else if (strncmp(http_jpg, ptr, 4) == 0) {
248 len = strlen(http_content_type_jpg);
249 memcpy(pos, http_content_type_jpg, len); pos += len;
250 } else if (strncmp(http_svg, ptr, 4) == 0) {
251 len = strlen(http_content_type_svg);
252 memcpy(pos, http_content_type_svg, len); pos += len;
253 } else {
254 len = strlen(http_content_type_plain);
255 memcpy(pos, http_content_type_plain, len); pos += len;
256 }
257 *pos = '\0';
258
259 PSOCK_SEND(&s->sout, temp_buf, pos - temp_buf);
260
261 PSOCK_END(&s->sout);
262}
263/*---------------------------------------------------------------------------*/
264static
265PT_THREAD(handle_output(struct httpd_state *s))
266{
267 PT_BEGIN(&s->outputpt);
268
269 if (!httpd_fs_open(s->drv, s->filename, sizeof(s->filename), &s->file)) {
270 s->drv = 0;
271 strcpy_s(s->filename, sizeof(s->filename), http_404_html);
272 httpd_fs_open(s->drv, s->filename, sizeof(s->filename), &s->file);
273 PT_WAIT_THREAD(&s->outputpt,
274 send_headers(s,
275 http_header_404));
276 PT_WAIT_THREAD(&s->outputpt, send_file(s));
277 } else {
278 PT_WAIT_THREAD(&s->outputpt,
279 send_headers(s,
280 s->file.redirect ? http_header_301 : http_header_200));
281 PT_WAIT_THREAD(&s->outputpt, send_file(s));
282 }
283
284 PSOCK_CLOSE(&s->sout);
285 PT_END(&s->outputpt);
286}
287/*---------------------------------------------------------------------------*/
288static
289PT_THREAD(send_ws_headers(struct httpd_state *s, const char *statushdr))
290{
291 char *pos = temp_buf;
292 int len;
293
294 PSOCK_BEGIN(&s->sout);
295
296 len = strlen(statushdr);
297 memcpy(pos, statushdr, len); pos += len;
298
299 len = strlen(http_upgrade);
300 memcpy(pos, http_upgrade, len); pos += len;
301 len = strlen(s->message.upgrade);
302 memcpy(pos, s->message.upgrade, len); pos += len;
303 len = strlen(http_crnl);
304 memcpy(pos, http_crnl, len); pos += len;
305
306 len = strlen(http_connection);
307 memcpy(pos, http_connection, len); pos += len;
308 len = strlen(s->message.connection);
309 memcpy(pos, s->message.connection, len); pos += len;
310 len = strlen(http_crnl);
311 memcpy(pos, http_crnl, len); pos += len;
312
313 len = strlen(http_sec_websocket_accept);
314 memcpy(pos, http_sec_websocket_accept, len); pos += len;
315 len = strlen(s->message.response_key);
316 memcpy(pos, s->message.response_key, len); pos += len;
317 len = strlen(http_crnl);
318 memcpy(pos, http_crnl, len); pos += len;
319
320 len = strlen(http_sec_websocket_protocol);
321 memcpy(pos, http_sec_websocket_protocol, len); pos += len;
322 len = strlen(s->message.sec_websocket_protocol);
323 memcpy(pos, s->message.sec_websocket_protocol, len); pos += len;
324 len = strlen(http_crnl);
325 memcpy(pos, http_crnl, len); pos += len;
326
327 len = strlen(http_crnl);
328 memcpy(pos, http_crnl, len); pos += len;
329 *pos = '\0';
330
331 PSOCK_SEND(&s->sout, temp_buf, pos - temp_buf);
332
333 PSOCK_END(&s->sout);
334}
335/*---------------------------------------------------------------------------*/
336static unsigned short
337generate_part_of_ws_data(void *state)
338{
339 struct httpd_state *s = (struct httpd_state *)state;
340
341 s->len = websocket_output(&s->websocket, uip_appdata, uip_mss());
342
343 return s->len;
344}
345/*---------------------------------------------------------------------------*/
346static
347PT_THREAD(send_ws_data(struct httpd_state *s))
348{
349 PSOCK_BEGIN(&s->sout);
350
351 PSOCK_GENERATOR_SEND(&s->sout, generate_part_of_ws_data, s);
352
353 PSOCK_END(&s->sout);
354}
355/*---------------------------------------------------------------------------*/
356static
357PT_THREAD(handle_ws_output(struct httpd_state *s))
358{
359 char shaHash[20];
360 SHA_CTX sha1;
361 int len;
362
363 PT_BEGIN(&s->outputpt);
364
365 strlncat(s->message.response_key, sizeof(s->message.response_key),
366 s->message.sec_websocket_key, sizeof(s->message.sec_websocket_key));
367 len = strlncat(s->message.response_key, sizeof(s->message.response_key),
368 http_websocket_guid, sizeof(http_websocket_guid));
369 memset(shaHash, 0, sizeof(shaHash));
370 SHA1_Init(&sha1);
371 SHA1_Update(&sha1, (sha1_byte *)s->message.response_key, len);
372 SHA1_Final((sha1_byte *)shaHash, &sha1);
373 base64_encode((unsigned char *)s->message.response_key,
374 sizeof(s->message.response_key), (unsigned char *)shaHash, sizeof(shaHash));
375
376 PT_WAIT_THREAD(&s->outputpt, send_ws_headers(s, http_header_101));
377
378 s->message.response_key[0] = '\0';
379
380 do {
381 while(!websocket_newdata(&s->websocket))
382 PT_YIELD(&s->outputpt);
383
384 PT_WAIT_THREAD(&s->outputpt, send_ws_data(s));
385 } while ((s->state == STATE_WS_OUTPUT) && (!s->close_req));
386 s->state = STATE_WAITING;
387 websocket_destroy(&s->websocket);
388 s->close_req = 0;
389
390 PSOCK_CLOSE(&s->sout);
391
392 PT_END(&s->outputpt);
393}
394/*---------------------------------------------------------------------------*/
395static
396PT_THREAD(handle_input(struct httpd_state *s))
397{
398 size_t done;
399 const char *data;
400 char *ptr;
401
402 PSOCK_BEGIN(&s->sin);
403
404 memset(&s->message, 0, sizeof(s->message));
405 http_parser_init(&s->parser, HTTP_REQUEST);
406
407 for (;;) {
408 PSOCK_WAIT_UNTIL(&s->sin, psock_newdata(&s->sin));
409
410 s->parse_pos = 0;
411 s->parse_len = uip_datalen();
412 data = &((const char *)uip_appdata)[s->parse_pos];
413 done = http_parser_execute(&s->parser, &websvr_settings, data, s->parse_len);
414 if (done != 0) {
415 s->parse_pos += done;
416 s->parse_len -= done;
417 break;
418 }
419 }
420
421 if (!s->message.message_complete_cb_called
422 && s->message.method != HTTP_GET) {
423 PSOCK_CLOSE_EXIT(&s->sin);
424 }
425
426 ptr = strrchr(s->message.request_url, '?');
427 if (ptr != NULL){
428 ptr[0] = '\0';
429 s->query = &ptr[1];
430 }
431 else
432 s->query = NULL;
433
434 /* ""か"/"なら"index.html"に変更 */
435 if ((s->message.request_url[0] == '\0') || ((s->message.request_url[0] == '/') && (s->message.request_url[1] == '\0'))) {
436 s->drv = 0;
437 strcpy_s(s->filename, sizeof(s->filename), http_index_html);
438 s->file.redirect = 1;
439 }
440 /* "/~/"ならSDカードから読み込み */
441 else if ((s->message.request_url[0] == '/') && (s->message.request_url[1] == '~') && (s->message.request_url[2] == '/')) {
442 s->drv = 1;
443 strcpy_s(s->filename, sizeof(s->filename), &s->message.request_url[2]);
444 }
445 else {
446 s->drv = 0;
447 strcpy_s(s->filename, sizeof(s->filename), s->message.request_url);
448 }
449
450 /* httpd_log_file(uip_conn->ripaddr, s->message.request_url);*/
451 /* httpd_log(s->message.referer);*/
452
453 if (httpd_strnicmp(http_websocket, s->message.upgrade, sizeof(s->message.upgrade)) == 0) {
454 s->state = STATE_WS_OUTPUT;
455
456 s->close_req = 0;
457 websocket_init(&s->websocket, uip_getid((struct uip_conn *)((intptr_t)s - offsetof(struct uip_conn, appstate))));
458 for (;;) {
459 if(s->parse_len <= 0){
460 PT_YIELD(&s->sin.pt);
461 PSOCK_WAIT_UNTIL(&s->sin, psock_newdata(&s->sin));
462
463 s->parse_pos = 0;
464 s->parse_len = uip_datalen();
465 }
466 data = &((const char *)uip_appdata)[s->parse_pos];
467 done = websocket_input(&s->websocket, (void *)data, s->parse_len);
468 if ((done != 0) || (s->websocket.rstate.opecode == connection_close)) {
469 s->close_req = 1;
470 PSOCK_CLOSE_EXIT(&s->sin);
471 break;
472 }
473 s->parse_pos = s->parse_len;
474 s->parse_len = 0;
475 }
476 }
477 else {
478 s->state = STATE_OUTPUT;
479
480 while (1) {
481 PSOCK_READTO(&s->sin, ISO_nl);
482 }
483 }
484
485 PSOCK_END(&s->sin);
486}
487/*---------------------------------------------------------------------------*/
488static void
489handle_connection(struct httpd_state *s)
490{
491 handle_input(s);
492 switch (s->state) {
493 case STATE_OUTPUT:
494 handle_output(s);
495 break;
496 case STATE_WS_OUTPUT:
497 handle_ws_output(s);
498 break;
499 }
500}
501/*---------------------------------------------------------------------------*/
502void
503httpd_appcall(void)
504{
505 struct httpd_state *s = (struct httpd_state *)&(uip_conn->appstate);
506
507 if (uip_closed() || uip_aborted() || uip_timedout()) {
508 } else if (uip_connected()) {
509 if(s->state != STATE_WS_OUTPUT){
510 PSOCK_INIT(&s->sin, s->inputbuf, sizeof(s->inputbuf) - 1);
511 PSOCK_INIT(&s->sout, s->inputbuf, sizeof(s->inputbuf) - 1);
512 PT_INIT(&s->outputpt);
513 s->state = STATE_WAITING;
514 /* timer_set(&s->timer, CLOCK_SECOND * 100);*/
515 s->timer = 0;
516 handle_connection(s);
517 }
518 else {
519 handle_connection(s);
520 }
521 } else if (s != NULL) {
522 if(s->state != STATE_WS_OUTPUT){
523 if (uip_poll()) {
524 ++s->timer;
525 if (s->timer >= 20) {
526 uip_abort();
527 }
528 } else {
529 s->timer = 0;
530 }
531 }
532 handle_connection(s);
533 } else {
534 uip_abort();
535 }
536}
537/*---------------------------------------------------------------------------*/
538/**
539 * \brief Initialize the web server
540 *
541 * This function initializes the web server and should be
542 * called at system boot-up.
543 */
544void
545httpd_init(void)
546{
547 uip_listen(HTONS(41314));
548
549 httpd_fs_init();
550 kadecot_names_init();
551}
552/*---------------------------------------------------------------------------*/
553/** @} */
Note: See TracBrowser for help on using the repository browser.