parent
f0d7323902
commit
78e5149495
|
@ -196,6 +196,7 @@ Http2Handler::~Http2Handler()
|
||||||
SSL_shutdown(ssl_);
|
SSL_shutdown(ssl_);
|
||||||
}
|
}
|
||||||
if(bev_) {
|
if(bev_) {
|
||||||
|
bufferevent_disable(bev_, EV_READ | EV_WRITE);
|
||||||
bufferevent_free(bev_);
|
bufferevent_free(bev_);
|
||||||
}
|
}
|
||||||
if(ssl_) {
|
if(ssl_) {
|
||||||
|
@ -398,19 +399,30 @@ int Http2Handler::verify_npn_result()
|
||||||
{
|
{
|
||||||
const unsigned char *next_proto = nullptr;
|
const unsigned char *next_proto = nullptr;
|
||||||
unsigned int next_proto_len;
|
unsigned int next_proto_len;
|
||||||
|
// Check the negotiated protocol in NPN or ALPN
|
||||||
SSL_get0_next_proto_negotiated(ssl_, &next_proto, &next_proto_len);
|
SSL_get0_next_proto_negotiated(ssl_, &next_proto, &next_proto_len);
|
||||||
if(next_proto) {
|
for(int i = 0; i < 2; ++i) {
|
||||||
std::string proto(next_proto, next_proto+next_proto_len);
|
if(next_proto) {
|
||||||
if(sessions_->get_config()->verbose) {
|
std::string proto(next_proto, next_proto+next_proto_len);
|
||||||
std::cout << "The negotiated next protocol: " << proto << std::endl;
|
if(sessions_->get_config()->verbose) {
|
||||||
}
|
std::cout << "The negotiated protocol: " << proto << std::endl;
|
||||||
if(proto == NGHTTP2_PROTO_VERSION_ID) {
|
}
|
||||||
return 0;
|
if(proto == NGHTTP2_PROTO_VERSION_ID) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
SSL_get0_alpn_selected(ssl_, &next_proto, &next_proto_len);
|
||||||
|
#else // OPENSSL_VERSION_NUMBER < 0x10002000L
|
||||||
|
break;
|
||||||
|
#endif // OPENSSL_VERSION_NUMBER < 0x10002000L
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::cerr << "The negotiated next protocol is not supported."
|
std::cerr << "Client did not advertise HTTP/2.0 protocol."
|
||||||
|
<< " (nghttp2 expects " << NGHTTP2_PROTO_VERSION_ID << ")"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
return 0;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Http2Handler::sendcb(const uint8_t *data, size_t len)
|
int Http2Handler::sendcb(const uint8_t *data, size_t len)
|
||||||
|
@ -1094,6 +1106,33 @@ int start_listen(event_base *evbase, Sessions *sessions,
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
namespace {
|
||||||
|
int alpn_select_proto_cb(SSL* ssl,
|
||||||
|
const unsigned char **out, unsigned char *outlen,
|
||||||
|
const unsigned char *in, unsigned int inlen,
|
||||||
|
void *arg)
|
||||||
|
{
|
||||||
|
auto config = reinterpret_cast<HttpServer*>(arg)->get_config();
|
||||||
|
if(config->verbose) {
|
||||||
|
std::cout << "[ALPN] client offers:" << std::endl;
|
||||||
|
}
|
||||||
|
if(config->verbose) {
|
||||||
|
for(unsigned int i = 0; i < inlen; i += in[i]+1) {
|
||||||
|
std::cout << " * ";
|
||||||
|
std::cout.write(reinterpret_cast<const char*>(&in[i+1]), in[i]);
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(nghttp2_select_next_protocol(const_cast<unsigned char**>(out), outlen,
|
||||||
|
in, inlen) <= 0) {
|
||||||
|
return SSL_TLSEXT_ERR_NOACK;
|
||||||
|
}
|
||||||
|
return SSL_TLSEXT_ERR_OK;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
|
||||||
int HttpServer::run()
|
int HttpServer::run()
|
||||||
{
|
{
|
||||||
SSL_CTX *ssl_ctx = nullptr;
|
SSL_CTX *ssl_ctx = nullptr;
|
||||||
|
@ -1138,6 +1177,10 @@ int HttpServer::run()
|
||||||
next_proto.second = proto_list[0] + 1;
|
next_proto.second = proto_list[0] + 1;
|
||||||
|
|
||||||
SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, &next_proto);
|
SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, &next_proto);
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
// ALPN selection callback
|
||||||
|
SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, this);
|
||||||
|
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
}
|
}
|
||||||
|
|
||||||
auto evbase = event_base_new();
|
auto evbase = event_base_new();
|
||||||
|
@ -1152,4 +1195,9 @@ int HttpServer::run()
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Config* HttpServer::get_config() const
|
||||||
|
{
|
||||||
|
return config_;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace nghttp2
|
} // namespace nghttp2
|
||||||
|
|
|
@ -137,6 +137,7 @@ public:
|
||||||
HttpServer(const Config* config);
|
HttpServer(const Config* config);
|
||||||
int listen();
|
int listen();
|
||||||
int run();
|
int run();
|
||||||
|
const Config* get_config() const;
|
||||||
private:
|
private:
|
||||||
const Config *config_;
|
const Config *config_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1223,6 +1223,15 @@ void print_stats(const HttpClient& client)
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void print_protocol_nego_error()
|
||||||
|
{
|
||||||
|
std::cerr << "Server did not select HTTP/2.0 protocol."
|
||||||
|
<< " (nghttp2 expects " << NGHTTP2_PROTO_VERSION_ID << ")"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
int client_select_next_proto_cb(SSL* ssl,
|
int client_select_next_proto_cb(SSL* ssl,
|
||||||
unsigned char **out, unsigned char *outlen,
|
unsigned char **out, unsigned char *outlen,
|
||||||
|
@ -1231,8 +1240,7 @@ int client_select_next_proto_cb(SSL* ssl,
|
||||||
{
|
{
|
||||||
if(config.verbose) {
|
if(config.verbose) {
|
||||||
print_timer();
|
print_timer();
|
||||||
std::cout << " NPN select next protocol: the remote server offers:"
|
std::cout << "[NPN] server offers:" << std::endl;
|
||||||
<< std::endl;
|
|
||||||
}
|
}
|
||||||
for(unsigned int i = 0; i < inlen; i += in[i]+1) {
|
for(unsigned int i = 0; i < inlen; i += in[i]+1) {
|
||||||
if(config.verbose) {
|
if(config.verbose) {
|
||||||
|
@ -1242,15 +1250,8 @@ int client_select_next_proto_cb(SSL* ssl,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
|
if(nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
|
||||||
std::cerr << "Server did not advertise HTTP/2.0 protocol."
|
print_protocol_nego_error();
|
||||||
<< " (nghttp2 expects " << NGHTTP2_PROTO_VERSION_ID << ")"
|
return SSL_TLSEXT_ERR_NOACK;
|
||||||
<< std::endl;
|
|
||||||
} else {
|
|
||||||
if(config.verbose) {
|
|
||||||
std::cout << " NPN selected the protocol: ";
|
|
||||||
std::cout.write(reinterpret_cast<const char*>(*out), (size_t)*outlen);
|
|
||||||
std::cout << std::endl;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return SSL_TLSEXT_ERR_OK;
|
return SSL_TLSEXT_ERR_OK;
|
||||||
}
|
}
|
||||||
|
@ -1312,7 +1313,37 @@ void eventcb(bufferevent *bev, short events, void *ptr)
|
||||||
if(client->need_upgrade()) {
|
if(client->need_upgrade()) {
|
||||||
rv = client->on_upgrade_connect();
|
rv = client->on_upgrade_connect();
|
||||||
} else {
|
} else {
|
||||||
// TODO Check NPN result and fail fast?
|
// Check NPN or ALPN result
|
||||||
|
const unsigned char *next_proto = nullptr;
|
||||||
|
unsigned int next_proto_len;
|
||||||
|
SSL_get0_next_proto_negotiated(client->ssl,
|
||||||
|
&next_proto, &next_proto_len);
|
||||||
|
for(int i = 0; i < 2; ++i) {
|
||||||
|
if(next_proto) {
|
||||||
|
if(config.verbose) {
|
||||||
|
std::cout << "The negotiated protocol: ";
|
||||||
|
std::cout.write(reinterpret_cast<const char*>(next_proto),
|
||||||
|
next_proto_len);
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
if(NGHTTP2_PROTO_VERSION_ID_LEN != next_proto_len ||
|
||||||
|
memcmp(NGHTTP2_PROTO_VERSION_ID, next_proto,
|
||||||
|
NGHTTP2_PROTO_VERSION_ID_LEN) != 0) {
|
||||||
|
next_proto = nullptr;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
SSL_get0_alpn_selected(client->ssl, &next_proto, &next_proto_len);
|
||||||
|
#else // OPENSSL_VERSION_NUMBER < 0x10002000L
|
||||||
|
break;
|
||||||
|
#endif // OPENSSL_VERSION_NUMBER < 0x10002000L
|
||||||
|
}
|
||||||
|
if(!next_proto) {
|
||||||
|
print_protocol_nego_error();
|
||||||
|
client->disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
rv = client->on_connect();
|
rv = client->on_connect();
|
||||||
}
|
}
|
||||||
if(rv != 0) {
|
if(rv != 0) {
|
||||||
|
@ -1404,6 +1435,14 @@ int communicate(const std::string& scheme, const std::string& host,
|
||||||
}
|
}
|
||||||
SSL_CTX_set_next_proto_select_cb(ssl_ctx,
|
SSL_CTX_set_next_proto_select_cb(ssl_ctx,
|
||||||
client_select_next_proto_cb, nullptr);
|
client_select_next_proto_cb, nullptr);
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
unsigned char proto_list[255];
|
||||||
|
proto_list[0] = NGHTTP2_PROTO_VERSION_ID_LEN;
|
||||||
|
memcpy(&proto_list[1], NGHTTP2_PROTO_VERSION_ID,
|
||||||
|
NGHTTP2_PROTO_VERSION_ID_LEN);
|
||||||
|
SSL_CTX_set_alpn_protos(ssl_ctx, proto_list, proto_list[0] + 1);
|
||||||
|
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
HttpClient client{callbacks, evbase, ssl_ctx};
|
HttpClient client{callbacks, evbase, ssl_ctx};
|
||||||
|
|
|
@ -299,26 +299,37 @@ int ClientHandler::validate_next_proto()
|
||||||
// First set callback for catch all cases
|
// First set callback for catch all cases
|
||||||
set_bev_cb(upstream_readcb, upstream_writecb, upstream_eventcb);
|
set_bev_cb(upstream_readcb, upstream_writecb, upstream_eventcb);
|
||||||
SSL_get0_next_proto_negotiated(ssl_, &next_proto, &next_proto_len);
|
SSL_get0_next_proto_negotiated(ssl_, &next_proto, &next_proto_len);
|
||||||
if(next_proto) {
|
for(int i = 0; i < 2; ++i) {
|
||||||
std::string proto(next_proto, next_proto+next_proto_len);
|
if(next_proto) {
|
||||||
if(LOG_ENABLED(INFO)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
CLOG(INFO, this) << "The negotiated next protocol: " << proto;
|
std::string proto(next_proto, next_proto+next_proto_len);
|
||||||
}
|
CLOG(INFO, this) << "The negotiated next protocol: " << proto;
|
||||||
if(proto == NGHTTP2_PROTO_VERSION_ID) {
|
|
||||||
set_bev_cb(upstream_http2_connhd_readcb, upstream_writecb,
|
|
||||||
upstream_eventcb);
|
|
||||||
upstream_ = util::make_unique<Http2Upstream>(this);
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
#ifdef HAVE_SPDYLAY
|
|
||||||
uint16_t version = spdylay_npn_get_version(next_proto, next_proto_len);
|
|
||||||
if(version) {
|
|
||||||
upstream_ = util::make_unique<SpdyUpstream>(version, this);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
if(next_proto_len == NGHTTP2_PROTO_VERSION_ID_LEN &&
|
||||||
|
memcmp(NGHTTP2_PROTO_VERSION_ID, next_proto,
|
||||||
|
NGHTTP2_PROTO_VERSION_ID_LEN) == 0) {
|
||||||
|
set_bev_cb(upstream_http2_connhd_readcb, upstream_writecb,
|
||||||
|
upstream_eventcb);
|
||||||
|
upstream_ = util::make_unique<Http2Upstream>(this);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
#ifdef HAVE_SPDYLAY
|
||||||
|
uint16_t version = spdylay_npn_get_version(next_proto, next_proto_len);
|
||||||
|
if(version) {
|
||||||
|
upstream_ = util::make_unique<SpdyUpstream>(version, this);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
#endif // HAVE_SPDYLAY
|
#endif // HAVE_SPDYLAY
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
SSL_get0_alpn_selected(ssl_, &next_proto, &next_proto_len);
|
||||||
|
#else // OPENSSL_VERSION_NUMBER < 0x10002000L
|
||||||
|
break;
|
||||||
|
#endif // OPENSSL_VERSION_NUMBER < 0x10002000L
|
||||||
|
}
|
||||||
|
if(!next_proto) {
|
||||||
if(LOG_ENABLED(INFO)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
CLOG(INFO, this) << "No proto negotiated.";
|
CLOG(INFO, this) << "No proto negotiated.";
|
||||||
}
|
}
|
||||||
|
|
|
@ -1120,15 +1120,30 @@ int on_unknown_frame_recv_callback(nghttp2_session *session,
|
||||||
int Http2Session::on_connect()
|
int Http2Session::on_connect()
|
||||||
{
|
{
|
||||||
int rv;
|
int rv;
|
||||||
const unsigned char *next_proto = nullptr;
|
|
||||||
unsigned int next_proto_len;
|
|
||||||
if(ssl_ctx_) {
|
if(ssl_ctx_) {
|
||||||
|
const unsigned char *next_proto = nullptr;
|
||||||
|
unsigned int next_proto_len;
|
||||||
SSL_get0_next_proto_negotiated(ssl_, &next_proto, &next_proto_len);
|
SSL_get0_next_proto_negotiated(ssl_, &next_proto, &next_proto_len);
|
||||||
std::string proto(next_proto, next_proto+next_proto_len);
|
for(int i = 0; i < 2; ++i) {
|
||||||
if(LOG_ENABLED(INFO)) {
|
if(next_proto) {
|
||||||
SSLOG(INFO, this) << "Negotiated next protocol: " << proto;
|
if(LOG_ENABLED(INFO)) {
|
||||||
|
std::string proto(next_proto, next_proto+next_proto_len);
|
||||||
|
SSLOG(INFO, this) << "Negotiated next protocol: " << proto;
|
||||||
|
}
|
||||||
|
if(next_proto_len != NGHTTP2_PROTO_VERSION_ID_LEN ||
|
||||||
|
memcmp(NGHTTP2_PROTO_VERSION_ID, next_proto,
|
||||||
|
NGHTTP2_PROTO_VERSION_ID_LEN) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
SSL_get0_alpn_selected(ssl_, &next_proto, &next_proto_len);
|
||||||
|
#else // OPENSSL_VERSION_NUMBER < 0x10002000L
|
||||||
|
break;
|
||||||
|
#endif // OPENSSL_VERSION_NUMBER < 0x10002000L
|
||||||
}
|
}
|
||||||
if(proto != NGHTTP2_PROTO_VERSION_ID) {
|
if(!next_proto) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,6 +132,23 @@ int servername_callback(SSL *ssl, int *al, void *arg)
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
namespace {
|
||||||
|
int alpn_select_proto_cb(SSL* ssl,
|
||||||
|
const unsigned char **out,
|
||||||
|
unsigned char *outlen,
|
||||||
|
const unsigned char *in, unsigned int inlen,
|
||||||
|
void *arg)
|
||||||
|
{
|
||||||
|
if(nghttp2_select_next_protocol
|
||||||
|
(const_cast<unsigned char**>(out), outlen, in, inlen) == -1) {
|
||||||
|
return SSL_TLSEXT_ERR_NOACK;
|
||||||
|
}
|
||||||
|
return SSL_TLSEXT_ERR_OK;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
|
||||||
SSL_CTX* create_ssl_context(const char *private_key_file,
|
SSL_CTX* create_ssl_context(const char *private_key_file,
|
||||||
const char *cert_file)
|
const char *cert_file)
|
||||||
{
|
{
|
||||||
|
@ -241,11 +258,16 @@ SSL_CTX* create_ssl_context(const char *private_key_file,
|
||||||
}
|
}
|
||||||
SSL_CTX_set_tlsext_servername_callback(ssl_ctx, servername_callback);
|
SSL_CTX_set_tlsext_servername_callback(ssl_ctx, servername_callback);
|
||||||
|
|
||||||
|
// NPN advertisement
|
||||||
auto proto_list_len = set_npn_prefs(proto_list, get_config()->npn_list,
|
auto proto_list_len = set_npn_prefs(proto_list, get_config()->npn_list,
|
||||||
get_config()->npn_list_len);
|
get_config()->npn_list_len);
|
||||||
next_proto.first = proto_list;
|
next_proto.first = proto_list;
|
||||||
next_proto.second = proto_list_len;
|
next_proto.second = proto_list_len;
|
||||||
SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, &next_proto);
|
SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, &next_proto);
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
// ALPN selection callback
|
||||||
|
SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, nullptr);
|
||||||
|
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
return ssl_ctx;
|
return ssl_ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,8 +344,18 @@ SSL_CTX* create_ssl_client_context()
|
||||||
DIE();
|
DIE();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// NPN selection callback
|
||||||
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, nullptr);
|
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, nullptr);
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
// ALPN advertisement
|
||||||
|
auto proto_list_len = set_npn_prefs(proto_list, get_config()->npn_list,
|
||||||
|
get_config()->npn_list_len);
|
||||||
|
next_proto.first = proto_list;
|
||||||
|
next_proto.second = proto_list_len;
|
||||||
|
SSL_CTX_set_alpn_protos(ssl_ctx, proto_list, proto_list[0] + 1);
|
||||||
|
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
|
||||||
return ssl_ctx;
|
return ssl_ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue