Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -module(websocketd).
- -compile(export_all).
- -author({jha, abhinav}).
- start() ->
- {ok, L} = gen_tcp:listen(8000, [binary, {active, true}, {reuseaddr, true}, {packet, 0}]),
- erlang:process_flag(trap_exit, true),
- Pid = spawn_link(websocketd, acceptor, [L]),
- receive
- {'EXIT', Pid, Why} ->
- io:format("Process ~p exited with ~p. ~n", [Pid, Why]);
- Any ->
- io:format("~p~n", [Any])
- end.
- % Accepts multiple connections and handles them separately.
- acceptor(L)->
- {ok, S} = gen_tcp:accept(L),
- spawn(fun()->acceptor(L) end),
- handle(S).
- % Not really required, just a wrapper over handle/2 in case we want to do something later.
- handle(S)->
- handle(S, []).
- handle(S, _Data)->
- Pid = self(),
- Child = spawn(fun() -> handshake_and_talk(Pid) end),
- loop(S, Child).
- loop(S, Child)->
- receive
- {tcp, S, Bin} ->
- Child ! {self(), Bin},
- loop(S, Child);
- {Child, X} ->
- gen_tcp:send(S, X),
- loop(S, Child);
- _Any ->
- loop(S, Child)
- end.
- % Handshake with the client and begin talking.
- handshake_and_talk(Ppid)->
- receive
- {Ppid, X} ->
- % Body = the checksum comprised after processing Key1, Key2 and the Client's request body.
- Body = process_client_handshake(binary_to_list(X)),
- % Send the Handshake stuff to the browser.
- Ppid ! {self(), "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"},
- Ppid ! {self(), "Upgrade: WebSocket\r\n"},
- Ppid ! {self(), "Connection: Upgrade\r\n"},
- Ppid ! {self(), "Sec-WebSocket-Origin: http://127.0.0.1:8080\r\n"},
- Ppid ! {self(), "Sec-WebSocket-Location: ws://127.0.0.1:8000/\r\n"},
- Ppid ! {self(), "Sec-WebSocket-Protocol: chat"},
- Ppid ! {self(), "\r\n\r\n"},
- % Send the body.
- Ppid ! {self(), Body},
- % Now call the talk method to do the actual talking with the browser.
- talk(Ppid);
- Any -> io:format("[Child] Random stuff received:~p~n. ~p", [Any, Ppid])
- end.
- % Function to actuall talk to the browser.
- % Dummy implementation - Implement as reuired.
- talk(Browser) ->
- receive
- after 1000 ->
- % This is the main communicator function to the Browser.
- % Whatever you write instead of "Hahah" will get sent to the browser.
- % the 0 and 255 is the framing ( required) - don't change that.
- Browser ! {self(), [0]},
- Browser ! {self(), "Hahah"},
- Browser ! {self(), [255]},
- talk(Browser)
- end.
- % Process client's handshake to retrieve information.
- process_client_handshake(X)->
- [Body|Head] = lists:reverse(string:tokens(X, "\r\n")),
- {Key1, Key2} = extract_keys(lists:reverse(Head)),
- {Skey1, Skey2} = process_keys(Key1, Key2),
- Bin_body = list_to_binary(Body),
- Key = <<Skey1:32/big-unsigned-integer, Skey2:32/big-unsigned-integer, Bin_body/binary>>,
- erlang:md5(Key).
- % Extract keys from the client's handshake.
- extract_keys([H|T])->
- Key1 = extract_key("Sec-WebSocket-Key1: ", [H|T]),
- Key2 = extract_key("Sec-WebSocket-Key2: ", [H|T]),
- {Key1, Key2}.
- extract_key(X, [H|T])->
- case string:str(H, X) of
- 0 -> extract_key(X, T);
- _Pos -> string:substr(H, string:len(X) + 1)
- end.
- % Process the keys as mentioned in the handshake 76 draft of the ietf.
- process_keys(Key1, Key2)->
- {Digits1, []} = string:to_integer(digits(Key1)),
- {Digits2, []} = string:to_integer(digits(Key2)),
- Spaces1 = spaces(Key1),
- Spaces2 = spaces(Key2),
- {Digits1 div Spaces1, Digits2 div Spaces2}.
- % Concatenate digits 0-9 of a string
- digits(X)-> [A || A<-X, A =< 57, A >= 48].
- % Count number of spaces in a string.
- spaces(X)-> string:len([ A || A<-X, A =:= 32]).
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement