Add client.{c,h}. Needs a lot more refactoring.

master
Nicolas Favre-Felix 14 years ago
parent b30a2976ca
commit 7cfb80d4ce

@ -4,7 +4,7 @@ JANSSON_OBJ=jansson/src/dump.o jansson/src/error.o jansson/src/hashtable.o janss
FORMAT_OBJS=formats/json.o formats/raw.o formats/common.o formats/custom-type.o FORMAT_OBJS=formats/json.o formats/raw.o formats/common.o formats/custom-type.o
HTTP_PARSER_OBJS=http-parser/http_parser.o HTTP_PARSER_OBJS=http-parser/http_parser.o
DEPS=$(FORMAT_OBJS) $(HIREDIS_OBJ) $(JANSSON_OBJ) $(HTTP_PARSER_OBJS) DEPS=$(FORMAT_OBJS) $(HIREDIS_OBJ) $(JANSSON_OBJ) $(HTTP_PARSER_OBJS)
OBJS=webdis.o conf.o cmd.o slog.o server.o libb64/cencode.o acl.o md5/md5.o http.o $(DEPS) OBJS=webdis.o conf.o cmd.o slog.o server.o libb64/cencode.o acl.o md5/md5.o http.o client.o $(DEPS)
CFLAGS=-O0 -ggdb -Wall -Wextra -I. -Ijansson/src -Ihttp-parser CFLAGS=-O0 -ggdb -Wall -Wextra -I. -Ijansson/src -Ihttp-parser
LDFLAGS=-levent LDFLAGS=-levent

@ -2,6 +2,7 @@
#include "cmd.h" #include "cmd.h"
#include "conf.h" #include "conf.h"
#include "http.h" #include "http.h"
#include "client.h"
#include <string.h> #include <string.h>
#include <evhttp.h> #include <evhttp.h>

