diff options
-rw-r--r-- | pttbbs/common/sys/Makefile | 2 | ||||
-rw-r--r-- | pttbbs/common/sys/buffer.c | 93 | ||||
-rw-r--r-- | pttbbs/common/sys/thttp.c | 179 | ||||
-rw-r--r-- | pttbbs/include/cmsys.h | 6 |
4 files changed, 279 insertions, 1 deletions
diff --git a/pttbbs/common/sys/Makefile b/pttbbs/common/sys/Makefile index 968ef937..d6634aeb 100644 --- a/pttbbs/common/sys/Makefile +++ b/pttbbs/common/sys/Makefile @@ -5,7 +5,7 @@ SRCROOT= ../.. SRCS:= daemon.c file.c lock.c log.c net.c sort.c string.c time.c \ crypt.c record.c vector.c telnet.c vbuf.c vtkbd.c \ - utf8.c big5.c + utf8.c big5.c buffer.c thttp.c LIB:= cmsys diff --git a/pttbbs/common/sys/buffer.c b/pttbbs/common/sys/buffer.c new file mode 100644 index 00000000..e1ff417a --- /dev/null +++ b/pttbbs/common/sys/buffer.c @@ -0,0 +1,93 @@ +#include <string.h> +#include <stdlib.h> +#include <stdint.h> +#include "buffer.h" + +static int buffer__aligned_size(int desired_size); + +int buffer_init(BUFFER *b, int initial_size) { + initial_size = buffer__aligned_size(initial_size); + + memset(b, 0, sizeof(*b)); + b->pool = (uint8_t *)malloc(initial_size); + if (!b->pool) + return -1; + b->allocated = initial_size; + b->length = 0; + return 0; +} + +int buffer_cleanup(BUFFER *b) { + free(b->pool); + return 0; +} + +/* Append data to buffer, automatically grows buffer if needed + * return the starting offset + * return -1 if failed + */ +int buffer_append(BUFFER *b, const void *data, int size) { + if (b->length + size > b->allocated + && buffer_grow(b, b->length + size) < 0) + return -1; + + int start = b->length; + memcpy(b->pool + start, data, size); + b->length += size; + return start; +} + +/* Grows the buffer to the desired size + * aligned to BUFFER_MIN_INCREMENT + * return allocated size on success + * return -1 on failure + */ +int buffer_grow(BUFFER *b, int desired_size) { + if (b->allocated >= desired_size) + return b->allocated; + desired_size = buffer__aligned_size(desired_size); + + void *newptr = realloc(b->pool, desired_size); + if (newptr == NULL) + return -1; + + b->pool = newptr; + return (b->allocated = desired_size); +} + +int buffer_length(BUFFER *b) { + return b->length; +} + +void *buffer_get(BUFFER *b, int offset) { + return (void *)(b->pool + offset); +} + +/* call readfunc once, passing the remaining buffer head and + * the remaining capacity. automatically grow the buffer once + * if out of space. + * return the value readfunc returns on success + * or -1 on failing to grow the buffer + */ +int buffer_read_from_func( + BUFFER *b, int (*readfunc)(void *ctx, void *buf, int maxread), + void *ctx) +{ + if (b->length == b->allocated && + buffer_grow(b, b->length + BUFFER_MIN_INCREMENT) < 0) + return -1; + + int nread = readfunc( + ctx, b->pool + b->length, b->allocated - b->length); + if (nread <= 0) + return nread; + b->length += nread; + return nread; +} + +static int buffer__aligned_size(int desired_size) { + int aligned_size = desired_size / BUFFER_MIN_INCREMENT; + if (aligned_size < desired_size) + aligned_size += BUFFER_MIN_INCREMENT; + return aligned_size; +} diff --git a/pttbbs/common/sys/thttp.c b/pttbbs/common/sys/thttp.c new file mode 100644 index 00000000..1c68e00c --- /dev/null +++ b/pttbbs/common/sys/thttp.c @@ -0,0 +1,179 @@ +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <sys/types.h> +#include <sys/socket.h> +#include "cmsys.h" +#include "buffer.h" +#include "thttp.h" + +static int _read(THTTP *t, void *buffer, int size) { + return read(t->fd, buffer, size); +} + +static int _write(THTTP *t, const void *buffer, int size) { + int wrote = towrite(t->fd, buffer, size); + if (wrote < 0) + t->failed = 1; + return wrote; +} + +static int write_string(THTTP *t, const char *str) { + return t->write(t, str, strlen(str)); +} + +static int write_int(THTTP *t, int val) { + char conv[16]; + snprintf(conv, sizeof(conv), "%d", val); + return write_string(t, conv); +} + +static int request(THTTP *t, const char *meth, const char *uri, + const char *headers, ...) { + write_string(t, meth); + write_string(t, " "); + write_string(t, uri); + write_string(t, " HTTP/1.0\r\n"); + + va_list va; + va_start(va, headers); + int i; + for (i = 0; headers[i]; i++) { + const char *key = va_arg(va, const char *); + write_string(t, key); + write_string(t, ": "); + + switch (headers[i]) { + case 's': + { + const char *val = va_arg(va, const char *); + write_string(t, val); + break; + } + case 'i': + { + int val = va_arg(va, int); + write_int(t, val); + break; + } + default: + /* WTF */ + return -1; + } + + write_string(t, "\r\n"); + } + va_end(va); + + write_string(t, "\r\n"); + return 0; +} + +static int read_to_buffer(THTTP *t, BUFFER *b) { + int total = 0; + int last; + while ((last = buffer_read_from_func( + b, (int (*)(void *, void *, int))t->read, t)) > 0) + total += last; + if (last < 0) + return last; + return total; +} + +static int read_response(THTTP *t) { + BUFFER *b = &t->bresp; + + if (read_to_buffer(t, b) < 0) + return -1; + + // to be safe + buffer_append(b, "\0", 1); + + // read response code + char *start = (char *)buffer_get(b, 0); + if (sscanf(start, "%*s%d", &t->code) != 1) + return -1; + + char *content_start; + if (t->code != 200) { + // although there can be content when error, + // we still ignore it. + t->content_at = -1; + t->content_length = -1; + } else if ((content_start = strstr(start, "\r\n\r\n")) != NULL) { + t->content_at = content_start + 4 - start; + t->content_length = buffer_length(b) - t->content_at - 1; // added '\0' + } else { + // invalid response + return -1; + } + + return buffer_length(b) - 1; +} + + +/* Export functions */ + +int thttp_init(THTTP *t) { + memset(t, 0, sizeof(*t)); + if (buffer_init(&t->bresp, BUFFER_MIN_INCREMENT) < 0) + return -1; + return 0; +} + +int thttp_cleanup(THTTP *t) { + buffer_cleanup(&t->bresp); + memset(t, 0, sizeof(*t)); + return 0; +} + +void thttp_set_connect_timeout(THTTP *t, int microsecond) { + t->timeout_connect = microsecond; +} + +void thttp_set_io_timeout(THTTP *t, int microsecond) { + t->timeout_read = microsecond; +} + +int thttp_get(THTTP *t, const char *addr, const char *uri, const char *host) { + t->fd = toconnect3(addr, 0, t->timeout_connect); + if (t->fd < 0) + return -1; + + if (t->timeout_read > 0) { + // set the io timeout + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = t->timeout_read; + setsockopt(t->fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + } + + t->read = _read; + t->write = _write; + + request(t, "GET", uri, "ssss", + "Accept", "text/plain", + "Host", host, + "Connection", "close", + "User-Agent", "pttbbs"); + + if (t->failed || read_response(t) < 0) { + close(t->fd); + return -1; + } + + close(t->fd); + return 0; +} + +int thttp_code(THTTP *t) { + return t->code; +} + +void *thttp_get_content(THTTP *t) { + return t->code == 200 ? buffer_get(&t->bresp, t->content_at) : ""; +} + +int thttp_content_length(THTTP *t) { + return t->code == 200 ? t->content_length : 0; +} diff --git a/pttbbs/include/cmsys.h b/pttbbs/include/cmsys.h index 8c0bf5e8..b9082763 100644 --- a/pttbbs/include/cmsys.h +++ b/pttbbs/include/cmsys.h @@ -307,4 +307,10 @@ extern const uint16_t b2u_table[]; extern const uint16_t u2b_table[]; extern const uint8_t b2u_ambiguous_width[]; +/* buffer.c */ +#include "buffer.h" + +/* thttp.c */ +#include "thttp.h" + #endif |