-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
feat(net, veb): add OpenSSL-based HTTPS server support #27480
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
base: master
Are you sure you want to change the base?
Changes from 1 commit
04e10f5
975c332
41d4b13
861e65d
1b6a63a
57a85b8
a83b139
5a64b92
009abcc
f34fa2e
7098911
89f9db3
02dc1a4
61636f2
df3a1b2
d05ee16
dcc2d8d
bc07637
acf577e
c860885
ccf15c8
1e942fd
7fd5b39
762ddef
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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() { | ||
| _ := 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 | ||
|
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 | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,147 @@ | ||
| module openssl | ||
|
|
||
| import net | ||
| import time | ||
| import os | ||
|
|
||
| // SSLListener is the SSL listener implementation for OpenSSL. | ||
| pub struct SSLListener { | ||
| saddr string | ||
| config SSLConnectConfig | ||
| mut: | ||
| tcp_listener &net.TcpListener = unsafe { nil } | ||
| sslctx &C.SSL_CTX = unsafe { nil } | ||
| } | ||
|
|
||
| // new_ssl_listener creates a new SSLListener binding to `saddr` with `config`. | ||
| pub fn new_ssl_listener(saddr string, config SSLConnectConfig) !&SSLListener { | ||
| mut listener := &SSLListener{ | ||
| saddr: saddr | ||
| config: config | ||
| } | ||
| listener.init()! | ||
| return listener | ||
| } | ||
|
|
||
| fn (mut l SSLListener) init() ! { | ||
| if l.config.cert == '' || l.config.cert_key == '' { | ||
| return error('net.openssl SSLListener.init, no certificate or key provided') | ||
| } | ||
|
guweigang marked this conversation as resolved.
|
||
|
|
||
| l.tcp_listener = net.listen_tcp(.ip, l.saddr, net.ListenOptions{})! | ||
|
guweigang marked this conversation as resolved.
Outdated
|
||
|
|
||
| l.sslctx = unsafe { C.SSL_CTX_new(C.TLS_server_method()) } | ||
|
guweigang marked this conversation as resolved.
Outdated
|
||
| if l.sslctx == 0 { | ||
| l.tcp_listener.close() or {} | ||
| return error('net.openssl SSLListener.init, could not get ssl context') | ||
| } | ||
|
|
||
| mut cert := l.config.cert | ||
| mut cert_key := l.config.cert_key | ||
|
|
||
| if l.config.in_memory_verification { | ||
| now := time.now().unix().str() | ||
| cert = os.temp_dir() + '/v_srv_cert' + now | ||
| cert_key = os.temp_dir() + '/v_srv_cert_key' + now | ||
| if l.config.cert != '' { | ||
| os.write_file(cert, l.config.cert)! | ||
| } | ||
| if l.config.cert_key != '' { | ||
| os.write_file(cert_key, l.config.cert_key)! | ||
|
guweigang marked this conversation as resolved.
Outdated
|
||
| } | ||
| } | ||
|
|
||
| mut res := C.SSL_CTX_use_certificate_file(voidptr(l.sslctx), &char(cert.str), | ||
| C.SSL_FILETYPE_PEM) | ||
| if res != 1 { | ||
| C.ERR_print_errors_fp(C.stderr) | ||
| l.shutdown() or {} | ||
| return error('net.openssl SSLListener.init, SSL_CTX_use_certificate_file failed') | ||
| } | ||
|
|
||
| res = C.SSL_CTX_use_PrivateKey_file(voidptr(l.sslctx), &char(cert_key.str), C.SSL_FILETYPE_PEM) | ||
| if res != 1 { | ||
| l.shutdown() or {} | ||
| return error('net.openssl SSLListener.init, SSL_CTX_use_PrivateKey_file failed') | ||
| } | ||
|
|
||
| res = C.SSL_CTX_check_private_key(voidptr(l.sslctx)) | ||
| if res != 1 { | ||
| l.shutdown() or {} | ||
| return error('net.openssl SSLListener.init, SSL_CTX_check_private_key failed') | ||
| } | ||
| } | ||
|
|
||
| // accept accepts a new TCP connection and performs the SSL handshake. | ||
| pub fn (mut l SSLListener) accept() !&SSLConn { | ||
| mut conn := l.accept_without_handshake()! | ||
| conn.accept_handshake() or { | ||
| conn.shutdown() or {} | ||
| return err | ||
| } | ||
| return conn | ||
| } | ||
|
|
||
| // accept_without_handshake accepts a new TCP connection but does not perform the handshake yet. | ||
| pub fn (mut l SSLListener) accept_without_handshake() !&SSLConn { | ||
| mut tcp_conn := l.tcp_listener.accept()! | ||
|
|
||
| ssl := unsafe { &C.SSL(C.SSL_new(l.sslctx)) } | ||
| if ssl == 0 { | ||
| tcp_conn.close() or {} | ||
| return error('net.openssl SSLListener.accept, could not create SSL instance') | ||
| } | ||
|
|
||
| if C.SSL_set_fd(voidptr(ssl), tcp_conn.sock.handle) != 1 { | ||
| C.SSL_free(voidptr(ssl)) | ||
| tcp_conn.close() or {} | ||
| return error('net.openssl SSLListener.accept, could not assign ssl to socket') | ||
| } | ||
|
|
||
| mut conn := &SSLConn{ | ||
| config: l.config | ||
| sslctx: unsafe { nil } // 设为 nil 避免连接关闭时意外释放共享的 sslctx | ||
|
guweigang marked this conversation as resolved.
Outdated
|
||
| ssl: ssl | ||
| handle: tcp_conn.sock.handle | ||
| duration: tcp_conn.read_timeout() | ||
| owns_socket: true | ||
| } | ||
|
|
||
| return conn | ||
| } | ||
|
|
||
| // accept_handshake performs the SSL handshake on the connection. | ||
| pub fn (mut conn SSLConn) accept_handshake() ! { | ||
| // 执行 SSL 握手过程 | ||
| deadline := ssl_timeout_deadline(conn.duration) | ||
| for { | ||
| res := C.SSL_accept(voidptr(conn.ssl)) | ||
|
guweigang marked this conversation as resolved.
guweigang marked this conversation as resolved.
|
||
| if res == 1 { | ||
| break | ||
| } | ||
|
|
||
| err_res := ssl_error(res, conn.ssl)! | ||
| if err_res == .ssl_error_want_read { | ||
| conn.wait_for_read(ssl_remaining_timeout(deadline))! | ||
| continue | ||
| } | ||
| if err_res == .ssl_error_want_write { | ||
| conn.wait_for_write(ssl_remaining_timeout(deadline))! | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Fresh evidence after the nonblocking fix is that the handshake still passes Useful? React with 👍 / 👎. |
||
| continue | ||
| } | ||
|
|
||
| // 握手失败 | ||
| return error('net.openssl SSLListener.accept, SSL handshake failed: ${err_res}') | ||
| } | ||
| } | ||
|
|
||
| // shutdown shuts down the SSL listener and releases the context. | ||
| pub fn (mut l SSLListener) shutdown() ! { | ||
| if l.sslctx != 0 { | ||
| C.SSL_CTX_free(l.sslctx) | ||
| l.sslctx = unsafe { nil } | ||
| } | ||
| if l.tcp_listener != unsafe { nil } { | ||
| l.tcp_listener.close()! | ||
|
guweigang marked this conversation as resolved.
|
||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.