|
|
@ -17,6 +17,8 @@ http_client_new(int fd, struct server *s) {
|
|
|
|
c->settings.on_path = http_on_path;
|
|
|
|
c->settings.on_path = http_on_path;
|
|
|
|
c->settings.on_body = http_on_body;
|
|
|
|
c->settings.on_body = http_on_body;
|
|
|
|
c->settings.on_message_complete = http_on_complete;
|
|
|
|
c->settings.on_message_complete = http_on_complete;
|
|
|
|
|
|
|
|
c->settings.on_header_field = http_on_header_name;
|
|
|
|
|
|
|
|
c->settings.on_header_value = http_on_header_value;
|
|
|
|
|
|
|
|
|
|
|
|
http_parser_init(&c->parser, HTTP_REQUEST);
|
|
|
|
http_parser_init(&c->parser, HTTP_REQUEST);
|
|
|
|
c->parser.data = c;
|
|
|
|
c->parser.data = c;
|
|
|
@ -37,7 +39,6 @@ http_client_read(int fd, short event, void *ctx) {
|
|
|
|
|
|
|
|
|
|
|
|
ret = read(c->fd, buffer, sizeof(buffer));
|
|
|
|
ret = read(c->fd, buffer, sizeof(buffer));
|
|
|
|
if(ret <= 0) { /* broken connection, bye */
|
|
|
|
if(ret <= 0) { /* broken connection, bye */
|
|
|
|
printf("close client %d\n", c->fd);
|
|
|
|
|
|
|
|
http_client_free(c);
|
|
|
|
http_client_free(c);
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -52,27 +53,65 @@ http_client_read(int fd, short event, void *ctx) {
|
|
|
|
/* TODO */
|
|
|
|
/* TODO */
|
|
|
|
} else if(nparsed != ret) { /* invalid */
|
|
|
|
} else if(nparsed != ret) { /* invalid */
|
|
|
|
http_client_free(c);
|
|
|
|
http_client_free(c);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
|
|
|
|
printf("parse %zd bytes: [", c->sz); fflush(stdout);
|
|
|
|
|
|
|
|
write(1, c->buffer, c->sz);
|
|
|
|
|
|
|
|
printf("]\n");
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* carry on */
|
|
|
|
|
|
|
|
http_client_serve(c);
|
|
|
|
http_client_serve(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
void
|
|
|
|
http_client_free(struct http_client *c) {
|
|
|
|
http_client_free(struct http_client *c) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
event_del(&c->ev);
|
|
|
|
close(c->fd);
|
|
|
|
close(c->fd);
|
|
|
|
free(c->buffer);
|
|
|
|
free(c->buffer);
|
|
|
|
free((char*)c->out_content_type.s);
|
|
|
|
free((char*)c->out_content_type.s);
|
|
|
|
free(c);
|
|
|
|
free(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
|
|
|
http_client_keep_alive(struct http_client *c) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* check disconnection */
|
|
|
|
|
|
|
|
int disconnect = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(c->parser.http_major == 1 && c->parser.http_minor == 0) {
|
|
|
|
|
|
|
|
disconnect = 1; /* HTTP 1.0: disconnect by default */
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if(c->header_connection.s) {
|
|
|
|
|
|
|
|
if(strncasecmp(c->header_connection.s, "Keep-Alive", 10) == 0) {
|
|
|
|
|
|
|
|
disconnect = 0;
|
|
|
|
|
|
|
|
} else if(strncasecmp(c->header_connection.s, "Close", 5) == 0) {
|
|
|
|
|
|
|
|
disconnect = 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return disconnect ? 0 : 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
|
|
http_client_reset(struct http_client *c) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(!http_client_keep_alive(c)) {
|
|
|
|
|
|
|
|
http_client_free(c);
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
memset(&c->path, 0, sizeof(c->path));
|
|
|
|
|
|
|
|
memset(&c->body, 0, sizeof(c->body));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
free((char*)c->out_content_type.s);
|
|
|
|
|
|
|
|
memset(&c->out_content_type, 0, sizeof(c->out_content_type));
|
|
|
|
|
|
|
|
memset(&c->out_etag, 0, sizeof(c->out_etag));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
free(c->buffer);
|
|
|
|
|
|
|
|
c->buffer = NULL;
|
|
|
|
|
|
|
|
c->sz = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
memset(&c->verb, 0, sizeof(c->verb));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
http_parser_init(&c->parser, HTTP_REQUEST);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Add read event callback
|
|
|
|
* Add read event callback
|
|
|
|
*/
|
|
|
|
*/
|
|
|
@ -81,7 +120,6 @@ http_client_serve(struct http_client *c) {
|
|
|
|
|
|
|
|
|
|
|
|
event_set(&c->ev, c->fd, EV_READ, http_client_read, c);
|
|
|
|
event_set(&c->ev, c->fd, EV_READ, http_client_read, c);
|
|
|
|
event_base_set(c->s->base, &c->ev);
|
|
|
|
event_base_set(c->s->base, &c->ev);
|
|
|
|
|
|
|
|
|
|
|
|
event_add(&c->ev, NULL);
|
|
|
|
event_add(&c->ev, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -112,7 +150,7 @@ http_on_body(http_parser *p, const char *at, size_t length) {
|
|
|
|
int
|
|
|
|
int
|
|
|
|
http_on_complete(http_parser *p) {
|
|
|
|
http_on_complete(http_parser *p) {
|
|
|
|
struct http_client *c = p->data;
|
|
|
|
struct http_client *c = p->data;
|
|
|
|
int ret;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
|
|
|
|
/* check that the command can be executed */
|
|
|
|
/* check that the command can be executed */
|
|
|
|
switch(c->verb) {
|
|
|
|
switch(c->verb) {
|
|
|
@ -141,7 +179,7 @@ http_on_complete(http_parser *p) {
|
|
|
|
/* evhttp_send_reply(rq, 405, "Method Not Allowed", NULL); */
|
|
|
|
/* evhttp_send_reply(rq, 405, "Method Not Allowed", NULL); */
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
void
|
|
|
@ -161,23 +199,28 @@ http_send_reply(struct http_client *c, short code, const char *msg,
|
|
|
|
ret = snprintf(out, sz, "HTTP/1.1 %d %s\r\n"
|
|
|
|
ret = snprintf(out, sz, "HTTP/1.1 %d %s\r\n"
|
|
|
|
"Content-Type: %s\r\n"
|
|
|
|
"Content-Type: %s\r\n"
|
|
|
|
"Content-Length: %zd\r\n"
|
|
|
|
"Content-Length: %zd\r\n"
|
|
|
|
|
|
|
|
"Connection: %s\r\n"
|
|
|
|
"Server: Webdis\r\n"
|
|
|
|
"Server: Webdis\r\n"
|
|
|
|
"\r\n", code, msg, ct, body_len);
|
|
|
|
"\r\n", code, msg, ct, body_len,
|
|
|
|
|
|
|
|
(http_client_keep_alive(c) ? "Keep-Alive" : "Close")
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if(!out) {
|
|
|
|
if(!out) { /* first step: allocate space */
|
|
|
|
sz = ret + body_len;
|
|
|
|
sz = ret + body_len;
|
|
|
|
out = malloc(sz);
|
|
|
|
out = malloc(sz);
|
|
|
|
} else {
|
|
|
|
} else { /* second step: copy body and leave loop */
|
|
|
|
if(body && body_len) memcpy(out + ret, body, body_len);
|
|
|
|
if(body && body_len) memcpy(out + ret, body, body_len);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ok = write(c->fd, out, sz);
|
|
|
|
ok = write(c->fd, out, sz);
|
|
|
|
if(!ok) {
|
|
|
|
if(ok != (int)sz) {
|
|
|
|
http_client_free(c);
|
|
|
|
http_client_free(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free(out);
|
|
|
|
free(out);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
http_client_reset(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
void
|
|
|
@ -186,3 +229,32 @@ http_set_header(str_t *h, const char *p) {
|
|
|
|
h->s = strdup(p);
|
|
|
|
h->s = strdup(p);
|
|
|
|
h->sz = strlen(p);
|
|
|
|
h->sz = strlen(p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Called when a header name is read
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
|
|
|
|
|
|
|
http_on_header_name(http_parser *p, const char *at, size_t length) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct http_client *c = p->data;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
c->last_header_name.s = at;
|
|
|
|
|
|
|
|
c->last_header_name.sz = length;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Called when a header value is read
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
|
|
|
|
|
|
|
http_on_header_value(http_parser *p, const char *at, size_t length) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct http_client *c = p->data;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(strncmp("Connection", c->last_header_name.s, length) == 0) {
|
|
|
|
|
|
|
|
c->header_connection.s = at;
|
|
|
|
|
|
|
|
c->header_connection.sz = length;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|