Example #1
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 #2
0
static int64_t neon_fread_real (void * ptr, int64_t size, int64_t nmemb, VFSFile * file)
{
    struct neon_handle * h = vfs_get_handle (file);

    if (! h->request)
    {
        _ERROR ("<%p> No request to read from, seek gone wrong?", (void *) h);
        return 0;
    }

    if (! size || ! nmemb || h->eof)
        return 0;

    /* If the buffer is empty, wait for the reader thread to fill it. */
    pthread_mutex_lock (& h->reader_status.mutex);

    for (int retries = 0; retries < NEON_RETRY_COUNT; retries ++)
    {
        if (used_rb_locked (& h->rb) / size > 0 || ! h->reader_status.reading ||
         h->reader_status.status != NEON_READER_RUN)
            break;

        pthread_cond_broadcast (& h->reader_status.cond);
        pthread_cond_wait (& h->reader_status.cond, & h->reader_status.mutex);
    }

    pthread_mutex_unlock (& h->reader_status.mutex);

    if (! h->reader_status.reading)
    {
        if (h->reader_status.status != NEON_READER_EOF || h->content_length != -1)
        {
            /* There is no reader thread yet. Read the first bytes from
             * the network ourselves, and then fire up the reader thread
             * to keep the buffer filled up. */
            _DEBUG ("<%p> Doing initial buffer fill", h);
            FillBufferResult ret = fill_buffer (h);

            if (ret == FILL_BUFFER_ERROR)
            {
                _ERROR ("<%p> Error while reading from the network", (void *) h);
                return 0;
            }

            /* We have some data in the buffer now.
             * Start the reader thread if we did not reach EOF during
             * the initial fill */
            pthread_mutex_lock (& h->reader_status.mutex);

            if (ret == FILL_BUFFER_SUCCESS)
            {
                h->reader_status.reading = TRUE;
                _DEBUG ("<%p> Starting reader thread", h);
                pthread_create (& h->reader, NULL, reader_thread, h);
                h->reader_status.status = NEON_READER_RUN;
            }
            else if (ret == FILL_BUFFER_EOF)
            {
                _DEBUG ("<%p> No reader thread needed (stream has reached EOF during fill)", h);
                h->reader_status.reading = FALSE;
                h->reader_status.status = NEON_READER_EOF;
            }

            pthread_mutex_unlock (& h->reader_status.mutex);
        }
    }
    else
    {
        /* There already is a reader thread. Look if it is in good shape. */
        pthread_mutex_lock (& h->reader_status.mutex);

        switch (h->reader_status.status)
        {
        case NEON_READER_INIT:
        case NEON_READER_RUN:
            /* All is well, nothing to be done. */
            break;

        case NEON_READER_ERROR:
            /* A reader error happened. Log it, and treat it like an EOF
             * condition, by falling through to the NEON_READER_EOF codepath. */
            _DEBUG ("<%p> NEON_READER_ERROR happened. Terminating reader thread and marking EOF.", h);
            h->reader_status.status = NEON_READER_EOF;
            pthread_mutex_unlock (& h->reader_status.mutex);

            if (h->reader_status.reading)
                kill_reader (h);

            pthread_mutex_lock (& h->reader_status.mutex);

        case NEON_READER_EOF:
            /* If there still is data in the buffer, carry on.
             * If not, terminate the reader thread and return 0. */
            if (! used_rb_locked (& h->rb))
            {
                _DEBUG ("<%p> Reached end of stream", h);
                pthread_mutex_unlock (& h->reader_status.mutex);

                if (h->reader_status.reading)
                    kill_reader (h);

                h->eof = TRUE;
                return 0;
            }

            break;

        case NEON_READER_TERM:
            /* The reader thread terminated gracefully, most likely on our own request.
             * We should not get here. */
            g_warn_if_reached ();
            pthread_mutex_unlock (& h->reader_status.mutex);
            return 0;
        }

        pthread_mutex_unlock (& h->reader_status.mutex);
    }

    /* Deliver data from the buffer */
    if (! used_rb (& h->rb))
    {
        /* The buffer is still empty, we can deliver no data! */
        _ERROR ("<%p> Buffer still underrun, fatal.", (void *) h);
        return 0;
    }

    int belem = used_rb (& h->rb) / size;

    if (h->icy_metaint)
    {
        if (! h->icy_metaleft)
        {
            /* The next data in the buffer is a ICY metadata announcement.
             * Get the length byte */
            unsigned char icy_metalen;
            read_rb (& h->rb, & icy_metalen, 1);

            /*  We need enough data in the buffer to
             * a) Read the complete ICY metadata block
             * b) deliver at least one byte to the reader */
            _DEBUG ("<%p> Expecting %d bytes of ICY metadata", h, (icy_metalen*16));

            if (free_rb (& h->rb) - icy_metalen * 16 < size)
            {
                /* There is not enough data. We do not have much choice at this point,
                 * so we'll deliver the metadata as normal data to the reader and
                 * hope for the best. */
                _ERROR ("<%p> Buffer underrun when reading metadata. Expect "
                 "audio degradation", (void *) h);
                h->icy_metaleft = h->icy_metaint + icy_metalen * 16;
            }
            else
            {
                /* Grab the metadata from the buffer and send it to the parser */
                char icy_metadata[NEON_ICY_BUFSIZE];
                read_rb (& h->rb, icy_metadata, icy_metalen * 16);
                parse_icy (& h->icy_metadata, icy_metadata, icy_metalen * 16);
                h->icy_metaleft = h->icy_metaint;
            }
        }

        /* The maximum number of bytes we can deliver is determined
         * by the number of bytes left until the next metadata announcement */
        belem = MIN (used_rb (& h->rb), h->icy_metaleft) / size;
    }

    nmemb = MIN (belem, nmemb);
    read_rb (& h->rb, ptr, nmemb * size);

    /* Signal the network thread to continue reading */
    pthread_mutex_lock (& h->reader_status.mutex);

    if (h->reader_status.status == NEON_READER_EOF)
    {
        if (! free_rb_locked (& h->rb))
        {
            _DEBUG ("<%p> stream EOF reached and buffer empty", h);
            h->eof = TRUE;
        }
    }
    else
        pthread_cond_broadcast (& h->reader_status.cond);

    pthread_mutex_unlock (& h->reader_status.mutex);

    h->pos += nmemb * size;
    h->icy_metaleft -= nmemb * size;

    return nmemb;
}
Example #3
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;
}