bool net_http_update(struct http_t *state, size_t* progress, size_t* total) { ssize_t newlen = 0; if (!state || state->error) goto fail; if (state->part < P_BODY) { if (state->error) newlen = -1; else newlen = socket_receive_all_nonblocking(state->fd, &state->error, (uint8_t*)state->data + state->pos, state->buflen - state->pos); 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; while (state->part < P_BODY) { char *dataend = state->data + state->pos; char *lineend = (char*)memchr(state->data, '\n', state->pos); if (!lineend) break; *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) goto fail; 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 + strlen("Content-Length: "), NULL, 10); } 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) 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) { if (state->error) newlen = -1; else newlen = socket_receive_all_nonblocking( state->fd, &state->error, (uint8_t*)state->data + state->pos, state->buflen - state->pos); if (newlen < 0) { if (state->bodytype == T_FULL) { state->part = P_DONE; state->data = (char*)realloc(state->data, state->len); } else goto fail; newlen=0; } 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) { /* * 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, '\n', state->pos - state->len - 2); 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); state->len = chunklen; newlen = (fullend - end); /* 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; if (state->pos == state->len) { state->part = P_DONE; state->data = (char*)realloc(state->data, state->len); } if (state->pos > state->len) goto fail; } } if (progress) *progress = state->pos; if (total) { if (state->bodytype == T_LEN) *total=state->len; else *total=0; } return (state->part == P_DONE); fail: if (state) { state->error = true; state->part = P_ERROR; state->status = -1; } return true; }
/** * netplay_recv * * Receive buffered or fresh data. * * Returns number of bytes returned, which may be short or 0, or -1 on error. */ ssize_t netplay_recv(struct socket_buffer *sbuf, int sockfd, void *buf, size_t len, bool block) { bool error; ssize_t recvd; /* Receive whatever we can into the buffer */ if (sbuf->end >= sbuf->start) { error = false; recvd = socket_receive_all_nonblocking(sockfd, &error, sbuf->data + sbuf->end, sbuf->bufsz - sbuf->end - ((sbuf->start == 0) ? 1 : 0)); if (recvd < 0 || error) return -1; sbuf->end += recvd; if (sbuf->end >= sbuf->bufsz) { sbuf->end = 0; error = false; recvd = socket_receive_all_nonblocking(sockfd, &error, sbuf->data, sbuf->start - 1); if (recvd < 0 || error) return -1; sbuf->end += recvd; } } else { error = false; recvd = socket_receive_all_nonblocking(sockfd, &error, sbuf->data + sbuf->end, sbuf->start - sbuf->end - 1); if (recvd < 0 || error) return -1; sbuf->end += recvd; } /* Now copy it into the reader */ if (sbuf->end >= sbuf->read || (sbuf->bufsz - sbuf->read) >= len) { size_t unread = buf_unread(sbuf); if (len <= unread) { memcpy(buf, sbuf->data + sbuf->read, len); sbuf->read += len; if (sbuf->read >= sbuf->bufsz) sbuf->read = 0; recvd = len; } else { memcpy(buf, sbuf->data + sbuf->read, unread); sbuf->read += unread; if (sbuf->read >= sbuf->bufsz) sbuf->read = 0; recvd = unread; } } else { /* Our read goes around the edge */ size_t chunka = sbuf->bufsz - sbuf->read, pchunklen = len - chunka, chunkb = (pchunklen >= sbuf->end) ? sbuf->end : pchunklen; memcpy(buf, sbuf->data + sbuf->read, chunka); memcpy((unsigned char *) buf + chunka, sbuf->data, chunkb); sbuf->read = chunkb; recvd = chunka + chunkb; } /* Perhaps block for more data */ if (block) { sbuf->start = sbuf->read; if (recvd < len) { if (!socket_receive_all_blocking(sockfd, (unsigned char *) buf + recvd, len - recvd)) return -1; recvd = len; } } return recvd; }