From cfaeccb1e8a62ef0a5f7af7d3496a3bfc5ca381e Mon Sep 17 00:00:00 2001 From: Lars-Dominik Braun Date: Mon, 19 Sep 2011 17:29:09 +0200 Subject: waitress: support chunked encoding as requested by rfc 2616 --- src/libwaitress/waitress.c | 124 +++++++++++++++++++++++++++++++-------------- src/libwaitress/waitress.h | 10 +++- src/player.c | 2 +- 3 files changed, 96 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/src/libwaitress/waitress.c b/src/libwaitress/waitress.c index 922cb91..9e56a5a 100644 --- a/src/libwaitress/waitress.c +++ b/src/libwaitress/waitress.c @@ -487,34 +487,6 @@ static const char *WaitressDefaultPort (WaitressUrl_t *url) { return url->port == NULL ? "80" : url->port; } -/* handle http header - */ -static void WaitressHandleHeader (WaitressHandle_t *waith, const char * const key, - const char * const value) { - if (streq (key, "Content-Length")) { - waith->contentLength = atol (value); - } -} - -/* identity encoding handler - */ -static WaitressCbReturn_t WaitressHandleIdentity (WaitressHandle_t *waith, - char *buf, size_t size) { - waith->contentReceived += size; - return waith->callback (buf, size, waith->data); -} - -/* parse http status line and return status code - */ -static int WaitressParseStatusline (const char * const line) { - char status[4] = "000"; - - if (sscanf (line, "HTTP/1.%*1[0-9] %3[0-9] ", status) == 1) { - return atoi (status); - } - return -1; -} - /* get line from string * @param string beginning/return value of last call * @return start of _next_ line or NULL if there is no next line @@ -538,6 +510,79 @@ static char *WaitressGetline (char * const str) { return eol; } +/* identity encoding handler + */ +static WaitressCbReturn_t WaitressHandleIdentity (WaitressHandle_t *waith, + char *buf, size_t size) { + waith->request.contentReceived += size; + return waith->callback (buf, size, waith->data); +} + +/* chunked encoding handler. buf must be \0-terminated, size does not include + * trailing \0. + */ +static WaitressCbReturn_t WaitressHandleChunked (WaitressHandle_t *waith, + char *buf, size_t size) { + char *content = buf, *nextContent; + + while (1) { + if (waith->request.chunkSize > 0) { + size_t remaining = size-(content-buf); + + if (remaining >= waith->request.chunkSize) { + WaitressHandleIdentity (waith, content, waith->request.chunkSize); + /* FIXME: skip trailing \r\n */ + content += waith->request.chunkSize+2; + waith->request.chunkSize = 0; + } else { + WaitressHandleIdentity (waith, content, remaining); + waith->request.chunkSize -= remaining; + return WAITRESS_CB_RET_OK; + } + } + + if ((nextContent = WaitressGetline (content)) != NULL) { + long int chunkSize = strtol (content, NULL, 16); + if (chunkSize == 0) { + return WAITRESS_CB_RET_OK; + } else if (chunkSize < 0) { + return WAITRESS_CB_RET_ERR; + } else { + waith->request.chunkSize = chunkSize; + content = nextContent; + } + } else { + return WAITRESS_CB_RET_OK; + } + } + + return WAITRESS_CB_RET_OK; +} + +/* handle http header + */ +static void WaitressHandleHeader (WaitressHandle_t *waith, const char * const key, + const char * const value) { + if (streq (key, "Content-Length")) { + waith->request.contentLength = atol (value); + } else if (streq (key, "Transfer-Encoding")) { + if (streq (value, "chunked")) { + waith->request.dataHandler = WaitressHandleChunked; + } + } +} + +/* parse http status line and return status code + */ +static int WaitressParseStatusline (const char * const line) { + char status[4] = "000"; + + if (sscanf (line, "HTTP/1.%*1[0-9] %3[0-9] ", status) == 1) { + return atoi (status); + } + return -1; +} + /* Receive data from host and call *callback () * @param waitress handle * @return WaitressReturn_t @@ -569,8 +614,8 @@ WaitressReturn_t WaitressFetchCall (WaitressHandle_t *waith) { unsigned int bufFilled = 0; /* initialize */ - waith->contentLength = 0; - waith->contentReceived = 0; + memset (&waith->request, 0, sizeof (waith->request)); + waith->request.dataHandler = WaitressHandleIdentity; memset (&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; @@ -646,7 +691,8 @@ WaitressReturn_t WaitressFetchCall (WaitressHandle_t *waith) { WRITE_RET (buf, strlen (buf)); snprintf (buf, WAITRESS_BUFFER_SIZE, - "Host: %s\r\nUser-Agent: " PACKAGE "\r\n", waith->url.host); + "Host: %s\r\nUser-Agent: " PACKAGE "\r\nConnection: Close\r\n", + waith->url.host); WRITE_RET (buf, strlen (buf)); if (waith->method == WAITRESS_METHOD_POST && waith->postData != NULL) { @@ -688,8 +734,8 @@ WaitressReturn_t WaitressFetchCall (WaitressHandle_t *waith) { thisLine = buf; /* split */ - while ((nextLine = WaitressGetline (thisLine)) != NULL && - hdrParseMode != HDRM_FINISHED) { + while (hdrParseMode != HDRM_FINISHED && + (nextLine = WaitressGetline (thisLine)) != NULL) { switch (hdrParseMode) { /* Status code */ case HDRM_HEAD: @@ -748,7 +794,9 @@ WaitressReturn_t WaitressFetchCall (WaitressHandle_t *waith) { /* push remaining bytes */ if (bufFilled > 0) { - if (WaitressHandleIdentity (waith, buf, bufFilled) == + /* data must be \0-terminated for chunked handler */ + buf[bufFilled] = '\0'; + if (waith->request.dataHandler (waith, buf, bufFilled) == WAITRESS_CB_RET_ERR) { FINISH (WAITRESS_RET_CB_ABORT); } @@ -756,9 +804,10 @@ WaitressReturn_t WaitressFetchCall (WaitressHandle_t *waith) { /* receive content */ do { - READ_RET (buf, WAITRESS_BUFFER_SIZE, &recvSize); + READ_RET (buf, WAITRESS_BUFFER_SIZE-1, &recvSize); + buf[recvSize] = '\0'; if (recvSize > 0) { - if (WaitressHandleIdentity (waith, buf, recvSize) == + if (waith->request.dataHandler (waith, buf, recvSize) == WAITRESS_CB_RET_ERR) { wRet = WAITRESS_RET_CB_ABORT; break; @@ -770,7 +819,8 @@ finish: close (sockfd); free (buf); - if (wRet == WAITRESS_RET_OK && waith->contentReceived < waith->contentLength) { + if (wRet == WAITRESS_RET_OK && + waith->request.contentReceived < waith->request.contentLength) { return WAITRESS_RET_PARTIAL_FILE; } return wRet; diff --git a/src/libwaitress/waitress.h b/src/libwaitress/waitress.h index 67c12d6..f3aff55 100644 --- a/src/libwaitress/waitress.h +++ b/src/libwaitress/waitress.h @@ -48,18 +48,24 @@ typedef struct { const char *path; /* without leading '/' */ } WaitressUrl_t; +/* reusable handle + */ typedef struct { WaitressUrl_t url; WaitressMethod_t method; const char *extraHeaders; const char *postData; - size_t contentLength; - size_t contentReceived; WaitressUrl_t proxy; /* extra data handed over to callback function */ void *data; WaitressCbReturn_t (*callback) (void *, size_t, void *); int socktimeout; + /* per-request data */ + struct { + size_t contentLength, contentReceived, chunkSize; + /* first argument is WaitressHandle_t, but that's not defined here */ + WaitressCbReturn_t (*dataHandler) (void *, char *, size_t); + } request; } WaitressHandle_t; typedef enum { diff --git a/src/player.c b/src/player.c index b45b40d..6cb8206 100644 --- a/src/player.c +++ b/src/player.c @@ -371,7 +371,7 @@ static WaitressCbReturn_t BarPlayerMp3Cb (void *ptr, size_t size, void *stream) } /* calc song length using the framerate of the first decoded frame */ - player->songDuration = (unsigned long long int) player->waith.contentLength / + player->songDuration = (unsigned long long int) player->waith.request.contentLength / ((unsigned long long int) player->mp3Frame.header.bitrate / (unsigned long long int) BAR_PLAYER_MS_TO_S_FACTOR / 8LL); -- cgit v1.2.3