Sun, 13 Nov 2022 10:57:38 +0100
add http_send_response function that is usable for non-blocking IO
--- a/src/server/daemon/protocol.c Sun Nov 13 09:41:07 2022 +0100 +++ b/src/server/daemon/protocol.c Sun Nov 13 10:57:38 2022 +0100 @@ -233,23 +233,23 @@ } -void add_http_status_line(sbuf_t *out, pool_handle_t *pool, Request *rq) { - sbuf_write(out, "HTTP/1.1 ", 9); +void add_http_status_line(CxBuffer *out, pool_handle_t *pool, Request *rq) { + cxBufferWrite("HTTP/1.1 ", 1, 9, out); - char *status_code_str = pool_malloc(pool, 8); + char status_code_str[8]; int sc_len = snprintf(status_code_str, 8, "%d ", rq->status_num); - sbuf_write(out, status_code_str, sc_len); + cxBufferWrite(status_code_str, 1, sc_len, out); char *scmsg = pblock_findkeyval(pb_key_status, rq->srvhdrs); if(scmsg == NULL) { scmsg = "OK"; } - sbuf_write(out, scmsg, strlen(scmsg)); + cxBufferWrite(scmsg, 1, strlen(scmsg), out); - sbuf_write(out, "\r\n", 2); + cxBufferWrite("\r\n", 1, 2, out); } -void add_http_response_header(sbuf_t *out, Request *rq) { +void add_http_response_header(CxBuffer *out, Request *rq) { pblock *h = rq->srvhdrs; pb_entry *p; @@ -275,53 +275,60 @@ * make first char uppercase and write the remaining chars * unmodified to the buffer */ - sbuf_put(out, name[0]-32); + cxBufferPut(out, name[0]-32); if(name_len > 1) { - sbuf_write(out, name+1, name_len-1); + cxBufferWrite(name+1, 1, name_len-1, out); } } else { // first char is already uppercase so just write the name - sbuf_write(out, name, name_len); + cxBufferWrite(name, 1, name_len, out); } - sbuf_write(out, ": ", 2); - sbuf_write(out, value, strlen(value)); - sbuf_write(out, "\r\n", 2); + cxBufferWrite(": ", 1, 2, out); + cxBufferWrite(value, 1, strlen(value), out); + cxBufferWrite("\r\n", 1, 2, out); p = p->next; } } } -int http_start_response(Session *sn, Request *rq) { - Connection *conn = ((NSAPISession*)sn)->connection; +struct HttpResponseWriter { + Session *sn; + Request *rq; + CxBuffer buf; +}; +HttpResponseWriter *http_create_response(Session *sn, Request *rq) { + HttpResponseWriter *writer = pool_malloc(sn->pool, sizeof(HttpResponseWriter)); + if(!writer) { + return NULL; + } + if(cxBufferInit(&writer->buf, NULL, 512, pool_allocator(sn->pool), CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS)) { + pool_free(sn->pool, writer); + return NULL; + } + writer->sn = sn; + writer->rq = rq; + if(rq->status_num == -1) { protocol_status(sn, rq, 200, "OK"); } - - // set socket blocking - //int flags; - //flags = fcntl(fd, F_GETFL, 0); - //fcntl(fd, F_SETFL, flags ^ O_NONBLOCK); - - // output buffer - sbuf_t *out = sbuf_new(512); - + // add the http status line to the output buffer - add_http_status_line(out, sn->pool, rq); + add_http_status_line(&writer->buf, sn->pool, rq); // add date header struct tm mtms; struct tm *mtm = system_gmtime(&rq->req_start, &mtms); char date[HTTP_DATE_LEN + 1]; strftime(date, HTTP_DATE_LEN, HTTP_DATE_FMT, mtm); - sbuf_write(out, "Date: ", 6); - sbuf_write(out, date, strlen(date)); - sbuf_write(out, "\r\n", 2); + cxBufferWrite("Date: ", 1, 6, &writer->buf); + cxBufferWrite(date, 1, strlen(date), &writer->buf); + cxBufferWrite("\r\n", 1, 2, &writer->buf); // add server header - sbuf_write(out, "Server: webserver\r\n", 19); + cxBufferWrite("Server: webserver\r\n", 1, 19, &writer->buf); // check content length ans transfer encoding char *ctlen = pblock_findkeyval(pb_key_content_length, rq->srvhdrs); @@ -353,30 +360,66 @@ } // add header from rq->srvhdrs - add_http_response_header(out, rq); + add_http_response_header(&writer->buf, rq); // add connection header if(rq->rq_attr.keep_alive) { - sbuf_write(out, "Connection: keep-alive\r\n", 24); + cxBufferWrite("Connection: keep-alive\r\n", 1, 24, &writer->buf); pblock_kvinsert(pb_key_connection, "keep-alive", 10, rq->srvhdrs); } else { - sbuf_write(out, "Connection: close\r\n", 19); + cxBufferWrite("Connection: close\r\n", 1, 19, &writer->buf); pblock_kvinsert(pb_key_connection, "close", 5, rq->srvhdrs); } - // response header end - sbuf_write(out, "\r\n", 2); + cxBufferWrite("\r\n", 1, 2,& writer->buf); + + // reset pos (required for http_start_response_async) + writer->buf.pos = 0; + + return writer; +} +int http_send_response(HttpResponseWriter *writer) { + Connection *conn = ((NSAPISession*)writer->sn)->connection; + CxBuffer *buf = &writer->buf; + // flush buffer to the socket - conn->write(conn, out->ptr, out->length); - sbuf_free(out); + while(buf->pos < buf->size) { + int w = conn->write(conn, buf->space + buf->pos, buf->size - buf->pos); + if(w <= 0) { + if(conn->ssl) { + if(conn->ssl_error == SSL_ERROR_WANT_WRITE) { + return 1; + } + } else { + if(errno == EWOULDBLOCK) { + return 1; + } + } + return -1; + } + buf->pos += w; + } - rq->senthdrs = 1; + writer->rq->senthdrs = 1; + + cxBufferDestroy(buf); + pool_free(writer->sn->pool, writer); return 0; } + +int http_start_response(Session *sn, Request *rq) { + HttpResponseWriter *writer = http_create_response(sn, rq); + if(!writer) { + return 1; + } + + return http_send_response(writer); +} + int http_send_continue(Session *sn) { NSAPISession *s = (NSAPISession*)sn; cxstring msg = CX_STR("HTTP/1.1 100 Continue\r\n\r\n");
--- a/src/server/daemon/protocol.h Sun Nov 13 09:41:07 2022 +0100 +++ b/src/server/daemon/protocol.h Sun Nov 13 10:57:38 2022 +0100 @@ -33,6 +33,7 @@ #include "../util/io.h" #include <sys/uio.h> #include "../util/strbuf.h" +#include <cx/buffer.h> #ifdef __cplusplus extern "C" { @@ -41,8 +42,8 @@ void protocol_status(Session *sn, Request *rq, int n, const char *m); const char* protocol_status_message(int code); -void add_http_status_line(sbuf_t *out, pool_handle_t *pool, Request *rq); -void add_http_response_header(sbuf_t *out, Request *rq); +void add_http_status_line(CxBuffer *out, pool_handle_t *pool, Request *rq); +void add_http_response_header(CxBuffer *out, Request *rq); int http_start_response(Session *sn, Request *rq);
--- a/src/server/public/nsapi.h Sun Nov 13 09:41:07 2022 +0100 +++ b/src/server/public/nsapi.h Sun Nov 13 10:57:38 2022 +0100 @@ -700,6 +700,9 @@ typedef struct ConfigNode WSConfigNode; typedef enum WSConfigNodeType WSConfigNodeType; +// new +typedef struct HttpResponseWriter HttpResponseWriter; + #ifndef PR_AF_INET typedef union PRNetAddr PRNetAddr; #endif @@ -1511,6 +1514,16 @@ int http_start_response(Session *sn, Request *rq); #define protocol_start_response http_start_response + +HttpResponseWriter *http_create_response(Session *sn, Request *rq); + +/* + * return: -1: error + * 0: finished + * 1: EWOULDBLOCK + */ +int http_send_response(HttpResponseWriter *writer); + int request_header(char *name, char **value, Session *sn, Request *rq); char *http_uri2url(const char *prefix, const char *suffix);