Update tutorials according to the updated tutorial client/server sources

This commit is contained in:
Tatsuhiro Tsujikawa 2016-06-19 23:03:04 +09:00
parent cdd72bad77
commit d6def22ad5
2 changed files with 104 additions and 2 deletions

View File

@ -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.

View File

@ -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