📄 http.c
字号:
/* * judges if http_hdr is complete HTTP header, or still needs some parts. * return value : 0 ... NOT complete * 1 ... COMPLETE!! */int http_is_entire_header(struct http_header_t *http_hdr){ if(http_hdr == NULL) return 0; /* unlikely, but error */ if(http_hdr->buffer == NULL) return 0; /* nothing received. */ if(strstr(http_hdr->buffer,"\r\n\r\n") || strstr(http_hdr->buffer,"\n\n")) { return 1; } return 0;}/* * http_response_parse : parse http response header * make http_hdr from raw data in http_hdr->buffer * * return value : -1 ... ERROR * 0 ... already parsed. * 1 ... success. */int http_response_parse(struct http_header_t *http_hdr){ char *hdr_ptr, *ptr; char *field = NULL; int pos_hdr_sep, hdr_sep_len; size_t len; if(http_hdr == NULL) return -1; if(http_hdr->is_parsed) return 0; /* already parsed. */ /* 1. get protocol. */ hdr_ptr = strstr(http_hdr->buffer, " "); if(hdr_ptr == NULL) { display(MSDL_ERR,"Malformed answer : No %20 separator\n"); return -1; } len = hdr_ptr - http_hdr->buffer; http_hdr->protocol = xmalloc(len + 1); strncpy(http_hdr->protocol,http_hdr->buffer,len); http_hdr->protocol[len] = '\0'; /* get (useless) minor version here. */ if(!strncasecmp(http_hdr->protocol,"HTTP",4)) { if(sscanf(http_hdr->protocol+5,"1.%d", &(http_hdr->http_minor_version)) != 1) display(MSDL_ERR,"Malformed answer: http minor version unsepcified\n"); /* actually, lack of HTTP version is not fatal AT ALL. */ } /* 2. get status code */ if(sscanf(++hdr_ptr,"%d",&(http_hdr->status_code)) != 1) { display(MSDL_ERR,"Malformed answer : No http status code!!\n"); return -1; } hdr_ptr += 4; /* "[0-9][0-9][0-9] " */ /* 3. get reason phrase */ ptr = strstr(hdr_ptr,"\n"); if(ptr == NULL) { display(MSDL_ERR,"Malformed answer : unable to get reason_phrase\n"); return -1; } len = ptr - hdr_ptr; /* len of reason phrase. */ http_hdr->reason_phrase = xmalloc(len + 1); strncpy(http_hdr->reason_phrase,hdr_ptr,len); if(http_hdr->reason_phrase[len - 1] == '\r') { /* M$ style newline! */ len --; /* we don't need \r. */ } http_hdr->reason_phrase[len] = '\0'; /* terminate string */ hdr_sep_len = 4; /* header separator length */ ptr = strstr(http_hdr->buffer,"\r\n\r\n"); if(!ptr) { ptr = strstr(http_hdr->buffer,"\n\n"); if(!ptr) { display(MSDL_ERR,"Header may be incomplete!\n"); return -1; } hdr_sep_len = 2; } pos_hdr_sep = ptr - http_hdr->buffer; /* points to first line after method line. */ hdr_ptr = strstr(http_hdr->buffer,"\n") + 1; /* get all fields. they are separated by '\n' */ do { ptr = hdr_ptr; while(*ptr != '\r' && *ptr != '\n') ptr++; len = ptr - hdr_ptr; if(len == 0) break; /* met \n\n --> end of header! */ field = (char *)xrealloc(field,len + 1); /* +1 for '\0' */ strncpy(field,hdr_ptr,len); field[len] = '\0'; http_set_field(http_hdr,field); hdr_ptr = ptr + ((*ptr == '\r') ? 2 : 1); /* points to next line */ } while(hdr_ptr < (http_hdr->buffer + pos_hdr_sep)); if(field) { free(field); } if(pos_hdr_sep + hdr_sep_len < http_hdr->buffer_len) { /* response has data within. --> store data !! or data will be lost! */ http_hdr->body = http_hdr->buffer + pos_hdr_sep + hdr_sep_len; /* now its pointing to data in buffer. */ http_hdr->body_len = http_hdr->buffer_len - (pos_hdr_sep + hdr_sep_len); } http_hdr->is_parsed = 1; return 1;}/* * return value: 0: not valid 1: valid * reason_ret ... NULL: no error * error reason string: some error */int http_byterange_spec_valid(const char *str,char **reason_ret){ const char *p = str; for(; *p ; p++) { if(!isdigit(*p)) { *reason_ret = "http: byterange must be all digits"; return 0; } } *reason_ret = NULL; return 1;}/* * check if Range: string is valid * return value ... 0: not valid 1: valid * reason_ret ... NULL: no error * error reason string: some error */int http_byterange_valid(const char *str,char **reason_ret){ char *p = NULL; char *range_str = NULL; int valid = 0; char *reason = NULL; p = strchr(str,'-'); if(p == NULL) { /* have to have exactly one '-' */ reason = "must use \'-\' to tell range"; if(reason_ret) { *reason_ret = reason; } return 0; } else if(p == str) { /* ( "-" npt-time ) */ valid = http_byterange_spec_valid(p + 1,reason_ret); return valid; } else { /* 00.23- */ int start_str_len = p - str; if(*(p+1) != '\0') { /* not 00.23- */ if(!http_byterange_spec_valid(p + 1,reason_ret)) { /* check end string */ return 0; } } /* end string OK */ /* check start string */ range_str = xmalloc(start_str_len + 1); strncpy(range_str,str,start_str_len); range_str[start_str_len] = '\0'; valid = http_byterange_spec_valid(range_str,reason_ret); free(range_str); return valid; } return 0;}void http_set_byterange_field(struct http_header_t *http_hdr,const char *rangestr){ char *buffer = xmalloc(BUFSIZE_1K); char *reason = NULL; if(http_byterange_valid(rangestr,&reason)) { /* valid */ snprintf(buffer,BUFSIZE_1K - 1,"Range: bytes=%s",rangestr); http_set_field(http_hdr,buffer); } else { display(MSDL_ERR, "invalid byterange \"%s\"\n" "%s\n",rangestr,reason); } free(buffer);}/* * prepare_resuming for http * set resumeinfo->resume_start_offset & resume_req_success * * return value: 1 ... success * -1 ... failed */int http_prepare_resuming(struct stream_t *stream){ int ret = 0; uint64_t filesize = 0; /* * find same file name */ ret = get_filesize(stream->localfile,&filesize); if(ret < 0) { display(MSDL_ERR, "http resume: no such file \"%s\", not resuming\n",stream->localfile); goto failed; } if(stream->dlopts->byterange) { free(stream->dlopts->byterange); } stream->dlopts->byterange = make_byterange_from_filesize(filesize); stream->resumeinfo->resume_start_offset = filesize; display(MSDL_DBG, "http resume: start pos: %lld [%llx]\n", filesize,filesize); return 1; failed: stream->resumeinfo->resume_start_offset = 0; /* don't check request result */ stream->resumeinfo->resume_req_success = 0; return -1;}/* * set standard fields for HTTP. * set uri * Accept: * User-Agent: * Host: * */int http_set_standard_fields(struct stream_t *stream, struct http_header_t *http_hdr){ struct url_t *url = NULL; struct download_opts_t *dlopts = NULL; char str[BUFSIZE_1K]; if(stream == NULL || http_hdr == NULL) { return -1; } url = stream->url; dlopts = stream->dlopts; if((url == NULL) || (dlopts == NULL)) { return -1; } http_set_field(http_hdr, "Accept: */*"); http_set_field(http_hdr, http_useragent); http_add_basic_authentication(http_hdr, (url->username) ? url->username : stream->dlopts->username, (url->password) ? url->password : stream->dlopts->password); if(dlopts->http_proxy) { http_set_uri(http_hdr,url->url); } else { http_set_uri(http_hdr,url->filepath); } snprintf(str,BUFSIZE_1K,"Host: %.220s:%d",stream->serverinfo->host,stream->serverinfo->port); http_set_field(http_hdr,str); http_set_field(http_hdr,"Connection: Close"); return 1;}int is_http_response_ok(int status_code){ return ((200 <= status_code) && (status_code <= 299)) ? 1 : 0;}/* * parse http response * return value: 1 ... success * -1 ... parse error */int http_parse_response(struct http_ctrl_t *http_ctrl, struct http_header_t *http_hdr){ char *content_length = NULL; content_length = http_get_field(http_hdr, "Content-Length"); if(content_length) { http_ctrl->content_length = atoi(content_length); } else { http_ctrl->content_length = 0; } return 1;}/* * send GET request and perse response * return value: 1 ... success * -1 ... comp */int http_get(struct stream_t *stream){ struct http_header_t *http_hdr = NULL; struct download_opts_t *dlopts = stream->dlopts; int ret = 0; http_hdr = new_http_header(); http_set_standard_fields(stream,http_hdr); if(dlopts->resume_download) { http_prepare_resuming(stream); } /* byte range specified */ if(dlopts->byterange) { http_set_byterange_field(http_hdr,dlopts->byterange); } http_request_get(http_hdr); http_send_header(stream,http_hdr); free_http_header(http_hdr); http_hdr = new_http_header(); http_recv_header(stream,http_hdr); ret = http_process_reply(stream,http_hdr); // interpret http_hdr which just received if(ret < 0) { goto failed; // including complete } free_http_header(http_hdr); return 1; failed: free_http_header(http_hdr); return -1;}int http_process_reply(struct stream_t *stream,struct http_header_t *http_hdr){ int status_code = http_hdr->status_code; if(status_code < 0) { display(MSDL_ERR,"response HTTP header parse failed\n"); goto failed; } if(stream->dlopts->resume_download) { /* resume case */ if(status_code == 206) { /* byterange req success */ stream->resumeinfo->resume_req_success = 1; } else if (status_code == 416){ stream->resumeinfo->resume_req_success = 0; stream->stream_ctrl->status = STREAMING_NO_NEED_TO_DOWNLOAD; goto complete; } else { stream->resumeinfo->resume_req_success = 0; } } if(!is_http_response_ok(status_code)) { /* other error case */ if(status_code == 301 || status_code == 302 || status_code == 303 || status_code == 307) { char *newurlstr = NULL; // HERE newurlstr = http_redirect_new_url(http_hdr); display(MSDL_NOR,"redirect to %s\n",newurlstr); if(newurlstr) { stream->stream_ctrl->retry_urlstr = newurlstr; stream->stream_ctrl->status = STREAMING_REDIRECTED; } goto failed; } else if(status_code == 401) { http_print_authenticate_required(http_hdr); goto failed; } else { display(MSDL_ERR,"%d %s\n",http_hdr->status_code,http_hdr->reason_phrase); goto failed; } } return 1; complete: failed: return -1;}/* * starts mmst streaming(actually this is downlaoding). * * return value : negative or 0 ... error * 1 ... success */int http_streaming_start(struct stream_t *stream){ struct stream_ctrl_t *stream_ctrl = stream->stream_ctrl; struct http_ctrl_t *http_ctrl = stream_ctrl->http_ctrl; struct url_t *url = stream->url; int sock = 0; int ret = 0; stream_ctrl->status = STREAMING_HANDSHAKING; set_serverinfo_by_proxy_string(stream->serverinfo,url->hostname,url->port, stream->dlopts->http_proxy, HTTP_PORT,HTTP_PROXY_PORT); sock = server_connect(stream->serverinfo->connect_host,stream->serverinfo->connect_port); if(sock < 0) { goto failed; } stream->netsock->sock = sock; ret = http_get(stream); if(ret < 0) { if(stream_ctrl->status == STREAMING_NO_NEED_TO_DOWNLOAD) { goto complete; } else { goto failed; } } /* set file size to download */ stream_ctrl->file_size = http_ctrl->content_length; stream_ctrl->status = STREAMING_DOWNLOADING; stream_ctrl->protocol = HTTP; return 1; failed: complete: return 0;}/* * read http (stream). filles buffer, and buffer size is 'size' * * read cached data from stream->netsock->buffer * check for to see if network access is necessary * get chunk(media packet) from network. * * return value: bytes written to buffer. -1 if error. */int http_streaming_read(struct stream_t *stream, uint8_t *buffer, size_t buffer_size){ struct http_ctrl_t *http_ctrl = stream->stream_ctrl->http_ctrl; int ret; if(http_ctrl->content_length) { int rest = http_ctrl->content_length - http_ctrl->down_length; if(rest == 0) { /* finished */ ret = 0; } else { /* needs to get from network or buffer */ int recv_length = (rest > buffer_size) ? buffer_size : rest; /* smaller */ ret = recv_data(stream,buffer,recv_length); http_ctrl->down_length += ret; } } else { /* recv() returns 0 if we call recv on already shutdown()ed socket */ ret = recv_data(stream,buffer,buffer_size); } if(ret == 0) { stream->stream_ctrl->status = STREAMING_FINISHED; } return ret;}struct http_ctrl_t *new_http_ctrl_t(void){ struct http_ctrl_t *ctrl = xmalloc(sizeof(struct http_ctrl_t)); return ctrl;}void free_http_ctrl_t(struct http_ctrl_t *ctrl){ free(ctrl);}struct stream_t *http_streaming_init(){ struct stream_t *stream = streaming_init_common(); stream->stream_ctrl->http_ctrl = new_http_ctrl_t(); stream->start = http_streaming_start; stream->read = http_streaming_read; stream->close = http_streaming_close; return stream;}void http_streaming_close(struct stream_t *stream){ if(stream->netsock->sock > 0) { close(stream->netsock->sock); } free_http_ctrl_t(stream->stream_ctrl->http_ctrl); streaming_close_common(stream);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -