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; }
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; }
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; }