-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathnano_get_url.erl
75 lines (60 loc) · 2.4 KB
/
nano_get_url.erl
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
-module(nano_get_url).
-export([get_url/0, get_url/1, get_domain/0, get_domain/1]).
get_url() ->
get_url("http://www.google.com/search?q=erlang").
get_url(Url) ->
{_Protocol, Host, Port, Path} = parse_url(Url),
{ok, Socket} = gen_tcp:connect(Host, Port, [binary, {packet, 0}]),
ok = gen_tcp:send(Socket, io_lib:format("GET ~s HTTP/1.0\r\n\r\n", [Path])),
{Status, Headers, Body} = parse_response(receive_data(Socket, [])),
case Status of
"302" ->
% Check if redirect header is present
case proplists:lookup("location", Headers) of
{_, Location} ->
% Follow redirect to location
io:format("Redirected to: ~s~n", [Location]),
get_url(Location);
none ->
io:format("No location header found!~n", []),
{Status, Headers, Body}
end;
_ ->
{Status, Headers, Body}
end.
get_domain() ->
get_url("www.google.com").
get_domain(Host) ->
{ok, Socket} = gen_tcp:connect(Host, 80, [binary, {packet, 0}]),
ok = gen_tcp:send(Socket, "GET / HTTP/1.0\r\n\r\n"),
{Status, Headers, Body} = parse_response(receive_data(Socket, [])),
{Status, Headers, Body}.
receive_data(Socket, SoFar) ->
receive
{tcp, Socket, Bin} ->
receive_data(Socket, [Bin|SoFar]);
{tcp_closed, Socket} ->
list_to_binary(lists:reverse(SoFar))
end.
parse_url(Url) ->
{ok, Parsed} = http_uri:parse(Url),
% We ignore the query string for simplicity here
{Protocol, _, Host, Port, Path, _Query} = Parsed,
{Protocol, Host, Port, Path}.
parse_response(ResponseBin) ->
% Find end of headers
[Headers, Body] = binary:split(ResponseBin, <<"\r\n\r\n">>),
[StatusLine|HeaderList] = binary:split(Headers, <<"\r\n">>, [global]),
% Parse status
{match, [[Status]]} = re:run(StatusLine,"(\\d{3})", [global, {capture, first, list}]),
% Parse headers
ParsedHeaders = lists:map(fun(Header) ->
[HeaderName, Value] = binary:split(Header, <<":">>),
{lowercase(trim(HeaderName)), trim(Value)}
end, HeaderList),
% Return body and parsed headers
{Status, ParsedHeaders, Body}.
lowercase(String) ->
string:to_lower(String).
trim(String) ->
re:replace(String, "(^\\s+)|(\\s+$)", "", [global,{return,list}]).