summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars-Dominik Braun <lars@6xq.net>2011-09-19 17:29:09 +0200
committerLars-Dominik Braun <lars@6xq.net>2011-11-09 20:10:03 +0100
commitcfaeccb1e8a62ef0a5f7af7d3496a3bfc5ca381e (patch)
tree89004114da0b676742b7d2730d9feedea87a3b5d
parentffb0ab45fb142fbad732562b787918ba503f0643 (diff)
downloadpianobar-cfaeccb1e8a62ef0a5f7af7d3496a3bfc5ca381e.tar.gz
pianobar-cfaeccb1e8a62ef0a5f7af7d3496a3bfc5ca381e.tar.bz2
pianobar-cfaeccb1e8a62ef0a5f7af7d3496a3bfc5ca381e.zip
waitress: support chunked encoding
as requested by rfc 2616
-rw-r--r--src/libwaitress/waitress.c124
-rw-r--r--src/libwaitress/waitress.h10
-rw-r--r--src/player.c2
3 files changed, 96 insertions, 40 deletions
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);