Skip to content

Commit 0df4435

Browse files
authored
db.redis: add TLS support (#25374)
1 parent 2015035 commit 0df4435

File tree

1 file changed

+63
-23
lines changed

1 file changed

+63
-23
lines changed

vlib/db/redis/redis.v

Lines changed: 63 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
module redis
44

55
import net
6+
import net.ssl
67
import strings
78

89
// RedisValue represents all possible RESP (Redis Serialization Protocol) data types
@@ -19,7 +20,9 @@ pub struct DB {
1920
pub mut:
2021
version int // RESP protocol version
2122
mut:
22-
conn &net.TcpConn = unsafe { nil } // TCP connection to Redis
23+
conn &net.TcpConn = unsafe { nil } // TCP connection to Redis
24+
ssl_conn &ssl.SSLConn = unsafe { nil } // SSL connection to Redis
25+
tls bool
2326

2427
// Pre-allocated buffers to reduce memory allocations
2528
cmd_buf []u8 // Buffer for building commands
@@ -36,18 +39,27 @@ pub mut:
3639
host string = '127.0.0.1' // Redis server host
3740
port u16 = 6379 // Redis server port
3841
password string // Redis server password (optional)
42+
tls bool // Enable TLS/SSL connection
3943
version int = 2 // RESP protocol version (default: v2)
4044
}
4145

4246
// connect establishes a connection to a Redis server
4347
pub fn connect(config Config) !DB {
4448
mut db := DB{
45-
conn: net.dial_tcp('${config.host}:${config.port}')!
4649
version: config.version
50+
tls: config.tls
4751
cmd_buf: []u8{cap: cmd_buf_pre_allocate_len}
4852
resp_buf: []u8{cap: resp_buf_pre_allocate_len}
4953
}
5054

55+
if config.tls {
56+
mut ssl_conn := ssl.new_ssl_conn(ssl.SSLConnectConfig{ validate: false })!
57+
ssl_conn.dial(config.host, int(config.port))!
58+
db.ssl_conn = ssl_conn
59+
} else {
60+
db.conn = net.dial_tcp('${config.host}:${config.port}')!
61+
}
62+
5163
// Authenticate if password is provided
5264
if config.password.len > 0 {
5365
db.auth(config.password)!
@@ -58,7 +70,36 @@ pub fn connect(config Config) !DB {
5870

5971
// close terminates the connection to Redis server
6072
pub fn (mut db DB) close() ! {
61-
db.conn.close()!
73+
if db.tls {
74+
db.ssl_conn.close()!
75+
} else {
76+
db.conn.close()!
77+
}
78+
}
79+
80+
// Helper methods for TLS abstraction
81+
fn (mut db DB) write_data(data []u8) ! {
82+
if db.tls {
83+
db.ssl_conn.write(data)!
84+
} else {
85+
db.conn.write(data)!
86+
}
87+
}
88+
89+
fn (mut db DB) read_data(mut buf []u8) !int {
90+
if db.tls {
91+
return db.ssl_conn.read(mut buf)
92+
} else {
93+
return db.conn.read(mut buf)
94+
}
95+
}
96+
97+
fn (mut db DB) read_ptr_data(ptr &u8, len int) !int {
98+
if db.tls {
99+
return db.ssl_conn.socket_read_into_ptr(ptr, len)!
100+
} else {
101+
return db.conn.read_ptr(ptr, len)!
102+
}
62103
}
63104

64105
// auth sends an AUTH command to the server with the given password.
@@ -78,8 +119,7 @@ pub fn (mut db DB) auth(password string) ! {
78119

79120
// ping sends a PING command to verify server responsiveness
80121
pub fn (mut db DB) ping() !string {
81-
db.conn.write_string('*1\r\n$4\r\nPING\r\n')!
82-
return db.read_response()! as string
122+
return db.cmd('PING')! as string
83123
}
84124

85125
// del deletes a `key`
@@ -92,7 +132,7 @@ pub fn (mut db DB) del(key string) !i64 {
92132
db.pipeline_buffer << db.cmd_buf
93133
db.pipeline_cmd_count++
94134
} else {
95-
db.conn.write(db.cmd_buf)!
135+
db.write_data(db.cmd_buf)!
96136

97137
// read resp
98138
return db.read_response()! as i64
@@ -121,7 +161,7 @@ pub fn (mut db DB) set[T](key string, value T) !string {
121161
db.pipeline_buffer << db.cmd_buf
122162
db.pipeline_cmd_count++
123163
} else {
124-
db.conn.write(db.cmd_buf)!
164+
db.write_data(db.cmd_buf)!
125165
return db.read_response()! as string
126166
}
127167
return ''
@@ -137,7 +177,7 @@ pub fn (mut db DB) get[T](key string) !T {
137177
db.pipeline_buffer << db.cmd_buf
138178
db.pipeline_cmd_count++
139179
} else {
140-
db.conn.write(db.cmd_buf)!
180+
db.write_data(db.cmd_buf)!
141181
resp := db.read_response()!
142182
match resp {
143183
[]u8 {
@@ -171,7 +211,7 @@ pub fn (mut db DB) incr(key string) !i64 {
171211
db.pipeline_buffer << db.cmd_buf
172212
db.pipeline_cmd_count++
173213
} else {
174-
db.conn.write(db.cmd_buf)!
214+
db.write_data(db.cmd_buf)!
175215

176216
// read resp
177217
return db.read_response()! as i64
@@ -189,7 +229,7 @@ pub fn (mut db DB) decr(key string) !i64 {
189229
db.pipeline_buffer << db.cmd_buf
190230
db.pipeline_cmd_count++
191231
} else {
192-
db.conn.write(db.cmd_buf)!
232+
db.write_data(db.cmd_buf)!
193233

194234
// read resp
195235
return db.read_response()! as i64
@@ -222,7 +262,7 @@ pub fn (mut db DB) hset[T](key string, m map[string]T) !int {
222262
db.pipeline_buffer << db.cmd_buf
223263
db.pipeline_cmd_count++
224264
} else {
225-
db.conn.write(db.cmd_buf)!
265+
db.write_data(db.cmd_buf)!
226266
return int(db.read_response()! as i64)
227267
}
228268
return 0
@@ -239,7 +279,7 @@ pub fn (mut db DB) hget[T](key string, m_key string) !T {
239279
db.pipeline_buffer << db.cmd_buf
240280
db.pipeline_cmd_count++
241281
} else {
242-
db.conn.write(db.cmd_buf)!
282+
db.write_data(db.cmd_buf)!
243283
resp := db.read_response()! as []u8
244284
$if T is string {
245285
return resp.bytestr()
@@ -266,7 +306,7 @@ pub fn (mut db DB) hgetall[T](key string) !map[string]T {
266306
db.pipeline_buffer << db.cmd_buf
267307
db.pipeline_cmd_count++
268308
} else {
269-
db.conn.write(db.cmd_buf)!
309+
db.write_data(db.cmd_buf)!
270310
resp := db.read_response()!
271311
match resp {
272312
[]RedisValue {
@@ -315,7 +355,7 @@ pub fn (mut db DB) expire(key string, seconds int) !bool {
315355
db.pipeline_buffer << db.cmd_buf
316356
db.pipeline_cmd_count++
317357
} else {
318-
db.conn.write(db.cmd_buf)!
358+
db.write_data(db.cmd_buf)!
319359

320360
// read resp
321361
resp := db.read_response()! as i64
@@ -331,7 +371,7 @@ fn (mut db DB) read_response_bulk_string() !RedisValue {
331371

332372
db.resp_buf.clear()
333373
for {
334-
bytes_read := db.conn.read(mut chunk) or {
374+
bytes_read := db.read_data(mut chunk) or {
335375
return error('`read_response_bulk_string()`: connection error ${err}')
336376
}
337377
if bytes_read == 0 {
@@ -355,7 +395,7 @@ fn (mut db DB) read_response_bulk_string() !RedisValue {
355395

356396
if data_length == 0 {
357397
mut terminator := []u8{len: 2}
358-
db.conn.read(mut terminator)!
398+
db.read_data(mut terminator)!
359399
if terminator[0] != `\r` || terminator[1] != `\n` {
360400
return error('invalid terminator for empty string')
361401
}
@@ -373,7 +413,7 @@ fn (mut db DB) read_response_bulk_string() !RedisValue {
373413
chunk_size := if remaining > 1 { 1 } else { remaining }
374414
mut chunk_ptr := unsafe { &data_buf[total_read] }
375415

376-
bytes_read := db.conn.read_ptr(chunk_ptr, chunk_size)!
416+
bytes_read := db.read_ptr_data(chunk_ptr, chunk_size)!
377417
total_read += bytes_read
378418

379419
if bytes_read == 0 && total_read < data_buf.len {
@@ -400,7 +440,7 @@ fn (mut db DB) read_response_i64() !i64 {
400440
chunk_size := if remaining > 1 { 1 } else { remaining }
401441
mut chunk_ptr := unsafe { &db.resp_buf[total_read] }
402442

403-
bytes_read := db.conn.read_ptr(chunk_ptr, chunk_size)!
443+
bytes_read := db.read_ptr_data(chunk_ptr, chunk_size)!
404444
total_read += bytes_read
405445

406446
if total_read > 2 {
@@ -427,7 +467,7 @@ fn (mut db DB) read_response_simple_string() !string {
427467
chunk_size := if remaining > 1 { 1 } else { remaining }
428468
mut chunk_ptr := unsafe { &db.resp_buf[total_read] }
429469

430-
bytes_read := db.conn.read_ptr(chunk_ptr, chunk_size)!
470+
bytes_read := db.read_ptr_data(chunk_ptr, chunk_size)!
431471
total_read += bytes_read
432472

433473
if total_read > 2 {
@@ -449,7 +489,7 @@ fn (mut db DB) read_response_array() !RedisValue {
449489

450490
db.resp_buf.clear()
451491
for {
452-
bytes_read := db.conn.read(mut chunk) or {
492+
bytes_read := db.read_data(mut chunk) or {
453493
return error('`read_response_array()`: connection error: ${err}')
454494
}
455495
if bytes_read == 0 {
@@ -492,7 +532,7 @@ fn (mut db DB) read_response_array() !RedisValue {
492532
fn (mut db DB) read_response() !RedisValue {
493533
db.resp_buf.clear()
494534
unsafe { db.resp_buf.grow_len(1) }
495-
read_len := db.conn.read(mut db.resp_buf)!
535+
read_len := db.read_data(mut db.resp_buf)!
496536
if read_len != 1 {
497537
return error('`read_response()`: empty response from server')
498538
}
@@ -532,7 +572,7 @@ pub fn (mut db DB) cmd(cmd ...string) !RedisValue {
532572
db.pipeline_buffer << unsafe { sb.reuse_as_plain_u8_array() }
533573
db.pipeline_cmd_count++
534574
} else {
535-
unsafe { db.conn.write(sb.reuse_as_plain_u8_array())! }
575+
db.write_data(unsafe { sb.reuse_as_plain_u8_array() })!
536576
return db.read_response()!
537577
}
538578
return RedisNull{}
@@ -557,7 +597,7 @@ pub fn (mut db DB) pipeline_execute() ![]RedisValue {
557597
return []RedisValue{}
558598
}
559599

560-
db.conn.write(db.pipeline_buffer)!
600+
db.write_data(db.pipeline_buffer)!
561601

562602
mut results := []RedisValue{cap: db.pipeline_cmd_count}
563603
for _ in 0 .. db.pipeline_cmd_count {

0 commit comments

Comments
 (0)