Squashed 'third_party/rawrtc/re/' content from commit f3163ce8b
Change-Id: I6a235e6ac0f03269d951026f9d195da05c40fdab
git-subtree-dir: third_party/rawrtc/re
git-subtree-split: f3163ce8b526a13b35ef71ce4dd6f43585064d8a
diff --git a/src/http/server.c b/src/http/server.c
new file mode 100644
index 0000000..ff04770
--- /dev/null
+++ b/src/http/server.c
@@ -0,0 +1,522 @@
+/**
+ * @file http/server.c HTTP Server
+ *
+ * Copyright (C) 2011 Creytiv.com
+ */
+
+#include <re_types.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include <re_sa.h>
+#include <re_list.h>
+#include <re_fmt.h>
+#include <re_tmr.h>
+#include <re_srtp.h>
+#include <re_tcp.h>
+#include <re_tls.h>
+#include <re_msg.h>
+#include <re_http.h>
+
+
+enum {
+ TIMEOUT_IDLE = 600000,
+ TIMEOUT_INIT = 10000,
+ BUFSIZE_MAX = 524288,
+};
+
+struct http_sock {
+ struct list connl;
+ struct tcp_sock *ts;
+ struct tls *tls;
+ http_req_h *reqh;
+ void *arg;
+};
+
+struct http_conn {
+ struct le le;
+ struct tmr tmr;
+ struct sa peer;
+ struct http_sock *sock;
+ struct tcp_conn *tc;
+ struct tls_conn *sc;
+ struct mbuf *mb;
+};
+
+
+static void conn_close(struct http_conn *conn);
+
+
+static void sock_destructor(void *arg)
+{
+ struct http_sock *sock = arg;
+ struct le *le;
+
+ for (le=sock->connl.head; le;) {
+
+ struct http_conn *conn = le->data;
+
+ le = le->next;
+
+ conn_close(conn);
+ mem_deref(conn);
+ }
+
+ mem_deref(sock->tls);
+ mem_deref(sock->ts);
+}
+
+
+static void conn_destructor(void *arg)
+{
+ struct http_conn *conn = arg;
+
+ list_unlink(&conn->le);
+ tmr_cancel(&conn->tmr);
+ mem_deref(conn->sc);
+ mem_deref(conn->tc);
+ mem_deref(conn->mb);
+}
+
+
+static void conn_close(struct http_conn *conn)
+{
+ list_unlink(&conn->le);
+ tmr_cancel(&conn->tmr);
+ conn->sc = mem_deref(conn->sc);
+ conn->tc = mem_deref(conn->tc);
+ conn->sock = NULL;
+}
+
+
+static void timeout_handler(void *arg)
+{
+ struct http_conn *conn = arg;
+
+ conn_close(conn);
+ mem_deref(conn);
+}
+
+
+static void recv_handler(struct mbuf *mb, void *arg)
+{
+ struct http_conn *conn = arg;
+ int err = 0;
+
+ if (conn->mb) {
+
+ const size_t len = mbuf_get_left(mb), pos = conn->mb->pos;
+
+ if ((mbuf_get_left(conn->mb) + len) > BUFSIZE_MAX) {
+ err = EOVERFLOW;
+ goto out;
+ }
+
+ conn->mb->pos = conn->mb->end;
+
+ err = mbuf_write_mem(conn->mb, mbuf_buf(mb), len);
+ if (err)
+ goto out;
+
+ conn->mb->pos = pos;
+ }
+ else {
+ conn->mb = mem_ref(mb);
+ }
+
+ while (conn->mb) {
+ size_t end, pos = conn->mb->pos;
+ struct http_msg *msg;
+
+ err = http_msg_decode(&msg, conn->mb, true);
+ if (err) {
+ if (err == ENODATA) {
+ conn->mb->pos = pos;
+ err = 0;
+ break;
+ }
+
+ goto out;
+ }
+
+ if (mbuf_get_left(conn->mb) < msg->clen) {
+ conn->mb->pos = pos;
+ mem_deref(msg);
+ break;
+ }
+
+ mem_deref(msg->mb);
+ msg->mb = mem_ref(msg->_mb);
+
+ mb = conn->mb;
+
+ end = mb->end;
+ mb->end = mb->pos + msg->clen;
+
+ if (end > mb->end) {
+ struct mbuf *mbn = mbuf_alloc(end - mb->end);
+ if (!mbn) {
+ mem_deref(msg);
+ err = ENOMEM;
+ goto out;
+ }
+
+ (void)mbuf_write_mem(mbn, mb->buf + mb->end,
+ end - mb->end);
+ mbn->pos = 0;
+
+ mem_deref(conn->mb);
+ conn->mb = mbn;
+ }
+ else {
+ conn->mb = mem_deref(conn->mb);
+ }
+
+ if (conn->sock)
+ conn->sock->reqh(conn, msg, conn->sock->arg);
+
+ mem_deref(msg);
+
+ if (!conn->tc) {
+ err = ENOTCONN;
+ goto out;
+ }
+
+ tmr_start(&conn->tmr, TIMEOUT_IDLE, timeout_handler, conn);
+ }
+
+ out:
+ if (err) {
+ conn_close(conn);
+ mem_deref(conn);
+ }
+}
+
+
+static void close_handler(int err, void *arg)
+{
+ struct http_conn *conn = arg;
+ (void)err;
+
+ conn_close(conn);
+ mem_deref(conn);
+}
+
+
+static void connect_handler(const struct sa *peer, void *arg)
+{
+ struct http_sock *sock = arg;
+ struct http_conn *conn;
+ int err;
+
+ conn = mem_zalloc(sizeof(*conn), conn_destructor);
+ if (!conn) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ list_append(&sock->connl, &conn->le, conn);
+ conn->peer = *peer;
+ conn->sock = sock;
+
+ err = tcp_accept(&conn->tc, sock->ts, NULL, recv_handler,
+ close_handler, conn);
+ if (err)
+ goto out;
+
+#ifdef USE_TLS
+ if (sock->tls) {
+ err = tls_start_tcp(&conn->sc, sock->tls, conn->tc, 0);
+ if (err)
+ goto out;
+ }
+#endif
+
+ tmr_start(&conn->tmr, TIMEOUT_INIT, timeout_handler, conn);
+
+ out:
+ if (err) {
+ mem_deref(conn);
+ tcp_reject(sock->ts);
+ }
+}
+
+
+/**
+ * Create an HTTP socket
+ *
+ * @param sockp Pointer to returned HTTP Socket
+ * @param laddr Network address to listen on
+ * @param reqh Request handler
+ * @param arg Handler argument
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int http_listen(struct http_sock **sockp, const struct sa *laddr,
+ http_req_h *reqh, void *arg)
+{
+ struct http_sock *sock;
+ int err;
+
+ if (!sockp || !laddr || !reqh)
+ return EINVAL;
+
+ sock = mem_zalloc(sizeof(*sock), sock_destructor);
+ if (!sock)
+ return ENOMEM;
+
+ err = tcp_listen(&sock->ts, laddr, connect_handler, sock);
+ if (err)
+ goto out;
+
+ sock->reqh = reqh;
+ sock->arg = arg;
+
+ out:
+ if (err)
+ mem_deref(sock);
+ else
+ *sockp = sock;
+
+ return err;
+}
+
+
+/**
+ * Create an HTTP secure socket
+ *
+ * @param sockp Pointer to returned HTTP Socket
+ * @param laddr Network address to listen on
+ * @param cert File path of TLS certificate
+ * @param reqh Request handler
+ * @param arg Handler argument
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int https_listen(struct http_sock **sockp, const struct sa *laddr,
+ const char *cert, http_req_h *reqh, void *arg)
+{
+ struct http_sock *sock;
+ int err;
+
+ if (!sockp || !laddr || !cert || !reqh)
+ return EINVAL;
+
+ err = http_listen(&sock, laddr, reqh, arg);
+ if (err)
+ return err;
+
+#ifdef USE_TLS
+ err = tls_alloc(&sock->tls, TLS_METHOD_SSLV23, cert, NULL);
+#else
+ err = EPROTONOSUPPORT;
+#endif
+ if (err)
+ goto out;
+
+ out:
+ if (err)
+ mem_deref(sock);
+ else
+ *sockp = sock;
+
+ return err;
+}
+
+
+/**
+ * Get the TCP socket of an HTTP socket
+ *
+ * @param sock HTTP socket
+ *
+ * @return TCP socket
+ */
+struct tcp_sock *http_sock_tcp(struct http_sock *sock)
+{
+ return sock ? sock->ts : NULL;
+}
+
+
+/**
+ * Get the peer address of an HTTP connection
+ *
+ * @param conn HTTP connection
+ *
+ * @return Peer address
+ */
+const struct sa *http_conn_peer(const struct http_conn *conn)
+{
+ return conn ? &conn->peer : NULL;
+}
+
+
+/**
+ * Get the TCP connection of an HTTP connection
+ *
+ * @param conn HTTP connection
+ *
+ * @return TCP connection
+ */
+struct tcp_conn *http_conn_tcp(struct http_conn *conn)
+{
+ return conn ? conn->tc : NULL;
+}
+
+
+/**
+ * Get the TLS connection of an HTTP connection
+ *
+ * @param conn HTTP connection
+ *
+ * @return TLS connection
+ */
+struct tls_conn *http_conn_tls(struct http_conn *conn)
+{
+ return conn ? conn->sc : NULL;
+}
+
+
+/**
+ * Close the HTTP connection
+ *
+ * @param conn HTTP connection
+ */
+void http_conn_close(struct http_conn *conn)
+{
+ if (!conn)
+ return;
+
+ conn->sc = mem_deref(conn->sc);
+ conn->tc = mem_deref(conn->tc);
+}
+
+
+static int http_vreply(struct http_conn *conn, uint16_t scode,
+ const char *reason, const char *fmt, va_list ap)
+{
+ struct mbuf *mb;
+ int err;
+
+ if (!conn || !scode || !reason)
+ return EINVAL;
+
+ if (!conn->tc)
+ return ENOTCONN;
+
+ mb = mbuf_alloc(8192);
+ if (!mb)
+ return ENOMEM;
+
+ err = mbuf_printf(mb, "HTTP/1.1 %u %s\r\n", scode, reason);
+ if (fmt)
+ err |= mbuf_vprintf(mb, fmt, ap);
+ else
+ err |= mbuf_write_str(mb, "Content-Length: 0\r\n\r\n");
+ if (err)
+ goto out;
+
+ mb->pos = 0;
+
+ err = tcp_send(conn->tc, mb);
+ if (err)
+ goto out;
+
+ out:
+ mem_deref(mb);
+
+ return err;
+}
+
+
+/**
+ * Send an HTTP response
+ *
+ * @param conn HTTP connection
+ * @param scode Response status code
+ * @param reason Response reason phrase
+ * @param fmt Formatted HTTP message
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int http_reply(struct http_conn *conn, uint16_t scode, const char *reason,
+ const char *fmt, ...)
+{
+ va_list ap;
+ int err;
+
+ va_start(ap, fmt);
+ err = http_vreply(conn, scode, reason, fmt, ap);
+ va_end(ap);
+
+ return err;
+}
+
+
+/**
+ * Send an HTTP response with content formatting
+ *
+ * @param conn HTTP connection
+ * @param scode Response status code
+ * @param reason Response reason phrase
+ * @param ctype Content type
+ * @param fmt Formatted HTTP content
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int http_creply(struct http_conn *conn, uint16_t scode, const char *reason,
+ const char *ctype, const char *fmt, ...)
+{
+ struct mbuf *mb;
+ va_list ap;
+ int err;
+
+ if (!ctype || !fmt)
+ return EINVAL;
+
+ mb = mbuf_alloc(8192);
+ if (!mb)
+ return ENOMEM;
+
+ va_start(ap, fmt);
+ err = mbuf_vprintf(mb, fmt, ap);
+ va_end(ap);
+ if (err)
+ goto out;
+
+ err = http_reply(conn, scode, reason,
+ "Content-Type: %s\r\n"
+ "Content-Length: %zu\r\n"
+ "\r\n"
+ "%b",
+ ctype,
+ mb->end,
+ mb->buf, mb->end);
+ if (err)
+ goto out;
+
+ out:
+ mem_deref(mb);
+
+ return err;
+}
+
+
+/**
+ * Send an HTTP error response
+ *
+ * @param conn HTTP connection
+ * @param scode Response status code
+ * @param reason Response reason phrase
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int http_ereply(struct http_conn *conn, uint16_t scode, const char *reason)
+{
+ return http_creply(conn, scode, reason, "text/html",
+ "<!DOCTYPE html>\n"
+ "<html>\n"
+ "<head><title>%u %s</title></head>\n"
+ "<body><h2>%u %s</h2></body>\n"
+ "</html>\n",
+ scode, reason,
+ scode, reason);
+}