From cf0354f50f506a3257ca8fcf5a700fc9489272c0 Mon Sep 17 00:00:00 2001 From: Joe Orton Date: Mon, 13 Nov 2023 19:40:40 +0000 Subject: [PATCH] Add ne_getbuf(). * src/ne_basic.c (ne_getbuf), * src/ne_basic.h (ne_getbuf): New function. * test/basic.c (getbuf, getbuf2): New tests. * test/common/child.c (new_spawn_server2): Close listening socket once last accept() is done. --- src/ne_basic.c | 44 ++++++++++++++++++++++++++++++++++ src/ne_basic.h | 8 +++++++ src/neon.vers | 1 + test/basic.c | 58 +++++++++++++++++++++++++++++++++++++++++++++ test/common/child.c | 7 +++++- 5 files changed, 117 insertions(+), 1 deletion(-) diff --git a/src/ne_basic.c b/src/ne_basic.c index 1dc9842b..cf5adf97 100644 --- a/src/ne_basic.c +++ b/src/ne_basic.c @@ -142,6 +142,50 @@ int ne_putbuf(ne_session *sess, const char *path, return ret; } +int ne_getbuf(ne_session *sess, const char *path, + char *buf, size_t *buflen) +{ + ne_request *req = ne_request_create(sess, "GET", path); + int ret; + char *ptr; + + do { + size_t remain = *buflen; + ssize_t rlen = -1; + + ptr = buf; + + ret = ne_begin_request(req); + if (ret != NE_OK) break; + + while (remain > 0 + && (rlen = ne_read_response_block(req, ptr, remain)) > 0) { + remain -= rlen; + ptr += rlen; + } + + if (rlen < 0) { + ret = NE_ERROR; + } + else if (rlen > 0) { + ne_set_error(sess, N_("Response buffer size too small")); + ret = NE_FAILED; + } + + if (ret == NE_OK) ret = ne_end_request(req); + } while (ret == NE_RETRY); + + if (ret == NE_OK) + *buflen = ptr - buf; + + if (ret != NE_OK) + ne_close_connection(sess); + + ne_request_destroy(req); + + return ret; +} + /* Dispatch a GET request REQ, writing the response body to FD fd. If * RANGE is non-NULL, then it is the value of the Range request * header, e.g. "bytes=1-5". Returns an NE_* error code. */ diff --git a/src/ne_basic.h b/src/ne_basic.h index 22f442e8..001de49d 100644 --- a/src/ne_basic.h +++ b/src/ne_basic.h @@ -41,6 +41,14 @@ int ne_put(ne_session *sess, const char *path, int fd); int ne_putbuf(ne_session *sess, const char *path, const char *buf, size_t buflen); +/* Perform a GET request on resource at 'path', writing the entity + * body which is returned to 'buffer', of maximum length *buflen. On + * return, *buflen is updated to reflect the number of bytes + * received. If the response content length exceeds the maximum, + * NE_FAILED is returned. */ +int ne_getbuf(ne_session *sess, const char *path, + char *buf, size_t *buflen); + #define NE_DEPTH_ZERO (0) #define NE_DEPTH_ONE (1) #define NE_DEPTH_INFINITE (2) diff --git a/src/neon.vers b/src/neon.vers index 38b1339f..d60126f9 100644 --- a/src/neon.vers +++ b/src/neon.vers @@ -38,4 +38,5 @@ NEON_0_32 { NEON_0_33 { ne_add_interim_handler; ne_putbuf; + ne_getbuf; }; diff --git a/test/basic.c b/test/basic.c index 6b6bd0cc..fa036398 100644 --- a/test/basic.c +++ b/test/basic.c @@ -267,6 +267,62 @@ static int get(void) return destroy_and_wait(sess); } + +#define CHUNK(len, data) #len "\r\n" data "\r\n" + +#define ABCDE_CHUNKS CHUNK(1, "a") CHUNK(1, "b") \ + CHUNK(1, "c") CHUNK(1, "d") \ + CHUNK(1, "e") CHUNK(0, "") + +static int getbuf(void) +{ + ne_session *sess; + char buffer[16]; + size_t buflen = sizeof buffer; + + CALL(make_session(&sess, single_serve_string, + "HTTP/1.1 200 OK\r\n" "Server: neon-test-server\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + ABCDE_CHUNKS)); + + ONREQ(ne_getbuf(sess, "/", buffer, &buflen)); + + ONV(buflen != 5, ("unexpected buffer length %" NE_FMT_SIZE_T, buflen)); + + ONV(memcmp("abcde", buffer, 5) != 0, + ("mismatched chunked response: [%5s]", buffer)); + + return destroy_and_wait(sess); +} + +static int getbuf2(void) +{ + ne_session *sess; + char buf; + size_t buflen = sizeof buf; + int ret; + + CALL(make_session(&sess, single_serve_string, + "HTTP/1.1 200 OK\r\n" "Server: neon-test-server\r\n" + "Content-Length: 5\r\n" + "\r\n" + "abcde" + "HTTP/1.1 200 OK\r\n" "Server: neon-test-server\r\n" + "Content-Length: 5\r\n" + "\r\n" + "abcde")); + + ret = ne_getbuf(sess, "/", &buf, &buflen); + ONV(ret != NE_FAILED, ("overflow case gave %d not FAILED", ret)); + ONV(buflen != 1, ("buffer length returned as %" NE_FMT_SIZE_T, buflen)); + + ret = any_request(sess, "/closeme"); + ONN("failed to close connection", ret != NE_CONNECT); + + return destroy_and_wait(sess); +} + #define CLASS_12 (NE_CAP_DAV_CLASS1 | NE_CAP_DAV_CLASS2) static int options2(void) @@ -335,6 +391,8 @@ ne_test tests[] = { T(fail_range_unsatify), T(dav_capabilities), T(get), + T(getbuf), + T(getbuf2), T(options2), T(put), T(NULL) diff --git a/test/common/child.c b/test/common/child.c index 94c0c3e6..e4ab610e 100644 --- a/test/common/child.c +++ b/test/common/child.c @@ -415,7 +415,12 @@ int new_spawn_server2(int count, server_fn fn, void *userdata, exit(FAIL); } - NE_DEBUG(NE_DBG_HTTP, "child got connection, invoking server\n"); + NE_DEBUG(NE_DBG_HTTP, "child: got connection, invoking server\n"); + if (iter == count) { + close(ls); + NE_DEBUG(NE_DBG_HTTP, "child: closed listening socket.\n"); + } + ret = fn(sock, userdata); NE_DEBUG(NE_DBG_HTTP, "child iteration #%d returns %d\n", iter, ret);