Skip to content
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
04e10f5
feat: add openssl-based https server support in net.openssl and veb
guweigang Jun 17, 2026
975c332
fix: address review feedback for OpenSSL HTTPS listener and veb
guweigang Jun 19, 2026
41d4b13
fix(net): honor OpenSSL listener family and timeouts
guweigang Jun 20, 2026
861e65d
test(net): cover OpenSSL listener family and timeout
guweigang Jun 20, 2026
1b6a63a
fix(veb): preserve handler responses through middleware
guweigang Jun 20, 2026
57a85b8
fix(net): honor OpenSSL listener memory and ALPN config
guweigang Jun 21, 2026
a83b139
fix(net): preserve OpenSSL listener certificate chains
guweigang Jun 23, 2026
5a64b92
Merge remote-tracking branch 'upstream/master' into openssl-https
guweigang Jun 23, 2026
009abcc
test(veb): make SSL tests discoverable
guweigang Jun 24, 2026
f34fa2e
Merge remote-tracking branch 'upstream/master' into openssl-https
guweigang Jun 24, 2026
7098911
net.openssl: always release SSLConn socket on shutdown
medvednikov Jun 26, 2026
89f9db3
test(veb): exclude mbedtls HTTPS test from -d use_openssl runs
medvednikov Jun 26, 2026
02dc1a4
Merge remote-tracking branch 'upstream/master' into openssl-https
medvednikov Jun 26, 2026
61636f2
net.openssl: clamp expired finite TLS deadlines to an immediate timeout
medvednikov Jun 26, 2026
df3a1b2
net.ssl: honor listener options for mbedtls
medvednikov Jun 26, 2026
d05ee16
net: address SSL listener review feedback
guweigang Jun 28, 2026
dcc2d8d
Merge remote-tracking branch 'upstream/master' into openssl-https
guweigang Jun 28, 2026
bc07637
Merge remote-tracking branch 'upstream/master' into openssl-https
guweigang Jun 30, 2026
acf577e
net: address OpenSSL listener review feedback
guweigang Jun 30, 2026
c860885
net: address SSL listener review feedback
guweigang Jul 1, 2026
ccf15c8
net: bound OpenSSL HTTPS handshakes
guweigang Jul 1, 2026
1e942fd
net: harden OpenSSL listener cleanup
guweigang Jul 9, 2026
7fd5b39
net: polish SSL listener review fixes
guweigang Jul 10, 2026
762ddef
net: tighten SSL listener edge cases
guweigang Jul 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 19 additions & 19 deletions vlib/net/mbedtls/ssl_connection.c.v
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,9 @@ pub mut:

// SSLListener listens on a TCP port and accepts connection secured with TLS
pub struct SSLListener {
saddr string
config SSLConnectConfig
saddr string
config SSLConnectConfig
options SSLListenerOptions
mut:
server_fd C.mbedtls_net_context
ssl C.mbedtls_ssl_context
Expand All @@ -183,11 +184,19 @@ mut:
// duration time.Duration
}

