diff options
Diffstat (limited to 'src/supplemental/http/http_client.c')
| -rw-r--r-- | src/supplemental/http/http_client.c | 139 |
1 files changed, 55 insertions, 84 deletions
diff --git a/src/supplemental/http/http_client.c b/src/supplemental/http/http_client.c index 8701a96b..7062ae3c 100644 --- a/src/supplemental/http/http_client.c +++ b/src/supplemental/http/http_client.c @@ -10,12 +10,14 @@ // #include <stdbool.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> #include "core/nng_impl.h" #include "http_api.h" +#include "http_msg.h" static nni_mtx http_txn_lk = NNI_MTX_INITIALIZER; @@ -24,6 +26,7 @@ struct nng_http_client { nni_mtx mtx; bool closed; nni_aio aio; + char host[260]; nng_stream_dialer *dialer; }; @@ -70,7 +73,9 @@ http_dial_cb(void *arg) stream = nni_aio_get_output(&c->aio, 0); NNI_ASSERT(stream != NULL); - rv = nni_http_conn_init(&conn, stream); + rv = nni_http_conn_init(&conn, stream, true); + + // set up the host header http_dial_start(c); nni_mtx_unlock(&c->mtx); @@ -79,7 +84,7 @@ http_dial_cb(void *arg) nni_aio_finish_error(aio, rv); return; } - + nni_http_set_host(conn, c->host); nni_aio_set_output(aio, 0, conn); nni_aio_finish(aio, 0, 0); } @@ -110,7 +115,8 @@ nni_http_client_init(nni_http_client **cp, const nng_url *url) memcpy(&my_url, url, sizeof(my_url)); my_url.u_scheme = (char *) scheme; - if (strlen(url->u_hostname) == 0) { + if ((strlen(url->u_hostname) == 0) || + (strlen(url->u_hostname) > 253)) { // We require a valid hostname. return (NNG_EADDRINVAL); } @@ -122,6 +128,16 @@ nni_http_client_init(nni_http_client **cp, const nng_url *url) nni_aio_list_init(&c->aios); nni_aio_init(&c->aio, http_dial_cb, c); + if (nni_url_default_port(url->u_scheme) == url->u_port) { + snprintf(c->host, sizeof(c->host), "%s", url->u_hostname); + } else if (strchr(url->u_hostname, ':') != NULL) { + // IPv6 address, needs [wrapping] + snprintf(c->host, sizeof(c->host), "[%s]:%d", url->u_hostname, + url->u_port); + } else { + snprintf(c->host, sizeof(c->host), "%s:%d", url->u_hostname, + url->u_port); + } if ((rv = nng_stream_dialer_alloc_url(&c->dialer, &my_url)) != 0) { nni_http_client_fini(c); return (rv); @@ -190,7 +206,6 @@ nni_http_client_connect(nni_http_client *c, nni_aio *aio) } typedef enum http_txn_state { - HTTP_CONNECTING, HTTP_SENDING, HTTP_RECVING, HTTP_RECVING_BODY, @@ -198,7 +213,7 @@ typedef enum http_txn_state { } http_txn_state; typedef struct http_txn { - nni_aio *aio; // lower level aio + nni_aio aio; // lower level aio nni_list aios; // upper level aio(s) -- maximum one nni_http_client *client; nni_http_conn *conn; @@ -206,12 +221,15 @@ typedef struct http_txn { nni_http_res *res; nni_http_chunks *chunks; http_txn_state state; + nni_reap_node reap; } http_txn; static void -http_txn_fini(void *arg) +http_txn_reap(void *arg) { http_txn *txn = arg; + + nni_aio_stop(&txn->aio); if (txn->client != NULL) { // We only close the connection if we created it. if (txn->conn != NULL) { @@ -220,10 +238,21 @@ http_txn_fini(void *arg) } } nni_http_chunks_free(txn->chunks); - nni_aio_reap(txn->aio); + nni_aio_fini(&txn->aio); NNI_FREE_STRUCT(txn); } +static nni_reap_list http_txn_reaplist = { + .rl_offset = offsetof(http_txn, reap), + .rl_func = (nni_cb) http_txn_reap, +}; + +static void +http_txn_fini(http_txn *txn) +{ + nni_reap(&http_txn_reaplist, txn); +} + static void http_txn_finish_aios(http_txn *txn, int rv) { @@ -248,31 +277,26 @@ http_txn_cb(void *arg) nni_http_chunk *chunk = NULL; nni_mtx_lock(&http_txn_lk); - if ((rv = nni_aio_result(txn->aio)) != 0) { + if ((rv = nni_aio_result(&txn->aio)) != 0) { http_txn_finish_aios(txn, rv); nni_mtx_unlock(&http_txn_lk); http_txn_fini(txn); return; } switch (txn->state) { - case HTTP_CONNECTING: - txn->conn = nni_aio_get_output(txn->aio, 0); - txn->state = HTTP_SENDING; - nni_http_write_req(txn->conn, txn->req, txn->aio); - nni_mtx_unlock(&http_txn_lk); - return; - case HTTP_SENDING: txn->state = HTTP_RECVING; - nni_http_read_res(txn->conn, txn->res, txn->aio); + nni_http_read_res(txn->conn, &txn->aio); nni_mtx_unlock(&http_txn_lk); return; case HTTP_RECVING: - // Detect chunked encoding. You poor bastard. - if (((str = nni_http_res_get_header( - txn->res, "Transfer-Encoding")) != NULL) && + // Detect chunked encoding. You poor bastard. (Only if not + // HEAD.) + if ((strcmp(nni_http_get_method(txn->conn), "HEAD") != 0) && + ((str = nni_http_get_header( + txn->conn, "Transfer-Encoding")) != NULL) && (strstr(str, "chunked") != NULL)) { if ((rv = nni_http_chunks_init(&txn->chunks, 0)) != @@ -280,15 +304,15 @@ http_txn_cb(void *arg) goto error; } txn->state = HTTP_RECVING_CHUNKS; - nni_http_read_chunks(txn->conn, txn->chunks, txn->aio); + nni_http_read_chunks( + txn->conn, txn->chunks, &txn->aio); nni_mtx_unlock(&http_txn_lk); return; } - str = nni_http_req_get_method(txn->req); - if ((nni_strcasecmp(str, "HEAD") == 0) || - ((str = nni_http_res_get_header( - txn->res, "Content-Length")) == NULL) || + if ((strcmp(nni_http_get_method(txn->conn), "HEAD") == 0) || + ((str = nni_http_get_header( + txn->conn, "Content-Length")) == NULL) || ((len = (uint64_t) strtoull(str, &end, 10)) == 0) || (end == NULL) || (*end != '\0')) { // If no content-length, or HEAD (which per RFC @@ -303,10 +327,10 @@ http_txn_cb(void *arg) 0) { goto error; } - nni_http_res_get_data(txn->res, &iov.iov_buf, &iov.iov_len); - nni_aio_set_iov(txn->aio, 1, &iov); + nni_http_get_body(txn->conn, &iov.iov_buf, &iov.iov_len); + nni_aio_set_iov(&txn->aio, 1, &iov); txn->state = HTTP_RECVING_BODY; - nni_http_read_full(txn->conn, txn->aio); + nni_http_read_full(txn->conn, &txn->aio); nni_mtx_unlock(&http_txn_lk); return; @@ -324,7 +348,7 @@ http_txn_cb(void *arg) if ((rv = nni_http_res_alloc_data(txn->res, sz)) != 0) { goto error; } - nni_http_res_get_data(txn->res, (void **) &dst, &sz); + nni_http_get_body(txn->conn, (void **) &dst, &sz); while ((chunk = nni_http_chunks_iter(txn->chunks, chunk)) != NULL) { memcpy(dst, nni_http_chunk_data(chunk), @@ -350,7 +374,7 @@ http_txn_cancel(nni_aio *aio, void *arg, int rv) http_txn *txn = arg; nni_mtx_lock(&http_txn_lk); if (nni_aio_list_active(aio)) { - nni_aio_abort(txn->aio, rv); + nni_aio_abort(&txn->aio, rv); } nni_mtx_unlock(&http_txn_lk); } @@ -364,18 +388,13 @@ void nni_http_transact_conn(nni_http_conn *conn, nni_aio *aio) { http_txn *txn; - int rv; nni_aio_reset(aio); if ((txn = NNI_ALLOC_STRUCT(txn)) == NULL) { nni_aio_finish_error(aio, NNG_ENOMEM); return; } - if ((rv = nni_aio_alloc(&txn->aio, http_txn_cb, txn)) != 0) { - NNI_FREE_STRUCT(txn); - nni_aio_finish_error(aio, rv); - return; - } + nni_aio_init(&txn->aio, http_txn_cb, txn); nni_aio_list_init(&txn->aios); txn->client = NULL; txn->conn = conn; @@ -391,55 +410,7 @@ nni_http_transact_conn(nni_http_conn *conn, nni_aio *aio) http_txn_fini(txn); return; } - nni_http_res_reset(txn->res); - nni_list_append(&txn->aios, aio); - nni_http_write_req(conn, txn->req, txn->aio); - nni_mtx_unlock(&http_txn_lk); -} - -// nni_http_transact_simple does a single transaction, creating a connection -// just for the purpose, and closing it when done. (No connection caching.) -// The reason we require a client to be created first is to deal with TLS -// settings. A single global client (per server) may be used. -void -nni_http_transact(nni_http_client *client, nni_http_req *req, - nni_http_res *res, nni_aio *aio) -{ - http_txn *txn; - int rv; - - nni_aio_reset(aio); - if ((txn = NNI_ALLOC_STRUCT(txn)) == NULL) { - nni_aio_finish_error(aio, NNG_ENOMEM); - return; - } - if ((rv = nni_aio_alloc(&txn->aio, http_txn_cb, txn)) != 0) { - NNI_FREE_STRUCT(txn); - nni_aio_finish_error(aio, rv); - return; - } - - if ((rv = nni_http_req_set_header(req, "Connection", "close")) != 0) { - nni_aio_finish_error(aio, rv); - http_txn_fini(txn); - return; - } - - nni_aio_list_init(&txn->aios); - txn->client = client; - txn->conn = NULL; - txn->req = req; - txn->res = res; - txn->state = HTTP_CONNECTING; - - nni_mtx_lock(&http_txn_lk); - if (!nni_aio_start(aio, http_txn_cancel, txn)) { - nni_mtx_unlock(&http_txn_lk); - http_txn_fini(txn); - return; - } - nni_http_res_reset(txn->res); nni_list_append(&txn->aios, aio); - nni_http_client_connect(client, txn->aio); + nni_http_write_req(conn, &txn->aio); nni_mtx_unlock(&http_txn_lk); } |
