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; }
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; }
int vlc_http_file_get_status(struct vlc_http_file *file) { if (file->resp == NULL) { file->resp = vlc_http_file_open(file, file->offset); if (file->resp == NULL) return -1; } return vlc_http_msg_get_status(file->resp); }
int vlc_http_res_get_status(struct vlc_http_resource *res) { if (res->response == NULL) { if (res->failure) return -1; res->response = vlc_http_res_open(res, res + 1); if (res->response == NULL) { res->failure = true; return -1; } } return vlc_http_msg_get_status(res->response); }
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 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; }
struct vlc_http_msg *vlc_http_msg_get_final(struct vlc_http_msg *m) { while (m != NULL && (vlc_http_msg_get_status(m) / 100) == 1) m = vlc_http_msg_iterate(m); return m; }
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; }