// create a new SSLListener binding to `saddr`
pub fn new_ssl_listener(saddr string, config SSLConnectConfig) !&SSLListener {
// SSLListenerOptions configures the TCP listener used by an SSLListener.
@[params]
pub struct SSLListenerOptions {
pub:
family net.AddrFamily = .ip
Comment thread
guweigang marked this conversation as resolved.
Outdated
}

// new_ssl_listener creates a new SSLListener binding to `saddr`.
pub fn new_ssl_listener(saddr string, config SSLConnectConfig, options SSLListenerOptions) !&SSLListener {
mut listener := &SSLListener{
saddr: saddr
config: config
saddr: saddr
config: config
options: options
}
listener.init()!
listener.opened = true
Expand Down Expand Up @@ -222,7 +231,6 @@ fn (mut l SSLListener) init() ! {
eprintln(@METHOD)
}

lhost, lport := net.split_address(l.saddr)!
if l.config.cert == '' || l.config.cert_key == '' {
return error('net.mbedtls SSLListener.init, no certificate or key provided')
}
Expand Down Expand Up @@ -259,18 +267,10 @@ fn (mut l SSLListener) init() ! {
C.mbedtls_ssl_conf_authmode(&l.conf, C.MBEDTLS_SSL_VERIFY_REQUIRED)
}

mut bind_ip := unsafe { nil }
if lhost != '' {
bind_ip = voidptr(lhost.str)
}
bind_port := lport.str()

ret = C.mbedtls_net_bind(&l.server_fd, bind_ip, voidptr(bind_port.str), C.MBEDTLS_NET_PROTO_TCP)

if ret != 0 {
return error_with_code("net.mbedtls SSLListener.init, mbedtls_net_bind can't bind to ${l.saddr} error ret: ${ret}",
ret)
}
tcp_listener := net.listen_tcp(l.options.family, l.saddr, net.ListenOptions{
backlog: C.MBEDTLS_NET_LISTEN_BACKLOG
}) or { return error('net.mbedtls SSLListener.init, listen_tcp failed for ${l.saddr}: ${err}') }
l.server_fd.fd = tcp_listener.sock.handle
Comment thread
guweigang marked this conversation as resolved.

ret = C.mbedtls_ssl_config_defaults(&l.conf, C.MBEDTLS_SSL_IS_SERVER,
C.MBEDTLS_SSL_TRANSPORT_STREAM, C.MBEDTLS_SSL_PRESET_DEFAULT)
Expand Down
22 changes: 22 additions & 0 deletions vlib/net/openssl/openssl.c.v
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,26 @@ fn C.SSL_CTX_set_options(ctx &C.SSL_CTX, options i32)

fn C.SSL_CTX_set_verify_depth(s &C.SSL_CTX, depth i32)

fn C.SSL_CTX_set_verify(ctx &C.SSL_CTX, mode i32, callback voidptr)

fn C.SSL_CTX_load_verify_locations(ctx &C.SSL_CTX, const_file &char, ca_path &char) i32

fn C.SSL_CTX_free(ctx &C.SSL_CTX)

fn C.SSL_CTX_use_certificate_file(ctx &C.SSL_CTX, const_file &char, file_type i32) i32

fn C.SSL_CTX_use_certificate_chain_file(ctx &C.SSL_CTX, const_file &char) i32

fn C.SSL_CTX_use_PrivateKey_file(ctx &C.SSL_CTX, const_file &char, file_type i32) i32

fn C.v_net_openssl_SSL_CTX_use_certificate_chain_memory(ctx &C.SSL_CTX, data &u8, len usize) i32

fn C.v_net_openssl_SSL_CTX_extra_chain_certs_count(ctx &C.SSL_CTX) int

fn C.v_net_openssl_SSL_CTX_use_PrivateKey_memory(ctx &C.SSL_CTX, data &u8, len usize) i32

fn C.v_net_openssl_SSL_CTX_load_verify_memory(ctx &C.SSL_CTX, data &u8, len usize) i32

fn C.SSL_new(&C.SSL_CTX) &C.SSL

fn C.SSL_set_fd(ssl &C.SSL, fd i32) i32
Expand Down Expand Up @@ -147,6 +159,8 @@ fn C.v_net_openssl_set_alpn_protos(ssl &C.SSL, protos &u8, protos_len u32) i32

fn C.v_net_openssl_get0_alpn_selected(ssl &C.SSL, data voidptr, len &u32)

fn C.v_net_openssl_SSL_CTX_set_alpn_select_protos(ctx &C.SSL_CTX, protos &u8, protos_len u32) i32

fn C.SSL_shutdown(&C.SSL) i32

fn C.SSL_free(&C.SSL)
Expand All @@ -159,8 +173,16 @@ fn C.SSLv23_client_method() &C.SSL_METHOD

fn C.TLS_method() voidptr

fn C.v_net_openssl_TLS_server_method() &C.SSL_METHOD

fn C.TLSv1_2_method() voidptr

fn C.SSL_CTX_check_private_key(ctx &C.SSL_CTX) i32

fn C.SSL_accept(ssl &C.SSL) i32

fn C.ERR_print_errors_fp(fp voidptr)

fn C.v_net_openssl_init_ssl() i32

fn init() {
Expand Down
167 changes: 167 additions & 0 deletions vlib/net/openssl/openssl_compat.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/pem.h>
#include <openssl/x509_vfy.h>

// Match the init API to the OpenSSL headers that are actually available.
#if defined(LIBRESSL_VERSION_NUMBER) || !defined(OPENSSL_VERSION_NUMBER) \
|| OPENSSL_VERSION_NUMBER < 0x10100000L
Expand All @@ -11,6 +17,91 @@ static int v_net_openssl_init_ssl(void) {
}
#endif

static int v_net_openssl_SSL_CTX_use_certificate_chain_memory(SSL_CTX *ctx,
const unsigned char *data, size_t len) {
if (len > INT_MAX) {
return 0;
}
BIO *bio = BIO_new_mem_buf((const void *)data, (int)len);
if (bio == NULL) {
return 0;
}
X509 *cert = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
if (cert == NULL) {
BIO_free(bio);
return 0;
}
int result = SSL_CTX_use_certificate(ctx, cert);
X509_free(cert);
if (result != 1) {
BIO_free(bio);
return 0;
}
while ((cert = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) {
// SSL_CTX_add_extra_chain_cert takes ownership of cert on success.
if (SSL_CTX_add_extra_chain_cert(ctx, cert) != 1) {
X509_free(cert);
BIO_free(bio);
return 0;
}
}
// Reaching the end of a PEM stream leaves PEM_R_NO_START_LINE queued.
ERR_clear_error();
Comment thread
guweigang marked this conversation as resolved.
Outdated
BIO_free(bio);
return 1;
}

static int v_net_openssl_SSL_CTX_extra_chain_certs_count(SSL_CTX *ctx) {
STACK_OF(X509) *chain = NULL;
SSL_CTX_get_extra_chain_certs(ctx, &chain);
return chain == NULL ? 0 : sk_X509_num(chain);
}

static int v_net_openssl_SSL_CTX_use_PrivateKey_memory(SSL_CTX *ctx,
const unsigned char *data, size_t len) {
if (len > INT_MAX) {
return 0;
}
BIO *bio = BIO_new_mem_buf((const void *)data, (int)len);
if (bio == NULL) {
return 0;
}
EVP_PKEY *key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
BIO_free(bio);
if (key == NULL) {
return 0;
}
int result = SSL_CTX_use_PrivateKey(ctx, key);
EVP_PKEY_free(key);
return result;
}

static int v_net_openssl_SSL_CTX_load_verify_memory(SSL_CTX *ctx,
const unsigned char *data, size_t len) {
if (len > INT_MAX) {
return 0;
}
BIO *bio = BIO_new_mem_buf((const void *)data, (int)len);
if (bio == NULL) {
return 0;
}
X509_STORE *store = SSL_CTX_get_cert_store(ctx);
X509 *cert = NULL;
int loaded = 0;
while ((cert = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL)) != NULL) {
if (X509_STORE_add_cert(store, cert) != 1) {
X509_free(cert);
BIO_free(bio);
return 0;
}
X509_free(cert);
loaded++;
}
ERR_clear_error();
BIO_free(bio);
return loaded > 0 ? 1 : 0;
}

// SSL_get1_peer_certificate is only available in OpenSSL 3.x.
#if defined(LIBRESSL_VERSION_NUMBER) || !defined(OPENSSL_VERSION_NUMBER) \
|| OPENSSL_VERSION_NUMBER < 0x30000000L
Expand Down Expand Up @@ -40,7 +131,70 @@ static void v_net_openssl_get0_alpn_selected(SSL *ssl, const unsigned char **dat
*data = NULL;
*len = 0;
}
static int v_net_openssl_SSL_CTX_set_alpn_select_protos(SSL_CTX *ctx,
const unsigned char *protos, unsigned int protos_len) {
(void)ctx;
(void)protos;
(void)protos_len;
return -1;
}
#else
typedef struct {
unsigned char *protos;
unsigned int protos_len;
} v_net_openssl_alpn_select_state;

static void v_net_openssl_free_alpn_select_state(void *parent, void *ptr,
CRYPTO_EX_DATA *ad, int idx, long argl, void *argp) {
(void)parent;
(void)ad;
(void)idx;
(void)argl;
(void)argp;
v_net_openssl_alpn_select_state *selection =
(v_net_openssl_alpn_select_state *)ptr;
if (selection != NULL) {
free(selection->protos);
free(selection);
}
}

static int v_net_openssl_alpn_select_cb(SSL *ssl, const unsigned char **out,
unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) {
(void)ssl;
v_net_openssl_alpn_select_state *state = (v_net_openssl_alpn_select_state *)arg;
if (SSL_select_next_proto((unsigned char **)out, outlen, state->protos,
state->protos_len, in, inlen) == OPENSSL_NPN_NEGOTIATED) {
return SSL_TLSEXT_ERR_OK;
}
return SSL_TLSEXT_ERR_NOACK;
}

static int v_net_openssl_SSL_CTX_set_alpn_select_protos(SSL_CTX *ctx,
const unsigned char *protos, unsigned int protos_len) {
v_net_openssl_alpn_select_state *selection =
(v_net_openssl_alpn_select_state *)malloc(sizeof(*selection));
if (selection == NULL) {
return -1;
}
selection->protos = (unsigned char *)malloc(protos_len);
if (selection->protos == NULL) {
free(selection);
return -1;
}
memcpy(selection->protos, protos, protos_len);
selection->protos_len = protos_len;
int state_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
v_net_openssl_free_alpn_select_state);
Comment thread
guweigang marked this conversation as resolved.
Outdated
if (state_index < 0 || SSL_CTX_set_ex_data(ctx, state_index, selection) != 1) {
free(selection->protos);
free(selection);
return -1;
}
SSL_CTX_set_alpn_select_cb(ctx, v_net_openssl_alpn_select_cb, selection);
return 0;
}

static int v_net_openssl_set_alpn_protos(SSL *ssl, const unsigned char *protos, unsigned int protos_len) {
return SSL_set_alpn_protos(ssl, protos, protos_len);
}
Expand All @@ -58,3 +212,16 @@ static void v_net_openssl_get0_alpn_selected(SSL *ssl, const unsigned char **dat
#ifndef SSL_ERROR_WANT_ASYNC_JOB
#define SSL_ERROR_WANT_ASYNC_JOB 10
#endif

// TLS_server_method() is only available in OpenSSL 1.1.0 and newer.
// On older OpenSSL/LibreSSL, fall back to SSLv23_server_method().
#if defined(LIBRESSL_VERSION_NUMBER) || !defined(OPENSSL_VERSION_NUMBER) \
|| OPENSSL_VERSION_NUMBER < 0x10100000L
static const SSL_METHOD *v_net_openssl_TLS_server_method(void) {
return SSLv23_server_method();
}
#else
static const SSL_METHOD *v_net_openssl_TLS_server_method(void) {
return TLS_server_method();
}
#endif
59 changes: 59 additions & 0 deletions vlib/net/openssl/openssl_sslconn_shutdown_does_not_panic_test.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import time
import context
import net.openssl

fn server() ! {
cfg := openssl.SSLConnectConfig{
cert: '-----BEGIN CERTIFICATE-----\nMIIEOTCCAyECFG64Q2g46jZb3kRbDOJWX/BwjSp6MA0GCSqGSIb3DQEBCwUAMEUx\nCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl\ncm5ldCBXaWRnaXRzIFB0eSBMdGQwIBcNMjMwODAyMTcyOTQyWhgPMjA1MDEyMTcx\nNzI5NDJaMGsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRQwEgYD\nVQQHDAtMb3MgQW5nZWxlczEdMBsGA1UECgwUQ2F0YWx5c3QgRGV2ZWxvcG1lbnQx\nEjAQBgNVBAMMCWxvY2FsaG9zdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC\nggIBALqAI4fqUi+QBVWcsXglouLdOML5+w0+1hSR1KdO0Q5XPdQAs/yYWJ+KUkDw\nG++rfy9DUPq7FNRBVurXQkcAtn6gXdllGUSjwUiDo/N4mMOyS/2sufBuaeww7jVi\nrppH+zwP1tUnjRd6khl6bi1Ian9VSzr3Iy9CkXIg1GU4CPXkOydLeoQfepXxWoK1\nOUNwT3VKC/stAfY3j/NIIeiJYkyuRGFCkxn/BUjN+AsXiTugRcYKEFHdIPkOuCXp\nYbhf+lLsczpxCs3rdZG9b/N6mEDCzXTmeHkmsjdPTf+1k5DZZvKzVBBrgdxCgBb7\n5RwjF5v9WmnIc33wWgfJC6FaUzj9NYxYUbPHD+jTz0rJB/jj4u/xJlM/e5NRmXdW\n70pOMKXtWjRSolLOFIPKLY1qs3KMTAZxKKWPDDF7WlMJxMRt7nnnks5yw43Nog4C\njDLk1ZgETnPpLgo3jbmJdIv+OHKTJrBlVvDq7VTyixCoS5G8KoOmyQJhaXG6NwE2\niVhH5JIKgzgCfetfDsnjxqJ/qtrFXPa8FF2TsomD0NK/GZmIcs+9OeVB75Jn5uhF\nfLHScpiTbuu5w3P/LI/MqihLRB6RRNnRzPH8fIg5bYC9b770ta/8GcFRuYE8t+UR\nGtqXJoIKixbDlqV54kal8FQzYzhETf9+NM6Kb/lKEfG/pslvAgMBAAEwDQYJKoZI\nhvcNAQELBQADggEBALI3uNiNO0QE1brA3QYFK+d9ZroB72NrJ0UNkzYHDg2Fc6xg\n4aVVfaxY08+TmKc0JlMOW+pUxeCW/+UBSngdQiR9EE9xm0k0XIrAsy9RXxRvEtPu\nM1VI2h7ayp1Y2BrnQinevTSgtqLRyS1VbOFRl1FiyVvinw2I0KsDdAMNevAPXcOa\nQ8pUgUq6f56DkhocQaj+hxD/uV8HryNxuoSXnPhvfTN3z4YRGzsaWevJ9EYJliOM\n+XugcqfFJ+W7/QCEcAHCL+Bw6OydG5NFORr3p57PXjjcL/uKmxPBrWg2Bz6uT4uR\nMhj0zttiFHLAt9jGfyk6W57UNUja1e1ggftJJhs=\n-----END CERTIFICATE-----\n'
cert_key: '-----BEGIN RSA PRIVATE KEY-----\n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\n-----END RSA PRIVATE KEY-----\n'
validate: false
in_memory_verification: true
}
mut srv := openssl.new_ssl_listener('127.0.0.1:64445', cfg) or {
eprintln('Listen: ${err}')
return err
}
eprintln('[+] Listening')
mut cli := srv.accept() or {
eprintln('Accept: ${err}')
return err
}
eprintln('[+] Accepted connection')
cli.shutdown()!
}

// 用 _test.v 内的 test_ 函数
fn test_shutdown_does_not_panic() {
Comment thread
guweigang marked this conversation as resolved.
_ := spawn server()
time.sleep(1 * time.second)
mut client := openssl.new_ssl_conn(validate: false)!
client.dial('127.0.0.1', 64445) or {
eprintln('Connect: ${err}')
return
Comment thread
guweigang marked this conversation as resolved.
Outdated
}
eprintln('[+] Connected')
time.sleep(1 * time.second)
mut background := context.background()
mut ctx, cancel := context.with_timeout(mut background, 2 * time.second)
spawn fn () {
mut i := 0
for {
i++
print('\r${i}...')
flush_stdout()
time.sleep(1 * time.second)
}
}()
mut done := ctx.done()
for {
select {
_ := <-done {
break
}
}
}

eprintln('\nTimeout without panic - OK')

assert true
}
12 changes: 11 additions & 1 deletion vlib/net/openssl/ssl_connection.c.v
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,17 @@ fn ssl_remaining_timeout(deadline time.Time) time.Duration {
if deadline.unix() == 0 {
return net.infinite_timeout
}
return deadline - time.now()
remaining := deadline - time.now()
if remaining <= 0 {
// The finite deadline has already expired. Return a minimal positive
// duration so select()/wait_for() perform an immediate, zero-length wait
// and report net.err_timed_out, instead of misreading a non-positive
// remaining time as net.infinite_timeout (== "wait forever"). Without
// this, a peer that stalls mid-handshake/read could park the caller
// indefinitely despite a finite timeout being configured.
return time.nanosecond
}
return remaining
}

// close closes the ssl connection and does cleanup
Expand Down
Loading
Loading