int vlc_http_file_seek(struct vlc_http_file *file, uintmax_t offset) { struct vlc_http_msg *resp = vlc_http_file_open(file, offset); if (resp == NULL) return -1; int status = vlc_http_msg_get_status(resp); if (file->resp != NULL) { /* Accept the new and ditch the old one if: * - requested succeeded and range was accepted (206), * - requested failed due to out-of-range (416), * - request succeeded and seek offset is zero (2xx). */ if (status != 206 && status != 416 && (offset != 0 || status >= 300)) { vlc_http_msg_destroy(resp); return -1; } vlc_http_msg_destroy(file->resp); } file->resp = resp; file->offset = offset; return 0; }
void vlc_http_file_destroy(struct vlc_http_file *file) { if (file->resp != NULL) vlc_http_msg_destroy(file->resp); vlc_http_res_deinit(&file->resource); free(file); }
struct vlc_http_msg * vlc_http_req_create(const char *method, const char *scheme, const char *authority, const char *path) { struct vlc_http_msg *m = malloc(sizeof (*m)); if (unlikely(m == NULL)) return NULL; assert(method != NULL); m->status = -1; m->method = strdup(method); m->scheme = (scheme != NULL) ? strdup(scheme) : NULL; m->authority = (authority != NULL) ? strdup(authority) : NULL; m->path = (path != NULL) ? strdup(path) : NULL; m->count = 0; m->headers = NULL; m->payload = NULL; if (unlikely(m->method == NULL || (scheme != NULL && m->scheme == NULL) || (authority != NULL && m->authority == NULL) || (path != NULL && m->path == NULL))) { /* LVP added */ //fprintf(stderr, "LVP msg_destroy because problem while creating the request\n"); vlc_http_msg_destroy(m); m = NULL; } return m; }
struct vlc_http_msg * vlc_http_req_create(const char *method, const char *scheme, const char *authority, const char *path) { struct vlc_http_msg *m = malloc(sizeof (*m)); if (unlikely(m == NULL)) return NULL; assert(method != NULL); m->status = -1; m->method = strdup(method); m->scheme = (scheme != NULL) ? strdup(scheme) : NULL; m->authority = (authority != NULL) ? strdup(authority) : NULL; m->path = (path != NULL) ? strdup(path) : NULL; m->count = 0; m->headers = NULL; m->payload = NULL; if (unlikely(m->method == NULL || (scheme != NULL && m->scheme == NULL) || (authority != NULL && m->authority == NULL) || (path != NULL && m->path == NULL))) { vlc_http_msg_destroy(m); m = NULL; } return m; }
static struct vlc_http_msg *vlc_http_file_open(struct vlc_http_file *file, uintmax_t offset) { struct vlc_http_msg *resp; resp = vlc_http_res_open(&file->resource, vlc_http_file_req, &offset); if (resp == NULL) return NULL; int status = vlc_http_msg_get_status(resp); if (status == 206) { const char *str = vlc_http_msg_get_header(resp, "Content-Range"); if (str == NULL) /* A multipart/byteranges response. This is not what we asked for * and we do not support it. */ goto fail; uintmax_t start, end; if (sscanf(str, "bytes %ju-%ju", &start, &end) != 2 || start != offset || start > end) /* A single range response is what we asked for, but not at that * start offset. */ goto fail; } return resp; fail: vlc_http_msg_destroy(resp); errno = EIO; return NULL; }
struct vlc_http_msg *vlc_http_msg_iterate(struct vlc_http_msg *m) { struct vlc_http_msg *next = vlc_http_stream_read_headers(m->payload); m->payload = NULL; vlc_http_msg_destroy(m); return next; }
struct vlc_http_msg *vlc_http_res_open(struct vlc_http_resource *res, void *opaque) { struct vlc_http_msg *req; retry: req = vlc_http_res_req(res, opaque); if (unlikely(req == NULL)) return NULL; struct vlc_http_msg *resp = vlc_http_mgr_request(res->manager, res->secure, res->host, res->port, req); vlc_http_msg_destroy(req); resp = vlc_http_msg_get_final(resp); if (resp == NULL) return NULL; vlc_http_msg_get_cookies(resp, vlc_http_mgr_get_jar(res->manager), res->secure, res->host, res->path); int status = vlc_http_msg_get_status(resp); if (status < 200 || status >= 599) goto fail; if (status == 406 && res->negotiate) { /* Not Acceptable: Content negotiation failed. Normally it means * one (or more) Accept or Accept-* header line does not match any * representation of the entity. So we set a flag to remove those * header lines (unless they accept everything), and retry. * In principles, it could be any header line, and the server can * pass Vary to clarify. It cannot be caused by If-*, Range, TE or the * other transfer- rather than representation-affecting header lines. */ vlc_http_msg_destroy(resp); res->negotiate = false; goto retry; } if (res->cbs->response_validate(res, resp, opaque)) goto fail; return resp; fail: vlc_http_msg_destroy(resp); return NULL; }
static void stream_reply(uint_fast32_t id, bool nodata) { struct vlc_http_msg *m = vlc_http_resp_create(200); assert(m != NULL); vlc_http_msg_add_agent(m, "VLC-h2-tester"); conn_send(vlc_http_msg_h2_frame(m, id, nodata)); vlc_http_msg_destroy(m); }
static struct vlc_http_stream *stream_open(void) { struct vlc_http_msg *m = vlc_http_req_create("GET", "https", "www.example.com", "/"); assert(m != NULL); struct vlc_http_stream *s = vlc_http_stream_open(conn, m); vlc_http_msg_destroy(m); return s; }
static void vlc_http_res_deinit(struct vlc_http_resource *res) { free(res->referrer); free(res->agent); free(res->path); free(res->authority); free(res->host); if (res->response != NULL) vlc_http_msg_destroy(res->response); }
struct vlc_http_msg *vlc_http_msg_iterate(struct vlc_http_msg *m) { struct vlc_http_msg *next = vlc_http_stream_read_headers(m->payload); m->payload = NULL; vlc_http_msg_destroy(m); /* LVP added */ //fprintf(stderr, "LVP msg_destroy inside of msg_iterate\n"); return next; }
static struct vlc_http_msg *vlc_http_tunnel_open(struct vlc_http_conn *conn, const char *hostname, unsigned port) { char *authority = vlc_http_authority(hostname, port); if (authority == NULL) return NULL; struct vlc_http_msg *req = vlc_http_req_create("CONNECT", NULL, authority, NULL); free(authority); if (unlikely(req == NULL)) return NULL; vlc_http_msg_add_header(req, "ALPN", "h2, http%%2F1.1"); vlc_http_msg_add_agent(req, PACKAGE_NAME "/" PACKAGE_VERSION); struct vlc_http_stream *stream = vlc_http_stream_open(conn, req); vlc_http_msg_destroy(req); if (stream == NULL) return NULL; struct vlc_http_msg *resp = vlc_http_msg_get_initial(stream); resp = vlc_http_msg_get_final(resp); if (resp == NULL) return NULL; int status = vlc_http_msg_get_status(resp); if ((status / 100) != 2) { vlc_http_msg_destroy(resp); resp = NULL; } return resp; }
static struct vlc_http_msg * vlc_http_res_req(const struct vlc_http_resource *res, void *opaque) { struct vlc_http_msg *req; req = vlc_http_req_create("GET", res->secure ? "https" : "http", res->authority, res->path); if (unlikely(req == NULL)) return NULL; /* Content negotiation */ vlc_http_msg_add_header(req, "Accept", "*/*"); if (res->negotiate) { const char *lang = vlc_gettext("C"); if (!strcmp(lang, "C")) lang = "en_US"; vlc_http_msg_add_header(req, "Accept-Language", "%s", lang); } /* Authentication */ if (res->username != NULL) vlc_http_msg_add_creds_basic(req, false, res->username, res->password); /* Request context */ if (res->agent != NULL) vlc_http_msg_add_agent(req, res->agent); if (res->referrer != NULL) /* TODO: validate URL */ vlc_http_msg_add_header(req, "Referer", "%s", res->referrer); vlc_http_msg_add_cookies(req, vlc_http_mgr_get_jar(res->manager)); /* TODO: vlc_http_msg_add_header(req, "TE", "gzip, deflate"); */ if (res->cbs->request_format(res, req, opaque)) { vlc_http_msg_destroy(req); return NULL; } return req; }
/** Reports received stream headers */ static void vlc_h2_stream_headers(void *ctx, unsigned count, char *hdrs[][2]) { struct vlc_h2_stream *s = ctx; /* NOTE: HTTP message trailers are not supported so far. Follow-up headers * can therefore only be a final response after a 1xx continue response. * Then it is safe to discard the existing header. */ if (s->recv_hdr != NULL) { msg_Dbg(SO(s), "stream %"PRIu32" discarding old headers", s->id); vlc_http_msg_destroy(s->recv_hdr); s->recv_hdr = NULL; } msg_Dbg(SO(s), "stream %"PRIu32" %u headers:", s->id, count); for (unsigned i = 0; i < count; i++) msg_Dbg(SO(s), " %s: \"%s\"", hdrs[i][0], hdrs[i][1]); s->recv_hdr = vlc_http_msg_h2_headers(count, hdrs); if (unlikely(s->recv_hdr == NULL)) vlc_h2_stream_fatal(s, VLC_H2_PROTOCOL_ERROR); vlc_cond_signal(&s->recv_wait); }
/** * Terminates a stream. * * Sends an HTTP/2 stream reset, removes the stream from the HTTP/2 connection * and deletes any stream resource. */ static void vlc_h2_stream_close(struct vlc_http_stream *stream) { struct vlc_h2_stream *s = (struct vlc_h2_stream *)stream; struct vlc_h2_conn *conn = s->conn; bool destroy = false; vlc_mutex_lock(&conn->lock); if (s->older != NULL) s->older->newer = s->newer; if (s->newer != NULL) s->newer->older = s->older; else { assert(conn->streams == s); conn->streams = s->older; destroy = (conn->streams == NULL) && conn->released; } vlc_mutex_unlock(&conn->lock); vlc_h2_stream_error(conn, s->id, VLC_H2_NO_ERROR); if (s->recv_hdr != NULL) vlc_http_msg_destroy(s->recv_hdr); for (struct vlc_h2_frame *f = s->recv_head, *next; f != NULL; f = next) { next = f->next; free(f); } vlc_cond_destroy(&s->recv_wait); free(s); if (destroy) vlc_h2_conn_destroy(conn); }
int main(void) { struct vlc_http_stream *s; struct vlc_http_msg *m; struct block_t *b; /* Dummy */ conn_create(); conn_destroy(); /* Test rejected connection */ conn_create(); conn_shutdown(SHUT_RD); s = stream_open(); assert(s == NULL); conn_destroy(); /* Test rejected stream */ conn_create(); s = stream_open(); assert(s != NULL); conn_shutdown(SHUT_WR); m = vlc_http_stream_read_headers(s); assert(m == NULL); b = vlc_http_stream_read(s); assert(b == NULL); m = vlc_http_stream_read_headers(s); assert(m == NULL); b = vlc_http_stream_read(s); assert(b == NULL); m = vlc_http_msg_get_initial(s); assert(m == NULL); s = stream_open(); assert(s == NULL); conn_destroy(); /* Test garbage */ conn_create(); s = stream_open(); assert(s != NULL); conn_send("Go away!\r\n\r\n"); conn_shutdown(SHUT_WR); m = vlc_http_stream_read_headers(s); assert(m == NULL); b = vlc_http_stream_read(s); assert(b == NULL); conn_destroy(); vlc_http_stream_close(s, false); /* Test HTTP/1.0 stream */ conn_create(); s = stream_open(); assert(s != NULL); conn_send("HTTP/1.0 200 OK\r\n\r\n"); m = vlc_http_msg_get_initial(s); assert(m != NULL); conn_send("Hello world!"); conn_shutdown(SHUT_WR); b = vlc_http_msg_read(m); assert(b != NULL); assert(b->i_buffer == 12); assert(!memcmp(b->p_buffer, "Hello world!", 12)); block_Release(b); b = vlc_http_msg_read(m); assert(b == NULL); vlc_http_msg_destroy(m); conn_destroy(); /* Test HTTP/1.1 with closed connection */ conn_create(); s = stream_open(); assert(s != NULL); conn_send("HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n"); m = vlc_http_msg_get_initial(s); assert(m != NULL); conn_send("Hello again!"); conn_shutdown(SHUT_WR); b = vlc_http_msg_read(m); assert(b != NULL); assert(b->i_buffer == 12); assert(!memcmp(b->p_buffer, "Hello again!", 12)); block_Release(b); b = vlc_http_msg_read(m); assert(b == NULL); vlc_http_msg_destroy(m); conn_destroy(); /* Test HTTP/1.1 with chunked transfer encoding */ conn_create(); s = stream_open(); assert(s != NULL); conn_send("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n" "Content-Length: 1000000\r\n\r\n"); /* length must be ignored */ m = vlc_http_msg_get_initial(s); assert(m != NULL); conn_send("C\r\nHello there!\r\n0\r\n\r\n"); b = vlc_http_msg_read(m); assert(b != NULL); assert(b->i_buffer == 12); assert(!memcmp(b->p_buffer, "Hello there!", 12)); block_Release(b); conn_destroy(); /* test connection release before closing stream */ b = vlc_http_msg_read(m); assert(b == NULL); vlc_http_msg_destroy(m); /* Test HTTP/1.1 with content length */ conn_create(); s = stream_open(); assert(s != NULL); conn_send("HTTP/1.1 200 OK\r\nContent-Length: 8\r\n\r\n"); m = vlc_http_msg_get_initial(s); assert(m != NULL); conn_send("Bye bye!"); b = vlc_http_msg_read(m); assert(b != NULL); assert(b->i_buffer == 8); assert(!memcmp(b->p_buffer, "Bye bye!", 8)); block_Release(b); b = vlc_http_msg_read(m); assert(b == NULL); vlc_http_msg_destroy(m); conn_destroy(); return 0; }
int main(void) { struct vlc_http_stream *s, *s2; struct vlc_http_msg *m; struct block_t *b; uint_fast32_t sid = -1; /* Second guessed stream IDs :-/ */ conn_create(); conn_destroy(); conn_create(); conn_send(vlc_h2_frame_ping(42)); conn_expect(PING); /* Test rejected stream */ sid += 2; s = stream_open(); assert(s != NULL); conn_expect(HEADERS); conn_send(vlc_h2_frame_rst_stream(sid, VLC_H2_REFUSED_STREAM)); m = vlc_http_stream_read_headers(s); assert(m == NULL); b = vlc_http_stream_read(s); assert(b == NULL); vlc_http_stream_close(s, false); conn_expect(RST_STREAM); /* Test accepted stream */ sid += 2; s = stream_open(); assert(s != NULL); stream_reply(sid, false); m = vlc_http_msg_get_initial(s); assert(m != NULL); vlc_http_msg_destroy(m); stream_data(3, "Hello ", false); /* late data */ stream_data(3, "world!", true); conn_expect(HEADERS); conn_expect(RST_STREAM); conn_expect(RST_STREAM); conn_expect(RST_STREAM); /* Test continuation then accepted stream */ sid += 2; s = stream_open(); assert(s != NULL); stream_continuation(sid); m = vlc_http_msg_get_initial(s); assert(m != NULL); assert(vlc_http_msg_get_status(m) == 100); stream_reply(sid, false); m = vlc_http_msg_iterate(m); assert(m != NULL); stream_data(sid, "Hello ", false); stream_data(sid, "world!", true); stream_data(sid, "Stray message", false); /* data after EOS */ b = vlc_http_msg_read(m); assert(b != NULL); block_Release(b); b = vlc_http_msg_read(m); assert(b != NULL); block_Release(b); b = vlc_http_msg_read(m); assert(b == NULL); vlc_http_msg_destroy(m); conn_expect(HEADERS); conn_expect(RST_STREAM); conn_expect(RST_STREAM); /* Test accepted stream after continuation */ sid += 2; s = stream_open(); assert(s != NULL); stream_continuation(sid); stream_reply(sid, true); sid += 2; s2 = stream_open(); /* second stream to enforce test timing/ordering */ assert(s2 != NULL); stream_reply(sid, true); m = vlc_http_msg_get_initial(s2); assert(m != NULL); vlc_http_msg_destroy(m); m = vlc_http_msg_get_initial(s); assert(m != NULL); assert(vlc_http_msg_get_status(m) == 200); b = vlc_http_msg_read(m); assert(b == NULL); vlc_http_msg_destroy(m); conn_expect(HEADERS); conn_expect(HEADERS); conn_expect(RST_STREAM); conn_expect(RST_STREAM); /* Test nonexistent stream reset */ conn_send(vlc_h2_frame_rst_stream(sid + 100, VLC_H2_REFUSED_STREAM)); /* Test multiple streams in non-LIFO order */ sid += 2; s = stream_open(); assert(s != NULL); sid += 2; s2 = stream_open(); assert(s2 != NULL); stream_reply(sid, false); stream_reply(sid - 2, true); stream_data(sid, "Discarded", false); /* not read data */ m = vlc_http_msg_get_initial(s); assert(m != NULL); vlc_http_msg_destroy(m); m = vlc_http_msg_get_initial(s2); assert(m != NULL); vlc_http_msg_destroy(m); conn_expect(HEADERS); conn_expect(HEADERS); conn_expect(RST_STREAM); conn_expect(RST_STREAM); /* might or might not seen one or two extra RST_STREAM now */ /* Test graceful connection termination */ sid += 2; s = stream_open(); assert(s != NULL); conn_send(vlc_h2_frame_goaway(sid - 2, VLC_H2_NO_ERROR)); m = vlc_http_stream_read_headers(s); assert(m == NULL); /* Test stream after connection shut down */ assert(stream_open() == NULL); /* Test releasing connection before stream */ conn_destroy(); vlc_http_stream_close(s, false); return 0; }