mpd/src/inputStream_http.c
Eric Wong 90847fc881 Replace strdup and {c,re,m}alloc with x* variants to check for OOM errors
I'm checking for zero-size allocations and assert()-ing them,
so we can more easily get backtraces and debug problems, but we'll
also allow -DNDEBUG people to live on the edge if they wish.

We do not rely on errno when checking for OOM errors because
some implementations of malloc do not set it, and malloc
is commonly overridden by userspace wrappers.

I've spent some time looking through the source and didn't find any
obvious places where we would explicitly allocate 0 bytes, so we
shouldn't trip any of those assertions.

We also avoid allocating zero bytes because C libraries don't
handle this consistently (some return NULL, some not); and it's
dangerous either way.

git-svn-id: https://svn.musicpd.org/mpd/trunk@4690 09075e82-0dd4-0310-85a5-a0d7c8717e4f
2006-08-26 06:25:57 +00:00

912 lines
21 KiB
C

/* the Music Player Daemon (MPD)
* (c)2003-2006 by Warren Dukes (warren.dukes@gmail.com)
* This project's homepage is: http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "inputStream_http.h"
#include "utils.h"
#include "log.h"
#include "conf.h"
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#define HTTP_CONN_STATE_CLOSED 0
#define HTTP_CONN_STATE_INIT 1
#define HTTP_CONN_STATE_HELLO 2
#define HTTP_CONN_STATE_OPEN 3
#define HTTP_CONN_STATE_REOPEN 4
#define HTTP_BUFFER_SIZE_DEFAULT 131072
#define HTTP_PREBUFFER_SIZE_DEFAULT (HTTP_BUFFER_SIZE_DEFAULT >> 2)
#define HTTP_REDIRECT_MAX 10
static char *proxyHost = NULL;
static char *proxyPort = NULL;
static char *proxyUser = NULL;
static char *proxyPassword = NULL;
static int bufferSize = HTTP_BUFFER_SIZE_DEFAULT;
static int prebufferSize = HTTP_PREBUFFER_SIZE_DEFAULT;
typedef struct _InputStreemHTTPData {
char *host;
char *path;
char *port;
int sock;
int connState;
char *buffer;
size_t buflen;
int timesRedirected;
int icyMetaint;
int prebuffer;
int icyOffset;
char *proxyAuth;
char *httpAuth;
} InputStreamHTTPData;
void inputStream_initHttp(void)
{
ConfigParam *param = getConfigParam(CONF_HTTP_PROXY_HOST);
char *test;
if (param) {
proxyHost = param->value;
param = getConfigParam(CONF_HTTP_PROXY_PORT);
if (!param) {
ERROR("%s specified but not %s", CONF_HTTP_PROXY_HOST,
CONF_HTTP_PROXY_PORT);
exit(EXIT_FAILURE);
}
proxyPort = param->value;
param = getConfigParam(CONF_HTTP_PROXY_USER);
if (param) {
proxyUser = param->value;
param = getConfigParam(CONF_HTTP_PROXY_PASSWORD);
if (!param) {
ERROR("%s specified but not %s\n",
CONF_HTTP_PROXY_USER,
CONF_HTTP_PROXY_PASSWORD);
exit(EXIT_FAILURE);
}
proxyPassword = param->value;
}
param = getConfigParam(CONF_HTTP_PROXY_PASSWORD);
if (param) {
ERROR("%s specified but not %s\n",
CONF_HTTP_PROXY_PASSWORD, CONF_HTTP_PROXY_USER);
exit(EXIT_FAILURE);
}
} else if ((param = getConfigParam(CONF_HTTP_PROXY_PORT))) {
ERROR("%s specified but not %s, line %i\n",
CONF_HTTP_PROXY_PORT, CONF_HTTP_PROXY_HOST, param->line);
exit(EXIT_FAILURE);
} else if ((param = getConfigParam(CONF_HTTP_PROXY_USER))) {
ERROR("%s specified but not %s, line %i\n",
CONF_HTTP_PROXY_USER, CONF_HTTP_PROXY_HOST, param->line);
exit(EXIT_FAILURE);
} else if ((param = getConfigParam(CONF_HTTP_PROXY_PASSWORD))) {
ERROR("%s specified but not %s, line %i\n",
CONF_HTTP_PROXY_PASSWORD, CONF_HTTP_PROXY_HOST,
param->line);
exit(EXIT_FAILURE);
}
param = getConfigParam(CONF_HTTP_BUFFER_SIZE);
if (param) {
bufferSize = strtol(param->value, &test, 10);
if (bufferSize <= 0 || *test != '\0') {
ERROR("\"%s\" specified for %s at line %i is not a "
"positive integer\n",
param->value, CONF_HTTP_BUFFER_SIZE, param->line);
exit(EXIT_FAILURE);
}
bufferSize *= 1024;
if (prebufferSize > bufferSize)
prebufferSize = bufferSize;
}
param = getConfigParam(CONF_HTTP_PREBUFFER_SIZE);
if (param) {
prebufferSize = strtol(param->value, &test, 10);
if (prebufferSize <= 0 || *test != '\0') {
ERROR("\"%s\" specified for %s at line %i is not a "
"positive integer\n",
param->value, CONF_HTTP_PREBUFFER_SIZE,
param->line);
exit(EXIT_FAILURE);
}
prebufferSize *= 1024;
}
if (prebufferSize > bufferSize)
prebufferSize = bufferSize;
}
/* base64 code taken from xmms */
#define BASE64_LENGTH(len) (4 * (((len) + 2) / 3))
static char *base64Dup(char *s)
{
int i;
int len = strlen(s);
char *ret = xcalloc(BASE64_LENGTH(len) + 1, 1);
unsigned char *p = (unsigned char *)ret;
char tbl[64] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/'
};
/* Transform the 3x8 bits to 4x6 bits, as required by base64. */
for (i = 0; i < len; i += 3) {
*p++ = tbl[s[0] >> 2];
*p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)];
*p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)];
*p++ = tbl[s[2] & 0x3f];
s += 3;
}
/* Pad the result if necessary... */
if (i == len + 1)
*(p - 1) = '=';
else if (i == len + 2)
*(p - 1) = *(p - 2) = '=';
/* ...and zero-terminate it. */
*p = '\0';
return ret;
}
static char *authString(char *header, char *user, char *password)
{
char *ret = NULL;
int templen;
char *temp;
char *temp64;
if (!user || !password)
return NULL;
templen = strlen(user) + strlen(password) + 2;
temp = xmalloc(templen);
strcpy(temp, user);
strcat(temp, ":");
strcat(temp, password);
temp64 = base64Dup(temp);
free(temp);
ret = xmalloc(strlen(temp64) + strlen(header) + 3);
strcpy(ret, header);
strcat(ret, temp64);
strcat(ret, "\r\n");
free(temp64);
return ret;
}
#define PROXY_AUTH_HEADER "Proxy-Authorization: Basic "
#define HTTP_AUTH_HEADER "Authorization: Basic "
#define proxyAuthString(x, y) authString(PROXY_AUTH_HEADER, x, y)
#define httpAuthString(x, y) authString(HTTP_AUTH_HEADER, x, y)
static InputStreamHTTPData *newInputStreamHTTPData(void)
{
InputStreamHTTPData *ret = xmalloc(sizeof(InputStreamHTTPData));
if (proxyHost) {
ret->proxyAuth = proxyAuthString(proxyUser, proxyPassword);
} else
ret->proxyAuth = NULL;
ret->httpAuth = NULL;
ret->host = NULL;
ret->path = NULL;
ret->port = NULL;
ret->connState = HTTP_CONN_STATE_CLOSED;
ret->timesRedirected = 0;
ret->icyMetaint = 0;
ret->prebuffer = 0;
ret->icyOffset = 0;
ret->buffer = xmalloc(bufferSize);
return ret;
}
static void freeInputStreamHTTPData(InputStreamHTTPData * data)
{
if (data->host)
free(data->host);
if (data->path)
free(data->path);
if (data->port)
free(data->port);
if (data->proxyAuth)
free(data->proxyAuth);
if (data->httpAuth)
free(data->httpAuth);
free(data->buffer);
free(data);
}
static int parseUrl(InputStreamHTTPData * data, char *url)
{
char *temp;
char *colon;
char *slash;
char *at;
int len;
if (strncmp("http://", url, strlen("http://")) != 0)
return -1;
temp = url + strlen("http://");
colon = strchr(temp, ':');
at = strchr(temp, '@');
if (data->httpAuth) {
free(data->httpAuth);
data->httpAuth = NULL;
}
if (at) {
char *user;
char *passwd;
if (colon && colon < at) {
user = xmalloc(colon - temp + 1);
memcpy(user, temp, colon - temp);
user[colon - temp] = '\0';
passwd = xmalloc(at - colon);
memcpy(passwd, colon + 1, at - colon - 1);
passwd[at - colon - 1] = '\0';
} else {
user = xmalloc(at - temp + 1);
memcpy(user, temp, at - temp);
user[at - temp] = '\0';
passwd = xstrdup("");
}
data->httpAuth = httpAuthString(user, passwd);
free(user);
free(passwd);
temp = at + 1;
colon = strchr(temp, ':');
}
slash = strchr(temp, '/');
if (slash && colon && slash <= colon)
return -1;
/* fetch the host portion */
if (colon)
len = colon - temp + 1;
else if (slash)
len = slash - temp + 1;
else
len = strlen(temp) + 1;
if (len <= 1)
return -1;
data->host = xmalloc(len);
memcpy(data->host, temp, len - 1);
data->host[len - 1] = '\0';
/* fetch the port */
if (colon && (!slash || slash != colon + 1)) {
len = strlen(colon) - 1;
if (slash)
len -= strlen(slash);
data->port = xmalloc(len + 1);
memcpy(data->port, colon + 1, len);
data->port[len] = '\0';
DEBUG(__FILE__ ": Port: %s\n", data->port);
} else {
data->port = xstrdup("80");
}
/* fetch the path */
if (proxyHost)
data->path = xstrdup(url);
else
data->path = xstrdup(slash ? slash : "/");
return 0;
}
static int initHTTPConnection(InputStream * inStream)
{
char *connHost;
char *connPort;
struct addrinfo *ans = NULL;
struct addrinfo *ap = NULL;
struct addrinfo hints;
int error, flags;
InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
/**
* Setup hints
*/
hints.ai_flags = 0;
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_addrlen = 0;
hints.ai_addr = NULL;
hints.ai_canonname = NULL;
hints.ai_next = NULL;
if (proxyHost) {
connHost = proxyHost;
connPort = proxyPort;
} else {
connHost = data->host;
connPort = data->port;
}
error = getaddrinfo(connHost, connPort, &hints, &ans);
if (error) {
DEBUG(__FILE__ ": Error getting address info: %s\n",
gai_strerror(error));
return -1;
}
/* loop through possible addresses */
for (ap = ans; ap != NULL; ap = ap->ai_next) {
if ((data->sock = socket(ap->ai_family, ap->ai_socktype,
ap->ai_protocol)) < 0) {
DEBUG(__FILE__ ": unable to connect: %s\n",
strerror(errno));
freeaddrinfo(ans);
return -1;
}
flags = fcntl(data->sock, F_GETFL, 0);
fcntl(data->sock, F_SETFL, flags | O_NONBLOCK);
if (connect(data->sock, ap->ai_addr, ap->ai_addrlen) >= 0
|| errno == EINPROGRESS) {
data->connState = HTTP_CONN_STATE_INIT;
data->buflen = 0;
freeaddrinfo(ans);
return 0; /* success */
}
/* failed, get the next one */
DEBUG(__FILE__ ": unable to connect: %s\n", strerror(errno));
close(data->sock);
}
freeaddrinfo(ans);
return -1; /* failed */
}
static int finishHTTPInit(InputStream * inStream)
{
InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
struct timeval tv;
fd_set writeSet;
fd_set errorSet;
int error;
socklen_t error_len = sizeof(int);
int ret;
char request[2049];
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&writeSet);
FD_ZERO(&errorSet);
FD_SET(data->sock, &writeSet);
FD_SET(data->sock, &errorSet);
ret = select(data->sock + 1, NULL, &writeSet, &errorSet, &tv);
if (ret == 0 || (ret < 0 && errno == EINTR))
return 0;
if (ret < 0) {
DEBUG(__FILE__ ": problem select'ing: %s\n", strerror(errno));
close(data->sock);
data->connState = HTTP_CONN_STATE_CLOSED;
return -1;
}
getsockopt(data->sock, SOL_SOCKET, SO_ERROR, &error, &error_len);
if (error) {
close(data->sock);
data->connState = HTTP_CONN_STATE_CLOSED;
return -1;
}
memset(request, 0, 2049);
/* deal with ICY metadata later, for now its fucking up stuff! */
snprintf(request, 2048, "GET %s HTTP/1.0\r\n" "Host: %s\r\n"
/*"Connection: close\r\n" */
"User-Agent: %s/%s\r\n"
/*"Range: bytes=%ld-\r\n" */
"%s" /* authorization */
"Icy-Metadata:1\r\n"
"\r\n", data->path, data->host, PACKAGE_NAME, PACKAGE_VERSION,
/*inStream->offset, */
data->proxyAuth ? data->proxyAuth :
(data->httpAuth ? data->httpAuth : "")
);
ret = write(data->sock, request, strlen(request));
if (ret != strlen(request)) {
close(data->sock);
data->connState = HTTP_CONN_STATE_CLOSED;
return -1;
}
data->connState = HTTP_CONN_STATE_HELLO;
return 0;
}
static int getHTTPHello(InputStream * inStream)
{
InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
fd_set readSet;
struct timeval tv;
int ret;
char *needle;
char *cur = data->buffer;
int rc;
long readed;
FD_ZERO(&readSet);
FD_SET(data->sock, &readSet);
tv.tv_sec = 0;
tv.tv_usec = 0;
ret = select(data->sock + 1, &readSet, NULL, NULL, &tv);
if (ret == 0 || (ret < 0 && errno == EINTR))
return 0;
if (ret < 0) {
data->connState = HTTP_CONN_STATE_CLOSED;
close(data->sock);
data->buflen = 0;
return -1;
}
if (data->buflen >= bufferSize - 1) {
data->connState = HTTP_CONN_STATE_CLOSED;
close(data->sock);
return -1;
}
readed = recv(data->sock, data->buffer + data->buflen,
bufferSize - 1 - data->buflen, 0);
if (readed < 0 && (errno == EAGAIN || errno == EINTR))
return 0;
if (readed <= 0) {
data->connState = HTTP_CONN_STATE_CLOSED;
close(data->sock);
data->buflen = 0;
return -1;
}
data->buffer[data->buflen + readed] = '\0';
data->buflen += readed;
needle = strstr(data->buffer, "\r\n\r\n");
if (!needle)
return 0;
if (0 == strncmp(cur, "HTTP/1.0 ", 9)) {
inStream->seekable = 0;
rc = atoi(cur + 9);
} else if (0 == strncmp(cur, "HTTP/1.1 ", 9)) {
inStream->seekable = 1;
rc = atoi(cur + 9);
} else if (0 == strncmp(cur, "ICY 200 OK", 10)) {
inStream->seekable = 0;
rc = 200;
} else if (0 == strncmp(cur, "ICY 400 Server Full", 19))
rc = 400;
else if (0 == strncmp(cur, "ICY 404", 7))
rc = 404;
else {
close(data->sock);
data->connState = HTTP_CONN_STATE_CLOSED;
return -1;
}
switch (rc) {
case 200:
case 206:
break;
case 301:
case 302:
cur = strstr(cur, "Location: ");
if (cur) {
char *url;
int curlen = 0;
cur += strlen("Location: ");
while (*(cur + curlen) != '\0'
&& *(cur + curlen) != '\r') {
curlen++;
}
url = xmalloc(curlen + 1);
memcpy(url, cur, curlen);
url[curlen] = '\0';
ret = parseUrl(data, url);
free(url);
if (ret == 0 && data->timesRedirected <
HTTP_REDIRECT_MAX) {
data->timesRedirected++;
close(data->sock);
data->connState = HTTP_CONN_STATE_REOPEN;
data->buflen = 0;
return 0;
}
}
case 400:
case 401:
case 403:
case 404:
default:
close(data->sock);
data->connState = HTTP_CONN_STATE_CLOSED;
data->buflen = 0;
return -1;
}
cur = strstr(data->buffer, "\r\n");
while (cur && cur != needle) {
if (0 == strncmp(cur, "\r\nContent-Length: ", 18)) {
if (!inStream->size)
inStream->size = atol(cur + 18);
} else if (0 == strncmp(cur, "\r\nicy-metaint:", 14)) {
data->icyMetaint = atoi(cur + 14);
} else if (0 == strncmp(cur, "\r\nicy-name:", 11) ||
0 == strncmp(cur, "\r\nice-name:", 11)) {
int incr = 11;
char *temp = strstr(cur + incr, "\r\n");
if (!temp)
break;
*temp = '\0';
if (inStream->metaName)
free(inStream->metaName);
while (*(incr + cur) == ' ')
incr++;
inStream->metaName = xstrdup(cur + incr);
*temp = '\r';
DEBUG("inputStream_http: metaName: %s\n",
inStream->metaName);
} else if (0 == strncmp(cur, "\r\nx-audiocast-name:", 19)) {
int incr = 19;
char *temp = strstr(cur + incr, "\r\n");
if (!temp)
break;
*temp = '\0';
if (inStream->metaName)
free(inStream->metaName);
while (*(incr + cur) == ' ')
incr++;
inStream->metaName = xstrdup(cur + incr);
*temp = '\r';
DEBUG("inputStream_http: metaName: %s\n",
inStream->metaName);
} else if (0 == strncmp(cur, "\r\nContent-Type:", 15)) {
int incr = 15;
char *temp = strstr(cur + incr, "\r\n");
if (!temp)
break;
*temp = '\0';
if (inStream->mime)
free(inStream->mime);
while (*(incr + cur) == ' ')
incr++;
inStream->mime = xstrdup(cur + incr);
*temp = '\r';
}
cur = strstr(cur + 2, "\r\n");
}
if (inStream->size <= 0)
inStream->seekable = 0;
needle += 4; /* 4 == strlen("\r\n\r\n") */
data->buflen -= (needle - data->buffer);
/*fwrite(data->buffer, 1, data->buflen, stdout); */
memmove(data->buffer, needle, data->buflen);
data->connState = HTTP_CONN_STATE_OPEN;
data->prebuffer = 1;
/*mark as unseekable till we actually implement seeking */
inStream->seekable = 0;
return 0;
}
int inputStream_httpOpen(InputStream * inStream, char *url)
{
InputStreamHTTPData *data = newInputStreamHTTPData();
inStream->data = data;
if (parseUrl(data, url) < 0) {
freeInputStreamHTTPData(data);
return -1;
}
if (initHTTPConnection(inStream) < 0) {
freeInputStreamHTTPData(data);
return -1;
}
inStream->seekFunc = inputStream_httpSeek;
inStream->closeFunc = inputStream_httpClose;
inStream->readFunc = inputStream_httpRead;
inStream->atEOFFunc = inputStream_httpAtEOF;
inStream->bufferFunc = inputStream_httpBuffer;
return 0;
}
int inputStream_httpSeek(InputStream * inStream, long offset, int whence)
{
/* hack to reopen an HTTP stream if we're trying to seek to
* the beginning */
if ((whence == SEEK_SET) && (offset == 0)) {
InputStreamHTTPData *data;
data = (InputStreamHTTPData *) inStream->data;
close(data->sock);
data->connState = HTTP_CONN_STATE_REOPEN;
data->buflen = 0;
inStream->offset = 0;
return 0;
}
/* otherwise, we don't know how to seek in HTTP yet */
return -1;
}
static void parseIcyMetadata(InputStream * inStream, char *metadata, int size)
{
char *r;
char *s;
char *temp = xmalloc(size + 1);
memcpy(temp, metadata, size);
temp[size] = '\0';
s = strtok_r(temp, ";", &r);
while (s) {
if (0 == strncmp(s, "StreamTitle=", 12)) {
int cur = 12;
if (inStream->metaTitle)
free(inStream->metaTitle);
if (*(s + cur) == '\'')
cur++;
if (s[strlen(s) - 1] == '\'') {
s[strlen(s) - 1] = '\0';
}
inStream->metaTitle = xstrdup(s + cur);
DEBUG("inputStream_http: metaTitle: %s\n",
inStream->metaTitle);
}
s = strtok_r(NULL, ";", &r);
}
free(temp);
}
size_t inputStream_httpRead(InputStream * inStream, void *ptr, size_t size,
size_t nmemb)
{
InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
long tosend = 0;
long inlen = size * nmemb;
long maxToSend = data->buflen;
inputStream_httpBuffer(inStream);
switch (data->connState) {
case HTTP_CONN_STATE_OPEN:
if (data->prebuffer || data->buflen < data->icyMetaint)
return 0;
break;
case HTTP_CONN_STATE_CLOSED:
if (data->buflen)
break;
default:
return 0;
}
if (data->icyMetaint > 0) {
if (data->icyOffset >= data->icyMetaint) {
int metalen = *(data->buffer);
metalen <<= 4;
if (metalen < 0)
metalen = 0;
if (metalen + 1 > data->buflen) {
/* damn that's some fucking big metadata! */
if (bufferSize < metalen + 1) {
data->connState =
HTTP_CONN_STATE_CLOSED;
close(data->sock);
data->buflen = 0;
}
return 0;
}
if (metalen > 0) {
parseIcyMetadata(inStream, data->buffer + 1,
metalen);
}
data->buflen -= metalen + 1;
memmove(data->buffer, data->buffer + metalen + 1,
data->buflen);
data->icyOffset = 0;
}
maxToSend = data->icyMetaint - data->icyOffset;
maxToSend = maxToSend > data->buflen ? data->buflen : maxToSend;
}
if (data->buflen > 0) {
tosend = inlen > maxToSend ? maxToSend : inlen;
tosend = (tosend / size) * size;
memcpy(ptr, data->buffer, tosend);
/*fwrite(ptr,1,readed,stdout); */
data->buflen -= tosend;
data->icyOffset += tosend;
/*fwrite(data->buffer,1,readed,stdout); */
memmove(data->buffer, data->buffer + tosend, data->buflen);
inStream->offset += tosend;
}
return tosend / size;
}
int inputStream_httpClose(InputStream * inStream)
{
InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
switch (data->connState) {
case HTTP_CONN_STATE_CLOSED:
break;
default:
close(data->sock);
}
freeInputStreamHTTPData(data);
return 0;
}
int inputStream_httpAtEOF(InputStream * inStream)
{
InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
switch (data->connState) {
case HTTP_CONN_STATE_CLOSED:
if (data->buflen == 0)
return 1;
default:
return 0;
}
}
int inputStream_httpBuffer(InputStream * inStream)
{
InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
ssize_t readed = 0;
if (data->connState == HTTP_CONN_STATE_REOPEN) {
if (initHTTPConnection(inStream) < 0)
return -1;
}
if (data->connState == HTTP_CONN_STATE_INIT) {
if (finishHTTPInit(inStream) < 0)
return -1;
}
if (data->connState == HTTP_CONN_STATE_HELLO) {
if (getHTTPHello(inStream) < 0)
return -1;
}
switch (data->connState) {
case HTTP_CONN_STATE_OPEN:
case HTTP_CONN_STATE_CLOSED:
break;
default:
return -1;
}
if (data->buflen == 0 || data->buflen < data->icyMetaint) {
data->prebuffer = 1;
} else if (data->buflen > prebufferSize)
data->prebuffer = 0;
if (data->connState == HTTP_CONN_STATE_OPEN &&
data->buflen < bufferSize - 1) {
readed = read(data->sock, data->buffer + data->buflen,
(size_t) (bufferSize - 1 - data->buflen));
if (readed < 0 && (errno == EAGAIN || errno == EINTR)) {
readed = 0;
} else if (readed <= 0) {
close(data->sock);
data->connState = HTTP_CONN_STATE_CLOSED;
readed = 0;
}
/*fwrite(data->buffer+data->buflen,1,readed,stdout); */
data->buflen += readed;
}
if (data->buflen > prebufferSize)
data->prebuffer = 0;
return (readed ? 1 : 0);
}