summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pttbbs/common/sys/Makefile2
-rw-r--r--pttbbs/common/sys/buffer.c93
-rw-r--r--pttbbs/common/sys/thttp.c179
-rw-r--r--pttbbs/include/cmsys.h6
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