| 1 | module veb |
| 2 | |
| 3 | import io |
| 4 | import net |
| 5 | import net.http |
| 6 | import net.mbedtls |
| 7 | import os |
| 8 | import time |
| 9 | |
| 10 | @[params] |
| 11 | pub struct RunParams { |
| 12 | pub: |
| 13 | // use `family: .ip, host: 'localhost'` when you want it to bind only to 127.0.0.1 |
| 14 | family net.AddrFamily = .ip6 |
| 15 | host string |
| 16 | port int = default_port |
| 17 | nr_workers int = 1 |
| 18 | show_startup_message bool = true |
| 19 | timeout_in_seconds int = 30 |
| 20 | max_request_buffer_size int = 8192 |
| 21 | benchmark_page_generation bool // for the "page rendered in X ms" |
| 22 | ssl_config mbedtls.SSLConnectConfig |
| 23 | } |
| 24 | |
| 25 | fn run_at_with_ssl[A, X](mut global_app A, params RunParams) ! { |
| 26 | routes := generate_routes[A, X](global_app)! |
| 27 | controllers_sorted := check_duplicate_routes_in_controllers[A](global_app, routes)! |
| 28 | if params.show_startup_message { |
| 29 | println('[veb] Running app on https://${startup_host(params)}:${params.port}/') |
| 30 | } |
| 31 | flush_stdout() |
| 32 | mut ssl_listener := mbedtls.new_ssl_listener(listen_addr(params), params.ssl_config)! |
| 33 | defer { |
| 34 | ssl_listener.shutdown() or {} |
| 35 | } |
| 36 | ssl_params := &SslRequestParams{ |
| 37 | global_app: unsafe { voidptr(&global_app) } |
| 38 | controllers_sorted: controllers_sorted |
| 39 | routes: &routes |
| 40 | benchmark_page_generation: params.benchmark_page_generation |
| 41 | max_request_buffer_size: if params.max_request_buffer_size > 0 { |
| 42 | params.max_request_buffer_size |
| 43 | } else { |
| 44 | max_read |
| 45 | } |
| 46 | } |
| 47 | $if A is BeforeAcceptApp { |
| 48 | global_app.before_accept_loop() |
| 49 | } |
| 50 | for { |
| 51 | mut ssl_conn := ssl_listener.accept() or { |
| 52 | eprintln('[veb] accept() failed, reason: ${err}; skipping') |
| 53 | continue |
| 54 | } |
| 55 | ssl_conn.duration = params.timeout_in_seconds * time.second |
| 56 | spawn handle_ssl_connection[A, X](mut ssl_conn, ssl_params) |
| 57 | } |
| 58 | } |
| 59 | |
| 60 | fn handle_ssl_connection[A, X](mut ssl_conn mbedtls.SSLConn, params &SslRequestParams) { |
| 61 | defer { |
| 62 | ssl_conn.shutdown() or {} |
| 63 | } |
| 64 | mut reader := io.new_buffered_reader( |
| 65 | reader: ssl_conn |
| 66 | cap: params.max_request_buffer_size |
| 67 | ) |
| 68 | defer { |
| 69 | unsafe { |
| 70 | reader.free() |
| 71 | } |
| 72 | } |
| 73 | for { |
| 74 | req := read_request_from_buffered_reader(mut reader) or { |
| 75 | if err !is io.Eof { |
| 76 | write_ssl_response(mut ssl_conn, http_400) or {} |
| 77 | } |
| 78 | return |
| 79 | } |
| 80 | completed_context := handle_ssl_request[A, X](req, params) or { |
| 81 | write_ssl_response(mut ssl_conn, http_400) or {} |
| 82 | return |
| 83 | } |
| 84 | if completed_context.takeover_mode != .none { |
| 85 | eprintln('[veb] HTTPS connections do not support takeover connections yet; closing the connection after this response.') |
| 86 | } |
| 87 | write_ssl_context_response(mut ssl_conn, completed_context) or { |
| 88 | eprintln('[veb] error sending HTTPS response: ${err}') |
| 89 | return |
| 90 | } |
| 91 | if completed_context.takeover_mode != .none |
| 92 | || should_close_connection(completed_context.req, completed_context.res, completed_context.client_wants_to_close) { |
| 93 | return |
| 94 | } |
| 95 | } |
| 96 | } |
| 97 | |
| 98 | fn write_ssl_context_response(mut ssl_conn mbedtls.SSLConn, completed_context &Context) ! { |
| 99 | if !completed_context.done && completed_context.return_type == .normal { |
| 100 | return error('context did not send a response') |
| 101 | } |
| 102 | match completed_context.return_type { |
| 103 | .normal { |
| 104 | write_ssl_response(mut ssl_conn, completed_context.res)! |
| 105 | } |
| 106 | .file { |
| 107 | write_ssl_response(mut ssl_conn, completed_context.res)! |
| 108 | if completed_context.return_file == '' { |
| 109 | return error('missing file response path') |
| 110 | } |
| 111 | mut file := os.open(completed_context.return_file)! |
| 112 | defer { |
| 113 | file.close() |
| 114 | } |
| 115 | mut buf := []u8{len: max_read} |
| 116 | for { |
| 117 | n := file.read(mut buf) or { |
| 118 | if err is io.Eof { |
| 119 | break |
| 120 | } |
| 121 | return err |
| 122 | } |
| 123 | if n <= 0 { |
| 124 | break |
| 125 | } |
| 126 | ssl_conn.write(buf[..n])! |
| 127 | } |
| 128 | } |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | fn write_ssl_response(mut ssl_conn mbedtls.SSLConn, resp http.Response) ! { |
| 133 | ssl_conn.write(resp.bytes())! |
| 134 | } |
| 135 | |