New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Using nghttp2's C API Directly (not as a reverse proxy)? #1768
Comments
I'd prefer to use the C API if possible since the C++ one is deprecated and will be removed after 2022, which makes it kind unreliable. But I still need to at least know what functions I need to use, so what example(s) should I look at? And would building just nghttp2.lib be enough for this use case? |
Your question is too broad to answer. |
Sorry for making the question too broad. I'll try to narrow it down. So, if given the code in this C++ file (my server app's main C++ code file), now that I've enabled ALPN and if it's correct, I probably just need to implement HTTP/2 features and get the server to "speak" the protocol. So my question is mainly just what functions of the nghttp2 C API I need to use and where. And also if I need to compile anything aside from just nghttp2 itself (like the httpd for example), if I just want to use the API and not try to drop the httpd in as a reverse proxy. I'll also show how I'm trying to enable ALPN in my app to ask if it's correct, since I'm not sure if it is: // Handles an HTTP server connection
void do_session(tcp::socket &socket, ssl::context &ctx, const std::string &doc_root, std::string_view googlekey,
std::string_view currencykey)
{
bool close{};
boost::system::error_code ec;
// Construct the stream around the socket
ssl::stream<tcp::socket&> stream{ socket, ctx };
// call client hello processing function and pass in callback
auto ssl_ctx{ ctx.native_handle() };
SSL* ssl{ SSL_new(ssl_ctx) };
int alert_val{ 1 };
int* alert{ &alert_val };
void* arg{};
SSL_CTX_set_client_hello_cb(ssl_ctx, set_client_hello_cb(ssl, alert, arg), nullptr);
// Perform the SSL handshake
stream.handshake(ssl::stream_base::server, ec);
if (ec)
{
std::cerr << "Lines 692 and 693:\n";
fail(ec, "handshake");
}
// This buffer is required to persist across reads
boost::beast::flat_buffer buffer;
// This lambda is used to send messages
send_lambda<ssl::stream<tcp::socket&>> lambda{ stream, close, ec };
for (;;)
{
// Read a request
http::request<http::string_body> req;
http::read(stream, buffer, req, ec);
if (ec == http::error::end_of_stream)
{
break;
}
if (ec)
{
std::cerr << "Lines 713 and 714:\n";
return fail(ec, "read");
}
// Send the response
handle_request(doc_root, std::move(req), lambda, googlekey, currencykey);
if (ec)
{
std::cerr << "Lines 721 and 722:\n";
return fail(ec, "write");
}
if (close)
{
// This means we should close the connection, usually because
// the response indicated the "Connection: close" semantic.
break;
}
}
// Perform the SSL shutdown
stream.shutdown(ec);
if (ec)
{
std::cerr << "Lines 736 and 737:\n";
return fail(ec, "shutdown");
}
// At this point the connection is closed gracefully
} ^that's the function that handles a server connection. As you can see, I'm calling SSL_client_hello_cb_fn set_client_hello_cb(SSL* ssl, int* alert, void* arg)
{
constexpr int ext_type{ TLSEXT_TYPE_application_layer_protocol_negotiation };
const unsigned char** alpn_str{};
std::size_t* alpn_str_len{};
const int alpn_ext_present{ SSL_client_hello_get0_ext(ssl, ext_type,
alpn_str, alpn_str_len) };
if (alpn_ext_present == 1) // 1 means alpn extension is present
{
ssl::context ctx{ ssl::context::tlsv13 };
load_server_certificate(ctx);
auto* ssl_ctx{ ctx.native_handle() };
const unsigned char** out{};
unsigned char* outlen{};
const unsigned char* in{};
unsigned inlen{};
void* arg{};
SSL_CTX_set_alpn_select_cb(ssl_ctx,
set_alpn_cb(ssl, out, outlen, in, inlen, arg),
nullptr);
return reinterpret_cast<SSL_client_hello_cb_fn>(SSL_CLIENT_HELLO_SUCCESS);
}
else if (alpn_ext_present == 0) // 0 means alpn extension isn't present
{
return reinterpret_cast<SSL_client_hello_cb_fn>(SSL_CLIENT_HELLO_ERROR);
}
} SSL_CTX_alpn_select_cb_func set_alpn_cb(SSL* ssl, const unsigned char** out, unsigned char* outlen,
const unsigned char* in, unsigned int inlen, void* arg)
{
for (std::size_t i{}; i < inlen; i += 1 + in[i])
{
if (in[i + 0] == 2 && in[i + 1] == 'h' && in[i + 2] == '2')
{
*out = in + i + 1;
*outlen = in[i];
}
}
if (out[0][0] == 2 && out[0][1] == 'h' && out[0][2] == '2')
{
return reinterpret_cast<SSL_CTX_alpn_select_cb_func>(SSL_TLSEXT_ERR_OK);
}
return reinterpret_cast<SSL_CTX_alpn_select_cb_func>(SSL_TLSEXT_ERR_NOACK);
} If ALPN is correctly enabled here, then my server probably already supports HTTP/2 and I just need to know what functions from the nghttp2 C API I need to use and where. [I also need to change the client code where I send requests for the currency conversion rates and such to enable ALPN there as well, and ask the server on the other end to negotiate HTTP/2 as well, but I'll do that after I've gotten this to work.] |
Seems I can't build nghttp2-asio without cloning nghttp2 itself as well. I don't understand why the dependency has to stay when it's already in its own separate repo now. Please consider removing the dependency (and also assigning a maintainer since you don't want to do it yourself). For now I'll clone the full C lib too, but I'll wait for you to remove the dependency. Could you also maybe add server and client examples that how how to use the C API with ASIO in a C++ project, though? I think that would help me out immensely. Thanks. |
This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days. |
If I want to use nghttp2's C API directly (its functions for HTTP/2 support), without using it as a reverse proxy, can it be done? And is it possible to do this without compiling httpd as well? I'm using Windows so I won't be able to compile httpd without using Cygwin or WSL. I have WSL 2, but my first option is to host the app on a Windows VPS.
I don't know how to implement HTTP/2 support, and rather than learning I'd rather use code developed by someone else. I'd like to use nghttp2 for that if possible. And not as a proxy but just using the code directly.
The text was updated successfully, but these errors were encountered: