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);
+}