/** * The poll function is called every 2nd second. * If there has been no data sent (which resets the retries) in 8 seconds, close. * If the last portion of a file has not been sent in 2 seconds, close. * * This could be increased, but we don't want to waste resources for bad connections. */ static err_t http_poll(void *arg, struct tcp_pcb *pcb) { struct http_state *hs = arg; LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: pcb=0x%08X hs=0x%08X pcb_state=%s\n", pcb, hs, tcp_debug_state_str(pcb->state))); if (hs == NULL) { if (pcb->state == ESTABLISHED) { /* arg is null, close. */ LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: arg is NULL, close\n")); http_close_conn(pcb, hs); return ERR_ABRT; } } else { hs->retries++; if (hs->retries == HTTPD_MAX_RETRIES) { LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: too many retries, close\n")); http_close_conn(pcb, hs); return ERR_ABRT; } LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: try to send more data\n")); http_send_data(pcb, hs); } return ERR_OK; }
/** * Data has been sent and acknowledged by the remote host. * This means that more data can be sent. */ static err_t http_sent(void *arg, struct tcp_pcb *pcb, u16_t len) { struct http_state *hs = arg; LWIP_DEBUGF(HTTPD_DEBUG, ("http_sent: pcb=0x%08X hs=0x%08X len=%d\n", pcb, hs, len)); LWIP_UNUSED_ARG(len); if (hs == NULL) { /* this should not happen, but just to be robust... */ return ERR_OK; } #if HTTPD_TRACK_SENT_BYTES hs->sent_total += len; LWIP_DEBUGF(HTTPD_DEBUG, ("http_sent: sent_total=%d, file size=%d\n", hs->sent_total, hs->file_size)); if(hs->sent_total > hs->file_size) { LWIP_DEBUGF(HTTPD_DEBUG, ("http_sent: sent %d bytes too much!\n", hs->sent_total - hs->file_size)); } #endif /* HTTPD_TRACK_SENT_BYTES */ /* reset retry counter */ hs->retries = 0; if (hs->left > 0) { LWIP_DEBUGF(HTTPD_DEBUG, ("http_sent: %d bytes left, calling http_send_data\n", hs->left)); http_send_data(pcb, hs); } else { /* this should normally not happen, print to be robust */ LWIP_DEBUGF(HTTPD_DEBUG, ("http_sent: no bytes left\n")); http_close_conn(pcb, hs); } return ERR_OK; }
int http_send_content_length_header(http_t *ctx) { if ((uint64_t) -1 != ctx->content_length) { static const char length_hdr[] = "Content-Length: "; char buf[UINT64_DEC_BUFLEN]; http_send_data(ctx, length_hdr, sizeof length_hdr - 1); if (ctx->content_length > (uint32_t) -1) { print_uint64(buf, sizeof buf, ctx->content_length); } else { print_uint32(buf, sizeof buf, ctx->content_length); } return http_send_line(ctx, buf); } return 0; }
/* Called by: */ int http_decode(struct hi_thr* hit, struct hi_io* io) { struct hi_pdu* req = io->cur_pdu; char* url; char* url_lim = 0; char* p = req->m; int n = req->ap - p; if (n < HTTP_MIN_PDU_SIZE) { /* too little, need more */ req->need = HTTP_MIN_PDU_SIZE - n; return 0; } if (memcmp(p, "GET /", sizeof("GET /")-1)) { ERR("Not a GET HTTP PDU. fd(%x). Got(%.*s)", io->fd, HTTP_MIN_PDU_SIZE, req->m); return HI_CONN_CLOSE; } for (p += 5; p < req->ap - (sizeof(" HTTP/1.0")-2); ++p) if (!memcmp(p, " HTTP/1.0\n", sizeof(" HTTP/1.0")-1)) { /* Found end of URL */ url = req->m + 4; url_lim = p; break; } if (!url_lim) { req->need = 1; return 0; } /* *** Proper processing of content-length and setting need to length of PDU is still needed. */ D("need=%d len=%d buf(%.*s)", req->need, (int)(req->ap-req->m), (int)(req->ap-req->m), req->m); hi_add_to_reqs(hit, io, req, HTTP_MIN_PDU_SIZE); /* 01234567890 * GET / HTTP/1.0 */ switch (req->m[6]) { case 'a': http_send_data(hit, io, req, url_lim-url, url); break; case 'b': http_send_file(hit, io, req, url_lim-url, url); break; /* *** */ default: http_send_err(hit, io, req, 500, "Error"); break; } return HI_CONN_CLOSE; /* HTTP/1.0 without keep-alive: close connection after every req-resp */ }
/** * Data has been received on this pcb. * For HTTP 1.0, this should normally only happen once (if the request fits in one packet). */ static err_t http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) { err_t parsed = ERR_ABRT; struct http_state *hs = arg; LWIP_DEBUGF(HTTPD_DEBUG, ("http_recv: pcb=0x%08X pbuf=0x%08X err=%s\n", pcb, p, lwip_strerr(err))); if (p != NULL) { /* Inform TCP that we have taken the data. */ tcp_recved(pcb, p->tot_len); } if (hs == NULL) { /* be robust */ LWIP_DEBUGF(HTTPD_DEBUG, ("Error, http_recv: hs is NULL, abort\n")); http_close_conn(pcb, hs); return ERR_OK; } if ((err != ERR_OK) || (p == NULL)) { /* error or closed by other side */ if (p != NULL) { pbuf_free(p); } http_close_conn(pcb, hs); return ERR_OK; } if (hs->file == NULL) { parsed = http_parse_request(p, hs); } else { LWIP_DEBUGF(HTTPD_DEBUG, ("http_recv: already sending data\n")); } pbuf_free(p); if (parsed == ERR_OK) { LWIP_DEBUGF(HTTPD_DEBUG, ("http_recv: data %p len %ld\n", hs->file, hs->left)); http_send_data(pcb, hs); } else if (parsed == ERR_ABRT) { http_close_conn(pcb, hs); return ERR_OK; } return ERR_OK; }
int http_redirect(http_t *ctx, const char *url, bool permanent) { static const char location[] = "Location: "; RUNTIME_ASSERT(url && strlen(url) > 0); if (permanent) http_send_status_line(ctx, 301, "Moved Permanently"); else http_send_status_line(ctx, 307, "Temporary Redirect"); http_send_line(ctx, http_connection_header(ctx)); http_send_data(ctx, location, sizeof location - 1); http_send_line(ctx, url); http_send_extra_headers(ctx); http_terminate_headers(ctx); return 0; }
int http_send_document(http_t *ctx, const char *data, size_t size) { RUNTIME_ASSERT(ctx != NULL); if (!ctx->status_code) { ctx->status_code = 200; ctx->status_msg = "OK"; } http_send_status_line(ctx, ctx->status_code, ctx->status_msg); http_set_content_length(ctx, size); http_send_content_length_header(ctx); http_send_line(ctx, http_connection_header(ctx)); http_send_extra_headers(ctx); ACCLOG_SET(ctx->acclog, size, size); http_terminate_headers(ctx); if (size > 0) http_send_data(ctx, data, size); return 0; }
static int handle_connection(HTTPContext *c) { int len, ret; switch(c->state) { case HTTPSTATE_WAIT_REQUEST: /* timeout ? */ if ((c->timeout - cur_time) < 0) return -1; if (c->poll_entry->revents & (POLLERR | POLLHUP)) return -1; /* no need to read if no events */ if (!(c->poll_entry->revents & POLLIN)) return 0; /* read the data */ read_loop: len = recv(c->fd, c->buffer_ptr, 1, 0); if (len < 0) { if (ff_neterrno() != AVERROR(EAGAIN) && ff_neterrno() != AVERROR(EINTR)) return -1; } else if (len == 0) { if(!c->keep_alive)return -1; } else { /* search for end of request. */ uint8_t *ptr; c->buffer_ptr += len; ptr = c->buffer_ptr; if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) || (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) { /* request found : parse it and reply */ ret = http_parse_request(c); if (ret < 0) return -1; } else if (ptr >= c->buffer_end) { /* request too long: cannot do anything */ return -1; } else goto read_loop; } break; case HTTPSTATE_SEND_HEADER: if (c->poll_entry->revents & (POLLERR | POLLHUP)) return -1; /* no need to write if no events */ if (!(c->poll_entry->revents & POLLOUT)) return 0; len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0); if (len < 0) { if (ff_neterrno() != AVERROR(EAGAIN) && ff_neterrno() != AVERROR(EINTR)) { goto close_conn; } } else { c->buffer_ptr += len; c->data_count += len; if (c->buffer_ptr >= c->buffer_end) { av_freep(&c->pb_buffer); if(c->keep_alive){ memset(c->buffer, 0, c->buffer_size); c->buffer_ptr = c->buffer; c->buffer_end = c->buffer + c->buffer_size - 1; c->timeout = cur_time + HTTP_REQUEST_TIMEOUT; c->state = HTTPSTATE_WAIT_REQUEST; c->hls_idx = -1; http_log("%u alive %s\n", ntohs(c->from_addr.sin_port), c->url); return 0; } /* if error, exit */ if (c->http_error) return -1; /* all the buffer was sent : synchronize to the incoming*/ c->state = HTTPSTATE_SEND_DATA_HEADER; c->buffer_ptr = c->buffer_end = c->buffer; } } break; case HTTPSTATE_SEND_DATA: case HTTPSTATE_SEND_DATA_HEADER: case HTTPSTATE_SEND_DATA_TRAILER: if (c->poll_entry->revents & (POLLERR | POLLHUP)) return -1; /* no need to read if no events */ if (!(c->poll_entry->revents & POLLOUT)) return 0; if (http_send_data(c) < 0) return -1; /* close connection if trailer sent */ if (c->state == HTTPSTATE_SEND_DATA_TRAILER) return -1; break; case HTTPSTATE_RECEIVE_DATA: /* no need to read if no events */ if (c->poll_entry->revents & (POLLERR | POLLHUP)){ HLS *s = NULL; if(c->hls_idx >= 0){ s = &s_hls[c->hls_idx]; s->flag = 2; } wake_others(c, HTTPSTATE_SEND_DATA_TRAILER); return -1; } if (!(c->poll_entry->revents & POLLIN)) return 0; if (http_receive_data(c) < 0) return -1; break; case HTTPSTATE_WAIT_FEED: /* no need to read if no events */ if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP)) /*19*/ {printf("line %d: %x:%d\n", __LINE__, c->poll_entry->revents, get_socket_error(c->fd)); return -1;} /* nothing to do, we'll be waken up by incoming feed packets */ break; default: return -1; } return 0; close_conn: av_freep(&c->pb_buffer); return -1; }