Commit c1df2e66 authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

https: factor common connection management code

parent 3dbc71f3
......@@ -9,8 +9,8 @@ libvlc_http_la_SOURCES = \
access/http/hpack.c access/http/hpack.h access/http/hpackenc.c \
access/http/h2frame.c access/http/h2frame.h \
access/http/h2output.c access/http/h2output.h \
access/http/h2conn.c access/http/h2conn.h \
access/http/h1conn.c access/http/h1conn.h access/http/chunked.c \
access/http/h2conn.c access/http/h1conn.c \
access/http/chunked.c access/http/conn.h \
access/http/connmgr.c access/http/connmgr.h
libvlc_http_la_CPPFLAGS = -Dneedsomethinghere
libvlc_http_la_LIBADD = \
......
......@@ -30,11 +30,10 @@
#include <string.h>
#include <vlc_common.h>
#include <vlc_block.h>
#include <vlc_tls.h> /* FIXME: remove this and test */
#include <vlc_tls.h> /* TODO: remove this */
#include "message.h"
#include "transport.h"
#include "h1conn.h"
#include "conn.h"
struct vlc_chunked_stream
{
......
......@@ -25,14 +25,13 @@
#undef NDEBUG
#include <assert.h>
//#include <inttypes.h>
//#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <vlc_common.h>
#include <vlc_tls.h>
#include <vlc_block.h>
#include "h1conn.h"
#include "conn.h"
#include "message.h"
/* I/O callbacks */
......
/*****************************************************************************
* h2conn.h: HTTP/2 connection handling
* conn.h: HTTP connections
*****************************************************************************
* Copyright (C) 2015 Rémi Denis-Courmont
*
......@@ -19,12 +19,36 @@
*****************************************************************************/
struct vlc_tls;
struct vlc_http_conn;
struct vlc_http_msg;
struct vlc_http_stream;
struct vlc_h2_conn;
struct vlc_http_conn_cbs
{
struct vlc_http_stream *(*stream_open)(struct vlc_http_conn *,
const struct vlc_http_msg *);
void (*release)(struct vlc_http_conn *);
};
struct vlc_h2_conn *vlc_h2_conn_create(struct vlc_tls *tls);
void vlc_h2_conn_release(struct vlc_h2_conn *conn);
struct vlc_http_conn
{
const struct vlc_http_conn_cbs *cbs;
struct vlc_tls *tls;
};
struct vlc_http_stream *vlc_h2_stream_open(struct vlc_h2_conn *conn,
const struct vlc_http_msg *msg);
static inline struct vlc_http_stream *
vlc_http_stream_open(struct vlc_http_conn *conn, const struct vlc_http_msg *m)
{
return conn->cbs->stream_open(conn, m);
}
static inline void vlc_http_conn_release(struct vlc_http_conn *conn)
{
conn->cbs->release(conn);
}
struct vlc_http_conn *vlc_h1_conn_create(struct vlc_tls *);
struct vlc_http_conn *vlc_h2_conn_create(struct vlc_tls *);
struct vlc_http_stream *vlc_chunked_open(struct vlc_http_stream *,
struct vlc_tls *);
......@@ -27,8 +27,7 @@
#include <vlc_tls.h>
#include <vlc_interrupt.h>
#include "transport.h"
#include "h1conn.h"
#include "h2conn.h"
#include "conn.h"
#include "connmgr.h"
#include "message.h"
......@@ -88,64 +87,48 @@ static vlc_tls_t *vlc_https_connect_i11e(vlc_tls_creds_t *creds,
struct vlc_http_mgr
{
vlc_tls_creds_t *creds;
struct vlc_h1_conn *conn1;
struct vlc_h2_conn *conn2;
struct vlc_http_conn *conn;
};
static struct vlc_h1_conn *vlc_h1_conn_find(struct vlc_http_mgr *mgr,
static struct vlc_http_conn *vlc_http_mgr_find(struct vlc_http_mgr *mgr,
const char *host, unsigned port)
{
(void) host; (void) port;
return mgr->conn1;
return mgr->conn;
}
static struct vlc_h2_conn *vlc_h2_conn_find(struct vlc_http_mgr *mgr,
const char *host, unsigned port)
static void vlc_http_mgr_release(struct vlc_http_mgr *mgr,
struct vlc_http_conn *conn)
{
(void) host; (void) port;
return mgr->conn2;
assert(mgr->conn == conn);
mgr->conn = NULL;
vlc_http_conn_release(conn);
}
static
struct vlc_http_msg *vlc_https_request_reuse(struct vlc_http_mgr *mgr,
struct vlc_http_msg *vlc_http_mgr_reuse(struct vlc_http_mgr *mgr,
const char *host, unsigned port,
const struct vlc_http_msg *req)
{
struct vlc_h2_conn *conn2 = vlc_h2_conn_find(mgr, host, port);
if (conn2 != NULL)
{
struct vlc_http_stream *s = vlc_h2_stream_open(conn2, req);
if (s != NULL)
{
struct vlc_http_msg *m = vlc_http_stream_read_headers(s);
if (m != NULL)
return m;
vlc_http_stream_close(s, false);
/* NOTE: If the request were not idempotent, NULL should be
* returned here. POST is not used/supported so far, and CONNECT is
* treated as if it were idempotent (which turns out OK here). */
}
/* Get rid of closing or reset connection */
vlc_h2_conn_release(conn2);
mgr->conn2 = NULL;
}
struct vlc_http_conn *conn = vlc_http_mgr_find(mgr, host, port);
if (conn == NULL)
return NULL;
struct vlc_h1_conn *conn1 = vlc_h1_conn_find(mgr, host, port);
if (conn1 != NULL)
struct vlc_http_stream *stream = vlc_http_stream_open(conn, req);
if (stream != NULL)
{
struct vlc_http_stream *s = vlc_h1_stream_open(conn1, req);
if (s != NULL)
{
struct vlc_http_msg *m = vlc_http_stream_read_headers(s);
struct vlc_http_msg *m = vlc_http_stream_read_headers(stream);
if (m != NULL)
return m;
vlc_http_stream_close(s, false);
}
vlc_h1_conn_release(conn1);
mgr->conn1 = NULL;
vlc_http_stream_close(stream, false);
/* NOTE: If the request were not idempotent, we do not know if it was
* process by the other end. So POST is not used/supported so far, and
* CONNECT is treated as if it were idempotent (which is OK here). */
}
/* Get rid of closing or reset connection */
vlc_http_mgr_release(mgr, conn);
return NULL;
}
......@@ -154,34 +137,31 @@ struct vlc_http_msg *vlc_https_request(struct vlc_http_mgr *mgr,
const struct vlc_http_msg *req)
{
/* TODO? non-idempotent request support */
struct vlc_http_msg *resp = vlc_https_request_reuse(mgr, host, port, req);
struct vlc_http_msg *resp = vlc_http_mgr_reuse(mgr, host, port, req);
if (resp != NULL)
return resp;
bool http2;
vlc_tls_t *tls = vlc_https_connect_i11e(mgr->creds, host, port, &http2);
if (tls == NULL)
return NULL;
struct vlc_http_conn *conn;
if (http2)
{
struct vlc_h2_conn *conn2 = vlc_h2_conn_create(tls);
if (likely(conn2 != NULL))
mgr->conn2 = conn2;
conn = vlc_h2_conn_create(tls);
else
vlc_tls_Close(tls);
}
else /* TODO: HTTP/1.x support */
conn = vlc_h1_conn_create(tls);
if (unlikely(conn == NULL))
{
struct vlc_h1_conn *conn1 = vlc_h1_conn_create(tls);
if (likely(conn1 != NULL))
mgr->conn1 = conn1;
else
vlc_tls_Close(tls);
return NULL;
}
return vlc_https_request_reuse(mgr, host, port, req);
mgr->conn = conn;
return vlc_http_mgr_reuse(mgr, host, port, req);
}
struct vlc_http_mgr *vlc_http_mgr_create(vlc_object_t *obj)
......@@ -197,17 +177,14 @@ struct vlc_http_mgr *vlc_http_mgr_create(vlc_object_t *obj)
return NULL;
}
mgr->conn1 = NULL;
mgr->conn2 = NULL;
mgr->conn = NULL;
return mgr;
}
void vlc_http_mgr_destroy(struct vlc_http_mgr *mgr)
{
if (mgr->conn2 != NULL)
vlc_h2_conn_release(mgr->conn2);
if (mgr->conn1 != NULL)
vlc_h1_conn_release(mgr->conn1);
if (mgr->conn != NULL)
vlc_http_mgr_release(mgr, mgr->conn);
vlc_tls_Delete(mgr->creds);
free(mgr);
}
......@@ -32,9 +32,8 @@
#include <vlc_tls.h>
#include <vlc_block.h>
#include "h1conn.h"
#include "conn.h"
#include "message.h"
#include "transport.h"
static unsigned vlc_http_can_read(const char *buf, size_t len)
{
......@@ -109,35 +108,41 @@ static int vlc_http_minor(const char *msg)
struct vlc_h1_conn
{
struct vlc_http_conn conn;
struct vlc_http_stream stream;
struct vlc_tls *tls;
uintmax_t content_length;
bool connection_close;
bool active;
bool released;
};
#define CO(conn) ((conn)->tls->obj)
#define CO(conn) ((conn)->conn.tls->obj)
static void vlc_h1_conn_destroy(struct vlc_h1_conn *conn);
static void *vlc_h1_stream_fatal(struct vlc_h1_conn *conn)
{
msg_Dbg(CO(conn), "connection failed");
vlc_tls_Close(conn->tls);
conn->tls = NULL;
vlc_tls_Close(conn->conn.tls);
conn->conn.tls = NULL;
return NULL;
}
static_assert(offsetof(struct vlc_h1_conn, stream) == 0, "Cast error");
static_assert(offsetof(struct vlc_h1_conn, conn) == 0, "Cast error");
struct vlc_http_stream *vlc_h1_stream_open(struct vlc_h1_conn *conn,
static struct vlc_h1_conn *vlc_h1_stream_conn(struct vlc_http_stream *stream)
{
return (void *)(((char *)stream) - offsetof(struct vlc_h1_conn, stream));
}
static struct vlc_http_stream *vlc_h1_stream_open(struct vlc_http_conn *c,
const struct vlc_http_msg *req)
{
struct vlc_h1_conn *conn = (struct vlc_h1_conn *)c;
size_t len;
ssize_t val;
if (conn->active || conn->tls == NULL)
if (conn->active || conn->conn.tls == NULL)
return NULL;
char *payload = vlc_http_msg_format(req, &len);
......@@ -145,7 +150,7 @@ struct vlc_http_stream *vlc_h1_stream_open(struct vlc_h1_conn *conn,
return NULL;
msg_Dbg(CO(conn), "outgoing request:\n%.*s", (int)len, payload);
val = vlc_tls_Write(conn->tls, payload, len);
val = vlc_tls_Write(conn->conn.tls, payload, len);
free(payload);
if (val < (ssize_t)len)
......@@ -159,12 +164,12 @@ struct vlc_http_stream *vlc_h1_stream_open(struct vlc_h1_conn *conn,
static struct vlc_http_msg *vlc_h1_stream_wait(struct vlc_http_stream *stream)
{
struct vlc_h1_conn *conn = (struct vlc_h1_conn *)stream;
struct vlc_h1_conn *conn = vlc_h1_stream_conn(stream);
struct vlc_http_msg *resp;
size_t len;
int minor;
char *payload = vlc_https_headers_recv(conn->tls, &len);
char *payload = vlc_https_headers_recv(conn->conn.tls, &len);
if (payload == NULL)
return vlc_h1_stream_fatal(conn);
......@@ -193,7 +198,7 @@ static struct vlc_http_msg *vlc_h1_stream_wait(struct vlc_http_stream *stream)
if ((str != NULL) && strcasestr(str, "chunked"))
{
assert(conn->content_length == UINTMAX_MAX);
stream = vlc_chunked_open(stream, conn->tls);
stream = vlc_chunked_open(stream, conn->conn.tls);
if (unlikely(stream == NULL))
return NULL;
}
......@@ -207,7 +212,7 @@ static struct vlc_http_msg *vlc_h1_stream_wait(struct vlc_http_stream *stream)
static block_t *vlc_h1_stream_read(struct vlc_http_stream *stream)
{
struct vlc_h1_conn *conn = (struct vlc_h1_conn *)stream;
struct vlc_h1_conn *conn = vlc_h1_stream_conn(stream);
size_t size = 2048;
if (size > conn->content_length)
......@@ -219,7 +224,7 @@ static block_t *vlc_h1_stream_read(struct vlc_http_stream *stream)
if (unlikely(block == NULL))
return NULL;
ssize_t val = vlc_tls_Read(conn->tls, block->p_buffer, size, false);
ssize_t val = vlc_tls_Read(conn->conn.tls, block->p_buffer, size, false);
if (val <= 0)
{
block_Release(block);
......@@ -235,57 +240,66 @@ static block_t *vlc_h1_stream_read(struct vlc_http_stream *stream)
static void vlc_h1_stream_close(struct vlc_http_stream *stream, bool abort)
{
struct vlc_h1_conn *conn = (struct vlc_h1_conn *)stream;
struct vlc_h1_conn *conn = vlc_h1_stream_conn(stream);
assert(conn->active);
conn->active = false;
if (abort)
{
vlc_tls_Close(conn->tls);
conn->tls = NULL;
vlc_tls_Close(conn->conn.tls);
conn->conn.tls = NULL;
}
if (conn->released)
vlc_h1_conn_destroy(conn);
}
static struct vlc_http_stream_cbs vlc_h1_stream_callbacks =
static const struct vlc_http_stream_cbs vlc_h1_stream_callbacks =
{
vlc_h1_stream_wait,
vlc_h1_stream_read,
vlc_h1_stream_close,
};
struct vlc_h1_conn *vlc_h1_conn_create(vlc_tls_t *tls)
{
struct vlc_h1_conn *conn = malloc(sizeof (*conn));
if (unlikely(conn == NULL))
return NULL;
conn->stream.cbs = &vlc_h1_stream_callbacks;
conn->tls = tls;
conn->active = false;
conn->released = false;
return conn;
}
static void vlc_h1_conn_destroy(struct vlc_h1_conn *conn)
{
assert(!conn->active);
assert(conn->released);
if (conn->tls != NULL)
vlc_tls_Close(conn->tls);
if (conn->conn.tls != NULL)
vlc_tls_Close(conn->conn.tls);
free(conn);
}
void vlc_h1_conn_release(struct vlc_h1_conn *conn)
static void vlc_h1_conn_release(struct vlc_http_conn *c)
{
struct vlc_h1_conn *conn = (struct vlc_h1_conn *)c;
assert(!conn->released);
conn->released = true;
if (!conn->active)
vlc_h1_conn_destroy(conn);
}
static const struct vlc_http_conn_cbs vlc_h1_conn_callbacks =
{
vlc_h1_stream_open,
vlc_h1_conn_release,
};
struct vlc_http_conn *vlc_h1_conn_create(vlc_tls_t *tls)
{
struct vlc_h1_conn *conn = malloc(sizeof (*conn));
if (unlikely(conn == NULL))
return NULL;
conn->conn.cbs = &vlc_h1_conn_callbacks;
conn->conn.tls = tls;
conn->stream.cbs = &vlc_h1_stream_callbacks;
conn->active = false;
conn->released = false;
return &conn->conn;
}
/*****************************************************************************
* h1conn.h: HTTP 1.x connection handling
*****************************************************************************
* Copyright © 2015 Rémi Denis-Courmont
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
struct vlc_h1_conn;
struct vlc_http_msg;
struct vlc_http_stream;
struct vlc_tls;
struct vlc_h1_conn *vlc_h1_conn_create(struct vlc_tls *);
void vlc_h1_conn_release(struct vlc_h1_conn *);
struct vlc_http_stream *vlc_h1_stream_open(struct vlc_h1_conn *conn,
const struct vlc_http_msg *req);
struct vlc_http_stream *vlc_chunked_open(struct vlc_http_stream *,
struct vlc_tls *);
......@@ -36,17 +36,16 @@
#include "h2frame.h"
#include "h2output.h"
#include "h2conn.h"
#include "transport.h"
#include "conn.h"
#include "message.h"
#define CO(c) ((c)->tls->obj)
#define CO(c) ((c)->conn.tls->obj)
#define SO(s) CO((s)->conn)
/** HTTP/2 connection */
struct vlc_h2_conn
{
struct vlc_tls *tls; /**< Underlying TLS session */
struct vlc_http_conn conn;
struct vlc_h2_output *out; /**< Send thread */
struct vlc_h2_stream *streams; /**< List of open streams */
......@@ -57,6 +56,8 @@ struct vlc_h2_conn
vlc_thread_t thread; /**< Receive thread */
};
static_assert(offsetof(struct vlc_h2_conn, conn) == 0, "Cast error");
static void vlc_h2_conn_destroy(struct vlc_h2_conn *conn);
/** HTTP/2 stream */
......@@ -370,9 +371,10 @@ static const struct vlc_http_stream_cbs vlc_h2_stream_callbacks =
* \param msg HTTP message headers (including response status or request)
* \return an HTTP stream, or NULL on error
*/
struct vlc_http_stream *vlc_h2_stream_open(struct vlc_h2_conn *conn,
static struct vlc_http_stream *vlc_h2_stream_open(struct vlc_http_conn *c,
const struct vlc_http_msg *msg)
{
struct vlc_h2_conn *conn = (struct vlc_h2_conn *)c;
struct vlc_h2_stream *s = malloc(sizeof (*s));
if (unlikely(s == NULL))
return NULL;
......@@ -497,7 +499,7 @@ static void vlc_h2_window_status(void *ctx, uint32_t *restrict rcwd)
}
/** HTTP/2 frames parser callbacks table */
static const struct vlc_h2_parser_cbs vlc_h2_conn_callbacks =
static const struct vlc_h2_parser_cbs vlc_h2_parser_callbacks =
{
vlc_h2_setting,
vlc_h2_settings_done,
......@@ -610,7 +612,7 @@ static void *vlc_h2_recv_thread(void *data)
int canc, val;
canc = vlc_savecancel();
parser = vlc_h2_parse_init(conn, &vlc_h2_conn_callbacks);
parser = vlc_h2_parse_init(conn, &vlc_h2_parser_callbacks);
if (unlikely(parser == NULL))
goto fail;
......@@ -618,7 +620,7 @@ static void *vlc_h2_recv_thread(void *data)
do
{
vlc_restorecancel(canc);
frame = vlc_h2_frame_recv(conn->tls);
frame = vlc_h2_frame_recv(conn->conn.tls);
canc = vlc_savecancel();
if (frame == NULL)
......@@ -643,37 +645,6 @@ fail:
return NULL;
}
struct vlc_h2_conn *vlc_h2_conn_create(struct vlc_tls *tls)
{
struct vlc_h2_conn *conn = malloc(sizeof (*conn));
if (unlikely(conn == NULL))
return NULL;
conn->tls = tls;
conn->out = vlc_h2_output_create(tls, true);
conn->streams = NULL;
conn->next_id = 1; /* TODO: server side */
conn->released = false;
if (unlikely(conn->out == NULL))
goto error;
vlc_mutex_init(&conn->lock);
if (vlc_h2_output_send(conn->out, vlc_h2_frame_settings())
|| vlc_clone(&conn->thread, vlc_h2_recv_thread, conn,
VLC_THREAD_PRIORITY_INPUT))
{
vlc_mutex_destroy(&conn->lock);
vlc_h2_output_destroy(conn->out);
goto error;
}
return conn;
error:
free(conn);
return NULL;
}
static void vlc_h2_conn_destroy(struct vlc_h2_conn *conn)
{
assert(conn->streams == NULL);
......@@ -686,12 +657,13 @@ static void vlc_h2_conn_destroy(struct vlc_h2_conn *conn)
vlc_mutex_destroy(&conn->lock);
vlc_h2_output_destroy(conn->out);
vlc_tls_Close(conn->tls);
vlc_tls_Close(conn->conn.tls);
free(conn);
}
void vlc_h2_conn_release(struct vlc_h2_conn *conn)
static void vlc_h2_conn_release(struct vlc_http_conn *c)
{
struct vlc_h2_conn *conn = (struct vlc_h2_conn *)c;
bool destroy;
vlc_mutex_lock(&conn->lock);
......@@ -704,3 +676,41 @@ void vlc_h2_conn_release(struct vlc_h2_conn *conn)
if (destroy)
vlc_h2_conn_destroy(conn);
}
static const struct vlc_http_conn_cbs vlc_h2_conn_callbacks =
{
vlc_h2_stream_open,
vlc_h2_conn_release,
};
struct vlc_http_conn *vlc_h2_conn_create(struct vlc_tls *tls)
{
struct vlc_h2_conn *conn = malloc(sizeof (*conn));
if (unlikely(conn == NULL))
return NULL;
conn->conn.cbs = &vlc_h2_conn_callbacks;
conn->conn.tls = tls;
conn->out = vlc_h2_output_create(tls, true);
conn->streams = NULL;
conn->next_id = 1; /* TODO: server side */
conn->released = false;
if (unlikely(conn->out == NULL))
goto error;
vlc_mutex_init(&conn->lock);
if (vlc_h2_output_send(conn->out, vlc_h2_frame_settings())
|| vlc_clone(&conn->thread, vlc_h2_recv_thread, conn,
VLC_THREAD_PRIORITY_INPUT))
{
vlc_mutex_destroy(&conn->lock);
vlc_h2_output_destroy(conn->out);
goto error;
}
return &conn->conn;
error:
free(conn);
return NULL;
}
......@@ -38,11 +38,10 @@
#include <vlc_block.h>
#include <vlc_tls.h>
#include "h2frame.h"
#include "h2conn.h"
#include "conn.h"
#include "message.h"
#include "transport.h"
static struct vlc_h2_conn *conn;
static struct vlc_http_conn *conn;
static int external_fd;
static void conn_send(struct vlc_h2_frame *f)
......@@ -116,7 +115,7 @@ static void conn_create(void)
static void conn_destroy(void)
{
shutdown(external_fd, SHUT_WR);
vlc_h2_conn_release(conn);
vlc_http_conn_release(conn);
close(external_fd);
}
......@@ -126,7 +125,7 @@ static struct vlc_http_stream *stream_open(void)
"www.example.com", "/");
assert(m != NULL);
struct vlc_http_stream *s = vlc_h2_stream_open(conn, m);
struct vlc_http_stream *s = vlc_http_stream_open(conn, m);
vlc_http_msg_destroy(m);
return s;
}
......
......@@ -32,7 +32,6 @@
#include <vlc_tls.h>
#include "h2frame.h"
#include "h2output.h"
#include "transport.h"
#define VLC_H2_MAX_QUEUE (1u << 24)
......
......@@ -32,7 +32,6 @@
#include <vlc_tls.h>
#include "h2frame.h"
#include "h2output.h"
#include "transport.h"
#undef msleep
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment