diff options
Diffstat (limited to 'examples/fileserver.c')
| -rw-r--r-- | examples/fileserver.c | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/examples/fileserver.c b/examples/fileserver.c new file mode 100644 index 0000000..9e3027e --- /dev/null +++ b/examples/fileserver.c @@ -0,0 +1,249 @@ +#include <uvh.h> +#include <stdlib.h> +#include "../src/sds.h" + +#define MAX_PATH 512 +#define MAX_CHUNK 1024 + +struct fileserver +{ + sds root; +}; + +struct chunker +{ + uv_fs_t open_req; + uv_fs_t read_req; + struct fileserver *fileserver; + struct uvh_request *req; + sds real_path; + sds req_path; + char buffer[MAX_CHUNK]; +}; + +struct dirreq +{ + uv_fs_t readdir_req; + struct fileserver *fileserver; + struct uvh_request *req; + sds real_path; + sds req_path; +}; + +sds path_join(sds a, sds b) +{ + sds result; + + if (b[0] == '/') + return sdsdup(b); + + result = sdsdup(a); + + if (result[sdslen(result)-1] != '/') + result = sdscatlen(result, "/", 1); + + return sdscatsds(result, b); +} + +void on_readdir(uv_fs_t *req) +{ + struct dirreq *dirreq = (struct dirreq *) req->data; + char *buf = (char *) req->ptr; + int i = 0; + + uvh_request_writef(dirreq->req, "<html><body><h2>%s</h2><ul>", + dirreq->req_path); + + for (i = 0; i < req->result; ++i) + { + sds filename = sdsnew(buf); + sds path = path_join(dirreq->req_path, filename); + + uvh_request_writef(dirreq->req, "<li><a href=\"%s\">%s</a></li>", + path, filename); + + sdsfree(filename); + sdsfree(path); + + buf += strlen(buf) + 1; + } + + uvh_request_writef(dirreq->req, "%s", "</ul></body></html>"); + + uvh_request_end(dirreq->req); +} + +void handle_dir(struct uvh_request *req, sds req_path, sds real_path) +{ + struct dirreq *dirreq = calloc(1, sizeof(*dirreq)); + + dirreq->fileserver = (struct fileserver *) req->server->data; + dirreq->req = req; + dirreq->readdir_req.data = dirreq; + dirreq->real_path = real_path; + dirreq->req_path = req_path; + + uv_fs_readdir(uv_default_loop(), &dirreq->readdir_req, real_path, O_RDONLY, + &on_readdir); +} + +void on_read(uv_fs_t *req) +{ + struct chunker *chunker = (struct chunker *) req->data; + + uv_fs_req_cleanup(req); + + printf("on_read: result: %d\n", (int)req->result); + + if (req->result == 0) + { + // eof + uv_fs_t close_req; + uv_fs_close(req->loop, &close_req, chunker->open_req.result, NULL); + + uvh_request_write(chunker->req, NULL, 0); + } + else if (req->result < 0) + { + // error + uv_fs_t close_req; + uv_fs_close(req->loop, &close_req, chunker->open_req.result, NULL); + + uvh_request_write(chunker->req, NULL, 0); + } + else + { + uvh_request_write(chunker->req, chunker->buffer, req->result); + + uv_fs_read(req->loop, &chunker->read_req, chunker->open_req.result, + chunker->buffer, MAX_CHUNK, -1, &on_read); + } +} + +void on_open(uv_fs_t *req) +{ + struct chunker *chunker = (struct chunker *) req->data; + + if (req->result == -1) + { + fprintf(stderr, "error opening: %s\n", chunker->real_path); + uvh_request_write_status(chunker->req, HTTP_INTERNAL_SERVER_ERROR); + uvh_request_end(chunker->req); + free(chunker); + } + else + { + uvh_request_stream(chunker->req, NULL, NULL); + + uv_fs_read(req->loop, &chunker->read_req, req->result, chunker->buffer, + MAX_CHUNK, -1, &on_read); + } + + uv_fs_req_cleanup(req); +} + +void handle_file(struct uvh_request *req, sds req_path, sds real_path) +{ + struct chunker *chunker = calloc(1, sizeof(*chunker)); + chunker->open_req.data = chunker; + chunker->read_req.data = chunker; + chunker->real_path = real_path; + chunker->req_path = req_path; + chunker->req = req; + chunker->fileserver = (struct fileserver *) req->server->data; + + uv_fs_open(uv_default_loop(), &chunker->open_req, chunker->real_path, + O_RDONLY, 0, &on_open); +} + +int request_handler(struct uvh_request *req) +{ + printf("%s\n", __FUNCTION__); + + struct fileserver *fileserver; + sds real_path; + sds req_path; + + fileserver = (struct fileserver *) req->server->data; + + req_path = sdsnew(req->url.path); + sds req_path2 = sdsnew(req_path+1); + real_path = path_join(fileserver->root, req_path2); + sdsfree(req_path2); + + printf("req path: %s\n", req_path); + printf("stat: <%s>\n", real_path); + + uv_fs_t stat_req; + uv_fs_stat(uv_default_loop(), &stat_req, real_path, NULL); + + if (S_ISDIR(stat_req.statbuf.st_mode)) + { + handle_dir(req, req_path, real_path); + } + else if (S_ISREG(stat_req.statbuf.st_mode)) + { + handle_file(req, req_path, real_path); + } + else + { + uvh_request_write_status(req, HTTP_NOT_FOUND); + uvh_request_end(req); + } + + uv_fs_req_cleanup(&stat_req); + + return 0; +} + +void ctrlc_handler(uv_signal_t *handle, int signum) +{ + struct uvh_server *server = handle->data; + uvh_server_stop(server); + uv_close((uv_handle_t *) handle, NULL); + (void) signum; +} + +int main(int argc, char **argv) +{ + uv_signal_t sig; + struct fileserver fileserver; + struct uvh_server *server; + + if (argc != 2) + { + printf("usage: %s document-root\n", argv[0]); + return 1; + } + + memset(&fileserver, 0, sizeof(fileserver)); + + fileserver.root = sdsnew(argv[1]); + + server = uvh_server_init(uv_default_loop(), &fileserver, &request_handler); + + if (!server) + goto error; + + if (uvh_server_listen(server, "127.0.0.1", 9898)) + goto error; + + uv_signal_init(uv_default_loop(), &sig); + sig.data = server; + uv_signal_start(&sig, &ctrlc_handler, SIGINT); + + printf("root: %s\n", fileserver.root); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + printf("done\n"); + + return 0; + +error: + + fprintf(stderr, "error: %s\n", + uv_strerror(uv_last_error(uv_default_loop()))); + + return 1; +} |
