-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Description
Describe the bug
The V language's net.mbedtls module currently uses compile-time constants for SSL read timeouts, which prevents runtime configuration. This forces users to recompile their applications with -d mbedtls_client_read_timeout_ms=<value> whenever they need different timeout values for different environments or use cases.
Current Implementation
In vlib/net/mbedtls/ssl_connection.c.v:
const mbedtls_client_read_timeout_ms = $d('mbedtls_client_read_timeout_ms', 10_000)
const mbedtls_server_read_timeout_ms = $d('mbedtls_server_read_timeout_ms', 41_000)Reproduction Steps
Example 1: WebSocket Connection Timeout
import net.websocket
import time
fn main() {
// Attempt to connect to a WebSocket server with infrequent data
mut ws := websocket.new_client('wss://stream.exchange.com/ws')!
// Subscribe to a low-frequency feed
ws.connect()!
// If no data arrives within 10 seconds, mbedtls will timeout
// even though the connection is healthy and the exchange will
// eventually send data
for {
ws.listen() or {
// Error: "did not receive any data within 10000ms"
// No way to increase this at runtime!
break
}
}
}Example 2: Different Timeouts for Different Connections
import net.mbedtls
import time
fn main() {
// I want Connection A to have 10s timeout (default)
config_a := mbedtls.SSLConnectConfig{}
mut conn_a := mbedtls.new_ssl_conn(config_a)!
// I want Connection B to have 5min timeout (for WebSocket)
// Currently impossible without recompiling with -d flag!
config_b := mbedtls.SSLConnectConfig{
// No read_timeout field available
}
mut conn_b := mbedtls.new_ssl_conn(config_b)!
}Workaround Required
Users must compile with:
v -d mbedtls_client_read_timeout_ms=300000 run main.vThis sets a global 5-minute timeout for all connections, which is suboptimal when different connections need different timeouts.
Expected Behavior
Consistent API with Other Net Modules
The net.mbedtls module should follow the same pattern as net.tcp.TcpConn and net.raw.RawConn:
// TcpConn pattern (already exists)
pub struct TcpConn {
pub mut:
read_timeout time.Duration
write_timeout time.Duration
}
pub fn (mut c TcpConn) set_read_timeout(t time.Duration) {
c.read_timeout = t
}
// RawConn pattern (already exists)
pub struct RawConn {
pub mut:
read_timeout time.Duration
write_timeout time.Duration
}
pub fn (mut c RawConn) set_read_timeout(t time.Duration) {
c.read_timeout = t
}
// Expected mbedtls pattern (proposed)
pub struct SSLConn {
pub mut:
read_timeout time.Duration
}
pub fn (mut s SSLConn) set_read_timeout(t time.Duration) {
s.read_timeout = t
C.mbedtls_ssl_conf_read_timeout(&s.conf, u32(t.milliseconds()))
}Per-Connection Timeout Configuration
Users should be able to configure timeouts per connection:
import net.mbedtls
import time
fn main() {
// Connection A: Default 10s timeout
mut conn_a := mbedtls.new_ssl_conn(mbedtls.SSLConnectConfig{})!
// Connection B: 5-minute timeout for WebSocket
mut conn_b := mbedtls.new_ssl_conn(mbedtls.SSLConnectConfig{
read_timeout: 300 * time.second
})!
// Connection C: Modify timeout at runtime
mut conn_c := mbedtls.new_ssl_conn(mbedtls.SSLConnectConfig{})!
conn_c.set_read_timeout(60 * time.second) // Change to 1 minute
}Backward Compatibility
- Existing code using
-d mbedtls_client_read_timeout_ms=...should continue to work - The compile-time value becomes the default for
SSLConnectConfig.read_timeout - No breaking changes to existing APIs
Current Behavior
Compile-Time Configuration Only
// File: vlib/net/mbedtls/ssl_connection.c.v:11-12
const mbedtls_client_read_timeout_ms = $d('mbedtls_client_read_timeout_ms', 10_000)
const mbedtls_server_read_timeout_ms = $d('mbedtls_server_read_timeout_ms', 41_000)Usage in Connection Initialization
// File: vlib/net/mbedtls/ssl_connection.c.v:391-394
$if trace_mbedtls_timeouts ? {
dump(mbedtls_client_read_timeout_ms)
}
C.mbedtls_ssl_conf_read_timeout(&s.conf, mbedtls_client_read_timeout_ms)Error Message
// File: vlib/net/mbedtls/ssl_connection.c.v:587-592
C.MBEDTLS_ERR_SSL_TIMEOUT {
return error_with_code(
'net.mbedtls SSLConn.socket_read_into_ptr, did not receive any data within ${mbedtls_client_read_timeout_ms}ms. compile with `-d mbedtls_client_read_timeout_ms=100000` to set a higher timeout',
res
)
}Limitations
- Single global value: All connections in the application share the same timeout
- Recompilation required: Changing timeout requires recompiling entire application
- No runtime API: No methods like
set_read_timeout()orget_read_timeout() - Inconsistent: Does not follow the pattern established by
TcpConnandRawConn
Possible Solution
Implementation Plan
Step 1: Add read_timeout to SSLConnectConfig
File: vlib/net/mbedtls/ssl_connection.c.v
Lines: 318-329
@[params]
pub struct SSLConnectConfig {
pub:
verify string
cert string
cert_key string
validate bool
in_memory_verification bool
get_certificate ?fn (mut SSLListener, string) !&SSLCerts
// NEW: Runtime-configurable timeout with compile-time default
read_timeout time.Duration = $d('mbedtls_client_read_timeout_ms', 10_000) * time.millisecond
}Step 2: Add read_timeout to SSLConn
File: vlib/net/mbedtls/ssl_connection.c.v
Lines: 139-155
pub struct SSLConn {
pub:
config SSLConnectConfig
pub mut:
server_fd C.mbedtls_net_context
ssl C.mbedtls_ssl_context
conf C.mbedtls_ssl_config
certs &SSLCerts = unsafe { nil }
handle int
duration time.Duration
opened bool
ip string
owns_socket bool
// NEW: Store current timeout for getter methods
read_timeout time.Duration
}Step 3: Initialize timeout from config
File: vlib/net/mbedtls/ssl_connection.c.v
Function: fn (mut s SSLConn) init() !
Lines: 391-394
$if trace_mbedtls_timeouts ? {
dump(s.config.read_timeout)
}
// Use config timeout instead of compile-time constant:
C.mbedtls_ssl_conf_read_timeout(&s.conf, u32(s.config.read_timeout.milliseconds()))
s.read_timeout = s.config.read_timeout // Store for later useStep 4: Add getter/setter methods
File: vlib/net/mbedtls/ssl_connection.c.v
**After line 329 (after SSLConnectConfig definition)
// read_timeout returns the current SSL read timeout for this connection
pub fn (s &SSLConn) read_timeout() time.Duration {
return s.read_timeout
}
// set_read_timeout sets the SSL read timeout for this connection.
// Note: This modifies the underlying mbedtls configuration immediately.
// The change takes effect for subsequent read operations.
pub fn (mut s SSLConn) set_read_timeout(t time.Duration) {
s.read_timeout = t
C.mbedtls_ssl_conf_read_timeout(&s.conf, u32(t.milliseconds()))
}Step 5: Update error message
File: vlib/net/mbedtls/ssl_connection.c.v
Lines: 587-592
C.MBEDTLS_ERR_SSL_TIMEOUT {
$if trace_ssl ? {
eprintln('${@METHOD} ---> res: C.MBEDTLS_ERR_SSL_TIMEOUT')
}
return error_with_code(
'net.mbedtls SSLConn.socket_read_into_ptr, did not receive any data within ${s.read_timeout.milliseconds()}ms. Use `conn.set_read_timeout(duration)` to increase timeout',
res
)
}Alternative: Apply Same Pattern to SSLListener
For consistency, apply the same changes to SSLListener:
// In SSLListener struct (line ~220)
pub mut:
read_timeout time.Duration
// In SSLConnectConfig
server_read_timeout time.Duration = $d('mbedtls_server_read_timeout_ms', 41_000) * time.millisecond
// In SSLListener.init()
C.mbedtls_ssl_conf_read_timeout(&l.conf, u32(l.config.server_read_timeout.milliseconds()))
l.read_timeout = l.config.server_read_timeout
// Add getter/setter
pub fn (l &SSLListener) read_timeout() time.Duration { return l.read_timeout }
pub fn (mut l SSLListener) set_read_timeout(t time.Duration) {
l.read_timeout = t
C.mbedtls_ssl_conf_read_timeout(&l.conf, u32(t.milliseconds()))
}Additional Information/Context
Benefits of This Fix
1. No Longer Need Compile-Time Flag
Before:
v -d mbedtls_client_read_timeout_ms=300000 run main.vAfter:
// In application code - no compile flag needed!
import net.mbedtls
import time
mut conn := mbedtls.new_ssl_conn(mbedtls.SSLConnectConfig{
read_timeout: 300 * time.second
})!2. Per-Connection Timeouts
Different connections can have different timeouts:
// API calls: Short timeout
mut api_conn := mbedtls.new_ssl_conn(mbedtls.SSLConnectConfig{
read_timeout: 10 * time.second
})!
// WebSocket: Long timeout
mut ws_conn := mbedtls.new_ssl_conn(mbedtls.SSLConnectConfig{
read_timeout: 300 * time.second
})!3. Runtime Adjustability
Adjust timeouts based on runtime conditions:
mut conn := mbedtls.new_ssl_conn(mbedtls.SSLConnectConfig{})!
// Later, based on user settings or network conditions:
if user_prefers_longer_timeout {
conn.set_read_timeout(5 * time.minute)
}4. Configuration File Support
Timeouts can now come from configuration files:
import toml
config := toml.parse_file('app.toml')!
ssl_timeout := config.value('websocket.ssl_timeout').i64()
mut conn := mbedtls.new_ssl_conn(mbedtls.SSLConnectConfig{
read_timeout: ssl_timeout * time.second
})!Comparison with Other Languages
| Language | SSL Timeout Configuration | Runtime API |
|---|---|---|
| Go | tls.Config.HandshakeTimeout |
✅ Yes |
| Python | ssl.SSLContext.timeout |
✅ Yes |
| Rust | native_tls::TlsConnectorBuilder.timeout() |
✅ Yes |
| Node.js | tls.connect({timeout: ...}) |
✅ Yes |
| V (current) | -d mbedtls_client_read_timeout_ms=... |
❌ No |
| V (proposed) | SSLConnectConfig{read_timeout: ...} |
✅ Yes |
This fix makes V's mbedtls module consistent with other net modules (TcpConn, RawConn) and industry standards. It enables:
- ✅ Runtime configuration without recompilation
- ✅ Per-connection timeouts instead of global value
- ✅ Configuration file support for timeouts
- ✅ Backward compatibility with existing code
- ✅ Consistent API across V's net modules
The implementation is minimal (~20 lines of changes) but provides significant flexibility improvements for applications requiring different SSL timeouts in different contexts.
V version
0.5
Environment details (OS name and version, etc.)
CachyOS
Note
You can use the 👍 reaction to increase the issue's priority for developers.
Please note that only the 👍 reaction to the issue itself counts as a vote.
Other reactions and those to comments will not be taken into account.