77]).
88
99-define (PORT , 8080 ).
10+ -define (PORT_SSL , 8443 ).
11+ -define (LISTEN_OPTIONS , [
12+ binary ,
13+ {active , false },
14+ {backlog , 128 },
15+ {packet , http_bin },
16+ {reuseaddr , true }
17+ ]).
1018
1119% % public
1220connection_count () ->
@@ -17,7 +25,9 @@ start() ->
1725 Pid = spawn (fun () -> init (Self ) end ),
1826 receive
1927 {Pid , started } ->
20- {ok , Pid }
28+ {ok , Pid };
29+ {Pid , {error , Reason }} ->
30+ {error , Reason }
2131 after 5000 ->
2232 {error , timeout }
2333 end .
@@ -27,82 +37,128 @@ stop() ->
2737 undefined ->
2838 ok ;
2939 Pid ->
40+ Ref = monitor (process , Pid ),
3041 exit (Pid , kill ),
31- ok
42+ receive
43+ {'DOWN' , Ref , process , Pid , _ } ->
44+ ok
45+ end
3246 end .
3347
3448% % private
3549init (Parent ) ->
36- register (? MODULE , self ()),
37- persistent_term :put ({? MODULE , connections }, counters :new (1 , [])),
38- {ok , LSocket } = gen_tcp :listen (? PORT , [
39- binary ,
40- {active , false },
41- {backlog , 128 },
42- {packet , http_bin },
43- {reuseaddr , true }
44- ]),
45- Parent ! {self (), started },
46- accept (LSocket ).
50+ try
51+ register (? MODULE , self ()),
52+ persistent_term :put ({? MODULE , connections }, counters :new (1 , [])),
53+ {ok , _ } = application :ensure_all_started (ssl ),
54+ {ok , LSocket } = listen (gen_tcp , ? PORT , ? LISTEN_OPTIONS ),
55+ % % the default pkix_test_data key (secp112r2, sha1) is not
56+ % % negotiable by a modern TLS client
57+ KeyOpts = [{key , {namedCurve , secp256r1 }}, {digest , sha256 }],
58+ SslOptions = public_key :pkix_test_data (#{root => KeyOpts ,
59+ peer => KeyOpts }),
60+ {ok , LSocketSsl } = listen (ssl , ? PORT_SSL ,
61+ ? LISTEN_OPTIONS ++ SslOptions ),
62+ spawn_link (fun () -> accept_ssl (LSocketSsl ) end ),
63+ Parent ! {self (), started },
64+ accept (LSocket )
65+ catch
66+ Class :Error :Stacktrace ->
67+ Parent ! {self (), {error , {Class , Error , Stacktrace }}}
68+ end .
69+
70+ % % the previous fixture's ports can linger briefly after its death:
71+ % % ERTS releases them asynchronously once the DOWN signal fires
72+ listen (Transport , Port , Options ) ->
73+ listen (Transport , Port , Options , 50 ).
74+
75+ listen (Transport , Port , Options , Retries ) ->
76+ case Transport :listen (Port , Options ) of
77+ {ok , LSocket } ->
78+ {ok , LSocket };
79+ {error , eaddrinuse } when Retries > 0 ->
80+ timer :sleep (10 ),
81+ listen (Transport , Port , Options , Retries - 1 );
82+ {error , _ } = E ->
83+ E
84+ end .
4785
4886accept (LSocket ) ->
4987 {ok , Socket } = gen_tcp :accept (LSocket ),
5088 counters :add (persistent_term :get ({? MODULE , connections }), 1 , 1 ),
5189 Pid = spawn_link (fun () ->
52- receive go -> connection (Socket ) end
90+ receive go -> connection (gen_tcp , Socket ) end
5391 end ),
5492 ok = gen_tcp :controlling_process (Socket , Pid ),
5593 Pid ! go ,
5694 accept (LSocket ).
5795
58- connection (Socket ) ->
59- case gen_tcp :recv (Socket , 0 ) of
96+ accept_ssl (LSocket ) ->
97+ {ok , TSocket } = ssl :transport_accept (LSocket ),
98+ counters :add (persistent_term :get ({? MODULE , connections }), 1 , 1 ),
99+ Pid = spawn_link (fun () ->
100+ receive
101+ go ->
102+ case ssl :handshake (TSocket ) of
103+ {ok , Socket } ->
104+ connection (ssl , Socket );
105+ {error , _ } ->
106+ ok
107+ end
108+ end
109+ end ),
110+ ok = ssl :controlling_process (TSocket , Pid ),
111+ Pid ! go ,
112+ accept_ssl (LSocket ).
113+
114+ connection (Transport , Socket ) ->
115+ case recv (Transport , Socket , 0 ) of
60116 {ok , {http_request , Method , {abs_path , Path }, _Version }} ->
61- ContentLength = headers (Socket , 0 ),
62- Body = body (Socket , ContentLength ),
63- respond (Socket , Method , Path , Body ),
64- connection (Socket );
117+ ContentLength = headers (Transport , Socket , 0 ),
118+ Body = body (Transport , Socket , ContentLength ),
119+ respond (Transport , Socket , Method , Path , Body ),
120+ connection (Transport , Socket );
65121 {ok , _ } ->
66- gen_tcp : close (Socket );
122+ close (Transport , Socket );
67123 {error , _ } ->
68- gen_tcp : close (Socket )
124+ close (Transport , Socket )
69125 end .
70126
71- headers (Socket , ContentLength ) ->
72- case gen_tcp : recv (Socket , 0 ) of
127+ headers (Transport , Socket , ContentLength ) ->
128+ case recv (Transport , Socket , 0 ) of
73129 {ok , {http_header , _ , 'Content-Length' , _ , Value }} ->
74- headers (Socket , binary_to_integer (Value ));
130+ headers (Transport , Socket , binary_to_integer (Value ));
75131 {ok , {http_header , _ , _ , _ , _ }} ->
76- headers (Socket , ContentLength );
132+ headers (Transport , Socket , ContentLength );
77133 {ok , http_eoh } ->
78134 ContentLength
79135 end .
80136
81- body (_Socket , 0 ) ->
137+ body (_Transport , _Socket , 0 ) ->
82138 <<>>;
83- body (Socket , ContentLength ) ->
84- ok = inet : setopts (Socket , [{packet , raw }]),
85- {ok , Body } = gen_tcp : recv (Socket , ContentLength ),
86- ok = inet : setopts (Socket , [{packet , http_bin }]),
139+ body (Transport , Socket , ContentLength ) ->
140+ ok = setopts (Transport , Socket , [{packet , raw }]),
141+ {ok , Body } = recv (Transport , Socket , ContentLength ),
142+ ok = setopts (Transport , Socket , [{packet , http_bin }]),
87143 Body .
88144
89- respond (Socket , Method , <<" /1" >>, _Body ) ->
90- reply (Socket , Method , <<" Hello world!" >>);
91- respond (Socket , Method , <<" /2" >>, _Body ) ->
92- reply (Socket , Method , binary :copy (<<" Hello world!" >>, 1000 ));
93- respond (Socket , Method , <<" /3" >>, Body ) ->
94- reply (Socket , Method , Body );
95- respond (Socket , Method , <<" /4" >>, _Body ) ->
96- chunked_reply (Socket , Method , [<<" Hello" >>, <<" world!" >>]);
97- respond (Socket , Method , <<" /5" >>, _Body ) ->
98- reply (Socket , Method , method (Method )).
145+ respond (Transport , Socket , Method , <<" /1" >>, _Body ) ->
146+ reply (Transport , Socket , Method , <<" Hello world!" >>);
147+ respond (Transport , Socket , Method , <<" /2" >>, _Body ) ->
148+ reply (Transport , Socket , Method , binary :copy (<<" Hello world!" >>, 1000 ));
149+ respond (Transport , Socket , Method , <<" /3" >>, Body ) ->
150+ reply (Transport , Socket , Method , Body );
151+ respond (Transport , Socket , Method , <<" /4" >>, _Body ) ->
152+ chunked_reply (Transport , Socket , Method , [<<" Hello" >>, <<" world!" >>]);
153+ respond (Transport , Socket , Method , <<" /5" >>, _Body ) ->
154+ reply (Transport , Socket , Method , method (Method )).
99155
100156method (Method ) when is_atom (Method ) ->
101157 atom_to_binary (Method , utf8 );
102158method (Method ) when is_binary (Method ) ->
103159 Method .
104160
105- reply (Socket , Method , Body ) ->
161+ reply (Transport , Socket , Method , Body ) ->
106162 Headers = [
107163 <<" HTTP/1.1 200 OK\r\n " >>,
108164 <<" Connection: Keep-Alive\r\n " >>,
@@ -112,12 +168,12 @@ reply(Socket, Method, Body) ->
112168 ],
113169 case Method of
114170 'HEAD' ->
115- ok = gen_tcp : send (Socket , Headers );
171+ ok = send (Transport , Socket , Headers );
116172 _ ->
117- ok = gen_tcp : send (Socket , [Headers , Body ])
173+ ok = send (Transport , Socket , [Headers , Body ])
118174 end .
119175
120- chunked_reply (Socket , Method , Chunks ) ->
176+ chunked_reply (Transport , Socket , Method , Chunks ) ->
121177 Headers = [
122178 <<" HTTP/1.1 200 OK\r\n " >>,
123179 <<" Connection: Keep-Alive\r\n " >>,
@@ -126,9 +182,29 @@ chunked_reply(Socket, Method, Chunks) ->
126182 ],
127183 case Method of
128184 'HEAD' ->
129- ok = gen_tcp : send (Socket , Headers );
185+ ok = send (Transport , Socket , Headers );
130186 _ ->
131187 Encoded = [[integer_to_binary (byte_size (Chunk ), 16 ), <<" \r\n " >>,
132188 Chunk , <<" \r\n " >>] || Chunk <- Chunks ],
133- ok = gen_tcp : send (Socket , [Headers , Encoded , <<" 0\r\n\r\n " >>])
189+ ok = send (Transport , Socket , [Headers , Encoded , <<" 0\r\n\r\n " >>])
134190 end .
191+
192+ close (gen_tcp , Socket ) ->
193+ gen_tcp :close (Socket );
194+ close (ssl , Socket ) ->
195+ ssl :close (Socket ).
196+
197+ recv (gen_tcp , Socket , Length ) ->
198+ gen_tcp :recv (Socket , Length );
199+ recv (ssl , Socket , Length ) ->
200+ ssl :recv (Socket , Length ).
201+
202+ send (gen_tcp , Socket , Data ) ->
203+ gen_tcp :send (Socket , Data );
204+ send (ssl , Socket , Data ) ->
205+ ssl :send (Socket , Data ).
206+
207+ setopts (gen_tcp , Socket , Opts ) ->
208+ inet :setopts (Socket , Opts );
209+ setopts (ssl , Socket , Opts ) ->
210+ ssl :setopts (Socket , Opts ).
0 commit comments