(net_http.c) Cleanups

This commit is contained in:
twinaphex 2015-01-26 20:47:59 +01:00
parent d8fd5e14d0
commit e893f98787

View File

@ -33,8 +33,9 @@ enum
enum
{
t_full,
t_len, t_chunk
T_FULL = 0,
T_LEN,
T_CHUNK
};
static bool net_http_parse_url(char *url, char **domain,
@ -80,60 +81,60 @@ static int net_http_new_socket(const char * domain, int port)
#ifdef _WIN32
u_long mode = 1;
#else
struct timeval timeout;
struct timeval timeout;
#endif
struct addrinfo hints, *addr = NULL;
char portstr[16];
struct addrinfo hints, *addr = NULL;
char portstr[16];
snprintf(portstr, sizeof(portstr), "%i", port);
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = 0;
if (getaddrinfo_rarch(domain, portstr, &hints, &addr) < 0)
snprintf(portstr, sizeof(portstr), "%i", port);
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = 0;
if (getaddrinfo_rarch(domain, portstr, &hints, &addr) < 0)
return -1;
(void)i;
fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
#ifndef _WIN32
timeout.tv_sec=4;
timeout.tv_usec=0;
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof timeout);
timeout.tv_sec=4;
timeout.tv_usec=0;
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof timeout);
#endif
if (connect(fd, addr->ai_addr, addr->ai_addrlen) != 0)
{
freeaddrinfo_rarch(addr);
socket_close(fd);
return -1;
}
freeaddrinfo_rarch(addr);
if (!socket_nonblock(fd))
if (connect(fd, addr->ai_addr, addr->ai_addrlen) != 0)
{
socket_close(fd);
freeaddrinfo_rarch(addr);
socket_close(fd);
return -1;
}
return fd;
freeaddrinfo_rarch(addr);
if (!socket_nonblock(fd))
{
socket_close(fd);
return -1;
}
return fd;
}
static void net_http_send(int fd, bool * error,
const char * data, size_t len)
{
if (*error)
if (*error)
return;
while (len)
{
ssize_t thislen = send(fd, data, len, MSG_NOSIGNAL);
while (len)
{
ssize_t thislen = send(fd, data, len, MSG_NOSIGNAL);
if (thislen <= 0)
if (thislen <= 0)
{
if (!isagain(thislen))
continue;
@ -142,14 +143,14 @@ static void net_http_send(int fd, bool * error,
return;
}
data += thislen;
len -= thislen;
}
data += thislen;
len -= thislen;
}
}
static void net_http_send_str(int fd, bool *error, const char *text)
{
net_http_send(fd, error, text, strlen(text));
net_http_send(fd, error, text, strlen(text));
}
static ssize_t net_http_recv(int fd, bool *error,
@ -175,297 +176,295 @@ static ssize_t net_http_recv(int fd, bool *error,
http_t *net_http_new(const char * url)
{
bool error;
bool error;
char *domain = NULL, *location = NULL;
int port = 0, fd = -1;
http_t *state = NULL;
char *urlcopy =(char*)malloc(strlen(url)+1);
int port = 0, fd = -1;
http_t *state = NULL;
char *urlcopy =(char*)malloc(strlen(url)+1);
strcpy(urlcopy, url);
strcpy(urlcopy, url);
if (!net_http_parse_url(urlcopy, &domain, &port, &location))
if (!net_http_parse_url(urlcopy, &domain, &port, &location))
goto fail;
fd = net_http_new_socket(domain, port);
if (fd == -1)
fd = net_http_new_socket(domain, port);
if (fd == -1)
goto fail;
error=false;
/* This is a bit lazy, but it works. */
net_http_send_str(fd, &error, "GET /");
net_http_send_str(fd, &error, location);
net_http_send_str(fd, &error, " HTTP/1.1\r\n");
net_http_send_str(fd, &error, "Host: ");
net_http_send_str(fd, &error, domain);
if (port!=80)
{
char portstr[16];
error=false;
snprintf(portstr, sizeof(portstr), ":%i", port);
net_http_send_str(fd, &error, portstr);
}
/* This is a bit lazy, but it works. */
net_http_send_str(fd, &error, "GET /");
net_http_send_str(fd, &error, location);
net_http_send_str(fd, &error, " HTTP/1.1\r\n");
net_http_send_str(fd, &error, "\r\n");
net_http_send_str(fd, &error, "Connection: close\r\n");
net_http_send_str(fd, &error, "\r\n");
if (error)
net_http_send_str(fd, &error, "Host: ");
net_http_send_str(fd, &error, domain);
if (port!=80)
{
char portstr[16];
snprintf(portstr, sizeof(portstr), ":%i", port);
net_http_send_str(fd, &error, portstr);
}
net_http_send_str(fd, &error, "\r\n");
net_http_send_str(fd, &error, "Connection: close\r\n");
net_http_send_str(fd, &error, "\r\n");
if (error)
goto fail;
free(urlcopy);
state = (http_t*)malloc(sizeof(http_t));
state->fd = fd;
state->status = -1;
state->data = NULL;
state->part = p_header_top;
state->bodytype= t_full;
state->error = false;
state->pos = 0;
state->len = 0;
state->buflen = 512;
state->data = (char*)malloc(state->buflen);
return state;
free(urlcopy);
state = (http_t*)malloc(sizeof(http_t));
state->fd = fd;
state->status = -1;
state->data = NULL;
state->part = p_header_top;
state->bodytype= T_FULL;
state->error = false;
state->pos = 0;
state->len = 0;
state->buflen = 512;
state->data = (char*)malloc(state->buflen);
return state;
fail:
if (fd != -1)
if (fd != -1)
socket_close(fd);
free(urlcopy);
return NULL;
free(urlcopy);
return NULL;
}
int net_http_fd(http_t *state)
{
return state->fd;
return state->fd;
}
bool net_http_update(http_t *state, size_t* progress, size_t* total)
{
ssize_t newlen = 0;
if (state->error)
ssize_t newlen = 0;
if (state->error)
goto fail;
if (state->part < p_body)
{
newlen = net_http_recv(state->fd, &state->error,
if (state->part < p_body)
{
newlen = net_http_recv(state->fd, &state->error,
(uint8_t*)state->data + state->pos, state->buflen - state->pos);
if (newlen < 0)
if (newlen < 0)
goto fail;
if (state->pos + newlen >= state->buflen - 64)
{
state->buflen *= 2;
state->data = (char*)realloc(state->data, state->buflen);
}
state->pos += newlen;
if (state->pos + newlen >= state->buflen - 64)
{
state->buflen *= 2;
state->data = (char*)realloc(state->data, state->buflen);
}
state->pos += newlen;
while (state->part < p_body)
{
char *dataend = state->data + state->pos;
char *lineend = (char*)memchr(state->data, '\n', state->pos);
while (state->part < p_body)
{
char *dataend = state->data + state->pos;
char *lineend = (char*)memchr(state->data, '\n', state->pos);
if (!lineend)
if (!lineend)
break;
*lineend='\0';
if (lineend != state->data && lineend[-1]=='\r')
*lineend='\0';
if (lineend != state->data && lineend[-1]=='\r')
lineend[-1]='\0';
if (state->part == p_header_top)
{
if (strncmp(state->data, "HTTP/1.", strlen("HTTP/1."))!=0)
if (state->part == p_header_top)
{
if (strncmp(state->data, "HTTP/1.", strlen("HTTP/1."))!=0)
goto fail;
state->status=strtoul(state->data + strlen("HTTP/1.1 "), NULL, 10);
state->part = p_header;
}
else
{
if (!strncmp(state->data, "Content-Length: ",
state->status=strtoul(state->data + strlen("HTTP/1.1 "), NULL, 10);
state->part = p_header;
}
else
{
if (!strncmp(state->data, "Content-Length: ",
strlen("Content-Length: ")))
{
state->bodytype = t_len;
state->len = strtol(state->data +
{
state->bodytype = T_LEN;
state->len = strtol(state->data +
strlen("Content-Length: "), NULL, 10);
}
if (!strcmp(state->data, "Transfer-Encoding: chunked"))
{
state->bodytype=t_chunk;
}
}
if (!strcmp(state->data, "Transfer-Encoding: chunked"))
state->bodytype = T_CHUNK;
/* TODO: save headers somewhere */
if (state->data[0]=='\0')
{
state->part = p_body;
if (state->bodytype == t_chunk)
/* TODO: save headers somewhere */
if (state->data[0]=='\0')
{
state->part = p_body;
if (state->bodytype == T_CHUNK)
state->part = p_body_chunklen;
}
}
memmove(state->data, lineend + 1, dataend-(lineend+1));
state->pos = (dataend-(lineend + 1));
}
if (state->part >= p_body)
{
newlen = state->pos;
state->pos = 0;
}
}
}
}
if (state->part >= p_body && state->part < p_done)
{
if (!newlen)
{
newlen = net_http_recv(state->fd, &state->error,
memmove(state->data, lineend + 1, dataend-(lineend+1));
state->pos = (dataend-(lineend + 1));
}
if (state->part >= p_body)
{
newlen = state->pos;
state->pos = 0;
}
}
if (state->part >= p_body && state->part < p_done)
{
if (!newlen)
{
newlen = net_http_recv(state->fd, &state->error,
(uint8_t*)state->data + state->pos,
state->buflen - state->pos);
if (newlen < 0)
{
if (state->bodytype == t_full)
{
if (newlen < 0)
{
if (state->bodytype == T_FULL)
{
state->part = p_done;
state->data = (char*)realloc(state->data, state->len);
}
else
}
else
goto fail;
newlen=0;
}
newlen=0;
}
if (state->pos + newlen >= state->buflen - 64)
{
state->buflen *= 2;
state->data = (char*)realloc(state->data, state->buflen);
}
}
if (state->pos + newlen >= state->buflen - 64)
{
state->buflen *= 2;
state->data = (char*)realloc(state->data, state->buflen);
}
}
parse_again:
if (state->bodytype == t_chunk)
{
if (state->part == p_body_chunklen)
{
state->pos += newlen;
if (state->pos - state->len >= 2)
{
/*
if (state->bodytype == T_CHUNK)
{
if (state->part == p_body_chunklen)
{
state->pos += newlen;
if (state->pos - state->len >= 2)
{
/*
* len=start of chunk including \r\n
* pos=end of data
*/
char *fullend = state->data + state->pos;
char *end = (char*)memchr(state->data + state->len + 2,
char *fullend = state->data + state->pos;
char *end = (char*)memchr(state->data + state->len + 2,
'\n', state->pos - state->len - 2);
if (end)
{
size_t chunklen = strtoul(state->data+state->len, NULL, 16);
state->pos = state->len;
end++;
if (end)
{
size_t chunklen = strtoul(state->data+state->len, NULL, 16);
state->pos = state->len;
end++;
memmove(state->data+state->len, end, fullend-end);
memmove(state->data+state->len, end, fullend-end);
state->len = chunklen;
newlen = (fullend - end);
state->len = chunklen;
newlen = (fullend - end);
/*
len=num bytes
newlen=unparsed bytes after \n
pos=start of chunk including \r\n
*/
len=num bytes
newlen=unparsed bytes after \n
pos=start of chunk including \r\n
*/
state->part = p_body;
if (state->len == 0)
{
state->part = p_done;
state->len = state->pos;
state->data = (char*)realloc(state->data, state->len);
}
goto parse_again;
}
}
}
else if (state->part == p_body)
{
if ((size_t)newlen >= state->len)
{
state->pos += state->len;
newlen -= state->len;
state->len = state->pos;
state->part = p_body_chunklen;
goto parse_again;
}
else
{
state->pos += newlen;
state->len -= newlen;
}
}
}
else
{
state->pos += newlen;
state->part = p_body;
if (state->len == 0)
{
state->part = p_done;
state->len = state->pos;
state->data = (char*)realloc(state->data, state->len);
}
goto parse_again;
}
}
}
else if (state->part == p_body)
{
if ((size_t)newlen >= state->len)
{
state->pos += state->len;
newlen -= state->len;
state->len = state->pos;
state->part = p_body_chunklen;
goto parse_again;
}
else
{
state->pos += newlen;
state->len -= newlen;
}
}
}
else
{
state->pos += newlen;
if (state->pos == state->len)
{
if (state->pos == state->len)
{
state->part=p_done;
state->data = (char*)realloc(state->data, state->len);
}
if (state->pos > state->len)
}
if (state->pos > state->len)
goto fail;
}
}
if (progress)
}
}
if (progress)
*progress = state->pos;
if (total)
{
if (state->bodytype == t_len)
if (total)
{
if (state->bodytype == T_LEN)
*total=state->len;
else
else
*total=0;
}
return (state->part==p_done);
fail:
state->error = true;
state->part = p_error;
state->status = -1;
}
return true;
return (state->part==p_done);
fail:
state->error = true;
state->part = p_error;
state->status = -1;
return true;
}
int net_http_status(http_t *state)
{
return state->status;
return state->status;
}
uint8_t* net_http_data(http_t *state, size_t* len, bool accept_error)
{
if (!accept_error &&
if (!accept_error &&
(state->error || state->status<200 || state->status>299))
{
if (len)
{
if (len)
*len=0;
return NULL;
}
return NULL;
}
if (len)
if (len)
*len=state->len;
return (uint8_t*)state->data;
return (uint8_t*)state->data;
}
void net_http_delete(http_t *state)
{
if (state->fd != -1)
if (state->fd != -1)
socket_close(state->fd);
if (state->data)
if (state->data)
free(state->data);
free(state);
free(state);
}