Skip to content

Commit

Permalink
Add initial version of http_parser bindings
Browse files Browse the repository at this point in the history
  • Loading branch information
creationix committed Mar 14, 2012
1 parent eb4d106 commit 50a990f
Show file tree
Hide file tree
Showing 5 changed files with 307 additions and 2 deletions.
4 changes: 2 additions & 2 deletions luvmonkey.gyp
Expand Up @@ -39,6 +39,7 @@
'dependencies': [
'deps/smjs/smjs.gyp:smjs',
'deps/uv/uv.gyp:uv',
'deps/http-parser/http_parser.gyp:http_parser',
'js2c#host'
],
'conditions': [
Expand All @@ -57,11 +58,10 @@
'include_dirs': [
'src',
'deps/uv/src/ares',
'deps/mozilla-central/js/src/dist/include',
'deps/mozilla-central/js/src',
'<(SHARED_INTERMEDIATE_DIR)' # for js_scripts.h
],
'sources': [
'src/lhttp_parser.c',
'src/luv_handle.c',
'src/luv_stream.c',
'src/luv_tcp.c',
Expand Down
269 changes: 269 additions & 0 deletions src/lhttp_parser.c
@@ -0,0 +1,269 @@
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include "lhttp_parser.h"
#include "http_parser.h"

/*static const char* method_to_str(unsigned short m) {
switch (m) {
case HTTP_DELETE: return "DELETE";
case HTTP_GET: return "GET";
case HTTP_HEAD: return "HEAD";
case HTTP_POST: return "POST";
case HTTP_PUT: return "PUT";
case HTTP_CONNECT: return "CONNECT";
case HTTP_OPTIONS: return "OPTIONS";
case HTTP_TRACE: return "TRACE";
case HTTP_COPY: return "COPY";
case HTTP_LOCK: return "LOCK";
case HTTP_MKCOL: return "MKCOL";
case HTTP_MOVE: return "MOVE";
case HTTP_PROPFIND: return "PROPFIND";
case HTTP_PROPPATCH: return "PROPPATCH";
case HTTP_UNLOCK: return "UNLOCK";
case HTTP_REPORT: return "REPORT";
case HTTP_MKACTIVITY: return "MKACTIVITY";
case HTTP_CHECKOUT: return "CHECKOUT";
case HTTP_MERGE: return "MERGE";
case HTTP_MSEARCH: return "MSEARCH";
case HTTP_NOTIFY: return "NOTIFY";
case HTTP_SUBSCRIBE: return "SUBSCRIBE";
case HTTP_UNSUBSCRIBE:return "UNSUBSCRIBE";
default: return "UNKNOWN_METHOD";

This comment has been minimized.

Copy link
@bnoordhuis

bnoordhuis Mar 15, 2012

Collaborator

Use the HTTP_METHOD_MAP macro here.

This comment has been minimized.

Copy link
@creationix

creationix Mar 15, 2012

Author Owner

Good idea, added.

}
}
*/
static struct http_parser_settings lhttp_parser_settings;

static int lhttp_parser_on_message_begin(http_parser *p) {
printf("on_message_begin\n");
return 0;
}

static int lhttp_parser_on_message_complete(http_parser *p) {
printf("on_message_complete\n");
return 0;
}


static int lhttp_parser_on_url(http_parser *p, const char *at, size_t length) {
printf("on_url\n");
return 0;
}

static int lhttp_parser_on_header_field(http_parser *p, const char *at, size_t length) {
printf("on_header_field\n");
return 0;
}

static int lhttp_parser_on_header_value(http_parser *p, const char *at, size_t length) {
printf("on_header_value\n");
return 0;
}

static int lhttp_parser_on_body(http_parser *p, const char *at, size_t length) {
printf("on_header_body\n");
return 0;
}

static int lhttp_parser_on_headers_complete(http_parser *p) {
printf("on_headers_complete\n");
return 0;
}

static void HttpParser_finalize(JSContext *cx, JSObject *obj);

static JSClass HttpParser_class = {
"HttpParser", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, HttpParser_finalize,
JSCLASS_NO_OPTIONAL_MEMBERS
};

static JSBool HttpParser_constructor(JSContext *cx, unsigned argc, jsval *vp) {
JSObject* obj = JS_NewObject(cx, &HttpParser_class, HttpParser_prototype, NULL);

http_parser* parser = malloc(sizeof(http_parser));
http_parser_init(parser, HTTP_REQUEST);
JS_SetPrivate(obj, parser);

JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj));

return JS_TRUE;
}

