static int hls_write(HTTPContext *c, uint8_t *data, int size) { int inc = 0; uint8_t *ptr = NULL; HLS *s = &s_hls[c->hls_idx]; if(!s->data){ http_log("internal error i %d\n", c->hls_idx); return -1; } if(s->csize + size > s->msize){ inc = (s->csize + size - s->msize + SEG_INC_SIZE)/SEG_INC_SIZE; ptr = av_realloc(s->data, inc*SEG_INC_SIZE + s->msize); if(ptr){ s->data = ptr; s->msize += inc*SEG_INC_SIZE; }else{ http_log("not enough mem %d\n", inc); return -2; } } memcpy(s->data + s->csize, data, size); s->csize += size; c->hls_wpos += size; return 0; }
static void new_connection(int server_fd) { http_log("new connection!.\n"); struct sockaddr_in from_addr; socklen_t len; int fd; RTSPContext *c = NULL; len = sizeof(from_addr); fd = accept(server_fd, (struct sockaddr *)&from_addr, &len); if (fd < 0) { http_log("error during accept %s\n", strerror(errno)); return; } if (nb_connections >= 100) { // http_send_too_busy_reply(fd); goto fail; } /* add a new connection */ c = av_mallocz(sizeof(RTSPContext)); if (!c) goto fail; c->fd = fd; c->poll_entry = NULL; c->from_addr = from_addr; c->buffer_size = IOBUFFER_INIT_SIZE; c->buffer = av_malloc(c->buffer_size); if (!c->buffer) goto fail; c->buffer_ptr = c->buffer; c->buffer_end = c->buffer + IOBUFFER_INIT_SIZE; c->next = first_rtsp_ctx; first_rtsp_ctx = c; nb_connections++; start_wait_request(c); return; fail: if (c) { av_free(c->buffer); av_free(c); } close(fd); }
void http_request(http_t *ctx, const char *verb, const char *host, uint16_t port, const char *uri) { http_log(ctx, "%s: host=\"%s\" uri=\"%s\"", __func__, host, uri); http_send_str(ctx, verb); http_send_str(ctx, " "); http_send_str(ctx, uri); http_send_line(ctx, " HTTP/1.1"); http_send_str(ctx, "Host: "); http_send_str(ctx, host); if (port != HTTP_DEFAULT_PORT) { char buf[32], *p = buf; size_t size = sizeof buf; p = append_char(p, &size, ':'); p = append_uint(p, &size, port); http_send_str(ctx, buf); } http_send_str(ctx, "\r\n"); http_send_str(ctx, http_useragent_header); http_send_line(ctx, http_connection_header(ctx)); http_send_extra_headers(ctx); }
static void log_connection(HTTPContext *c) { //if(av_match_ext(c->url, "m3u8")) http_log("%s:%u %d \"%s\" %lld %d %d\n", inet_ntoa(c->from_addr.sin_addr), ntohs(c->from_addr.sin_port), c->post, c->url, c->data_count, c->http_error, nb_connections); }
static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs) { static int print_prefix = 1; AVClass *avc = ptr ? *(AVClass**)ptr : NULL; if (level > av_log_get_level()) return; if (print_prefix && avc) http_log("[%s @ %p]", avc->item_name(ptr), ptr); print_prefix = strstr(fmt, "\n") != NULL; http_vlog(fmt, vargs); }
static void build_file_streams(void) { iStream *stream, *stream_next; int i, ret; for(stream = first_stream; stream != NULL; stream = stream_next) { stream_next = stream->next; printf("filename = %s\n", stream->filename); printf("idx filename = %s\n", stream->idx_filename); if (!stream->filename[0] && !stream->idx_filename[0]) { http_log("Unspecified feed file for stream '%s' or idx file '%s'\n", stream->filename, stream->idx_filename); } else { /* find all the AVStreams inside and reference them in 'stream' */ //index parsing iIndexHeader *hdr = malloc(sizeof(*hdr)); if (hdr < 0) printf("malloc error! of iIndexHeader \n"); // error 처리 생략 printf("start parse index file!!\n"); iIndex *first_idx = start_parse_index_file(stream->idx_filename, hdr); if (first_idx == NULL) { http_log("%s Index file parsing error!!!\n", stream->idx_filename); return; } stream->idx_hdr = hdr; stream->first_idx = first_idx; stream->codec_name = hdr->encoding_format == 0 ? "H264" : "H265"; stream->total_length = hdr->total_length; stream->bit_rate = hdr->bit_rate; stream->duration = hdr->duration; } } }
int flvsrv_sendHttpResp(FLVSRV_CTXT_T *pFlvCtxt) { int rc; FILE_OFFSET_T lenLive = 0; http_log(pFlvCtxt->pSd, pFlvCtxt->pReq, HTTP_STATUS_OK, lenLive); if((rc = http_resp_sendhdr(pFlvCtxt->pSd, pFlvCtxt->pReq->version, HTTP_STATUS_OK, lenLive, CONTENT_TYPE_FLV, http_getConnTypeStr(pFlvCtxt->pReq->connType), pFlvCtxt->pReq->cookie, NULL, NULL, NULL, NULL)) < 0) { pFlvCtxt->state = FLVSRV_STATE_ERROR; return rc; } return rc; }
static int sff_prepare_data(HTTPContext *c) { SFF *sff = NULL; AVPacket pkt = {0}; switch(c->state) { case HTTPSTATE_SEND_DATA_HEADER: sff = sff_read(c, 1); if(!sff){ printf("prepare no sff for %s\n", c->url); c->state = HTTPSTATE_WAIT_FEED; return 1; } c->buffer_ptr = sff->data; c->buffer_end = sff->data + sff->size; c->state = HTTPSTATE_SEND_DATA; c->last_packet_sent = 0; break; case HTTPSTATE_SEND_DATA: sff = sff_read(c, 2); if(!sff){ //printf("2prepare no sff\n"); c->state = HTTPSTATE_WAIT_FEED; return 1; } if(sff_parse(sff, &pkt)){ printf("parse erro\n"); return AVERROR(EAGAIN); } c->buffer_ptr = pkt.data; c->buffer_end = pkt.data + pkt.size; if(-1 == pkt.stream_index){ c->state = HTTPSTATE_SEND_DATA_TRAILER; } break; default: case HTTPSTATE_SEND_DATA_TRAILER: c->last_packet_sent = 1; http_log("send trailer status\n"); return -2; break; } return 0; }
int main(int argc, char **argv) { //av_register_all(); //avformat_network_init(); unsetenv("http_proxy"); /* Kill the http_proxy */ parse_config(); signal(SIGPIPE, SIG_IGN); if (http_server() < 0) { http_log("Could not start server\n"); exit(1); } return 0; }
/** * @return * -1 if a header was invalid (see http status for details) * 0 if all headers are successfully handled (end-of-header reached) */ int http_process_headers(http_t *ctx, char *buf, size_t buf_size) { RUNTIME_ASSERT(ctx); RUNTIME_ASSERT(HTTP_STATE_HEADERS == ctx->state); for (;;) { char *name, *value; name = value = NULL; switch (http_parse_header(ctx, buf, buf_size, &name, &value)) { case -1: DBUG("http_parse_header() failed: %u \"%s\"", ctx->status_code, ctx->status_msg); return -1; case 0: RUNTIME_ASSERT(HTTP_STATE_BODY == ctx->state); return 0; case 1: break; default: RUNTIME_ASSERT(0); http_set_status(ctx, 500, "Internal Server Error"); return -1; } RUNTIME_ASSERT(HTTP_STATE_HEADERS == ctx->state); RUNTIME_ASSERT(name); RUNTIME_ASSERT(value); #if 0 http_log(ctx, "name=\"%s\"; value=\"%s\"", name, value); #endif if (0 != http_handle_header(ctx, name, value)) break; } return -1; }
static int prepare_local_file(HTTPContext *c) {/*return 1 if local file exist and can be read to buffer.*/ int len; unsigned char tmp[64] = ""; char prefix[32] = "."; int fd = -1; struct stat st = {0}; snprintf(tmp, sizeof(tmp)-1, "%s/%s", prefix, c->url); fd = open(tmp, O_RDONLY); if(fd < 0){ return 0; } if((fstat(fd, &st) < 0) || (st.st_size > SIZE_MAX)){ return 0; } c->pb_buffer = av_malloc(1024 + st.st_size); if(!c->pb_buffer){ c->buffer_ptr = c->buffer; c->buffer_end = c->buffer; close(fd); return 0; } len = sprintf(c->pb_buffer, "HTTP/1.1 200 OK\r\n" "Content-type: %s;charset=UTF-8\r\n" "Content-Length: %u\r\n" "Connection: %s\r\n" "\r\n", get_mine_type(c->url), (unsigned int)st.st_size, (c->keep_alive ? "keep-alive" : "close") ); len += read(fd, c->pb_buffer + len, st.st_size); close(fd); http_log("local file %s size %lld\n", c->url, st.st_size); c->buffer_ptr = c->pb_buffer; c->buffer_end = c->pb_buffer + len; return 1; }
int http_read_response(http_t *ctx) { char buf[BUFFERSIZE]; char *p, *endptr; uint32_t code; ssize_t ret, size; int error; RUNTIME_ASSERT(ctx); RUNTIME_ASSERT(ctx->state == HTTP_STATE_REQUEST); ret = fifo_findchar(ctx->input, '\n', sizeof buf); if (ret < 0) { return 0; } if ((size_t) ret >= sizeof buf) { http_log(ctx, "HTTP response line is too long"); return -1; } size = ret + 1; ret = fifo_read(ctx->input, buf, size); RUNTIME_ASSERT(ret > 0 && (size_t) ret <= sizeof buf); ret--; RUNTIME_ASSERT(buf[ret] == '\n'); while (ret >= 0 && isspace((unsigned char) buf[ret])) buf[ret--] = '\0'; http_log(ctx, "buf=\"%s\"", buf); p = http_parse_version(ctx, buf); if (!p) { http_log(ctx, "Not a HTTP reply: %s", buf); return -1; } if (!isspace((unsigned char) *p)) { http_log(ctx, "Missing space after HTTP/x.y: %s", buf); return -1; } p = skip_spaces(p); if (!isdigit((unsigned char) *p)) { http_log(ctx, "Invalid HTTP status code: %s", buf); return -1; } code = parse_uint32(p, &endptr, 10, &error); if (*endptr != '\0' && !isspace((unsigned char) *endptr)) { http_log(ctx, "Invalid HTTP status code: %s", buf); return -1; } if (code < 100 || code > 999) { http_log(ctx, "HTTP status code is out-of-range: %s", buf); return -1; } /* Don't care about the rest of the line */ ctx->status_code = code; ctx->state = HTTP_STATE_HEADERS; return 0; }
static int hls_parse_request(HTTPContext *c, char *name, int is_first) { int idx = -1, ret = 0; char *ext = NULL; HLS *s = NULL; ext = strrchr(name, '.'); if(!ext){ http_log("bad name %s\n", name); return -1; } if(ext - name > sizeof(s_hls_name)-1){ http_log("name %s too long\n", name); return -1; } if(!strcmp(ext, ".m3u8")){ idx = S; }else if(!strcmp(ext, ".ts")){ idx = ext[-1] - '0';//todo: S > 10 without get basename. if(!(0 <= idx && idx < S)){ http_log("too large index %d\n", idx); return -1; } } if(-1 == idx){ http_log("unkown name %s\n", name); return -1; } c->hls_idx = idx; s = &s_hls[idx]; if(c->post){/*writer*/ //todo: close http conn with same name //http_log("hls post c %p name %s:%d data %p size %d:%d\n", c, name, idx, s->data, s->msize, s->csize); if(!s->data){ s->data = av_malloc(SEG_INC_SIZE); s->msize = SEG_INC_SIZE; }else{ /*intended not to free*/ } //todo: if someone is reading s->flag = 1; s->csize = 0; c->hls_wpos = 0; c->http_error = 0; c->state = HTTPSTATE_RECEIVE_DATA; ret = 0; }else{/*reader*/ if(is_first && (S == idx) && strncmp(s_hls_name, name, ext-name) ){ hls_close(); strncpy(s_hls_name, name, ext - name); s_hls_name[ext - name] = 0; ret = 1; /*request switch*/ } c->hls_rpos = 0; c->http_error = 0; c->state = HTTPSTATE_SEND_HEADER; } return ret; }
static void new_connection(int server_fd, int is_rtsp) { struct sockaddr_in from_addr; socklen_t len; int fd; HTTPContext *c = NULL; int val; val = 0; len = sizeof(from_addr); memset(&from_addr, 0, len); fd = accept(server_fd, (struct sockaddr *)&from_addr, &len); if (fd < 0) { http_log("error during accept %s\n", strerror(errno)); return; } if (ff_socket_nonblock(fd, 1) < 0) av_log(NULL, AV_LOG_WARNING, "ff_socket_nonblock failed\n"); #if 0 /*prevent myself close_wait*/ val = 1; setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)); val = 5; setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)); val = 3; setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)); val = 2; setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)); #endif if (nb_connections >= nb_max_connections) { http_send_too_busy_reply(fd); goto fail; } /* add a new connection */ c = av_mallocz(sizeof(HTTPContext)); if (!c) goto fail; c->fd = fd; c->poll_entry = NULL; c->from_addr = from_addr; c->buffer_size = IOBUFFER_INIT_SIZE; c->buffer = av_malloc(c->buffer_size); if (!c->buffer) goto fail; c->next = first_http_ctx; first_http_ctx = c; nb_connections++; c->buffer_ptr = c->buffer; c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */ c->timeout = cur_time + HTTP_REQUEST_TIMEOUT; c->state = HTTPSTATE_WAIT_REQUEST; c->hls_idx = -1; return; fail: if (c) { av_free(c->buffer); av_free(c); } closesocket(fd); }
/* main loop of the Streaming server */ static int start_event_loop(void) { int server_fd = 0, rtsp_server_fd = 0; int ret, delay; struct pollfd *poll_table, *poll_entry; RTSPContext *c, *c_next; if(!(poll_table = av_mallocz( 100 *sizeof(*poll_table)))) { http_log("Impossible to allocate a poll table handling %d connections.\n", 100); return -1; } if (my_rtsp_addr.sin_port) { rtsp_server_fd = socket_open_listen(&my_rtsp_addr); if (rtsp_server_fd < 0) { http_log("HTTP and RTSP disabled.\n"); return -1; } } printf("%s started.\n", program_name); for(;;) { poll_entry = poll_table; if (rtsp_server_fd) { poll_entry->fd = rtsp_server_fd; poll_entry->events = POLLIN; poll_entry++; } /* wait for events on each HTTP handle */ c = first_rtsp_ctx; //delay = 1000; delay = 10; while (c != NULL) { int fd; fd = c->fd; switch(c->state) { case RTSPSTATE_SEND_REPLY: case RTSPSTATE_SEND_PACKET: c->poll_entry = poll_entry; poll_entry->fd = fd; poll_entry->events = POLLOUT; poll_entry++; break; case RTPSTATE_SEND_DATA: /* for TCP, we output as much as we can * (may need to put a limit) */ if (strcmp(c->protocol, "RTP/UDP") == 0) { fd = c->udp_fd; } c->poll_entry = poll_entry; poll_entry->fd = fd; poll_entry->events = POLLOUT; poll_entry++; break; case RTSPSTATE_WAIT_REQUEST: /* need to catch errors */ c->poll_entry = poll_entry; poll_entry->fd = fd; poll_entry->events = POLLIN;/* Maybe this will work */ poll_entry++; break; default: c->poll_entry = NULL; break; } c = c->next; } /* wait for an event on one connection. We poll at least every second to handle timeouts */ do { ret = poll(poll_table, poll_entry - poll_table, delay); if (ret < 0) { printf ("poll errorr \n"); return -1; } } while (ret < 0); cur_time = av_gettime() / 1000; /* now handle the events */ for(c = first_rtsp_ctx; c != NULL; c = c_next) { c_next = c->next; if (handle_connection(c) < 0) { /* close and free the connection */ close_connection(c); } } // 처음에 rtsp_server_fd로 셋팅했던 거. 로 포인터 값 변경. poll_entry = poll_table; if (rtsp_server_fd) { /* new RTSP connection request ? */ if (poll_entry->revents & POLLIN) new_connection(rtsp_server_fd); } } }
static int http_server(void) { int server_fd = 0; int ctrl_fd = 0, ctrl_fd2 = 0; int ret, delay; struct pollfd *poll_table, *poll_entry; HTTPContext *c, *c_next; if(!(poll_table = av_mallocz_array(nb_max_http_connections + 1, sizeof(*poll_table)))) { http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections); return -1; } #if defined(PLUGIN_DVB) ctrl_fd = ff_ctl_open(1234); if (ctrl_fd < 0) { av_free(poll_table); return -1; } #endif if (my_http_addr.sin_port) { server_fd = socket_open_listen(&my_http_addr); if (server_fd < 0) { av_free(poll_table); return -1; } } if ( !server_fd) { http_log("HTTP disabled.\n"); av_free(poll_table); return -1; } #if defined(PLUGIN_SSDP) ssdp_fd = mcast_open(ssdp_ip, ssdp_port); if(ssdp_fd <= 0){ http_log("ssdp disabled\n"); } ssdp_notify(ssdp_fd, ssdp_ip, ssdp_port, "ssdp:alive"); #endif http_log("FFserver started.\n"); for(;;) { poll_entry = poll_table; #if defined(PLUGIN_DVB) if(ctrl_fd){ poll_entry->fd = ctrl_fd; poll_entry->events = POLLIN; poll_entry++; } if(ctrl_fd2){ poll_entry->fd = ctrl_fd2; poll_entry->events = POLLIN; if(ctl_msg_pending() > 0){ poll_entry->events |= POLLOUT; } poll_entry++; } #endif if (server_fd) { poll_entry->fd = server_fd; poll_entry->events = POLLIN; poll_entry++; } #if defined(PLUGIN_SSDP) if(ssdp_fd){ poll_entry->fd = ssdp_fd; poll_entry->events = POLLIN; poll_entry++; } #endif /* wait for events on each HTTP handle */ c = first_http_ctx; delay = 1500; while (c != NULL) { int fd; fd = c->fd; switch(c->state) { case HTTPSTATE_SEND_HEADER: c->poll_entry = poll_entry; poll_entry->fd = fd; poll_entry->events = POLLOUT; poll_entry++; break; case HTTPSTATE_SEND_DATA_HEADER: case HTTPSTATE_SEND_DATA: case HTTPSTATE_SEND_DATA_TRAILER: /*for TCP, we output as much as we can*/ c->poll_entry = poll_entry; poll_entry->fd = fd; poll_entry->events = POLLOUT; poll_entry++; break; case HTTPSTATE_WAIT_REQUEST: case HTTPSTATE_RECEIVE_DATA: case HTTPSTATE_WAIT_FEED: /* need to catch errors */ c->poll_entry = poll_entry; poll_entry->fd = fd; poll_entry->events = POLLIN;/* Maybe this will work */ poll_entry++; break; default: c->poll_entry = NULL; break; } c = c->next; } /* wait for an event on one connection. We poll at least every second to handle timeouts */ do { ret = poll(poll_table, poll_entry - poll_table, delay); if (ret < 0 && ff_neterrno() != AVERROR(EAGAIN) && ff_neterrno() != AVERROR(EINTR)) { av_free(poll_table); return -1; } } while (ret < 0); cur_time = av_gettime() / 1000; /* now handle the events */ for(c = first_http_ctx; c != NULL; c = c_next) { c_next = c->next; if (handle_connection(c) < 0) { log_connection(c); close_connection(c); } } poll_entry = poll_table; #if defined(PLUGIN_DVB) if(ctrl_fd){ if(poll_entry->revents & POLLIN){ ctrl_fd2 = ctl_msg_open(ctrl_fd); } poll_entry++; } if(ctrl_fd2 && poll_entry->fd == ctrl_fd2){ if(poll_entry->revents & POLLIN){ ctl_msg_recv(); ff_ctl_recv(ctl_msg_cb); }else if(poll_entry->revents & POLLOUT){ ctl_msg_send(); } poll_entry++; } #endif if(poll_entry->fd != server_fd){ printf("bad entry\n"); } if (server_fd) { if (poll_entry->revents & POLLIN) new_connection(server_fd, 0); poll_entry++; } #if defined(PLUGIN_SSDP) if (ssdp_fd) { if (poll_entry->revents & POLLIN) ssdp_response(ssdp_fd); poll_entry++; } #endif } }
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; }
/* parse HTTP request and prepare header */ static int http_parse_request(HTTPContext *c) { char *q, msg[1024]; const char *mime_type, *p; HTTPContext *ctx; int ret = 0, is_first = 0; const char *first_tag = "First-Request=0"; RequestData rd = {{0}}; p = c->buffer; while(get_line(msg, sizeof(msg), &p) > 0){ ret = handle_line(c, msg, sizeof(msg), &rd); if(ret < 0)return ret; } is_first = !av_stristr(rd.cookie, first_tag); if(c->post && c->content_length && !av_match_ext(c->url, "m3u8") && !av_match_ext(c->url, "ts") && !av_match_ext(c->url, "flv")){ c->post = 0; c->content_length = read_request_content(c, rd.content, sizeof(rd.content)); } #if defined(PLUGIN_DVB) if(!c->post && !strcmp(c->url, "digitalDvb/allServiceType/getClientInfo")){ uint32_t *ptr = (uint32_t*)rd.content, *ptr_end = (uint32_t*)(rd.content+sizeof(rd.content)-8); for(ctx = first_http_ctx; ctx; ctx = ctx->next) if(!ctx->post && av_match_ext(ctx->url, "flv") ) {/*todo: record hls*/ if(ptr < ptr_end){ int chid = -1; sscanf(ctx->url, "%d", &chid); *ptr++ = inet_addr(inet_ntoa(ctx->from_addr.sin_addr)); *ptr++ = chid; printf("ip %s id %u %s\t", inet_ntoa(ctx->from_addr.sin_addr), chid, ctx->url); } } } #endif //http_log("New conn: %s:%u %d %s cookie:%s\n", inet_ntoa(c->from_addr.sin_addr), ntohs(c->from_addr.sin_port), c->post, c->url, rd.cookie); /*handle m3u8/ts request solely*/ if(av_match_ext(c->url, "m3u8") || av_match_ext(c->url, "ts")){ c->keep_alive = 0; ret = hls_parse_request(c, c->url, is_first); if(ret < 0)goto send_error; else if(ret == 1){ long chid = atoi(c->url); if(!(0 <= chid && chid <= 10000)){ sprintf(msg, "bad request: %s-->%ld", c->url, chid); http_log("%s\n", msg); goto send_error; } #if defined(PLUGIN_DVB) ff_ctl_send_string(1, c->url, rd.content); #endif http_log("wait get %s\n", c->url); } if(c->state == HTTPSTATE_SEND_HEADER) goto send_header; return 0; /*end here*/ } #if defined(PLUGIN_DVB) ret = plugin_dvb(c, &rd); if(ret < 0){ goto send_error; }else if(ret > 0){ return 0; } #endif /*handle feed request*/ if (c->post) { ctx = find_feed(c->url); if(ctx && ctx != c){ sprintf(msg, "file %s has been feeded", c->url); http_log("%s\n", msg); goto send_error; } c->http_error = 0; c->state = HTTPSTATE_RECEIVE_DATA; return 0; /*end here*/ }else{ if(prepare_local_file(c) > 0){ c->http_error = 200; c->state = HTTPSTATE_SEND_HEADER; return 0; /*no need feed, send local files directly.*/ } ctx = find_feed(c->url); if(!ctx){ c->keep_alive = 0; sprintf(msg, "wait to get %s", c->url); http_log("%s\n", msg); #if defined(PLUGIN_DVB) ff_ctl_send(2, c->url, strlen(c->url)+1, rd.content, sizeof(rd.content)); #endif }else{ ctx->sff_ref_cnt++; } c->feed_ctx = ctx; } send_header: /* prepare HTTP header */ c->buffer[0] = 0; av_strlcatf(c->buffer, c->buffer_size, "HTTP/1.1 200 OK\r\n"); mime_type = get_mine_type(c->url); av_strlcatf(c->buffer, c->buffer_size, "Pragma: no-cache\r\n"); av_strlcatf(c->buffer, c->buffer_size, "Content-Type: %s\r\n", mime_type); av_strlcatf(c->buffer, c->buffer_size, "Connection: %s\r\n", (c->keep_alive ? "keep-alive" : "close")); av_strlcatf(c->buffer, c->buffer_size, "Set-Cookie: %s; Path=/; Domain=%s\r\n", first_tag, rd.domain); av_strlcatf(c->buffer, c->buffer_size, "\r\n"); q = c->buffer + strlen(c->buffer); /* prepare output buffer */ c->http_error = 0; c->buffer_ptr = c->buffer; c->buffer_end = q; c->state = HTTPSTATE_SEND_HEADER; #if 0 if(S == c->hls_idx){ HLS *s = &s_hls[c->hls_idx]; char *ext = strrchr(c->url, '.'); if(!(2 == s->flag && s->data && s->csize > 0)){/*not exist yet, fake one*/ c->http_error = 200; c->buffer_end += sprintf(c->buffer_end, "#EXTM3U\n" "#EXT-X-VERSION:3\n" "#EXT-X-TARGETDURATION:2\n" "#EXT-X-MEDIA-SEQUENCE:0\n" "#EXTINF:1.283989,\n" "%.*s0.ts\n", ext - c->url, c->url); } } #endif return 0; send_error: c->keep_alive = 0; c->http_error = 404; q = c->buffer; htmlstrip(msg); snprintf(q, c->buffer_size, "HTTP/1.1 404 Not Found\r\n" "Content-type: text/html\r\n" "\r\n" "<html>\n" "<head><title>404 Not Found</title></head>\n" "<body>%s</body>\n" "</html>\n", msg); q += strlen(q); /* prepare output buffer */ c->buffer_ptr = c->buffer; c->buffer_end = q; c->state = HTTPSTATE_SEND_HEADER; return 0; }
void show_help_default(const char *opt, const char *arg) { http_log("dumpy default help\n"); }
/** * Reads data from the http context buffer (decodes it if the transfer- * encoding isn't "identity") and transfers up to ``buflen'' bytes into * ``buf''. The http context MUST be in state HTTP_STATE_BODY. * * @returns (ssize_t) -1 on failure and sets errno. If errno is set to EAGAIN * further data may be available later. * * If there's a transfer-encoding * error, errno will be set to EIO. If (-1) is returned and errno * has any other value than EAGAIN, the function MUST NOT be called * again. */ ssize_t http_read_body(http_t *ctx, char *buf, ssize_t buflen) { ssize_t ret = 0, count = 0; uint64_t left; RUNTIME_ASSERT(ctx != NULL); RUNTIME_ASSERT(buf != NULL); RUNTIME_ASSERT(buflen >= 0); RUNTIME_ASSERT(ctx->sane); RUNTIME_ASSERT(ctx->state == HTTP_STATE_BODY); switch (ctx->encoding) { case HTTP_TRANSFER_ENCODING_IDENTITY: RUNTIME_ASSERT(ctx->content_length >= ctx->received); left = ctx->content_length - ctx->received; if (left == 0) { return 0; } RUNTIME_ASSERT(left > 0); ret = fifo_read(ctx->input, buf, MIN(left, (size_t) buflen)); if (ret > 0) { ctx->received += ret; } else if (ret == 0) { errno = EAGAIN; return -1; } count = ret; break; case HTTP_TRANSFER_ENCODING_CHUNKED: while (!ctx->last_chunk && buflen > 0) { RUNTIME_ASSERT(ctx->content_length >= ctx->received); left = ctx->chunk_size - ctx->chunk_received; if (left > 0) { ret = fifo_read(ctx->input, buf, MIN(left, (size_t) buflen)); if (ret > 0) { RUNTIME_ASSERT(ret <= buflen); ctx->chunk_received += ret; ctx->received += ret; buf += ret; buflen -= ret; count += ret; } else { if (ret == 0 && count == 0) { errno = EAGAIN; return -1; } return count; } } else { char chunk_intro[1024]; ssize_t pos, size; uint64_t v; char *endptr; int error; /* If this isn't the first chunk, wait for trailing <CR><LF> */ if (ctx->chunk_size > 0) { pos = fifo_findchar(ctx->input, '\n', sizeof chunk_intro); if (pos == (ssize_t) -1) { if (count > 0) return count; errno = EAGAIN; return -1; } RUNTIME_ASSERT(pos >= 0 && (size_t) pos < sizeof chunk_intro); size = fifo_read(ctx->input, chunk_intro, pos + 1); RUNTIME_ASSERT(size == pos + 1); RUNTIME_ASSERT(chunk_intro[pos] == '\n'); chunk_intro[pos] = '\0'; } RUNTIME_ASSERT(ctx->chunk_received == ctx->chunk_size); /* Mark the previous chunk as completely handled */ ctx->chunk_sum_received += ctx->chunk_size; ctx->chunk_size = 0; ctx->chunk_received = 0; /* Determine the size of the next chunk */ pos = fifo_findchar(ctx->input, '\n', sizeof chunk_intro); if (pos == (ssize_t) -1) { if (count > 0) return count; errno = EAGAIN; return -1; } RUNTIME_ASSERT(pos >= 0 && (size_t) pos < sizeof chunk_intro); size = fifo_read(ctx->input, chunk_intro, pos + 1); RUNTIME_ASSERT(size == pos + 1); RUNTIME_ASSERT(chunk_intro[pos] == '\n'); chunk_intro[pos] = '\0'; /* Read the chunk header and determine its size (hex value) */ v = parse_uint64(chunk_intro, &endptr, 16, &error); if ( (!v && error) || (*endptr != '\0' && !isspace((unsigned char) *endptr)) ) { errno = EIO; ctx->sane = false; return -1; } if (v == 0) { /* Only the last chunk has a zero length */ #if 1 http_log(ctx, "Last chunk reached"); #endif ctx->last_chunk = true; return count > 0 ? count : 0; } #if 0 http_log(ctx, "chunk_size=%" PRIu64, v); #endif ctx->chunk_size = v; } } break; default: RUNTIME_ASSERT(0); } return count; }
/** * Reads a single HTTP header line from ctx->input and parses it. * Header continuation is handled transparently. Trailing space characters * are discarded from the header line and the line is NUL-terminated. * * @param ctx an initialized ``http_t'' in state HTTP_STATE_HEADERS. * @param buf a work buffer. * @param buf_size the size of buf; this defines the maximum length of * headers that can be handled. * * @return * -1 if a header was invalid (see http status for details). * 0 if the end-of-headers was reached. * 1 if a header was successfully parsed. */ int http_read_header(http_t *ctx, char *buf, size_t buf_size) { char *p; size_t size; RUNTIME_ASSERT(ctx); RUNTIME_ASSERT(buf); RUNTIME_ASSERT(buf_size > 0); RUNTIME_ASSERT(ctx->state == HTTP_STATE_HEADERS); p = buf; size = buf_size; for (;;) { ssize_t r; r = fifo_findchar(ctx->input, '\n', size); if ((ssize_t) -1 == r) { if ((size_t) fifo_fill(ctx->input) > size) DBUG("Header line is too long"); else DBUG("Header line is not yet terminated"); ctx->keep_alive = false; http_set_status(ctx, 413, "Request Entity Too Large"); return -1; } RUNTIME_ASSERT(r >= 0 && (size_t) r < size); /* Make sure the headers don't contain any NUL characters */ if ((ssize_t) -1 != fifo_findchar(ctx->input, '\0', r)) { ctx->keep_alive = false; http_set_status(ctx, 400, "Bad Request (NUL in Header)"); return -1; } fifo_read(ctx->input, p, 1 + r); RUNTIME_ASSERT('\n' == p[r]); /* Discard trailing '\r' characters */ for (/* NOTHING*/; r > 0; r--) { if ('\r' != p[r - 1]) break; } RUNTIME_ASSERT('\n' == p[r] || '\r' == p[r]); p[r] = '\0'; /* Check for a header continuation with HT (0x09) or a space (0x20) */ if ( 0 != fifo_findchar(ctx->input, 0x09, 1) && 0 != fifo_findchar(ctx->input, 0x20, 1) ) { /* Discard all trailing spaces */ if (isspace((unsigned char) p[r])) { while (r > 0 && isspace((unsigned char) p[r - 1])) r--; p[r] = '\0'; } break; } p += r; size -= r; /* Discard all consecutive HT and SP characters */ fifo_skip_chars(ctx->input, fifo_fill(ctx->input), "\x09\x20"); /* Replace all skipped space by a single space character */ if (size > 0) { *p++ = 0x20; size--; } } if (ctx->debug_dump_headers) { http_log(ctx, "\t%s", buf); } if ('\0' == *buf) { ctx->state = HTTP_STATE_BODY; return 0; } return 1; }
bool http_t::get( std::string& result, const std::string& url, cache::behavior_t caching, const std::string& confirmation, int throttle_seconds ) { result.clear(); std::string encoded_url; format( encoded_url, url ); auto_lock_t lock( cache_mutex ); url_cache_entry_t& entry = url_db[ encoded_url ]; if ( HTTP_CACHE_DEBUG ) { std::ofstream http_log( "simc_http_log.txt", std::ios::app ); std::ostream::sentry s( http_log ); if ( s ) { http_log << cache::era() << ": get(\"" << url << "\") ["; if ( entry.validated != cache::INVALID_ERA ) { if ( entry.validated >= cache::era() ) http_log << "hot"; else if ( caching != cache::CURRENT ) http_log << "warm"; else http_log << "cold"; http_log << ": (" << entry.modified << ", " << entry.validated << ')'; } else http_log << "miss"; if ( caching != cache::ONLY && ( entry.validated == cache::INVALID_ERA || ( caching == cache::CURRENT && entry.validated < cache::era() ) ) ) http_log << " download"; http_log << "]\n"; } } if ( entry.validated < cache::era() && ( caching == cache::CURRENT || entry.validated == cache::INVALID_ERA ) ) { if ( caching == cache::ONLY ) return false; util_t::printf( "@" ); fflush( stdout ); throttle( throttle_seconds ); if ( ! download( entry, encoded_url ) ) return false; if ( HTTP_CACHE_DEBUG && entry.modified < entry.validated ) { std::ofstream http_log( "simc_http_log.txt", std::ios::app ); http_log << cache::era() << ": Unmodified (" << entry.modified << ", " << entry.validated << ")\n"; } if ( confirmation.size() && ( entry.result.find( confirmation ) == std::string::npos ) ) { //util_t::printf( "\nsimulationcraft: HTTP failed on '%s'\n", url.c_str() ); //util_t::printf( "%s\n", ( result.empty() ? "empty" : result.c_str() ) ); //fflush( stdout ); return false; } } result = entry.result; return true; }