Update Iot.js
[platform/upstream/iotjs.git] / src / module / iotjs_module_httpparser.c
1 /* Copyright 2015-present Samsung Electronics Co., Ltd. and other contributors
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16
17 #include "iotjs_def.h"
18 #include "iotjs_module_httpparser.h"
19 #include "iotjs_module_buffer.h"
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24
25 #define THIS iotjs_httpparserwrap_t* httpparserwrap
26
27
28 static void iotjs_httpparserwrap_destroy(THIS);
29
30
31 iotjs_httpparserwrap_t* iotjs_httpparserwrap_create(const iotjs_jval_t* jparser,
32                                                     http_parser_type type) {
33   iotjs_httpparserwrap_t* httpparserwrap = IOTJS_ALLOC(iotjs_httpparserwrap_t);
34   IOTJS_VALIDATED_STRUCT_CONSTRUCTOR(iotjs_httpparserwrap_t, httpparserwrap);
35
36   iotjs_jobjectwrap_initialize(&_this->jobjectwrap, jparser,
37                                (JFreeHandlerType)iotjs_httpparserwrap_destroy);
38
39   _this->url = iotjs_string_create();
40   _this->status_msg = iotjs_string_create();
41   for (size_t i = 0; i < HEADER_MAX; i++) {
42     _this->fields[i] = iotjs_string_create();
43     _this->values[i] = iotjs_string_create();
44   }
45
46   iotjs_httpparserwrap_initialize(httpparserwrap, type);
47   _this->parser.data = httpparserwrap;
48
49   return httpparserwrap;
50 }
51
52
53 static void iotjs_httpparserwrap_destroy(THIS) {
54   IOTJS_VALIDATED_STRUCT_DESTRUCTOR(iotjs_httpparserwrap_t, httpparserwrap);
55
56   iotjs_string_destroy(&_this->url);
57   iotjs_string_destroy(&_this->status_msg);
58   for (size_t i = 0; i < HEADER_MAX; i++) {
59     iotjs_string_destroy(&_this->fields[i]);
60     iotjs_string_destroy(&_this->values[i]);
61   }
62   iotjs_jobjectwrap_destroy(&_this->jobjectwrap);
63
64   IOTJS_RELEASE(httpparserwrap);
65 }
66
67
68 void iotjs_httpparserwrap_initialize(THIS, http_parser_type type) {
69   IOTJS_VALIDATED_STRUCT_METHOD(iotjs_httpparserwrap_t, httpparserwrap);
70
71   http_parser_init(&_this->parser, type);
72   iotjs_string_make_empty(&_this->url);
73   iotjs_string_make_empty(&_this->status_msg);
74   _this->n_fields = 0;
75   _this->n_values = 0;
76   _this->flushed = false;
77   _this->cur_jbuf = NULL;
78   _this->cur_buf = NULL;
79   _this->cur_buf_len = 0;
80 }
81
82
83 // http-parser callbacks
84 static int iotjs_httpparserwrap_on_message_begin(http_parser* parser) {
85   iotjs_httpparserwrap_t* httpparserwrap =
86       (iotjs_httpparserwrap_t*)(parser->data);
87   IOTJS_VALIDATED_STRUCT_METHOD(iotjs_httpparserwrap_t, httpparserwrap);
88   iotjs_string_make_empty(&_this->url);
89   iotjs_string_make_empty(&_this->status_msg);
90   return 0;
91 }
92
93
94 static int iotjs_httpparserwrap_on_url(http_parser* parser, const char* at,
95                                        size_t length) {
96   iotjs_httpparserwrap_t* httpparserwrap =
97       (iotjs_httpparserwrap_t*)(parser->data);
98   IOTJS_VALIDATED_STRUCT_METHOD(iotjs_httpparserwrap_t, httpparserwrap);
99   iotjs_string_append(&_this->url, at, length);
100   return 0;
101 }
102
103
104 static int iotjs_httpparserwrap_on_status(http_parser* parser, const char* at,
105                                           size_t length) {
106   iotjs_httpparserwrap_t* httpparserwrap =
107       (iotjs_httpparserwrap_t*)(parser->data);
108   IOTJS_VALIDATED_STRUCT_METHOD(iotjs_httpparserwrap_t, httpparserwrap);
109   iotjs_string_append(&_this->status_msg, at, length);
110   return 0;
111 }
112
113
114 static int iotjs_httpparserwrap_on_header_field(http_parser* parser,
115                                                 const char* at, size_t length) {
116   iotjs_httpparserwrap_t* httpparserwrap =
117       (iotjs_httpparserwrap_t*)(parser->data);
118   IOTJS_VALIDATED_STRUCT_METHOD(iotjs_httpparserwrap_t, httpparserwrap);
119   if (_this->n_fields == _this->n_values) {
120     _this->n_fields++;
121     // values and fields are flushed to JS
122     // before corresponding OnHeaderValue is called.
123     if (_this->n_fields == HEADER_MAX) {
124       iotjs_httpparserwrap_flush(httpparserwrap); // to JS world
125       _this->n_fields = 1;
126       _this->n_values = 0;
127     }
128     iotjs_string_make_empty(&_this->fields[_this->n_fields - 1]);
129   }
130   IOTJS_ASSERT(_this->n_fields == _this->n_values + 1);
131   iotjs_string_append(&_this->fields[_this->n_fields - 1], at, length);
132
133   return 0;
134 }
135
136
137 static int iotjs_httpparserwrap_on_header_value(http_parser* parser,
138                                                 const char* at, size_t length) {
139   iotjs_httpparserwrap_t* httpparserwrap =
140       (iotjs_httpparserwrap_t*)(parser->data);
141   IOTJS_VALIDATED_STRUCT_METHOD(iotjs_httpparserwrap_t, httpparserwrap);
142   if (_this->n_fields != _this->n_values) {
143     _this->n_values++;
144     iotjs_string_make_empty(&_this->values[_this->n_values - 1]);
145   }
146
147   IOTJS_ASSERT(_this->n_fields == _this->n_values);
148
149   iotjs_string_append(&_this->values[_this->n_values - 1], at, length);
150
151   return 0;
152 }
153
154
155 static int iotjs_httpparserwrap_on_headers_complete(http_parser* parser) {
156   iotjs_httpparserwrap_t* httpparserwrap =
157       (iotjs_httpparserwrap_t*)(parser->data);
158   IOTJS_VALIDATED_STRUCT_METHOD(iotjs_httpparserwrap_t, httpparserwrap);
159
160   const iotjs_jval_t* jobj = iotjs_httpparserwrap_jobject(httpparserwrap);
161   iotjs_jval_t func =
162       iotjs_jval_get_property(jobj, IOTJS_MAGIC_STRING_ONHEADERSCOMPLETE);
163   IOTJS_ASSERT(iotjs_jval_is_function(&func));
164
165   // URL
166   iotjs_jargs_t argv = iotjs_jargs_create(1);
167   iotjs_jval_t info = iotjs_jval_create_object();
168
169   if (_this->flushed) {
170     // If some headers already are flushed,
171     // flush the remaining headers.
172     // In Flush function, url is already flushed to JS.
173     iotjs_httpparserwrap_flush(httpparserwrap);
174   } else {
175     // Here, there was no flushed header.
176     // We need to make a new header object with all header fields
177     iotjs_jval_t jheader = iotjs_httpparserwrap_make_header(httpparserwrap);
178     iotjs_jval_set_property_jval(&info, IOTJS_MAGIC_STRING_HEADERS, &jheader);
179     iotjs_jval_destroy(&jheader);
180     if (_this->parser.type == HTTP_REQUEST) {
181       IOTJS_ASSERT(!iotjs_string_is_empty(&_this->url));
182       iotjs_jval_set_property_string(&info, IOTJS_MAGIC_STRING_URL,
183                                      &_this->url);
184     }
185   }
186   _this->n_fields = _this->n_values = 0;
187
188   // Method
189   if (_this->parser.type == HTTP_REQUEST) {
190     iotjs_jval_set_property_number(&info, IOTJS_MAGIC_STRING_METHOD,
191                                    _this->parser.method);
192   }
193
194   // Status
195   if (_this->parser.type == HTTP_RESPONSE) {
196     iotjs_jval_set_property_number(&info, IOTJS_MAGIC_STRING_STATUS,
197                                    _this->parser.status_code);
198     iotjs_jval_set_property_string(&info, IOTJS_MAGIC_STRING_STATUS_MSG,
199                                    &_this->status_msg);
200   }
201
202
203   // For future support, current http_server module does not support
204   // upgrade and keepalive.
205   // upgrade
206   iotjs_jval_set_property_boolean(&info, IOTJS_MAGIC_STRING_UPGRADE,
207                                   _this->parser.upgrade);
208   // shouldkeepalive
209   iotjs_jval_set_property_boolean(&info, IOTJS_MAGIC_STRING_SHOULDKEEPALIVE,
210                                   http_should_keep_alive(&_this->parser));
211
212
213   iotjs_jargs_append_jval(&argv, &info);
214
215   iotjs_jval_t res = iotjs_make_callback_with_result(&func, jobj, &argv);
216   bool ret = iotjs_jval_as_boolean(&res);
217
218   iotjs_jargs_destroy(&argv);
219   iotjs_jval_destroy(&func);
220   iotjs_jval_destroy(&res);
221   iotjs_jval_destroy(&info);
222
223   return ret;
224 }
225
226
227 static int iotjs_httpparserwrap_on_body(http_parser* parser, const char* at,
228                                         size_t length) {
229   iotjs_httpparserwrap_t* httpparserwrap =
230       (iotjs_httpparserwrap_t*)(parser->data);
231   IOTJS_VALIDATED_STRUCT_METHOD(iotjs_httpparserwrap_t, httpparserwrap);
232
233   const iotjs_jval_t* jobj = iotjs_httpparserwrap_jobject(httpparserwrap);
234   iotjs_jval_t func = iotjs_jval_get_property(jobj, IOTJS_MAGIC_STRING_ONBODY);
235   IOTJS_ASSERT(iotjs_jval_is_function(&func));
236
237   iotjs_jargs_t argv = iotjs_jargs_create(3);
238   iotjs_jargs_append_jval(&argv, _this->cur_jbuf);
239   iotjs_jargs_append_number(&argv, at - _this->cur_buf);
240   iotjs_jargs_append_number(&argv, length);
241
242
243   iotjs_make_callback(&func, jobj, &argv);
244
245   iotjs_jargs_destroy(&argv);
246   iotjs_jval_destroy(&func);
247
248   return 0;
249 }
250
251
252 static int iotjs_httpparserwrap_on_message_complete(http_parser* parser) {
253   iotjs_httpparserwrap_t* httpparserwrap =
254       (iotjs_httpparserwrap_t*)(parser->data);
255   IOTJS_VALIDATABLE_STRUCT_METHOD_VALIDATE(iotjs_httpparserwrap_t,
256                                            httpparserwrap);
257
258   const iotjs_jval_t* jobj = iotjs_httpparserwrap_jobject(httpparserwrap);
259   iotjs_jval_t func =
260       iotjs_jval_get_property(jobj, IOTJS_MAGIC_STRING_ONMESSAGECOMPLETE);
261   IOTJS_ASSERT(iotjs_jval_is_function(&func));
262
263   iotjs_make_callback(&func, jobj, iotjs_jargs_get_empty());
264
265   iotjs_jval_destroy(&func);
266
267   return 0;
268 }
269
270
271 iotjs_jval_t iotjs_httpparserwrap_make_header(THIS) {
272   IOTJS_VALIDATED_STRUCT_METHOD(iotjs_httpparserwrap_t, httpparserwrap);
273
274   iotjs_jval_t jheader = iotjs_jval_create_array(_this->n_values * 2);
275   for (size_t i = 0; i < _this->n_values; i++) {
276     iotjs_jval_t f = iotjs_jval_create_string(&_this->fields[i]);
277     iotjs_jval_t v = iotjs_jval_create_string(&_this->values[i]);
278     iotjs_jval_set_property_by_index(&jheader, i * 2, &f);
279     iotjs_jval_set_property_by_index(&jheader, i * 2 + 1, &v);
280     iotjs_jval_destroy(&f);
281     iotjs_jval_destroy(&v);
282   }
283   return jheader;
284 }
285
286
287 void iotjs_httpparserwrap_flush(THIS) {
288   IOTJS_VALIDATED_STRUCT_METHOD(iotjs_httpparserwrap_t, httpparserwrap);
289
290   const iotjs_jval_t* jobj = iotjs_httpparserwrap_jobject(httpparserwrap);
291   iotjs_jval_t func =
292       iotjs_jval_get_property(jobj, IOTJS_MAGIC_STRING_ONHEADERS);
293   IOTJS_ASSERT(iotjs_jval_is_function(&func));
294
295   iotjs_jargs_t argv = iotjs_jargs_create(2);
296   iotjs_jval_t jheader = iotjs_httpparserwrap_make_header(httpparserwrap);
297   iotjs_jargs_append_jval(&argv, &jheader);
298   iotjs_jval_destroy(&jheader);
299   if (_this->parser.type == HTTP_REQUEST &&
300       !iotjs_string_is_empty(&_this->url)) {
301     iotjs_jargs_append_string(&argv, &_this->url);
302   }
303
304   iotjs_make_callback(&func, jobj, &argv);
305
306   iotjs_string_make_empty(&_this->url);
307   iotjs_jargs_destroy(&argv);
308   iotjs_jval_destroy(&func);
309   _this->flushed = true;
310 }
311
312
313 void iotjs_httpparserwrap_set_buf(THIS, iotjs_jval_t* jbuf, char* buf, int sz) {
314   IOTJS_VALIDATED_STRUCT_METHOD(iotjs_httpparserwrap_t, httpparserwrap);
315   _this->cur_jbuf = jbuf;
316   _this->cur_buf = buf;
317   _this->cur_buf_len = sz;
318 }
319
320
321 iotjs_jval_t* iotjs_httpparserwrap_jobject(THIS) {
322   IOTJS_VALIDATED_STRUCT_METHOD(iotjs_httpparserwrap_t, httpparserwrap);
323
324   return iotjs_jobjectwrap_jobject(&_this->jobjectwrap);
325 }
326
327
328 http_parser* iotjs_httpparserwrap_parser(THIS) {
329   IOTJS_VALIDATED_STRUCT_METHOD(iotjs_httpparserwrap_t, httpparserwrap);
330   return &_this->parser;
331 }
332
333
334 #undef THIS
335
336
337 const struct http_parser_settings settings = {
338   iotjs_httpparserwrap_on_message_begin,
339   iotjs_httpparserwrap_on_url,
340   iotjs_httpparserwrap_on_status,
341   iotjs_httpparserwrap_on_header_field,
342   iotjs_httpparserwrap_on_header_value,
343   iotjs_httpparserwrap_on_headers_complete,
344   iotjs_httpparserwrap_on_body,
345   iotjs_httpparserwrap_on_message_complete,
346   NULL, /* on_chunk_header */
347   NULL, /* on_chunk_complete */
348 };
349
350
351 static iotjs_httpparserwrap_t* get_parser_wrap(const iotjs_jval_t* jparser) {
352   uintptr_t handle = iotjs_jval_get_object_native_handle(jparser);
353   return (iotjs_httpparserwrap_t*)(handle);
354 }
355
356
357 JHANDLER_FUNCTION(Reinitialize) {
358   JHANDLER_CHECK_THIS(object);
359   JHANDLER_CHECK_ARGS(1, number);
360
361   const iotjs_jval_t* jparser = JHANDLER_GET_THIS(object);
362
363   http_parser_type httpparser_type =
364       (http_parser_type)(JHANDLER_GET_ARG(0, number));
365   IOTJS_ASSERT(httpparser_type == HTTP_REQUEST ||
366                httpparser_type == HTTP_RESPONSE);
367
368   iotjs_httpparserwrap_t* parser = get_parser_wrap(jparser);
369   iotjs_httpparserwrap_initialize(parser, httpparser_type);
370 }
371
372
373 JHANDLER_FUNCTION(Finish) {
374   JHANDLER_CHECK_THIS(object);
375   JHANDLER_CHECK_ARGS(0);
376
377   const iotjs_jval_t* jparser = JHANDLER_GET_THIS(object);
378   iotjs_httpparserwrap_t* parser = get_parser_wrap(jparser);
379
380   http_parser* nativeparser = iotjs_httpparserwrap_parser(parser);
381   int rv = http_parser_execute(nativeparser, &settings, NULL, 0);
382
383   if (rv != 0) {
384     enum http_errno err = HTTP_PARSER_ERRNO(nativeparser);
385
386     iotjs_jval_t eobj = iotjs_jval_create_error("Parse Error");
387     iotjs_jval_set_property_number(&eobj, IOTJS_MAGIC_STRING_BYTEPARSED, 0);
388     iotjs_jval_set_property_string_raw(&eobj, IOTJS_MAGIC_STRING_CODE,
389                                        http_errno_name(err));
390     iotjs_jhandler_return_jval(jhandler, &eobj);
391     iotjs_jval_destroy(&eobj);
392   }
393 }
394
395
396 JHANDLER_FUNCTION(Execute) {
397   JHANDLER_CHECK_THIS(object);
398   JHANDLER_CHECK_ARGS(1, object);
399
400   const iotjs_jval_t* jparser = JHANDLER_GET_THIS(object);
401   iotjs_httpparserwrap_t* parser = get_parser_wrap(jparser);
402
403
404   const iotjs_jval_t* jbuffer = JHANDLER_GET_ARG(0, object);
405   iotjs_bufferwrap_t* buffer_wrap = iotjs_bufferwrap_from_jbuffer(jbuffer);
406   char* buf_data = iotjs_bufferwrap_buffer(buffer_wrap);
407   int buf_len = iotjs_bufferwrap_length(buffer_wrap);
408   JHANDLER_CHECK(buf_data != NULL);
409   JHANDLER_CHECK(buf_len > 0);
410
411   iotjs_httpparserwrap_set_buf(parser, (iotjs_jval_t*)jbuffer, buf_data,
412                                buf_len);
413
414   http_parser* nativeparser = iotjs_httpparserwrap_parser(parser);
415   int nparsed = http_parser_execute(nativeparser, &settings, buf_data, buf_len);
416
417   iotjs_httpparserwrap_set_buf(parser, NULL, NULL, 0);
418
419
420   if (!nativeparser->upgrade && nparsed != buf_len) {
421     // nparsed should equal to buf_len except UPGRADE protocol
422     enum http_errno err = HTTP_PARSER_ERRNO(nativeparser);
423     iotjs_jval_t eobj = iotjs_jval_create_error("Parse Error");
424     iotjs_jval_set_property_number(&eobj, IOTJS_MAGIC_STRING_BYTEPARSED, 0);
425     iotjs_jval_set_property_string_raw(&eobj, IOTJS_MAGIC_STRING_CODE,
426                                        http_errno_name(err));
427     iotjs_jhandler_return_jval(jhandler, &eobj);
428     iotjs_jval_destroy(&eobj);
429   } else {
430     iotjs_jhandler_return_number(jhandler, nparsed);
431   }
432 }
433
434
435 JHANDLER_FUNCTION(Pause) {
436   JHANDLER_CHECK_THIS(object);
437   JHANDLER_CHECK_ARGS(0);
438   const iotjs_jval_t* jparser = JHANDLER_GET_THIS(object);
439   iotjs_httpparserwrap_t* parser = get_parser_wrap(jparser);
440   http_parser* nativeparser = iotjs_httpparserwrap_parser(parser);
441   http_parser_pause(nativeparser, 1);
442 }
443
444
445 JHANDLER_FUNCTION(Resume) {
446   JHANDLER_CHECK_THIS(object);
447   JHANDLER_CHECK_ARGS(0);
448   const iotjs_jval_t* jparser = JHANDLER_GET_THIS(object);
449   iotjs_httpparserwrap_t* parser = get_parser_wrap(jparser);
450   http_parser* nativeparser = iotjs_httpparserwrap_parser(parser);
451   http_parser_pause(nativeparser, 0);
452 }
453
454
455 JHANDLER_FUNCTION(HTTPParserCons) {
456   JHANDLER_CHECK_THIS(object);
457   JHANDLER_CHECK_ARGS(1, number);
458
459   const iotjs_jval_t* jparser = JHANDLER_GET_THIS(object);
460
461   http_parser_type httpparser_type =
462       (http_parser_type)(JHANDLER_GET_ARG(0, number));
463   IOTJS_ASSERT(httpparser_type == HTTP_REQUEST ||
464                httpparser_type == HTTP_RESPONSE);
465   iotjs_httpparserwrap_t* parser =
466       iotjs_httpparserwrap_create(jparser, httpparser_type);
467   IOTJS_ASSERT(iotjs_jval_is_object(iotjs_httpparserwrap_jobject(parser)));
468   IOTJS_ASSERT(get_parser_wrap(jparser) == parser);
469 }
470
471
472 iotjs_jval_t InitHttpparser() {
473   iotjs_jval_t httpparser = iotjs_jval_create_object();
474
475   iotjs_jval_t jParserCons =
476       iotjs_jval_create_function_with_dispatch(HTTPParserCons);
477   iotjs_jval_set_property_jval(&httpparser, IOTJS_MAGIC_STRING_HTTPPARSER,
478                                &jParserCons);
479
480   iotjs_jval_set_property_number(&jParserCons, IOTJS_MAGIC_STRING_REQUEST,
481                                  HTTP_REQUEST);
482   iotjs_jval_set_property_number(&jParserCons, IOTJS_MAGIC_STRING_RESPONSE,
483                                  HTTP_RESPONSE);
484
485   iotjs_jval_t methods = iotjs_jval_create_object();
486 #define V(num, name, string) \
487   iotjs_jval_set_property_string_raw(&methods, #num, #string);
488   HTTP_METHOD_MAP(V)
489 #undef V
490
491   iotjs_jval_set_property_jval(&jParserCons, IOTJS_MAGIC_STRING_METHODS,
492                                &methods);
493
494   iotjs_jval_t prototype = iotjs_jval_create_object();
495
496   iotjs_jval_set_method(&prototype, IOTJS_MAGIC_STRING_EXECUTE, Execute);
497   iotjs_jval_set_method(&prototype, IOTJS_MAGIC_STRING_REINITIALIZE,
498                         Reinitialize);
499   iotjs_jval_set_method(&prototype, IOTJS_MAGIC_STRING_FINISH, Finish);
500   iotjs_jval_set_method(&prototype, IOTJS_MAGIC_STRING_PAUSE, Pause);
501   iotjs_jval_set_method(&prototype, IOTJS_MAGIC_STRING_RESUME, Resume);
502
503   iotjs_jval_set_property_jval(&jParserCons, IOTJS_MAGIC_STRING_PROTOTYPE,
504                                &prototype);
505
506   iotjs_jval_destroy(&jParserCons);
507   iotjs_jval_destroy(&methods);
508   iotjs_jval_destroy(&prototype);
509
510   return httpparser;
511 }