static void HttpParser_finalize(JSContext *cx, JSObject *this) {
free(JS_GetPrivate(this));
}

static JSBool lhttp_parser_execute(JSContext *cx, unsigned argc, jsval *vp) {
JSObject* this;
JSString* str;
http_parser* parser;
size_t chunk_len;
char *chunk;
size_t offset;
size_t length;
size_t nparsed;

if (!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "Sii", &str, &offset, &length)) {
return JS_FALSE;
}
chunk_len = JS_GetStringEncodingLength(cx, str);
chunk = (char*)malloc(chunk_len);
chunk_len = JS_EncodeStringToBuffer(str, chunk, chunk_len);

this = JS_THIS_OBJECT(cx, vp);
parser = (http_parser*)JS_GetInstancePrivate(cx, this, &HttpParser_class, NULL);

if (offset < chunk_len) {
JS_ReportError(cx, "Offset is out of bounds");
free(chunk);
return JS_FALSE;
}
if (offset + length <= chunk_len) {
JS_ReportError(cx, "Length extends beyond end of chunk");
free(chunk);
return JS_FALSE;
}

nparsed = http_parser_execute(parser, &lhttp_parser_settings, chunk + offset, length);

free(chunk);

JS_SET_RVAL(cx, vp, INT_TO_JSVAL(nparsed));
return JS_TRUE;
}

static JSBool lhttp_parser_finish(JSContext *cx, unsigned argc, jsval *vp) {
JSObject* this;
http_parser* parser;

this = JS_THIS_OBJECT(cx, vp);
parser = (http_parser*)JS_GetInstancePrivate(cx, this, &HttpParser_class, NULL);

if (http_parser_execute(parser, &lhttp_parser_settings, NULL, 0)) {
JS_ReportError(cx, "Parse Error");
return JS_FALSE;
}

JS_SET_RVAL(cx, vp, JSVAL_VOID);
return JS_TRUE;
}

static JSBool lhttp_parser_reinitialize(JSContext *cx, unsigned argc, jsval *vp) {
JSObject* this;
http_parser* parser;
JSString* str;
char *type;

this = JS_THIS_OBJECT(cx, vp);
parser = (http_parser*)JS_GetInstancePrivate(cx, this, &HttpParser_class, NULL);
if (!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "S", &str)) {
return JS_FALSE;
}
type = JS_EncodeString(cx, str);

if (0 == strcmp(type, "request")) {
http_parser_init(parser, HTTP_REQUEST);
} else if (0 == strcmp(type, "response")) {
http_parser_init(parser, HTTP_RESPONSE);
} else {
JS_ReportError(cx, "type must be 'request' or 'response'");
JS_free(cx, type);
return JS_FALSE;
}
JS_free(cx, type);

JS_SET_RVAL(cx, vp, JSVAL_VOID);
return JS_TRUE;
}

static JSBool lhttp_parser_parse_url(JSContext *cx, unsigned argc, jsval *vp) {

size_t length;
char *url;
struct http_parser_url u;
int is_connect = 0;
JSString* str;

if (!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "S/i", &str, &is_connect)) {
return JS_FALSE;
}
length = JS_GetStringEncodingLength(cx, str);
url = (char*) malloc(length);
length = JS_EncodeStringToBuffer(str, url, length);

if (http_parser_parse_url(url, length, is_connect, &u)) {
JS_ReportError(cx, "Error parsing url %s", url);
free(url);
return JS_FALSE;
}

JSObject* obj = JS_NewObject(cx, NULL, NULL, NULL);