@ -0,0 +1,246 @@
#include "client.h"
#include "server.h"
#include "cmd.h"
#include "http.h"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "hiredis/async.h"
struct http_client *
http_client_new(int fd, struct server *s) {
struct http_client *c = calloc(1, sizeof(struct http_client));
c->fd = fd;
c->s = s;
c->settings.on_path = http_on_path;
c->settings.on_body = http_on_body;
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;
c->settings.on_query_string = http_on_query_string;
http_parser_init(&c->parser, HTTP_REQUEST);
c->parser.data = c;
return c;
}
void
http_client_read(int fd, short event, void *ctx) {
struct http_client *c = ctx;
char buffer[64*1024];
int ret, nparsed;
(void)fd;
(void)event;
ret = read(c->fd, buffer, sizeof(buffer));
if(ret <= 0) { /* broken connection, bye */
http_client_free(c);
return;
}
/* TODO: http parse. */
nparsed = http_parser_execute(&c->parser, &c->settings, buffer, ret);
if(c->parser.upgrade) {
/* TODO */
} else if(nparsed != ret) { /* invalid */
http_client_free(c);
return;
}
if(!c->executing) {
http_client_serve(c);
}
}
static void
http_client_cleanup(struct http_client *c) {
if(c->sub) {
return; /* we need to keep those. */
}
free(c->path.s);
memset(&c->path, 0, sizeof(str_t));
free(c->body.s);
memset(&c->body, 0, sizeof(str_t));
free(c->input_headers.connection.s);
memset(&c->input_headers.connection, 0, sizeof(str_t));
free(c->input_headers.if_none_match.s);
memset(&c->input_headers.if_none_match, 0, sizeof(str_t));
free(c->input_headers.authorization.s);
memset(&c->input_headers.authorization, 0, sizeof(str_t));
free(c->output_headers.content_type.s);
memset(&c->output_headers.content_type, 0, sizeof(str_t));
free(c->output_headers.etag.s);
memset(&c->output_headers.etag, 0, sizeof(str_t));
free(c->qs_type.s);
memset(&c->qs_type, 0, sizeof(str_t));
free(c->qs_jsonp.s);
memset(&c->qs_jsonp, 0, sizeof(str_t));
memset(&c->verb, 0, sizeof(c->verb));
c->executing = 0;
}
void
http_client_free(struct http_client *c) {
event_del(&c->ev);
close(c->fd);
if(c->sub) {
/* clean up redis object */
redisAsyncFree(c->sub->s->ac);
/* clean up command object */
if(c->sub->cmd) {
cmd_free(c->sub->cmd);
}
free(c->sub);
c->sub = NULL;
}
http_client_cleanup(c);
free(c);
}
int
http_client_keep_alive(struct http_client *c) {
/* check disconnection */
int disconnect = 0;
if(c->parser.http_major == 0) {
disconnect = 1; /* No version given. */
} else if(c->parser.http_major == 1 && c->parser.http_minor == 0) {
disconnect = 1; /* HTTP 1.0: disconnect by default */
}
if(c->input_headers.connection.s) {
if(strncasecmp(c->input_headers.connection.s, "Keep-Alive", 10) == 0) {
disconnect = 0;
} else if(strncasecmp(c->input_headers.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) && !c->sub) {
http_client_free(c);
return;
}
http_client_cleanup(c);
http_parser_init(&c->parser, HTTP_REQUEST);
}
/**
* Add read event callback
*/
void
http_client_serve(struct http_client *c) {
event_set(&c->ev, c->fd, EV_READ, http_client_read, c);
event_base_set(c->s->base, &c->ev);
event_add(&c->ev, NULL);
}
/**** Parser callbacks ****/
/**
* Called when the path has been found. This is before any `?query-string'.
*/
int
http_on_path(http_parser *p, const char *at, size_t length) {
struct http_client *c = p->data;
c->path.s = calloc(length+1, 1);
memcpy(c->path.s, at, length);
c->path.sz = length;
/* save HTTP verb as well */
c->verb = (enum http_method)p->method;
return 0;
}
/**
* Called when the whole body has been read.
*/
int
http_on_body(http_parser *p, const char *at, size_t length) {
struct http_client *c = p->data;
c->body.s = calloc(length+1, 1);
memcpy(c->body.s, at, length);
c->body.sz = length;
return 0;
}
/**
* Called when the query string has been completely read.
*/
int
http_on_query_string(http_parser *parser, const char *at, size_t length) {
struct http_client *c = parser->data;
const char *p = at;
while(p < at + length) {
const char *key = p, *val;
int key_len, val_len;
char *eq = memchr(key, '=', length - (p-at));
if(!eq || eq > at + length) { /* last argument */
break;
} else { /* found an '=' */
char *and;
val = eq + 1;
key_len = eq - key;
p = eq + 1;
and = memchr(p, '&', length - (p-at));
if(!and || and > at + length) {
val_len = at + length - p; /* last arg */
} else {
val_len = and - val; /* cur arg */
p = and + 1;
}
if(key_len == 4 && strncmp(key, "type", 4) == 0) {
http_set_header(&c->qs_type, val, val_len);
} else if(key_len == 5 && strncmp(key, "jsonp", 5) == 0) {
http_set_header(&c->qs_jsonp, val, val_len);
}
if(!and) {
break;
}
}
}
return 0;
}

@ -0,0 +1,91 @@
#ifndef CLIENT_H
#define CLIENT_H
#include "http.h"
struct server;
struct http_client {
/* socket and server reference */
int fd;
in_addr_t addr;
struct event ev;
struct server *s;
int executing;
/* http parser */
http_parser_settings settings;
http_parser parser;
/* decoded http */
enum http_method verb;
str_t path;
str_t body;
/* input headers from client */
struct {
str_t connection;
str_t if_none_match;
str_t authorization;
} input_headers;
/* response headers */
struct input_headers {
str_t content_type;
str_t etag;
} output_headers;
/* query string */
str_t qs_type;
str_t qs_jsonp;
/* pub/sub */
struct subscription *sub;
/* private, used in HTTP parser */
str_t last_header_name;
};
struct http_client *
http_client_new(int fd, struct server *s);
void
http_client_serve(struct http_client *c);
void
http_client_free(struct http_client *c);
void
http_client_reset(struct http_client *c);
int
http_on_path(http_parser*, const char *at, size_t length);
int
http_on_path(http_parser*, const char *at, size_t length);
int
http_on_body(http_parser*, const char *at, size_t length);
int
http_on_header_name(http_parser*, const char *at, size_t length);
int
http_on_header_value(http_parser*, const char *at, size_t length);
int
http_on_complete(http_parser*);
int
http_on_query_string(http_parser*, const char *at, size_t length);
void
http_send_reply(struct http_client *c, short code, const char *msg,
const char *body, size_t body_len);
int
http_client_keep_alive(struct http_client *c);
#endif

@ -3,6 +3,7 @@
#include "conf.h" #include "conf.h"
#include "acl.h" #include "acl.h"
#include "http.h" #include "http.h"
#include "client.h"
#include "formats/json.h" #include "formats/json.h"
#include "formats/raw.h" #include "formats/raw.h"

@ -1,6 +1,7 @@
#include "common.h" #include "common.h"
#include "cmd.h" #include "cmd.h"
#include "http.h" #include "http.h"
#include "client.h"
#include "md5/md5.h" #include "md5/md5.h"
#include <string.h> #include <string.h>

@ -2,6 +2,7 @@
#include "cmd.h" #include "cmd.h"
#include "common.h" #include "common.h"
#include "http.h" #include "http.h"
#include "client.h"
#include <string.h> #include <string.h>
#include <hiredis/hiredis.h> #include <hiredis/hiredis.h>

@ -2,6 +2,7 @@
#include "common.h" #include "common.h"
#include "cmd.h" #include "cmd.h"
#include "http.h" #include "http.h"
#include "client.h"
#include <string.h> #include <string.h>
#include <hiredis/hiredis.h> #include <hiredis/hiredis.h>

236
http.c

@ -2,194 +2,12 @@
#include "server.h" #include "server.h"
#include "slog.h" #include "slog.h"
#include "cmd.h" #include "cmd.h"
#include "client.h"
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
struct http_client *
http_client_new(int fd, struct server *s) {
struct http_client *c = calloc(1, sizeof(struct http_client));
c->fd = fd;
c->s = s;
c->settings.on_path = http_on_path;
c->settings.on_body = http_on_body;
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;
c->settings.on_query_string = http_on_query_string;
http_parser_init(&c->parser, HTTP_REQUEST);
c->parser.data = c;
return c;
}
void
http_client_read(int fd, short event, void *ctx) {
struct http_client *c = ctx;
char buffer[64*1024];
int ret, nparsed;
(void)fd;
(void)event;
ret = read(c->fd, buffer, sizeof(buffer));
if(ret <= 0) { /* broken connection, bye */
http_client_free(c);
return;
}
/* TODO: http parse. */
nparsed = http_parser_execute(&c->parser, &c->settings, buffer, ret);
if(c->parser.upgrade) {
/* TODO */
} else if(nparsed != ret) { /* invalid */
http_client_free(c);
return;
}
if(!c->executing) {
http_client_serve(c);
}
}
static void
http_client_cleanup(struct http_client *c) {
if(c->sub) {
return; /* we need to keep those. */
}
free(c->path.s);
memset(&c->path, 0, sizeof(str_t));
free(c->body.s);
memset(&c->body, 0, sizeof(str_t));
free(c->input_headers.connection.s);
memset(&c->input_headers.connection, 0, sizeof(str_t));
free(c->input_headers.if_none_match.s);
memset(&c->input_headers.if_none_match, 0, sizeof(str_t));
free(c->input_headers.authorization.s);
memset(&c->input_headers.authorization, 0, sizeof(str_t));
free(c->output_headers.content_type.s);
memset(&c->output_headers.content_type, 0, sizeof(str_t));
free(c->output_headers.etag.s);
memset(&c->output_headers.etag, 0, sizeof(str_t));
free(c->qs_type.s);
memset(&c->qs_type, 0, sizeof(str_t));
free(c->qs_jsonp.s);
memset(&c->qs_jsonp, 0, sizeof(str_t));
memset(&c->verb, 0, sizeof(c->verb));
c->executing = 0;
}
void
http_client_free(struct http_client *c) {
event_del(&c->ev);
close(c->fd);
if(c->sub) {
/* clean up redis object */
redisAsyncFree(c->sub->s->ac);
/* clean up command object */
if(c->sub->cmd) {
cmd_free(c->sub->cmd);
}
free(c->sub);
c->sub = NULL;
}
http_client_cleanup(c);
free(c);
}
static int
http_client_keep_alive(struct http_client *c) {
/* check disconnection */
int disconnect = 0;
if(c->parser.http_major == 0) {
disconnect = 1; /* No version given. */
} else if(c->parser.http_major == 1 && c->parser.http_minor == 0) {
disconnect = 1; /* HTTP 1.0: disconnect by default */
}
if(c->input_headers.connection.s) {
if(strncasecmp(c->input_headers.connection.s, "Keep-Alive", 10) == 0) {
disconnect = 0;
} else if(strncasecmp(c->input_headers.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) && !c->sub) {
http_client_free(c);
return;
}
http_client_cleanup(c);
http_parser_init(&c->parser, HTTP_REQUEST);
}
/**
* Add read event callback
*/
void
http_client_serve(struct http_client *c) {
event_set(&c->ev, c->fd, EV_READ, http_client_read, c);
event_base_set(c->s->base, &c->ev);
event_add(&c->ev, NULL);
}
int
http_on_path(http_parser *p, const char *at, size_t length) {
struct http_client *c = p->data;
c->path.s = calloc(length+1, 1);
memcpy(c->path.s, at, length);
c->path.sz = length;
/* save HTTP verb as well */
c->verb = (enum http_method)p->method;
return 0;
}
int
http_on_body(http_parser *p, const char *at, size_t length) {
struct http_client *c = p->data;
c->body.s = calloc(length+1, 1);
memcpy(c->body.s, at, length);
c->body.sz = length;
return 0;
}
/* Adobe flash cross-domain request */ /* Adobe flash cross-domain request */
static int static int
http_crossdomain(struct http_client *c) { http_crossdomain(struct http_client *c) {
@ -211,7 +29,6 @@ http_crossdomain(struct http_client *c) {
return 0; return 0;
} }
/* reply to OPTIONS HTTP verb */ /* reply to OPTIONS HTTP verb */
static int static int
http_options(struct http_client *c) { http_options(struct http_client *c) {
@ -276,6 +93,8 @@ http_on_complete(http_parser *p) {
return ret; return ret;
} }
/* HTTP Replies */
void void
http_send_error(struct http_client *c, short code, const char *msg) { http_send_error(struct http_client *c, short code, const char *msg) {
@ -310,7 +129,7 @@ http_send_reply(struct http_client *c, short code, const char *msg,
http_response_set_body(&resp, body, body_len); http_response_set_body(&resp, body, body_len);
/* flush response in the socket */ /* flush response in the socket */
if(http_response_write(&resp, c->fd)) { if(http_response_write(&resp, c->fd)) { /* failure */
http_client_free(c); http_client_free(c);
} else { } else {
if(c->sub) { /* don't free the client, but monitor fd. */ if(c->sub) { /* don't free the client, but monitor fd. */
@ -344,6 +163,7 @@ http_send_reply_chunk(struct http_client *c, const char *p, size_t sz) {
write(c->fd, "\r\n", 2); write(c->fd, "\r\n", 2);
} }
/* send nil chunk to mark the end of a stream. */
void void
http_send_reply_end(struct http_client *c) { http_send_reply_end(struct http_client *c) {
@ -401,50 +221,6 @@ http_on_header_value(http_parser *p, const char *at, size_t length) {
return 0; return 0;
} }
/**
* Called when the query string is found
*/
int
http_on_query_string(http_parser *parser, const char *at, size_t length) {
struct http_client *c = parser->data;
const char *p = at;
while(p < at + length) {
const char *key = p, *val;
int key_len, val_len;
char *eq = memchr(key, '=', length - (p-at));
if(!eq || eq > at + length) { /* last argument */
break;
} else { /* found an '=' */
char *and;
val = eq + 1;
key_len = eq - key;
p = eq + 1;
and = memchr(p, '&', length - (p-at));
if(!and || and > at + length) {
val_len = at + length - p; /* last arg */
} else {
val_len = and - val; /* cur arg */
p = and + 1;
}
if(key_len == 4 && strncmp(key, "type", 4) == 0) {
http_set_header(&c->qs_type, val, val_len);
} else if(key_len == 5 && strncmp(key, "jsonp", 5) == 0) {
http_set_header(&c->qs_jsonp, val, val_len);
}
if(!and) {
break;
}
}
}
return 0;
}
/* HTTP Response */ /* HTTP Response */
void void
@ -478,11 +254,13 @@ http_response_set_header(struct http_response *r, const char *k, const char *v)
size_t klen = strlen(k); size_t klen = strlen(k);
if(strncmp(r->headers[i].s, k, klen) == 0 && r->headers[i].s[klen] == ':') { if(strncmp(r->headers[i].s, k, klen) == 0 && r->headers[i].s[klen] == ':') {
pos = i; pos = i;
/* free old value before replacing it. */
free(r->headers[i].s); free(r->headers[i].s);
break; break;
} }
} }
/* extend array */
if(pos == r->header_count) { if(pos == r->header_count) {
r->headers = realloc(r->headers, sizeof(str_t)*(r->header_count + 1)); r->headers = realloc(r->headers, sizeof(str_t)*(r->header_count + 1));
r->header_count++; r->header_count++;

@ -5,53 +5,13 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include "http-parser/http_parser.h" #include "http-parser/http_parser.h"
struct http_client; /* FIXME: this shouldn't be here. */
typedef struct { typedef struct {
char *s; char *s;
size_t sz; size_t sz;
} str_t; } str_t;
struct http_client {
/* socket and server reference */
int fd;
in_addr_t addr;
struct event ev;
struct server *s;
int executing;
/* http parser */
http_parser_settings settings;
http_parser parser;
/* decoded http */
enum http_method verb;
str_t path;
str_t body;
/* input headers from client */
struct {
str_t connection;
str_t if_none_match;
str_t authorization;
} input_headers;
/* response headers */
struct input_headers {
str_t content_type;
str_t etag;
} output_headers;
/* query string */
str_t qs_type;
str_t qs_jsonp;
/* pub/sub */
struct subscription *sub;
/* private, used in HTTP parser */
str_t last_header_name;
};
struct http_response { struct http_response {
short code; short code;
const char *msg; const char *msg;
@ -63,44 +23,9 @@ struct http_response {
size_t body_len; size_t body_len;
}; };
struct http_client *
http_client_new(int fd, struct server *s);
void
http_client_serve(struct http_client *c);
void
http_client_free(struct http_client *c);
int
http_on_path(http_parser*, const char *at, size_t length);
int
http_on_path(http_parser*, const char *at, size_t length);
int
http_on_body(http_parser*, const char *at, size_t length);
int
http_on_header_name(http_parser*, const char *at, size_t length);
int
http_on_header_value(http_parser*, const char *at, size_t length);
int
http_on_complete(http_parser*);
int
http_on_query_string(http_parser*, const char *at, size_t length);
void void
http_set_header(str_t *h, const char *p, size_t sz); http_set_header(str_t *h, const char *p, size_t sz);
void
http_send_reply(struct http_client *c, short code, const char *msg,
const char *body, size_t body_len);
/* Transfer-encoding: chunked */ /* Transfer-encoding: chunked */
void void
http_send_reply_start(struct http_client *c, short code, const char *msg); http_send_reply_start(struct http_client *c, short code, const char *msg);

@ -3,6 +3,7 @@
#include "cmd.h" #include "cmd.h"
#include "slog.h" #include "slog.h"
#include "http.h" #include "http.h"
#include "client.h"
#include <hiredis/hiredis.h> #include <hiredis/hiredis.h>
#include <hiredis/adapters/libevent.h> #include <hiredis/adapters/libevent.h>
@ -173,6 +174,7 @@ on_possible_accept(int fd, short event, void *ctx) {
http_client_serve(c); http_client_serve(c);
} }
void void
server_start(struct server *s) { server_start(struct server *s) {

Loading…
Cancel
Save