/// \return number of bytes processed static size_t headerfunc_range_request ( char * ptr, size_t size, size_t nmemb, range_request_t* se ) { int start = 0; int stop = 0; if (!strncmp ( ptr, "HTTP/1.1", 8 )) { se->error = atoi ( ptr + 9 ); } if (!strncmp ( ptr, "Content-Range", 13 )) { parse_content_range(ptr + 21, &start, &stop, &se->full_size); } return nmemb * size; }
/* Curl WRITEFUNCTION. Gets response data in batches, plus a range_fetch struct. */ size_t range_fetch_read_http_content( void *ptr, size_t size, size_t nmemb, void *userdata ) { struct range_fetch *rf = (struct range_fetch *)userdata; char *buf = (char *)ptr; size_t len = size * nmemb; /* Make sure we're reading content from a 200 (ok) or 206 (partial content) */ if( rf->http_code != 200 && rf->http_code != 206 ) { fprintf( stderr, "Expected HTTP 200 or 206 (partial content) but got code %d!\n", rf->http_code ); return 0; } /* Keep bytes_down up-to-date */ rf->bytes_down += len; /* Loop until nothing left to read in this chunk */ while( len ) { if( rf->block_left > 0 && rf->multipart == 0 ) { /* In the middle of reading a block */ /* Read the min of block_left and len */ size_t block_todo = rf->block_left > len ? len : rf->block_left; if( zsync_receive_data( rf->zr, buf, rf->offset, block_todo ) != 0 ) return 0; rf->offset += block_todo; rf->block_left -= block_todo; if( rf->block_left == 0 ) { /* One more range done */ rf->rangesdone ++; } /* Move buf/len up past what we just read */ buf += block_todo; len -= block_todo; } if( len ) { /* More to read, not part of a block. We should be inside a multipart delimiter */ if( rf->multipart == -1 ) { /* In the middle of the MIME epilogue. Ignore */ break; } if( !rf->boundary ) { /* We weren't expecting a multipart delimiter. Abort. */ fprintf( stderr, "Not expecting more content from the server!\n" ); return 0; } /* Copy one line at a time into rf->buf (until CRLF) or until we run out of * stuff to copy. The purpose of buffering is that curl might split a line. */ while( len ) { /* Make sure we have enough space in rf->buf for this char and possibly a nul */ if( rf->buf_end >= (int)sizeof(rf->buf) - 2 ) { fprintf( stderr, "Overflow buffering multipart response!\n" ); return 0; } /* Copy one more character */ rf->buf[rf->buf_end++] = *buf; buf++; len--; /* Detect possible end-of-line (CRLF) */ if( rf->buf_end >= 2 && rf->buf[rf->buf_end-2] == '\r' && rf->buf[rf->buf_end-1] == '\n' ) { /* nul-terminate rf->buf before the CRLF */ rf->buf[rf->buf_end-2] = '\0'; rf->buf_end -= 2; if( rf->multipart == 0 ) { /* Expecting empty line to toss. Just confirm it. */ if( rf->buf_end > 0 ) { fprintf(stderr, "Missing CRLF before multipart boundary! [%s]\n", rf->buf); return 0; } rf->multipart ++; } else if( rf->multipart == 1 ) { /* Expecting --boundary */ size_t boundarysz = strlen(rf->boundary); if( (size_t)rf->buf_end < 2 + boundarysz /* too short */ || rf->buf[0] != '-' || rf->buf[1] != '-' /* no -- */ || memcmp(rf->buf+2,rf->boundary,boundarysz) /* no boundary string */ ) { fprintf(stderr, "Missing multipart boundary string! [%s]\n", rf->buf); return 0; } /* Two more dashes means that the multipart message is over */ if( (size_t)rf->buf_end >= 4 + boundarysz && rf->buf[2+boundarysz] == '-' && rf->buf[3+boundarysz] == '-' ) { rf->multipart = -1; /* Clear rf->boundary so we don't try to read another multipart delimiter */ free(rf->boundary); rf->boundary = NULL; } else { rf->multipart ++; } } else if( rf->multipart > 1 ) { /* Looking for content-range OR empty line */ if( rf->buf_end > strlen("content-range: bytes ") && strncasecmp(rf->buf, "content-range: bytes ", strlen("content-range: bytes ")) == 0 ) { /* This is the content-range line */ off_t from, to; if( parse_content_range( rf->buf + strlen("content-range: "), rf->buf_end - strlen("content-range: "), &from, &to) != 0 ) { /* This Content-Range header is bogus */ fprintf(stderr, "Server sent us a bogus Content-Range!\n" ); return 0; } /* Found from, to */ if( range_fetch_expect( rf, from, to ) != 0 ) { /* Error. range_fetch_expect has already printed a message, so just leave */ return 0; } rf->multipart ++; } else if( rf->buf_end == 0 ) { /* End of multipart delimiter */ rf->multipart = 0; } else { /* Uninteresting header line */ rf->multipart ++; } } /* Reset rf->buf */ rf->buf_end = 0; /* Break out of the multipart delimiter loop. We'll come back to it if we need to. */ break; } } } } return size * nmemb; }
static int process_line(URLContext *h, char *line, int line_count, int *new_location) { HTTPContext *s = h->priv_data; const char *auto_method = h->flags & AVIO_FLAG_READ ? "POST" : "GET"; char *tag, *p, *end, *method, *resource, *version; int ret; /* end of header */ if (line[0] == '\0') { s->end_header = 1; return 0; } p = line; if (line_count == 0) { if (s->listen) { // HTTP method method = p; while (!av_isspace(*p)) p++; *(p++) = '\0'; av_log(h, AV_LOG_TRACE, "Received method: %s\n", method); if (s->method) { if (av_strcasecmp(s->method, method)) { av_log(h, AV_LOG_ERROR, "Received and expected HTTP method do not match. (%s expected, %s received)\n", s->method, method); return ff_http_averror(400, AVERROR(EIO)); } } else { // use autodetected HTTP method to expect av_log(h, AV_LOG_TRACE, "Autodetected %s HTTP method\n", auto_method); if (av_strcasecmp(auto_method, method)) { av_log(h, AV_LOG_ERROR, "Received and autodetected HTTP method did not match " "(%s autodetected %s received)\n", auto_method, method); return ff_http_averror(400, AVERROR(EIO)); } } // HTTP resource while (av_isspace(*p)) p++; resource = p; while (!av_isspace(*p)) p++; *(p++) = '\0'; av_log(h, AV_LOG_TRACE, "Requested resource: %s\n", resource); // HTTP version while (av_isspace(*p)) p++; version = p; while (!av_isspace(*p)) p++; *p = '\0'; if (av_strncasecmp(version, "HTTP/", 5)) { av_log(h, AV_LOG_ERROR, "Malformed HTTP version string.\n"); return ff_http_averror(400, AVERROR(EIO)); } av_log(h, AV_LOG_TRACE, "HTTP version string: %s\n", version); } else { while (!av_isspace(*p) && *p != '\0') p++; while (av_isspace(*p)) p++; s->http_code = strtol(p, &end, 10); av_log(h, AV_LOG_TRACE, "http_code=%d\n", s->http_code); if ((ret = check_http_code(h, s->http_code, end)) < 0) return ret; } } else { while (*p != '\0' && *p != ':') p++; if (*p != ':') return 1; *p = '\0'; tag = line; p++; while (av_isspace(*p)) p++; if (!av_strcasecmp(tag, "Location")) { if ((ret = parse_location(s, p)) < 0) return ret; *new_location = 1; } else if (!av_strcasecmp(tag, "Content-Length") && s->filesize == -1) { s->filesize = strtoll(p, NULL, 10); } else if (!av_strcasecmp(tag, "Content-Range")) { parse_content_range(h, p); } else if (!av_strcasecmp(tag, "Accept-Ranges") && !strncmp(p, "bytes", 5) && s->seekable == -1) { h->is_streamed = 0; } else if (!av_strcasecmp(tag, "Transfer-Encoding") && !av_strncasecmp(p, "chunked", 7)) { s->filesize = -1; s->chunksize = 0; } else if (!av_strcasecmp(tag, "WWW-Authenticate")) { ff_http_auth_handle_header(&s->auth_state, tag, p); } else if (!av_strcasecmp(tag, "Authentication-Info")) { ff_http_auth_handle_header(&s->auth_state, tag, p); } else if (!av_strcasecmp(tag, "Proxy-Authenticate")) { ff_http_auth_handle_header(&s->proxy_auth_state, tag, p); } else if (!av_strcasecmp(tag, "Connection")) { if (!strcmp(p, "close")) s->willclose = 1; } else if (!av_strcasecmp(tag, "Server")) { if (!av_strcasecmp(p, "AkamaiGHost")) { s->is_akamai = 1; } else if (!av_strncasecmp(p, "MediaGateway", 12)) { s->is_mediagateway = 1; } } else if (!av_strcasecmp(tag, "Content-Type")) { av_free(s->mime_type); s->mime_type = av_strdup(p); } else if (!av_strcasecmp(tag, "Set-Cookie")) { if (parse_cookie(s, p, &s->cookie_dict)) av_log(h, AV_LOG_WARNING, "Unable to parse '%s'\n", p); } else if (!av_strcasecmp(tag, "Icy-MetaInt")) { s->icy_metaint = strtoll(p, NULL, 10); } else if (!av_strncasecmp(tag, "Icy-", 4)) { if ((ret = parse_icy(s, tag, p)) < 0) return ret; } else if (!av_strcasecmp(tag, "Content-Encoding")) { if ((ret = parse_content_encoding(h, p)) < 0) return ret; } } return 1; }
/* Curl HEADERFUNCTION. Gets headers one-by-one (including the HTTP status line) plus a range_fetch struct. */ size_t range_fetch_read_http_headers( void *ptr, size_t size, size_t nmemb, void *userdata ) { struct range_fetch *rf = (struct range_fetch *)userdata; char *buf = (char *)ptr; size_t len = size * nmemb; char *end = buf + len; /* Keep bytes_down up-to-date */ rf->bytes_down += len; /* Look for HTTP status line */ if( len >= strlen("HTTP/1.1 XXX") && memcmp(buf, "HTTP/1.1 ", strlen("HTTP/1.1 ")) == 0 ) { /* If previous HTTP code was 2xx, something bad is happening. We should only get * one 2xx (previous codes in this request chain would have been redirects, if anything) */ if( rf->http_code >= 200 && rf->http_code < 300 ) { fprintf(stderr, "Not expecting to see further HTTP responses after a 2xx (last code was %d)!\n", rf->http_code); return 0; } /* Remember most recent HTTP code */ rf->http_code = (int)(buf[9]-'0')*100 + (int)(buf[10]-'0')*10 + (int)(buf[11]-'0'); } /* HTTP 200 + Content-Length -> Entire file */ else if( rf->http_code == 200 && len > strlen("content-length: x") && strncasecmp(buf, "content-length: ", strlen("content-length: ")) == 0) { off_t to = (off_t)strtoll(buf + strlen("content-length: "), (void*)(buf + len), 10) - 1; /* Found to, and from = 0 */ if( range_fetch_expect( rf, 0, to ) != 0 ) { /* Error. range_fetch_expect has already printed a message, so just leave */ return 0; } } /* HTTP 206 + Content-Range -> Single range */ else if( rf->http_code == 206 && len > strlen("content-range: bytes ") && strncasecmp(buf, "content-range: bytes ", strlen("content-range: bytes ")) == 0 ) { /* Okay, we're getting a non-MIME block from the remote. */ off_t from, to; if( parse_content_range(buf + strlen("content-range: "), len - strlen("content-range: "), &from, &to) != 0 ) { /* This Content-Range header is bogus */ fprintf(stderr, "Server sent us a bogus Content-Range!\n" ); return 0; } /* Found from, to */ if( range_fetch_expect( rf, from, to ) != 0 ) { /* Error. range_fetch_expect has already printed a message, so just leave */ return 0; } } /* HTTP 206 + Content-Type multipart/byteranges -> Multiple ranges */ else if( rf->http_code == 206 && len > strlen("content-type: multipart/byteranges") && strncasecmp(buf, "content-type: multipart/byteranges", strlen("content-type: multipart/byteranges")) == 0 ) { char *p; /* Goal is the multipart boundary string */ /* We don't expect it to be set already... */ if( rf->boundary ) { fprintf(stderr, "Not expecting to see more than one multipart/byteranges response!\n"); return 0; } /* Place pointer after "multipart/byteranges" */ p = buf + strlen("content-type: multipart/byteranges"); /* Look for the boundary= string */ while( p + strlen("boundary=") <= end ) { if( memcmp(p, "boundary=", strlen("boundary=")) == 0 ) break; p++; } if( p + strlen("boundary=") < end ) { char *boundary; int quoted = 0; /* Found the boundary= string */ p += strlen("boundary="); /* Allocate a buffer that might be a little big */ rf->boundary = boundary = malloc(end-p+1); if(!boundary) { perror("malloc"); return 0; } /* Possibly skip a quote mark */ if( *p == '"' ) { quoted = 1; p++; } /* Copy until the of the boundary */ while( p < end ) { if( *p == 0 ) break; if( quoted && *p == '"' ) break; if( !quoted && ( *p == '\r' || *p == ' ' || *p == '\n' ) ) break; *boundary = *p; boundary++; p++; } /* nul-terminate the boundary */ *boundary = 0; } } return len; }
static int process_line(URLContext *h, char *line, int line_count, int *new_location) { HTTPContext *s = h->priv_data; char *tag, *p, *end; int ret; /* end of header */ if (line[0] == '\0') { s->end_header = 1; return 0; } p = line; if (line_count == 0) { while (!av_isspace(*p) && *p != '\0') p++; while (av_isspace(*p)) p++; s->http_code = strtol(p, &end, 10); av_log(NULL, AV_LOG_TRACE, "http_code=%d\n", s->http_code); if ((ret = check_http_code(h, s->http_code, end)) < 0) return ret; } else { while (*p != '\0' && *p != ':') p++; if (*p != ':') return 1; *p = '\0'; tag = line; p++; while (av_isspace(*p)) p++; if (!av_strcasecmp(tag, "Location")) { if ((ret = parse_location(s, p)) < 0) return ret; *new_location = 1; } else if (!av_strcasecmp(tag, "Content-Length") && s->filesize == -1) { s->filesize = strtoll(p, NULL, 10); } else if (!av_strcasecmp(tag, "Content-Range")) { parse_content_range(h, p); } else if (!av_strcasecmp(tag, "Accept-Ranges") && !strncmp(p, "bytes", 5)) { h->is_streamed = 0; } else if (!av_strcasecmp(tag, "Transfer-Encoding") && !av_strncasecmp(p, "chunked", 7)) { s->filesize = -1; s->chunksize = 0; } else if (!av_strcasecmp(tag, "WWW-Authenticate")) { ff_http_auth_handle_header(&s->auth_state, tag, p); } else if (!av_strcasecmp(tag, "Authentication-Info")) { ff_http_auth_handle_header(&s->auth_state, tag, p); } else if (!av_strcasecmp(tag, "Proxy-Authenticate")) { ff_http_auth_handle_header(&s->proxy_auth_state, tag, p); } else if (!av_strcasecmp(tag, "Connection")) { if (!strcmp(p, "close")) s->willclose = 1; } else if (!av_strcasecmp(tag, "Content-Type")) { av_free(s->mime_type); s->mime_type = av_strdup(p); } else if (!av_strcasecmp(tag, "Icy-MetaInt")) { s->icy_metaint = strtoll(p, NULL, 10); } else if (!av_strncasecmp(tag, "Icy-", 4)) { if ((ret = parse_icy(s, tag, p)) < 0) return ret; } else if (!av_strcasecmp(tag, "Content-Encoding")) { if ((ret = parse_content_encoding(h, p)) < 0) return ret; } } return 1; }