[Pkg-voip-commits] [libre] 10/21: http: added support for chunked transfer encoding (#90)
Jonas Smedegaard
dr at jones.dk
Sat Dec 2 22:41:35 UTC 2017
This is an automated email from the git hooks/post-receive script.
js pushed a commit to annotated tag debian/0.5.6-1
in repository libre.
commit 5bb8c0712efcb93d83503c54732a78920f80d229
Author: Richard Aas <richaraas at gmail.com>
Date: Sat Nov 4 19:22:05 2017 +0100
http: added support for chunked transfer encoding (#90)
---
include/re_http.h | 13 ++--
src/http/chunk.c | 111 ++++++++++++++++++++++++++++++
src/http/client.c | 187 ++++++++++++++++++++++++++++++--------------------
src/http/http.h | 17 +++++
src/http/mod.mk | 1 +
src/http/msg.c | 9 ++-
src/http/server.c | 3 +
src/websock/websock.c | 23 +++----
8 files changed, 273 insertions(+), 91 deletions(-)
diff --git a/include/re_http.h b/include/re_http.h
index 58195f6..2789442 100644
--- a/include/re_http.h
+++ b/include/re_http.h
@@ -82,7 +82,8 @@ struct http_msg {
struct pl reason; /**< Response Reason phrase */
struct list hdrl; /**< List of HTTP headers (struct http_hdr) */
struct msg_ctype ctyp; /**< Content-type */
- struct mbuf *mb; /**< Buffer containing the HTTP message */
+ struct mbuf *_mb; /**< Buffer containing the HTTP message */
+ struct mbuf *mb; /**< Buffer containing the HTTP body */
uint32_t clen; /**< Content length */
};
@@ -114,16 +115,20 @@ int http_msg_print(struct re_printf *pf, const struct http_msg *msg);
struct http_cli;
struct http_req;
struct dnsc;
+struct tcp_conn;
+struct tls_conn;
typedef void (http_resp_h)(int err, const struct http_msg *msg, void *arg);
-typedef void (http_data_h)(struct mbuf *mb, void *arg);
+typedef int (http_data_h)(const uint8_t *buf, size_t size,
+ const struct http_msg *msg, void *arg);
+typedef void (http_conn_h)(struct tcp_conn *tc, struct tls_conn *sc,
+ void *arg);
int http_client_alloc(struct http_cli **clip, struct dnsc *dnsc);
int http_request(struct http_req **reqp, struct http_cli *cli, const char *met,
const char *uri, http_resp_h *resph, http_data_h *datah,
void *arg, const char *fmt, ...);
-struct tcp_conn *http_req_tcp(struct http_req *req);
-struct tls_conn *http_req_tls(struct http_req *req);
+void http_req_set_conn_handler(struct http_req *req, http_conn_h *connh);
/* Server */
diff --git a/src/http/chunk.c b/src/http/chunk.c
new file mode 100644
index 0000000..ec772f0
--- /dev/null
+++ b/src/http/chunk.c
@@ -0,0 +1,111 @@
+/**
+ * @file http/chunk.c Chunked Transfer Encoding
+ *
+ * Copyright (C) 2011 Creytiv.com
+ */
+
+#include <re_types.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include "http.h"
+
+
+static int decode_chunk_size(struct http_chunk *chunk, struct mbuf *mb)
+{
+ while (mbuf_get_left(mb)) {
+
+ char ch = (char)mbuf_read_u8(mb);
+ uint8_t c;
+
+ if (ch == '\n') {
+ if (chunk->digit) {
+ chunk->digit = false;
+ chunk->param = false;
+
+ return 0;
+ }
+ else
+ continue;
+ }
+
+ if (chunk->param)
+ continue;
+
+ if ('0' <= ch && ch <= '9')
+ c = ch - '0';
+ else if ('A' <= ch && ch <= 'F')
+ c = ch - 'A' + 10;
+ else if ('a' <= ch && ch <= 'f')
+ c = ch - 'a' + 10;
+ else if (ch == '\r' || ch == ' ' || ch == '\t')
+ continue;
+ else if (ch == ';' && chunk->digit) {
+ chunk->param = true;
+ continue;
+ }
+ else
+ return EPROTO;
+
+ chunk->digit = true;
+
+ chunk->size <<= 4;
+ chunk->size += c;
+ }
+
+ return ENODATA;
+}
+
+
+static int decode_trailer(struct http_chunk *chunk, struct mbuf *mb)
+{
+ while (mbuf_get_left(mb)) {
+
+ char ch = (char)mbuf_read_u8(mb);
+
+ if (ch == '\n') {
+ if (++chunk->lf >= 2)
+ return 0;
+ }
+ else if (ch != '\r')
+ chunk->lf = 0;
+ }
+
+ return ENODATA;
+}
+
+
+int http_chunk_decode(struct http_chunk *chunk, struct mbuf *mb, size_t *size)
+{
+ int err;
+
+ if (!chunk || !mb || !size)
+ return EINVAL;
+
+ if (chunk->trailer) {
+ err = decode_trailer(chunk, mb);
+ if (err)
+ return err;
+
+ *size = 0;
+
+ return 0;
+ }
+
+ err = decode_chunk_size(chunk, mb);
+ if (err)
+ return err;
+
+ if (chunk->size == 0) {
+ chunk->trailer = true;
+ chunk->lf = 1;
+
+ err = decode_trailer(chunk, mb);
+ if (err)
+ return err;
+ }
+
+ *size = chunk->size;
+ chunk->size = 0;
+
+ return 0;
+}
diff --git a/src/http/client.c b/src/http/client.c
index 8abec92..39afa39 100644
--- a/src/http/client.c
+++ b/src/http/client.c
@@ -19,6 +19,7 @@
#include <re_dns.h>
#include <re_msg.h>
#include <re_http.h>
+#include "http.h"
enum {
@@ -39,10 +40,12 @@ struct http_cli {
struct conn;
struct http_req {
+ struct http_chunk chunk;
struct sa srvv[16];
struct le le;
struct http_req **reqp;
struct http_cli *cli;
+ struct http_msg *msg;
struct dns_query *dq;
struct conn *conn;
struct mbuf *mbreq;
@@ -50,14 +53,14 @@ struct http_req {
char *host;
http_resp_h *resph;
http_data_h *datah;
+ http_conn_h *connh;
void *arg;
- size_t rx_bytes;
size_t rx_len;
unsigned srvc;
uint16_t port;
+ bool chunked;
bool secure;
bool close;
- bool data;
};
@@ -102,6 +105,7 @@ static void req_destructor(void *arg)
struct http_req *req = arg;
list_unlink(&req->le);
+ mem_deref(req->msg);
mem_deref(req->dq);
mem_deref(req->conn);
mem_deref(req->mbreq);
@@ -136,7 +140,10 @@ static void req_close(struct http_req *req, int err,
req->datah = NULL;
if (req->conn) {
- if (err || req->close)
+ if (req->connh)
+ req->connh(req->conn->tc, req->conn->sc, req->arg);
+
+ if (err || req->close || req->connh)
mem_deref(req->conn);
else
conn_idle(req->conn);
@@ -144,12 +151,17 @@ static void req_close(struct http_req *req, int err,
req->conn = NULL;
}
+ req->connh = NULL;
+
if (req->reqp) {
*req->reqp = NULL;
req->reqp = NULL;
}
if (req->resph) {
+ if (msg)
+ msg->mb->pos = 0;
+
req->resph(err, msg, req->arg);
req->resph = NULL;
}
@@ -173,7 +185,7 @@ static void try_next(struct conn *conn, int err)
if (retry)
++req->srvc;
- if (req->srvc > 0 && !req->data) {
+ if (req->srvc > 0 && !req->msg) {
err = req_connect(req);
if (!err)
@@ -184,27 +196,77 @@ static void try_next(struct conn *conn, int err)
}
-static void req_recv(struct http_req *req, struct mbuf *mb)
+static int write_body_buf(struct http_msg *msg, const uint8_t *buf, size_t sz)
{
- uint32_t nrefs;
+ if ((msg->mb->pos + sz) > BUFSIZE_MAX)
+ return EOVERFLOW;
+
+ return mbuf_write_mem(msg->mb, buf, sz);
+}
- req->rx_bytes += mbuf_get_left(mb);
- mem_ref(req);
+static int write_body(struct http_req *req, struct mbuf *mb)
+{
+ const size_t size = min(mbuf_get_left(mb), req->rx_len);
+ int err;
+
+ if (size == 0)
+ return 0;
if (req->datah)
- req->datah(mb, req->arg);
+ err = req->datah(mbuf_buf(mb), size, req->msg, req->arg);
+ else
+ err = write_body_buf(req->msg, mbuf_buf(mb), size);
- nrefs = mem_nrefs(req);
- mem_deref(req);
+ if (err)
+ return err;
- if (nrefs == 1)
- return;
+ req->rx_len -= size;
+ mb->pos += size;
- if (req->rx_bytes < req->rx_len)
- return;
+ return 0;
+}
+
+
+static int req_recv(struct http_req *req, struct mbuf *mb, bool *last)
+{
+ int err;
+
+ *last = false;
- req_close(req, 0, NULL);
+ if (!req->chunked) {
+
+ err = write_body(req, mb);
+ if (err)
+ return err;
+
+ if (req->rx_len == 0)
+ *last = true;
+
+ return 0;
+ }
+
+ while (mbuf_get_left(mb)) {
+
+ if (req->rx_len == 0) {
+
+ err = http_chunk_decode(&req->chunk, mb, &req->rx_len);
+ if (err == ENODATA)
+ return 0;
+ else if (err)
+ return err;
+ else if (req->rx_len == 0) {
+ *last = true;
+ return 0;
+ }
+ }
+
+ err = write_body(req, mb);
+ if (err)
+ return err;
+ }
+
+ return 0;
}
@@ -237,18 +299,21 @@ static void estab_handler(void *arg)
static void recv_handler(struct mbuf *mb, void *arg)
{
- struct http_msg *msg = NULL;
const struct http_hdr *hdr;
struct conn *conn = arg;
struct http_req *req = conn->req;
size_t pos;
+ bool last;
int err;
if (!req)
return;
- if (req->data) {
- req_recv(req, mb);
+ if (req->msg) {
+ err = req_recv(req, mb, &last);
+ if (err || last)
+ goto out;
+
return;
}
@@ -276,7 +341,7 @@ static void recv_handler(struct mbuf *mb, void *arg)
pos = req->mb->pos;
- err = http_msg_decode(&msg, req->mb, false);
+ err = http_msg_decode(&req->msg, req->mb, false);
if (err) {
if (err == ENODATA) {
req->mb->pos = pos;
@@ -285,50 +350,27 @@ static void recv_handler(struct mbuf *mb, void *arg)
goto out;
}
- hdr = http_msg_hdr(msg, HTTP_HDR_CONNECTION);
+ if (req->datah)
+ tmr_cancel(&conn->tmr);
+ hdr = http_msg_hdr(req->msg, HTTP_HDR_CONNECTION);
if (hdr && !pl_strcasecmp(&hdr->val, "close"))
req->close = true;
- if (req->datah) {
-
- uint32_t nrefs;
-
- if (http_msg_hdr(msg, HTTP_HDR_CONTENT_LENGTH))
- req->rx_len = msg->clen;
- else
- req->rx_len = -1;
-
- tmr_cancel(&conn->tmr);
- req->data = true;
-
- mem_ref(req);
-
- if (req->resph)
- req->resph(0, msg, req->arg);
-
- nrefs = mem_nrefs(req);
- mem_deref(req);
-
- mem_deref(msg);
-
- if (nrefs > 1 && mbuf_get_left(req->mb))
- req_recv(req, req->mb);
-
- return;
- }
+ if (http_msg_hdr_has_value(req->msg, HTTP_HDR_TRANSFER_ENCODING,
+ "chunked"))
+ req->chunked = true;
+ else
+ req->rx_len = req->msg->clen;
- if (mbuf_get_left(req->mb) < msg->clen) {
- req->mb->pos = pos;
- mem_deref(msg);
- return;
- }
+ err = req_recv(req, req->mb, &last);
+ if (err || last)
+ goto out;
- req->mb->end = req->mb->pos + msg->clen;
+ return;
out:
- req_close(req, err, msg);
- mem_deref(msg);
+ req_close(req, err, req->msg);
}
@@ -601,6 +643,21 @@ int http_request(struct http_req **reqp, struct http_cli *cli, const char *met,
/**
+ * Set HTTP request connection handler
+ *
+ * @param req HTTP request object
+ * @param connh Connection handler
+ */
+void http_req_set_conn_handler(struct http_req *req, http_conn_h *connh)
+{
+ if (!req)
+ return;
+
+ req->connh = connh;
+}
+
+
+/**
* Allocate an HTTP client instance
*
* @param clip Pointer to allocated HTTP client
@@ -642,21 +699,3 @@ int http_client_alloc(struct http_cli **clip, struct dnsc *dnsc)
return err;
}
-
-
-struct tcp_conn *http_req_tcp(struct http_req *req)
-{
- if (!req || !req->conn)
- return NULL;
-
- return req->conn->tc;
-}
-
-
-struct tls_conn *http_req_tls(struct http_req *req)
-{
- if (!req || !req->conn)
- return NULL;
-
- return req->conn->sc;
-}
diff --git a/src/http/http.h b/src/http/http.h
new file mode 100644
index 0000000..ba63999
--- /dev/null
+++ b/src/http/http.h
@@ -0,0 +1,17 @@
+/**
+ * @file http.h HTTP Private Interface
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+
+struct http_chunk {
+ size_t size;
+ unsigned lf;
+ bool trailer;
+ bool digit;
+ bool param;
+};
+
+
+int http_chunk_decode(struct http_chunk *chunk, struct mbuf *mb, size_t *size);
diff --git a/src/http/mod.mk b/src/http/mod.mk
index a194109..4394f58 100644
--- a/src/http/mod.mk
+++ b/src/http/mod.mk
@@ -5,6 +5,7 @@
#
SRCS += http/auth.c
+SRCS += http/chunk.c
SRCS += http/client.c
SRCS += http/msg.c
SRCS += http/server.c
diff --git a/src/http/msg.c b/src/http/msg.c
index ef462c9..7b1f68a 100644
--- a/src/http/msg.c
+++ b/src/http/msg.c
@@ -32,6 +32,7 @@ static void destructor(void *arg)
struct http_msg *msg = arg;
list_flush(&msg->hdrl);
+ mem_deref(msg->_mb);
mem_deref(msg->mb);
}
@@ -163,7 +164,13 @@ int http_msg_decode(struct http_msg **msgp, struct mbuf *mb, bool req)
if (!msg)
return ENOMEM;
- msg->mb = mem_ref(mb);
+ msg->_mb = mem_ref(mb);
+
+ msg->mb = mbuf_alloc(8192);
+ if (!msg->mb) {
+ err = ENOMEM;
+ goto out;
+ }
if (req) {
if (re_regex(s.p, s.l, "[a-z]+ [^? ]+[^ ]* HTTP/[0-9.]+",
diff --git a/src/http/server.c b/src/http/server.c
index 6abd879..ff04770 100644
--- a/src/http/server.c
+++ b/src/http/server.c
@@ -144,6 +144,9 @@ static void recv_handler(struct mbuf *mb, void *arg)
break;
}
+ mem_deref(msg->mb);
+ msg->mb = mem_ref(msg->_mb);
+
mb = conn->mb;
end = mb->end;
diff --git a/src/websock/websock.c b/src/websock/websock.c
index e5edffe..e781477 100644
--- a/src/websock/websock.c
+++ b/src/websock/websock.c
@@ -412,13 +412,8 @@ static void http_resp_handler(int err, const struct http_msg *msg, void *arg)
/* here we are ok */
conn->state = OPEN;
- conn->tc = mem_ref(http_req_tcp(conn->req));
- conn->sc = mem_ref(http_req_tls(conn->req));
(void)tcp_conn_peer_get(conn->tc, &conn->peer);
- tcp_set_handlers(conn->tc, NULL, recv_handler, close_handler, conn);
- conn->req = mem_deref(conn->req);
-
if (conn->kaint)
tmr_start(&conn->tmr, conn->kaint, keepalive_handler, conn);
@@ -430,13 +425,15 @@ static void http_resp_handler(int err, const struct http_msg *msg, void *arg)
}
-/* dummy HTTP data handler, this must be here so that HTTP client
- * is not closing the underlying TCP-connection (which we need ..)
- */
-static void http_data_handler(struct mbuf *mb, void *arg)
+static void http_conn_handler(struct tcp_conn *tc, struct tls_conn *sc,
+ void *arg)
{
- (void)mb;
- (void)arg;
+ struct websock_conn *conn = arg;
+
+ conn->tc = mem_ref(tc);
+ conn->sc = mem_ref(sc);
+
+ tcp_set_handlers(conn->tc, NULL, recv_handler, close_handler, conn);
}
@@ -480,7 +477,7 @@ int websock_connect(struct websock_conn **connp, struct websock *sock,
/* Protocol Handshake */
va_start(ap, fmt);
err = http_request(&conn->req, cli, "GET", uri,
- http_resp_handler, http_data_handler, conn,
+ http_resp_handler, NULL, conn,
"Upgrade: websocket\r\n"
"Connection: upgrade\r\n"
"Sec-WebSocket-Key: %b\r\n"
@@ -493,6 +490,8 @@ int websock_connect(struct websock_conn **connp, struct websock *sock,
if (err)
goto out;
+ http_req_set_conn_handler(conn->req, http_conn_handler);
+
out:
if (err)
mem_deref(conn);
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-voip/libre.git
More information about the Pkg-voip-commits
mailing list