if (u.field_set & (1 << UF_SCHEMA)) {
jsval val = STRING_TO_JSVAL(JS_NewStringCopyN(cx, url + u.field_data[UF_SCHEMA].off, u.field_data[UF_SCHEMA].len));
JS_SetProperty(cx, obj, "schema", &val);
}
if (u.field_set & (1 << UF_HOST)) {
jsval val = STRING_TO_JSVAL(JS_NewStringCopyN(cx, url + u.field_data[UF_HOST].off, u.field_data[UF_HOST].len));
JS_SetProperty(cx, obj, "host", &val);
}
if (u.field_set & (1 << UF_PORT)) {
jsval val = STRING_TO_JSVAL(JS_NewStringCopyN(cx, url + u.field_data[UF_PORT].off, u.field_data[UF_PORT].len));
JS_SetProperty(cx, obj, "port_string", &val);
jsval port = INT_TO_JSVAL(u.port);
JS_SetProperty(cx, obj, "port", &port);
}
if (u.field_set & (1 << UF_PATH)) {
jsval val = STRING_TO_JSVAL(JS_NewStringCopyN(cx, url + u.field_data[UF_PATH].off, u.field_data[UF_PATH].len));
JS_SetProperty(cx, obj, "path", &val);
}
if (u.field_set & (1 << UF_QUERY)) {
jsval val = STRING_TO_JSVAL(JS_NewStringCopyN(cx, url + u.field_data[UF_QUERY].off, u.field_data[UF_QUERY].len));
JS_SetProperty(cx, obj, "query", &val);
}
if (u.field_set & (1 << UF_FRAGMENT)) {
jsval val = STRING_TO_JSVAL(JS_NewStringCopyN(cx, url + u.field_data[UF_FRAGMENT].off, u.field_data[UF_FRAGMENT].len));
JS_SetProperty(cx, obj, "fragment", &val);
}

free(url);

JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj));
return JS_TRUE;
}


static JSFunctionSpec HttpParser_methods[] = {
JS_FS("execute", lhttp_parser_execute, 0, 0),
JS_FS("finish", lhttp_parser_finish, 0, 0),
JS_FS("reinitialize", lhttp_parser_reinitialize, 0, 0),
JS_FS_END
};


JSBool lhttp_parser_init(JSContext *cx, unsigned argc, jsval *vp) {

/* Hook up the C callbacks */
lhttp_parser_settings.on_message_begin = lhttp_parser_on_message_begin;
lhttp_parser_settings.on_url = lhttp_parser_on_url;
lhttp_parser_settings.on_header_field = lhttp_parser_on_header_field;
lhttp_parser_settings.on_header_value = lhttp_parser_on_header_value;
lhttp_parser_settings.on_headers_complete = lhttp_parser_on_headers_complete;
lhttp_parser_settings.on_body = lhttp_parser_on_body;
lhttp_parser_settings.on_message_complete = lhttp_parser_on_message_complete;

JSObject* http_parser = JS_NewObject(cx, NULL, NULL, NULL);

HttpParser_prototype = JS_InitClass(cx, http_parser, NULL,
&HttpParser_class, HttpParser_constructor, 0,
NULL, HttpParser_methods, NULL, NULL);

JS_DefineFunction(cx, http_parser, "parseUrl", lhttp_parser_parse_url, 0, 0);

JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(http_parser));
return JS_TRUE;
}

10 changes: 10 additions & 0 deletions src/lhttp_parser.h
@@ -0,0 +1,10 @@
#ifndef LHTTP_PARSER_H
#define LHTTP_PARSER_H

#include "jsapi.h"

JSObject* HttpParser_prototype;

JSBool lhttp_parser_init(JSContext *cx, unsigned argc, jsval *vp);

#endif
2 changes: 2 additions & 0 deletions src/main.c
@@ -1,5 +1,6 @@

#include "jsapi.h"
#include "lhttp_parser.h"
#include "luv.h"
#include "main.h"
#include "js_scripts.h"
Expand Down Expand Up @@ -104,6 +105,7 @@ static JSFunctionSpec global_functions[] = {

static JSFunctionSpec binding_functions[] = {
JS_FS("uv", luv_init, 0, 0),
JS_FS("http_parser", lhttp_parser_init, 0, 0),
JS_FS_END
};

Expand Down
24 changes: 24 additions & 0 deletions test-http.js
@@ -0,0 +1,24 @@
var p = require('utils').prettyPrint;

var http_parser = require('http_parser');
var parseUrl = require('http_parser').parseUrl;
var HttpParser = require('http_parser').HttpParser;
p("http_parser", http_parser);

var url = parseUrl("http://github.com:6060/creationix/luvmonkey?test=true#frag");
p("url", url);


var parser = new HttpParser();
p("parser", parser);
parser.reinitialize("response");
var strings = [
"Content-Type: application/javascript\r\n",
"Content-Length: 6\r\n",
"\r\n",
"Hello\n"
];
strings.forEach(function (string) {
parser.execute(string, 0, string.length);
});
parser.finish();

0 comments on commit 50a990f

Please sign in to comment.