Example #1
0
/// \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;
}
Example #2
0
/* 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;
}
Example #3
0
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;
}
Example #4
0
/* 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;
}
Example #5
0
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;
}