Update tutorials according to the updated tutorial client/server sources
This commit is contained in:
parent
cdd72bad77
commit
d6def22ad5
|
@ -31,6 +31,17 @@ protocol the library supports::
|
||||||
return SSL_TLSEXT_ERR_OK;
|
return SSL_TLSEXT_ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
If you are following TLS related RFC, you know that NPN is not the
|
||||||
|
standardized way to negotiate HTTP/2. NPN itself is not event
|
||||||
|
published as RFC. The standard way to negotiate HTTP/2 is ALPN,
|
||||||
|
Application-Layer Protocol Negotiation Extension, defined in `RFC 7301
|
||||||
|
<https://tools.ietf.org/html/rfc7301>`_. The one caveat of ALPN is
|
||||||
|
that OpenSSL >= 1.0.2 is required. We use macro to enable/disable
|
||||||
|
ALPN support depending on OpenSSL version. OpenSSL's ALPN
|
||||||
|
implementation does not require callback function like the above. But
|
||||||
|
we have to instruct OpenSSL SSL_CTX to use ALPN, which we'll talk
|
||||||
|
about soon.
|
||||||
|
|
||||||
The callback is added to the SSL_CTX object using
|
The callback is added to the SSL_CTX object using
|
||||||
``SSL_CTX_set_next_proto_select_cb()``::
|
``SSL_CTX_set_next_proto_select_cb()``::
|
||||||
|
|
||||||
|
@ -46,9 +57,18 @@ The callback is added to the SSL_CTX object using
|
||||||
SSL_OP_NO_COMPRESSION |
|
SSL_OP_NO_COMPRESSION |
|
||||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||||
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
|
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
SSL_CTX_set_alpn_protos(ssl_ctx, (const unsigned char *)"\x02h2", 3);
|
||||||
|
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
|
||||||
return ssl_ctx;
|
return ssl_ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Here we see ``SSL_CTX_get_alpn_protos()`` function call. We instructs
|
||||||
|
OpenSSL to notify the server that we support h2, ALPN identifier for
|
||||||
|
HTTP/2.
|
||||||
|
|
||||||
The example client defines a couple of structs:
|
The example client defines a couple of structs:
|
||||||
|
|
||||||
We define and use a ``http2_session_data`` structure to store data
|
We define and use a ``http2_session_data`` structure to store data
|
||||||
|
@ -124,7 +144,27 @@ underlying network socket::
|
||||||
if (events & BEV_EVENT_CONNECTED) {
|
if (events & BEV_EVENT_CONNECTED) {
|
||||||
int fd = bufferevent_getfd(bev);
|
int fd = bufferevent_getfd(bev);
|
||||||
int val = 1;
|
int val = 1;
|
||||||
|
const unsigned char *alpn = NULL;
|
||||||
|
unsigned int alpnlen = 0;
|
||||||
|
SSL *ssl;
|
||||||
|
|
||||||
fprintf(stderr, "Connected\n");
|
fprintf(stderr, "Connected\n");
|
||||||
|
|
||||||
|
ssl = bufferevent_openssl_get_ssl(session_data->bev);
|
||||||
|
|
||||||
|
SSL_get0_next_proto_negotiated(ssl, &alpn, &alpnlen);
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
if (alpn == NULL) {
|
||||||
|
SSL_get0_alpn_selected(ssl, &alpn, &alpnlen);
|
||||||
|
}
|
||||||
|
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
|
||||||
|
if (alpn == NULL || alpnlen != 2 || memcmp("h2", alpn, 2) != 0) {
|
||||||
|
fprintf(stderr, "h2 is not negotiated\n");
|
||||||
|
delete_http2_session_data(session_data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
|
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
|
||||||
initialize_nghttp2_session(session_data);
|
initialize_nghttp2_session(session_data);
|
||||||
send_client_connection_header(session_data);
|
send_client_connection_header(session_data);
|
||||||
|
@ -144,6 +184,9 @@ underlying network socket::
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Here we validate that HTTP/2 is negotiated, and if not, drop
|
||||||
|
connection.
|
||||||
|
|
||||||
For ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR``, and ``BEV_EVENT_TIMEOUT``
|
For ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR``, and ``BEV_EVENT_TIMEOUT``
|
||||||
events, we just simply tear down the connection.
|
events, we just simply tear down the connection.
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,17 @@ application protocols the server supports to a client. In this
|
||||||
example program, when creating the ``SSL_CTX`` object, we store the
|
example program, when creating the ``SSL_CTX`` object, we store the
|
||||||
application protocol name in the wire format of NPN in a statically
|
application protocol name in the wire format of NPN in a statically
|
||||||
allocated buffer. This is safe because we only create one ``SSL_CTX``
|
allocated buffer. This is safe because we only create one ``SSL_CTX``
|
||||||
object in the program's entire lifetime::
|
object in the program's entire lifetime.
|
||||||
|
|
||||||
|
If you are following TLS related RFC, you know that NPN is not the
|
||||||
|
standardized way to negotiate HTTP/2. NPN itself is not even
|
||||||
|
published as RFC. The standard way to negotiate HTTP/2 is ALPN,
|
||||||
|
Application-Layer Protocol Negotiation Extension, defined in `RFC 7301
|
||||||
|
<https://tools.ietf.org/html/rfc7301>`_. The one caveat of ALPN is
|
||||||
|
that OpenSSL >= 1.0.2 is required. We use macro to enable/disable
|
||||||
|
ALPN support depending on OpenSSL version. In ALPN, client sends the
|
||||||
|
list of supported application protocols, and server selects one of
|
||||||
|
them. We provide the callback for it::
|
||||||
|
|
||||||
static unsigned char next_proto_list[256];
|
static unsigned char next_proto_list[256];
|
||||||
static size_t next_proto_list_len;
|
static size_t next_proto_list_len;
|
||||||
|
@ -37,6 +47,22 @@ object in the program's entire lifetime::
|
||||||
return SSL_TLSEXT_ERR_OK;
|
return SSL_TLSEXT_ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
static int alpn_select_proto_cb(SSL *ssl _U_, const unsigned char **out,
|
||||||
|
unsigned char *outlen, const unsigned char *in,
|
||||||
|
unsigned int inlen, void *arg _U_) {
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
rv = nghttp2_select_next_protocol((unsigned char **)out, outlen, in, inlen);
|
||||||
|
|
||||||
|
if (rv != 1) {
|
||||||
|
return SSL_TLSEXT_ERR_NOACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SSL_TLSEXT_ERR_OK;
|
||||||
|
}
|
||||||
|
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
|
||||||
static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
|
static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
|
||||||
SSL_CTX *ssl_ctx;
|
SSL_CTX *ssl_ctx;
|
||||||
EC_KEY *ecdh;
|
EC_KEY *ecdh;
|
||||||
|
@ -51,6 +77,11 @@ object in the program's entire lifetime::
|
||||||
next_proto_list_len = 1 + NGHTTP2_PROTO_VERSION_ID_LEN;
|
next_proto_list_len = 1 + NGHTTP2_PROTO_VERSION_ID_LEN;
|
||||||
|
|
||||||
SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, NULL);
|
SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, NULL);
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, NULL);
|
||||||
|
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
|
||||||
return ssl_ctx;
|
return ssl_ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,6 +95,11 @@ OpenSSL implementation, we just assign the pointer to the NPN buffers
|
||||||
we filled in earlier. The NPN callback function is set to the
|
we filled in earlier. The NPN callback function is set to the
|
||||||
``SSL_CTX`` object using ``SSL_CTX_set_next_protos_advertised_cb()``.
|
``SSL_CTX`` object using ``SSL_CTX_set_next_protos_advertised_cb()``.
|
||||||
|
|
||||||
|
In ``alpn_select_proto_cb()``, we use `nghttp2_select_next_protocol()`
|
||||||
|
to select application protocol. The `nghttp2_select_next_protocol()`
|
||||||
|
returns 1 only if it selected h2 (ALPN identifier for HTTP/2), and out
|
||||||
|
parameters were assigned accordingly.
|
||||||
|
|
||||||
Next, let's take a look at the main structures used by the example
|
Next, let's take a look at the main structures used by the example
|
||||||
application:
|
application:
|
||||||
|
|
||||||
|
@ -167,11 +203,31 @@ underlying network socket::
|
||||||
static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) {
|
static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) {
|
||||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
if (events & BEV_EVENT_CONNECTED) {
|
if (events & BEV_EVENT_CONNECTED) {
|
||||||
|
const unsigned char *alpn = NULL;
|
||||||
|
unsigned int alpnlen = 0;
|
||||||
|
SSL *ssl;
|
||||||
|
|
||||||
fprintf(stderr, "%s connected\n", session_data->client_addr);
|
fprintf(stderr, "%s connected\n", session_data->client_addr);
|
||||||
|
|
||||||
|
ssl = bufferevent_openssl_get_ssl(session_data->bev);
|
||||||
|
|
||||||
|
SSL_get0_next_proto_negotiated(ssl, &alpn, &alpnlen);
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
if (alpn == NULL) {
|
||||||
|
SSL_get0_alpn_selected(ssl, &alpn, &alpnlen);
|
||||||
|
}
|
||||||
|
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
|
||||||
|
if (alpn == NULL || alpnlen != 2 || memcmp("h2", alpn, 2) != 0) {
|
||||||
|
fprintf(stderr, "%s h2 is not negotiated\n", session_data->client_addr);
|
||||||
|
delete_http2_session_data(session_data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
initialize_nghttp2_session(session_data);
|
initialize_nghttp2_session(session_data);
|
||||||
|
|
||||||
if (send_server_connection_header(session_data) != 0) {
|
if (send_server_connection_header(session_data) != 0 ||
|
||||||
|
session_send(session_data) != 0) {
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -188,6 +244,9 @@ underlying network socket::
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Here we validate that HTTP/2 is negotiated, and if not, drop
|
||||||
|
connection.
|
||||||
|
|
||||||
For the ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR``, and
|
For the ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR``, and
|
||||||
``BEV_EVENT_TIMEOUT`` events, we just simply tear down the connection.
|
``BEV_EVENT_TIMEOUT`` events, we just simply tear down the connection.
|
||||||
The ``delete_http2_session_data()`` function destroys the
|
The ``delete_http2_session_data()`` function destroys the
|
||||||
|
|
Loading…
Reference in New Issue