// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. module http import net.ssl import strings fn (req &Request) ssl_do(port int, method Method, host_name string, path string, data string, header Header) !Response { $if windows && !no_vschannel ? { return vschannel_ssl_do(req, port, method, host_name, path, data, header) } return net_ssl_do(req, port, method, host_name, path, data, header) } fn net_ssl_do(req &Request, port int, method Method, host_name string, path string, data string, header Header) !Response { mut retries := 0 req_headers := req.build_request_headers_with(method, host_name, port, path, data, header) $if trace_http_request ? { eprint('> ') eprint(req_headers) eprintln('') } for { mut ssl_conn := ssl.new_ssl_conn( verify: req.verify cert: req.cert cert_key: req.cert_key validate: req.validate in_memory_verification: req.in_memory_verification )! ssl_conn.dial(host_name, port) or { retries++ if is_no_need_retry_error(err.code()) || retries >= req.max_retries { return err } continue } // Propagate the request's read timeout into the SSL backend. // Without this, mbedtls keeps its init-time default and openssl falls back to no // timeout at all on a stalled socket — see issue surfaced by macOS arm64 + tcc CI hangs. if req.read_timeout > 0 { ssl_conn.set_read_timeout(req.read_timeout) } return req.do_request(req_headers, mut ssl_conn)! } return error('http.net_ssl_do: exhausted retries') } fn read_from_ssl_connection_cb(con voidptr, buf &u8, bufsize int) !int { mut ssl_conn := unsafe { &ssl.SSLConn(con) } return ssl_conn.socket_read_into_ptr(buf, bufsize) } fn (req &Request) do_request(req_headers string, mut ssl_conn ssl.SSLConn) !Response { defer { ssl_conn.shutdown() or {} } ssl_conn.write_string(req_headers) or { return err } mut content := strings.new_builder(4096) response_info := req.receive_all_data_from_cb_in_builder(mut content, voidptr(ssl_conn), read_from_ssl_connection_cb)! response_text := content.str() $if trace_http_response ? { eprint('< ') eprint(response_text) eprintln('') } if req.on_finish != unsafe { nil } { req.on_finish(req, u64(response_text.len))! } return parse_received_response(response_text, response_info) }