1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
module Aws_error = Error
open Base
module Format = Stdlib.Format
module Scheme = struct
type t = [ `Http | `Https ] [@@deriving show, eq]
let to_string = function `Http -> "http" | `Https -> "https"
end
type t = { scheme : Scheme.t; host : string; port : int option } [@@deriving eq]
let bracket_ipv6_host host =
if String.contains host ':' then Fmt.str "[%s]" host else host
let authority_host_port ~host ~port =
let host = bracket_ipv6_host host in
match port with None -> host | Some port -> Fmt.str "%s:%d" host port
let pp fmt t =
Format.fprintf fmt "%s://%s"
(Scheme.to_string t.scheme)
(authority_host_port ~host:t.host ~port:t.port)
let invalid = Aws_validation.invalid
let validate_host = Aws_validation.Host.validate ~allow_ipv6_brackets:false
let validate_port = Aws_validation.validate_port
let create ~scheme ~host ?port () =
match validate_host host with
| Error _ as error -> error
| Ok () -> (
match validate_port port with
| Error _ as error -> error
| Ok () -> Ok { scheme; host; port })
let create_exn ~scheme ~host ?port () =
Aws_error.Producer.get_ok_exn (create ~scheme ~host ?port ())
let split_scheme input =
match String.substr_index input ~pattern:"://" with
| None -> Ok (`Https, input)
| Some index ->
let scheme = String.prefix input index |> String.lowercase in
let rest = String.drop_prefix input (index + 3) in
let scheme =
match scheme with
| "http" -> Ok `Http
| "https" -> Ok `Https
| _ ->
invalid ~field:"scheme"
(Fmt.str "unsupported endpoint scheme: %s" scheme)
in
Result.map scheme ~f:(fun scheme -> (scheme, rest))
let split_ipv6_authority authority =
match String.substr_index authority ~pattern:"]" with
| None -> invalid ~field:"endpoint" "bracketed IPv6 endpoint is missing ']'"
| Some close_index ->
let host = String.sub authority ~pos:1 ~len:(close_index - 1) in
let rest = String.drop_prefix authority (close_index + 1) in
if String.is_empty rest then Ok (host, None)
else if String.is_prefix rest ~prefix:":" then
let port_string = String.drop_prefix rest 1 in
match Int.of_string_opt port_string with
| Some port -> Ok (host, Some port)
| None ->
invalid ~field:"port"
(Fmt.str "invalid endpoint port: %s" port_string)
else invalid ~field:"endpoint" "invalid bracketed IPv6 endpoint authority"
let split_host_port authority =
if String.is_prefix authority ~prefix:"[" then split_ipv6_authority authority
else
match String.rsplit2 authority ~on:':' with
| None -> Ok (authority, None)
| Some (host, port_string) when String.contains host ':' ->
invalid ~field:"endpoint" "IPv6 endpoint hosts must use brackets"
| Some (host, port_string) -> (
match Int.of_string_opt port_string with
| Some port -> Ok (host, Some port)
| None ->
invalid ~field:"port"
(Fmt.str "invalid endpoint port: %s" port_string))
let of_string input =
if String.is_empty input then
invalid ~field:"endpoint" "endpoint must be non-empty"
else if Aws_validation.has_leading_or_trailing_whitespace input then
invalid ~field:"endpoint"
"endpoint must not have leading/trailing whitespace"
else
match split_scheme input with
| Error _ as error -> error
| Ok (scheme, authority) -> (
if
String.exists authority ~f:(function
| '/' | '?' | '#' | '@' -> true
| _ -> false)
then
invalid ~field:"endpoint"
"endpoint must not include path, query, fragment, or userinfo"
else
match split_host_port authority with
| Error _ as error -> error
| Ok (host, port) -> create ~scheme ~host ?port ())
let of_string_exn input = Aws_error.Producer.get_ok_exn (of_string input)
let http ~host ?port () = create ~scheme:`Http ~host ?port ()
let http_exn ~host ?port () =
Aws_error.Producer.get_ok_exn (http ~host ?port ())
let https ~host ?port () = create ~scheme:`Https ~host ?port ()
let https_exn ~host ?port () =
Aws_error.Producer.get_ok_exn (https ~host ?port ())
let scheme t = t.scheme
let host t = t.host
let port t = t.port
let authority t = authority_host_port ~host:t.host ~port:t.port
let to_url_prefix t =
Fmt.str "%s://%s" (Scheme.to_string t.scheme) (authority t)