ez-set.h \
ez-tree.h
-test_SRCS=test-bitset.c test-blob.c test-port.c test-set.c test-tree.c test-list.c
+test_SRCS=test-bitset.c test-blob.c test-port.c test-set.c test-tree.c test-list.c test-http.c
TESTS=$(test_SRCS:.c=)
#include <stdlib.h>
#include "ez-blob-io.h"
#include "ez-list.h"
-#include "ez-set.h"
+#include "ez-tree.h"
#define obstack_chunk_alloc malloc
#define obstack_chunk_free free
return 0;
}
-static unsigned handler_hash(const void *a) {
- return ez_hash_string(((struct ez_httphandler *)a)->path);
-}
-
-static int handler_equals(const void *a, const void *b) {
- return strcmp(((struct ez_httphandler *)a)->path, ((struct ez_httphandler *)b)->path) == 0;
-}
-
// read all data, data may be null (== noop)
int httpconnection_read(struct ez_httpconnection *s, struct ez_blobio *data) {
if (data) {
while (len > 0
&& (len = write(s->fd, data->data + data->index, len)) >= 0) {
- printf("send:\n");
- fwrite(data->data + data->index, 1, len, stdout);
- printf("\n--\n");
+ //printf("send:\n");
+ //fwrite(data->data + data->index, 1, len, stdout);
+ //printf("\n--\n");
data->index += len;
len = data->size - data->index;
}
do {
ssize_t len = read(s->fd, s->io.data, s->io.alloc);
- if (len < 0)
- return -1;
+ if (len <= 0)
+ break;
+
s->io.index = 0;
s->io.size = len;
- printf("read:\n");
- fwrite(s->io.data, 1, len, stdout);
- printf("\n--\n");
+ //printf("read:\n");
+ //fwrite(s->io.data, 1, len, stdout);
+ //printf("\n--\n");
res = parse_http(http, &s->io);
if (res == 2)
int httpserver_init(struct ez_httpserver *s, int port) {
memset(s, 0, sizeof(*s));
- ez_set_init(&s->handlers, handler_hash, handler_equals, NULL);
+ ez_tree_init(&s->handlers, ez_string_node_cmp);
memset(&s->addr, 0, sizeof(s->addr));
s->addr.sin_family = AF_INET;
}
void httpserver_free(struct ez_httpserver *s) {
- ez_set_clear(&s->handlers);
+ // all static alloc
}
int httpconnection_init(struct ez_httpconnection *conn, struct ez_httpserver *s) {
memset(conn, 0, sizeof(*conn));
- conn->server = s;
conn->io.alloc = 4096;
conn->io.data = malloc(conn->io.alloc);
conn->io.mode = BLOBIO_READ;
void httpserver_addhandlers(struct ez_httpserver *s, struct ez_httphandler *list, int count) {
for (int i=0;i<count;i++)
- ez_set_put(&s->handlers, &list[i]);
+ ez_tree_put(&s->handlers, &list[i]);
}
int httpserver_run(struct ez_httpserver *s) {
res = parse_request_status(&req);
if (res == 0) {
const struct ez_httphandler key = { .path = req.url };
- const struct ez_httphandler *handler = ez_set_get(&s->handlers, &key);
+ ez_tree_scan scan;
+ struct ez_httphandler *handler = ez_tree_scan_init_key(&s->handlers, &scan, EZ_LINK_RIGHT, EZ_LINK_LEFT, &key);
+
+ if (handler && handler->mode == EZ_PATH_EXACT && strcmp(req.url, handler->path) != 0)
+ handler = NULL;
+
+ if (handler && handler->mode == EZ_PATH_PREFIX && strncmp(req.url, handler->path, strlen(handler->path)) != 0)
+ handler = NULL;
- if (!handler)
+ if (handler)
quit = handler->fn(&req, &rep);
else
httpresponse_set_response(&rep, 404, "Missing");
* Requests and responses are all in-memory.
* All operations are synchronous.
* Server is single threaded.
+ *
+ * Memory is managed using obstacks so each request/response is
+ * cleared after use.
+ *
+ * You need to define _GNU_SOURCE and various things for obstacks
+ * and the types to include this file.
*/
/**
* HTTP connection, used for both client and server endpoints.
*/
struct ez_httpconnection {
- struct ez_httpserver *server;
-
- struct obstack os;
+ struct obstack os; // Use for building responses and other per-request objects
void *empty;
- struct sockaddr_in addr;
+ struct sockaddr_in addr; // remote address of connection
+ struct ez_blobio io; // used for buffering i/o for headers
int fd;
- struct ez_blobio io;
};
/**
* HTTP server listening socket and state.
*/
struct ez_httpserver {
- ez_set handlers; // set of struct ez_httphandler
+ ez_tree handlers; // tree of struct ez_httphandler
struct sockaddr_in addr;
int fd;
};
+enum ez_httphandler_t {
+ EZ_PATH_EXACT, // exact match for url
+ EZ_PATH_PREFIX // just match prefix provided
+};
+
/**
* Handler for a given url path.
*
ez_node sn;
char *path;
+ enum ez_httphandler_t mode;
int (*fn)(struct ez_httprequest *req, struct ez_httpresponse *rep);
};
--- /dev/null
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+
+#include <stdlib.h>
+#include "ez-blob-io.h"
+#include "ez-list.h"
+#include "ez-set.h"
+
+#define obstack_chunk_alloc malloc
+#define obstack_chunk_free free
+#include <obstack.h>
+#include <stdio.h>
+#include <errno.h>
+#define D(x)
+
+#include "ez-http.h"
+
+// copy to use
+const static struct ez_pair ct_text_html = {
+ .name = "Content-Type",
+ .value = "text/html;charset=utf-8"
+};
+const static struct ez_pair ct_text_xml = {
+ .name = "Content-Type",
+ .value = "text/xml"
+};
+
+void demo_client(void) {
+ const char *cmd = "<YAMAHA_AV cmd=\"PUT\"><Main_Zone><Volume><Lvl><Val>-350</Val><Exp>1</Exp><Unit>dB</Unit></Lvl></Volume></Main_Zone></YAMAHA_AV>";
+ struct ez_httpclient c;
+ struct ez_pair h0 = ct_text_xml;
+
+ httpclient_init(&c, "amplifier", 80);
+
+ http_addheaders(&c.request.http, &h0, 1);
+ http_set_data(&c.request.http, (char *)cmd, strlen(cmd));
+
+ httpclient_run(&c, "POST", "/YamahaRemoteControl/ctrl");
+
+ printf("response: %s\n", c.response.http.status);
+ for (struct ez_pair *w = ez_list_head(&c.response.http.headers), *n = ez_node_succ(w);n;w=n,n = ez_node_succ(w))
+ printf("%s:%s\n", w->name, w->value);
+ printf("payload:\n");
+ fwrite(c.response.http.data.data + c.response.http.data.index, 1, c.response.http.data.size - c.response.http.data.index, stdout);
+ printf("\n--\n");
+
+ httpclient_free(&c);
+}
+
+static int handle_quit(struct ez_httprequest *r, struct ez_httpresponse *rep) {
+ httpresponse_set_response(rep, 200, "Byte");
+ return 1;
+}
+
+static int handle_root(struct ez_httprequest *req, struct ez_httpresponse *rep) {
+ struct ez_pair h0 = ct_text_html;
+ const char *msg = "<h1>It Works!</h1><a href='/quit'>Quit</a> | <a href='/'>Player</a>";
+
+ httpresponse_set_response(rep, 200, "Ok");
+ ez_list_addtail(&rep->http.headers, &h0);
+ http_set_data(&rep->http, msg, strlen(msg));
+
+ return 0;
+}
+
+static struct ez_httphandler handler_list[] = {
+ { .path = "/index.html", .fn = handle_root },
+ { .path = "/quit", .fn = handle_quit },
+};
+
+void demo_server(void) {
+ struct ez_httpserver serv;
+
+ httpserver_init(&serv, 8081);
+ httpserver_addhandlers(&serv, handler_list, 2);
+ httpserver_run(&serv);
+ httpserver_free(&serv);
+}
+
+int main(int argc, char **argv) {
+ demo_client();
+ //demo_server();
+ return 0;
+}