server.ml (2411B)
1 open Lwt 2 open Lwt.Syntax 3 open Config 4 open Response 5 6 exception Bad of string 7 8 let init_socket config = 9 let open Lwt_unix in 10 let sock = config.sock in 11 setsockopt sock SO_REUSEADDR true; 12 let addr = ADDR_INET (config.host, config.port) in 13 bind sock addr >|= fun () -> listen sock config.max_req 14 15 let init_ssl config = 16 let+ cert = X509_lwt.private_of_pems ~cert:config.cert ~priv_key:config.key in 17 Tls.Config.server ~reneg:true ~certificates:(`Single cert) ~version:config.tls () 18 19 let receive server config = Tls_lwt.Unix.accept server config.sock 20 21 let read session = 22 let buf = Cstruct.create (1024 + 2) in 23 let+ len = Tls_lwt.Unix.read session buf in 24 let msg = Cstruct.to_string @@ Cstruct.sub buf 0 len in 25 let req = 26 if Text.ends_with msg "\r\n" 27 then ( 28 match Text.check @@ Uri.pct_decode @@ msg with 29 | None -> msg 30 | Some _ -> raise (Bad "Bad Request Encoding")) 31 else "" (* request is too long, so we send an empty one *) 32 in 33 Uri.of_string @@ Text.strip req 34 35 let pipe fdin fdout = 36 let open Lwt_io in 37 write_lines fdout 38 @@ read_lines 39 @@ of_unix_fd ~mode:Input 40 @@ Unix.descr_of_in_channel fdin 41 42 (** The status line is a single UTF-8 line formatted as: 43 <STATUS><SPACE><META><CR><LF> *) 44 let write_header session status meta = 45 Tls_lwt.Unix.write session 46 @@ Cstruct.of_string 47 @@ Text.encode ~encoding:"utf-8" 48 @@ Printf.sprintf "%d %s\r\n" (int_of_status status) meta 49 50 let write_body session status meta fd = 51 write_header session status meta >>= fun () -> pipe fd (snd @@ Tls_lwt.of_t session) 52 53 let write_response session resp = 54 (match resp with 55 | Head (status, meta) -> write_header session status meta 56 | Body (status, meta, fd) -> write_body session status meta fd) 57 >>= fun () -> Tls_lwt.Unix.close_tls session 58 59 60 let initialize config = 61 let* _ = init_socket config in 62 let* s = init_ssl config in 63 let _ = Sys.chdir config.dir in 64 Lwt.return s 65 66 let main (config) = 67 let* server = initialize config in 68 let rec loop () = 69 let* session, addr = receive server config in 70 let a = match addr with 71 | Unix.ADDR_INET (a, _) -> (Unix.string_of_inet_addr a) ^ ":" ^ (string_of_int config.port) 72 | _ -> failwith "not INET" in 73 let* request = read session in 74 print_endline (a ^ " " ^ Uri.path request); 75 let resp = handle request in 76 let* res = write_response session resp in 77 loop res 78 in 79 loop ()