(net_http.c) Cleanups

This commit is contained in:
twinaphex 2015-01-26 20:47:59 +01:00
parent d8fd5e14d0
commit e893f98787
1 changed files with 249 additions and 250 deletions

View File

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