source: EcnlProtoTool/trunk/ntshell/webserver/httpd.c@ 278

Last change on this file since 278 was 270, checked in by coas-nagasima, 7 years ago

mruby版ECNLプロトタイピング・ツールを追加

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
  • Property svn:mime-type set to text/x-csrc
File size: 10.8 KB
Line 
1/*
2 * TOPPERS ECHONET Lite Communication Middleware
3 *
4 * Copyright (C) 2017 Cores Co., Ltd. Japan
5 *
6 * 上記著作権者
7は,以下の(1)~(4)の条件を満たす場合に限り,本ソフトウェ
8 * ア(本ソフトウェアを改変したものを含む.以下同じ)を使用・複製・改
9 * 変・再é…
10å¸ƒï¼ˆä»¥ä¸‹ï¼Œåˆ©ç”¨ã¨å‘¼ã¶ï¼‰ã™ã‚‹ã“とを無償で許諾する.
11 * (1) 本ソフトウェアをソースコードの形で利用する場合には,上記の著作
12 * 権表示,この利用条件および下記の無保証規定が,そのままの形でソー
13 * スコード中に含まれていること.
14 * (2) 本ソフトウェアを,ライブラリ形式など,他のソフトウェア開発に使
15 * 用できる形で再é…
16å¸ƒã™ã‚‹å ´åˆã«ã¯ï¼Œå†é…
17å¸ƒã«ä¼´ã†ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆï¼ˆåˆ©ç”¨
18 * 者
19マニュアルなど)に,上記の著作権表示,この利用条件および下記
20 * の無保証規定を掲載すること.
21 * (3) 本ソフトウェアを,機器に組み込むなど,他のソフトウェア開発に使
22 * 用できない形で再é…
23å¸ƒã™ã‚‹å ´åˆã«ã¯ï¼Œæ¬¡ã®ã„ずれかの条件を満たすこ
24 * と.
25 * (a) 再é…
26å¸ƒã«ä¼´ã†ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆï¼ˆåˆ©ç”¨è€…
27マニュアルなど)に,上記の著
28 * 作権表示,この利用条件および下記の無保証規定を掲載すること.
29 * (b) 再é…
30å¸ƒã®å½¢æ…
31‹ã‚’,別に定める方法によって,TOPPERSプロジェクトに
32 * 報告すること.
33 * (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損
34 * 害からも,上記著作権者
35およびTOPPERSプロジェクトをå…
36è²¬ã™ã‚‹ã“と.
37 * また,本ソフトウェアのユーザまたはエンドユーザからのいかなる理
38 * 由に基づく請求からも,上記著作権者
39およびTOPPERSプロジェクトを
40 * å…
41è²¬ã™ã‚‹ã“と.
42 *
43 * 本ソフトウェアは,無保証で提供されているものである.上記著作権者
44お
45 * よびTOPPERSプロジェクトは,本ソフトウェアに関して,特定の使用目的
46 * に対する適合性も含めて,いかなる保証も行わない.また,本ソフトウェ
47 * アの利用により直接的または間接的に生じたいかなる損害に関しても,そ
48 * の責任を負わない.
49 *
50 * @(#) $Id: httpd.c 270 2017-02-09 04:03:47Z coas-nagasima $
51 */
52
53#include "httpd.h"
54#include <string.h>
55#include <stdlib.h>
56#include "syssvc/syslog.h"
57#include "http-strings.h"
58#include "netinet/in.h"
59#include "arduino.h"
60
61#define TRUE 1
62#define FALSE 0
63
64#ifndef _MSC_VER
65/* strnlen() is a POSIX.2008 addition. Can't rely on it being available so
66 * define it ourselves.
67 */
68size_t
69strnlen(const char *s, size_t maxlen)
70{
71 const char *p;
72
73 p = memchr(s, '\0', maxlen);
74 if (p == NULL)
75 return maxlen;
76
77 return p - s;
78}
79
80void strcpy_s(char *dst, int size, const char *src)
81{
82 int slen = strlen(src);
83 if (slen >= size)
84 slen = size - 1;
85 memcpy(dst, src, slen);
86 dst[slen] = '\0';
87}
88
89void strcat_s(char *dst, int size, const char *src)
90{
91 int dlen = strlen(dst);
92 int slen = strlen(src);
93 if (dlen + slen >= size)
94 slen = size - 1 - dlen;
95 memcpy(&dst[dlen], src, slen);
96 dst[dlen + slen] = '\0';
97}
98#endif
99
100int httpd_strnicmp(const char *s1, const char *s2, size_t n)
101{
102 int i;
103 char c1, c2;
104
105 for (i = 0; i < n; i++, s1++, s2++) {
106 c1 = *s1;
107 c2 = *s2;
108 if (c1 == '\0' && c2 == '\0')
109 return 0;
110
111 if (c1 >= 'a' && c1 <= 'z')
112 c1 += 'A' - 'a';
113
114 if (c2 >= 'a' && c2 <= 'z')
115 c2 += 'A' - 'a';
116
117 if (c1 < c2)
118 return -1;
119
120 if (c1 > c2)
121 return 1;
122 }
123
124 return 0;
125}
126
127size_t
128strlncat(char *dst, size_t len, const char *src, size_t n)
129{
130 size_t slen;
131 size_t dlen;
132 size_t rlen;
133 size_t ncpy = 0;
134
135 slen = strnlen(src, n);
136 dlen = strnlen(dst, len);
137
138 if (dlen < len) {
139 rlen = len - dlen;
140 ncpy = slen < rlen ? slen : (rlen - 1);
141 memcpy(dst + dlen, src, ncpy);
142 dst[dlen + ncpy] = '\0';
143 }
144
145 //assert(len > slen + dlen);
146 //return slen + dlen;
147 return ncpy;
148}
149
150extern struct httpd_state *uploding;
151
152int websvr_message_begin(http_parser *p)
153{
154 struct httpd_state *s = get_context(p);
155 memset(&s->message, 0, sizeof(s->message));
156 s->message.message_begin_cb_called = TRUE;
157 return 0;
158}
159
160int websvr_request_url(http_parser *p, const char *buf, size_t len)
161{
162 struct httpd_state *s = get_context(p);
163
164 strlncat(s->message.request_url, sizeof(s->message.request_url), buf, len);
165
166 char *ptr = strrchr(s->message.request_url, '?');
167 if (ptr != NULL) {
168 ptr[0] = '\0';
169 s->query = &ptr[1];
170 }
171 else
172 s->query = NULL;
173
174 /* ""か"/"なら"index.html"に変更 */
175 if ((s->message.request_url[0] == '\0') || ((s->message.request_url[0] == '/') && (s->message.request_url[1] == '\0'))) {
176 s->filename = &s->message.filename[sizeof(s->message.filename) - 2];
177 strcpy_s(s->filename, sizeof(s->message.request_url) + 2, "0:");
178 strcat_s(s->filename, sizeof(s->message.request_url) + 2, http_index_html);
179 s->file.redirect = 1;
180 }
181 /* "/~/"ならSDカードから読み込み */
182 else if ((s->message.request_url[0] == '/') && (s->message.request_url[1] == '~') && (s->message.request_url[2] == '/')) {
183 s->filename = &s->message.filename[sizeof(s->message.filename) - 2 - sizeof(http_www) - 1 + 2];
184 memcpy(s->filename, "1:", 2);
185 memcpy(s->filename, http_www, sizeof(http_www) - 1);
186 }
187 else {
188 s->filename = &s->message.filename[sizeof(s->message.filename) - 2];
189 memcpy(s->filename, "0:", 2);
190 }
191 return 0;
192}
193
194int websvr_response_status(http_parser *p, const char *buf, size_t len)
195{
196 struct httpd_state *s = get_context(p);
197
198 strlncat(s->message.response_status, sizeof(s->message.response_status), buf, len);
199
200 return 0;
201}
202
203int websvr_header_field(http_parser *p, const char *buf, size_t len)
204{
205 struct httpd_state *s = get_context(p);
206 struct message *m = &s->message;
207
208 if (strncmp("Referer", buf, len) == 0) {
209 m->num_headers = 1;
210 }
211 else if (strncmp("Host", buf, len) == 0) {
212 m->num_headers = 2;
213 }
214 else if (strncmp("Upgrade", buf, len) == 0) {
215 m->num_headers = 3;
216 }
217 else if (strncmp("Connection", buf, len) == 0) {
218 m->num_headers = 4;
219 }
220 else if (strncmp("Sec-WebSocket-Key", buf, len) == 0) {
221 m->num_headers = 5;
222 }
223 else if (strncmp("Origin", buf, len) == 0) {
224 m->num_headers = 6;
225 }
226 else if (strncmp("Sec-WebSocket-Protocol", buf, len) == 0) {
227 m->num_headers = 7;
228 }
229 else if (strncmp("Sec-WebSocket-Version", buf, len) == 0) {
230 m->num_headers = 8;
231 }
232 else {
233 m->num_headers = 0;
234 }
235
236 return 0;
237}
238
239int websvr_header_value(http_parser *p, const char *buf, size_t len)
240{
241 struct httpd_state *s = get_context(p);
242 struct message *m = &s->message;
243
244 switch (m->num_headers) {
245 case 1:
246 strlncat(m->referer, sizeof(m->referer), buf, len);
247 break;
248 case 2:
249 strlncat(m->host, sizeof(m->host), buf, len);
250 break;
251 case 3:
252 strlncat(m->upgrade, sizeof(m->upgrade), buf, len);
253 break;
254 case 4:
255 strlncat(m->connection, sizeof(m->connection), buf, len);
256 break;
257 case 5:
258 strlncat(m->sec_websocket_key, sizeof(m->sec_websocket_key), buf, len);
259 break;
260 case 6:
261 strlncat(m->origin, sizeof(m->origin), buf, len);
262 break;
263 case 7:
264 strlncat(m->sec_websocket_protocol, sizeof(m->sec_websocket_protocol), buf, len);
265 break;
266 case 8:
267 strlncat(m->sec_websocket_version, sizeof(m->sec_websocket_version), buf, len);
268 break;
269 }
270
271 return 0;
272}
273
274int websvr_headers_complete(http_parser *p)
275{
276 struct httpd_state *s = get_context(p);
277
278 s->message.method = p->method;
279 s->message.http_major = p->http_major;
280 s->message.http_minor = p->http_minor;
281 s->message.headers_complete_cb_called = TRUE;
282 s->message.should_keep_alive = http_should_keep_alive(p);
283
284 if ((s->message.method == HTTP_GET)
285 && httpd_strnicmp(http_websocket, s->message.upgrade, sizeof(s->message.upgrade)) == 0) {
286 s->in.state = IN_STATE_WEBSOCKET;
287 s->state = STATE_WEBSOCKET;
288 s->close_req = 0;
289 websocket_init(&s->websocket, s->cepid);
290 return 0;
291 }
292 else if (s->message.method == HTTP_GET) {
293 s->in.state = IN_STATE_RESPONSE;
294 s->out.state = OUT_STATE_OPEN_GET_FILE;
295 return 1;
296 }
297 else if (s->message.method == HTTP_POST) {
298 if ((s->parser.content_length > 16 * 1024)
299 || httpd_strnicmp(s->message.request_url, http_upload, sizeof(http_upload) - 1) != 0) {
300 goto error;
301 }
302
303 if (uploding == NULL) {
304 uploding = s;
305 // アップロードå…
306ˆã¯SDカード
307 s->filename[0] = '1';
308 syslog(LOG_NOTICE, "create: %s.%d %s", s->addr, ((T_IPV4EP *)s->dst)->portno, s->filename);
309 if (!httpd_fs_create(s->filename, &s->file)) {
310 goto error;
311 }
312
313 s->in.state = IN_STATE_UPLOAD;
314 }
315 else if (strcmp(s->filename, uploding->filename) == 0) {
316 syslog(LOG_NOTICE, "collision: %s.%d %s", s->addr, ((T_IPV4EP *)s->dst)->portno, s->filename);
317 goto error;
318 }
319 else {
320 s->in.state = IN_STATE_UPLOAD_WAIT;
321 s->in.wait = true;
322 }
323
324 s->out.state = OUT_STATE_WAIT_POST_BODY;
325 s->out.wait = true;
326 return 0;
327 }
328 else {
329 s->state = STATE_CLOSING;
330 return 1;
331 }
332error:
333 s->filename = NULL;
334 s->response_body = http_content_403;
335 s->response_pos = 0;
336 s->response_len = sizeof(http_content_403) - 1;
337
338 s->out.statushdr = http_header_403;
339 s->out.state = OUT_STATE_SEND_HEADER;
340 return 1;
341}
342
343int websvr_body(http_parser *p, const char *buf, size_t len)
344{
345 struct httpd_state *s = get_context(p);
346
347 if (s->message.body_is_final) {
348 syslog(LOG_ERROR, "\n\n *** Error http_body_is_final() should return 1 "
349 "on last on_body callback call "
350 "but it doesn't! ***\n\n");
351 s->state = STATE_CLOSING;
352 return 0;
353 }
354
355 httpd_fs_write(&s->file, buf, len);
356
357 s->message.body_size += len;
358 s->message.body_is_final = http_body_is_final(p);
359
360 if (s->message.body_is_final) {
361 syslog(LOG_NOTICE, "close: %s.%d %s", s->addr, ((T_IPV4EP *)s->dst)->portno, s->filename);
362 httpd_fs_close(&s->file);
363 memset(&s->file, 0, sizeof(s->file));
364
365 strcpy_s(RubyFilename, sizeof(RubyFilename), s->filename);
366 s->reset = 1;
367
368 s->filename = NULL;
369 s->response_body = http_content_200;
370 s->response_pos = 0;
371 s->response_len = sizeof(http_content_200) - 1;
372
373 uploding = NULL;
374 s->in.state = IN_STATE_END;
375 s->out.state = OUT_STATE_BODY_RECEIVED;
376 }
377
378 return 0;
379}
380
381int websvr_message_complete(http_parser *p)
382{
383 struct httpd_state *s = get_context(p);
384 if (s->message.should_keep_alive != http_should_keep_alive(p)) {
385 syslog(LOG_ERROR, "\n\n *** Error http_should_keep_alive() should have same "
386 "value in both on_message_complete and on_headers_complete "
387 "but it doesn't! ***\n\n");
388 assert(0);
389 }
390
391 if (s->message.body_size &&
392 http_body_is_final(p) &&
393 !s->message.body_is_final) {
394 syslog(LOG_ERROR, "\n\n *** Error http_body_is_final() should return 1 "
395 "on last on_body callback call "
396 "but it doesn't! ***\n\n");
397 assert(0);
398 }
399
400 s->message.message_complete_cb_called = TRUE;
401 return 0;
402}
403
404http_parser_settings websvr_settings =
405{
406 websvr_message_begin,
407 websvr_request_url,
408 websvr_response_status,
409 websvr_header_field,
410 websvr_header_value,
411 websvr_headers_complete,
412 websvr_body,
413 websvr_message_complete,
414};
Note: See TracBrowser for help on using the repository browser.