<commit_before>-module(websocket).
-author({ "<NAME>", "<EMAIL>" }).
-copyright(<<"© 2012,2013 <NAME>"/utf8>>).
-behavior(gen_server).
-export([ start/3, send/2, socket/1, headers/1, path/1, stop/1, uuid/1, method/1, protocol/1,
	wait_headers/2, upgrade/2, bind/3  ]).
-export([ init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3 ]).

-record(websocket, { uuid, handler, protocol, headers, socket, data, module, function, connecting }).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Public Methods
%%

%% Starts a websocket by accepting a connection form Listen port
start(Listen,Module,Function) ->
	gen_server:start_link(?MODULE, { Listen, Module, Function }, []).

%% Waits for the HTTP headers to determine if we can upgrade
wait_headers(WebSocket,Data) ->
	gen_server:cast(WebSocket,{ wait_headers, Data }).

%% Upgrades to the WebSocket of the appropriate protocol
upgrade(WebSocket,Data) ->
	gen_server:cast(WebSocket,{ upgrade, Data }).

%% Sends data to the websocket
send(WebSocket,Data) ->
	gen_server:call(WebSocket,{ send, Data }).

%% Returns the UUID of the WebSocket
uuid(WebSocket) ->
	gen_server:call(WebSocket,uuid).

%% Returns the raw TCP socket of the WebSocket
socket(WebSocket) ->
	gen_server:call(WebSocket,socket).

%% Returns the HTTP headers of the WebSocket
headers(WebSocket) ->
	gen_server:call(WebSocket,headers).

%% Returns the path of the WebSocket
path(WebSocket) ->
	gen_server:call(WebSocket,path).

%% Returns the method of the WebSocket
method(WebSocket) ->
	gen_server:call(WebSocket,method).

%% Returns the protocol of the WebSocket
protocol(WebSocket) ->
	gen_server:call(WebSocket,protocol).

%% Stops the WebSocket
stop(WebSocket) ->
	gen_server:cast(WebSocket,stop).

%% Binds a Module and Function to be called when a message is encountered
bind(WebSocket,Module,Function) ->
	gen_server:cast(WebSocket, { bind, Module, Function }).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% gen_server methods
init({ Listen, Module, Function }) ->
	%% accept the socket in this process
	case gen_tcp:accept(Listen) of
		{ ok, Socket } -> 
			%% if we get a socket, wait for the headers
			wait_headers(self(), <<"">>),
			{ ok, #websocket{ 
				uuid = uuid:id(), 
				socket = Socket, 
				headers = [], 
				data = [],
				module = Module,
				function = Function,
				connecting = true
			}};
		{ error, closed } ->
			%% if we don't get a socket, just bail!
			{ stop, closed }
	end.

handle_call({ send, Data } , _From, WebSocket = #websocket{ protocol = Protocol, socket = Socket }) ->
	ok = gen_tcp:send(Socket,Protocol:frame(Data)),
	{ reply, ok, WebSocket };

handle_call( uuid, _From, WebSocket = #websocket{ uuid = UUID }) ->
	{ reply, UUID, WebSocket };

handle_call( socket, _From, WebSocket = #websocket{ socket = Socket }) ->
	{ reply, Socket, WebSocket };

handle_call( headers, _From, WebSocket = #websocket{ headers = Headers }) ->
	{ reply, Headers, WebSocket };

handle_call( path, _From, WebSocket = #websocket{ headers = Headers }) ->
	{ path, Path } = lists:keyfind(path,1,Headers),
	{ reply, Path, WebSocket };

handle_call( method, _From, WebSocket = #websocket{ headers = Headers }) ->
	{ method, Method } = lists:keyfind(method,1,Headers),
	{ reply, Method, WebSocket };

handle_call( protocol, _From, WebSocket = #websocket{ protocol = Protocol }) ->
	{ reply, Protocol, WebSocket }.

handle_cast({ bind, Module, Function }, WebSocket) ->
	{ noreply, WebSocket#websocket{ module = Module, function = Function }};

handle_cast( stop, WebSocket ) ->
	{ stop, normal, WebSocket };

handle_cast({ wait_headers, Seen }, WebSocket) ->
	{ noreply, WebSocket#websocket{ data = Seen, connecting = true }};

handle_cast({ upgrade, Data }, WebSocket = #websocket{ socket = Socket, module = Module, function = Function }) ->
	%% parse the headers
	Headers = parse_headers(Data),
	%% determine which protocol to use, draft00 is now deprecated
	Protocol =  case proplists:get_value(<<"Sec-WebSocket-Version">>,Headers) of
		<<"13">> -> 
			io:format("using rfc6455~n"),
			websocket_rfc6455;
		_ ->
			io:format("Protocol not supported")
	end,
	%% generate the server handshake
	Handshake = Protocol:handshake(Headers,Data),
	%% find if we have any websocket data already in hand
	%% send the handshake
	case gen_tcp:send(Socket,Handshake) of
		ok -> 
			spawn(Module,Function,[self(),connected]),
			{ noreply, WebSocket#websocket{ 
				protocol = Protocol,
				handler = Protocol:handler(self(),Socket),
				headers = Headers, 
				data = [],
				connecting = false
			}};
		{ error, Reason } ->	
			io:format("Handshake failed ~p~n", [ Reason ]),
			{ stop, handshake_failed, WebSocket }
	end.

handle_info({tcp, _Socket, Data}, WebSocket = #websocket{ connecting = true, data = Seen }) ->
	case contains_blank_line(<<Seen/binary,Data/binary>>) of
		yes -> 
			upgrade(self(),<<Seen/binary,Data/binary>>),
			{ noreply, WebSocket };
		_ -> 
			wait_headers(self(),<<Seen/binary,Data/binary>>),
			{ noreply, WebSocket }
	end;	

handle_info({tcp_closed, Socket }, WebSocket = #websocket{ data = Seen, connecting = true }) ->
	io:format("Closed socket ~p with data ~p ~n", [ Socket, Seen ]),
	{ stop, normal, WebSocket };

handle_info({ message, Data }, WebSocket = #websocket{ module = Module, function = Function, uuid = UUID }) ->	
	io:format("[websocket] Got message ~p~n", [ Data ]),
	io:format("invoking ~p:~p for ~p~n", [ Module, Function, UUID ]),
	spawn(Module,Function,[ self(), Data ] ),
	{ noreply, WebSocket#websocket{ data = [] }};

handle_info({ send, Data }, WebSocket = #websocket { protocol = Protocol, socket = Socket }) ->
	ok = gen_tcp:send(Socket,Protocol:frame(Data)),
	{ noreply, WebSocket };

handle_info({ ping, _Socket }, WebSocket = #websocket{ protocol = Protocol, socket = Socket }) -> 
	gen_tcp:send(Socket,Protocol:frame(<<"pong">>,10)),	%% send pong
	{ noreply, WebSocket };

handle_info({ pong, _Socket },WebSocket) ->
	{ noreply, WebSocket };

handle_info({ unknown, Any }, WebSocket) ->
	io:format("Unknown message ~p~n", [ Any ]),
	{ stop, unknown_message, WebSocket };

handle_info({ close, _Socket }, WebSocket = #websocket{ }) ->
	%% after a second stop the websocket
	timer:apply_after(1000, ?MODULE, stop, [ self() ]),
	{ noreply, WebSocket };

handle_info({tcp, Socket, NewData}, WebSocket = #websocket{ handler = Handler, socket = Socket }) ->
	Handler ! NewData,
	{ noreply, WebSocket };

handle_info({tcp_closed, _Socket }, WebSocket) ->
	timer:apply_after(1000, ?MODULE, stop, [ self() ]),
	{ noreply, WebSocket };

handle_info( Message, WebSocket = #websocket{ protocol = Protocol, socket = Socket, data = Data }) ->
	io:format("[websocket] message  ~p~n", [ Message ]),
	NewData = Protocol:handle(self(),Socket,Message,Data),
	io:format("[websocket] new data ~p~n", [ NewData ]),
	{ noreply, WebSocket#websocket{ data = NewData }}.

terminate( normal, #websocket{ uuid = UUID, socket = Socket, module = Module, function = Function }) ->
	spawn(Module,Function,[UUID, closed]),
	gen_tcp:close(Socket),
	io:format("Closed socket ~p~n", [ UUID ]),
	ok;

terminate( Reason, #websocket{ uuid = UUID, socket = Socket, module = Module, function = Function }) ->
	io:format("Terminating socket ~p with reason ~p~n", [ UUID, Reason ]),
	spawn(Module,Function,[UUID, closed]),
	gen_tcp:close(Socket),
	ok.
<commit_msg>it works<commit_after>-module(websocket).
-author({ "<NAME>", "<EMAIL>" }).
-copyright(<<"© 2012,2013 <NAME>"/utf8>>).
-behavior(gen_server).
-export([ start/3, send/2, socket/1, headers/1, path/1, stop/1, uuid/1, method/1, protocol/1,
	wait_headers/2, upgrade/2, bind/3  ]).
-export([ init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3 ]).

-record(websocket, { uuid, handler, protocol, headers, socket, data, module, function, connecting }).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Public Methods
%%

%% Starts a websocket by accepting a connection form Listen port
start(Listen,Module,Function) ->
	gen_server:start_link(?MODULE, { Listen, Module, Function }, []).

%% Waits for the HTTP headers to determine if we can upgrade
wait_headers(WebSocket,Data) ->
	gen_server:cast(WebSocket,{ wait_headers, Data }).

%% Upgrades to the WebSocket of the appropriate protocol
upgrade(WebSocket,Data) ->
	gen_server:cast(WebSocket,{ upgrade, Data }).

%% Sends data to the websocket
send(WebSocket,Data) ->
	gen_server:call(WebSocket,{ send, Data }).

%% Returns the UUID of the WebSocket
uuid(WebSocket) ->
	gen_server:call(WebSocket,uuid).

%% Returns the raw TCP socket of the WebSocket
socket(WebSocket) ->
	gen_server:call(WebSocket,socket).

%% Returns the HTTP headers of the WebSocket
headers(WebSocket) ->
	gen_server:call(WebSocket,headers).

%% Returns the path of the WebSocket
path(WebSocket) ->
	gen_server:call(WebSocket,path).

%% Returns the method of the WebSocket
method(WebSocket) ->
	gen_server:call(WebSocket,method).

%% Returns the protocol of the WebSocket
protocol(WebSocket) ->
	gen_server:call(WebSocket,protocol).

%% Stops the WebSocket
stop(WebSocket) ->
	gen_server:cast(WebSocket,stop).

%% Binds a Module and Function to be called when a message is encountered
bind(WebSocket,Module,Function) ->
	gen_server:cast(WebSocket, { bind, Module, Function }).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% gen_server methods
init({ Listen, Module, Function }) ->
	%% accept the socket in this process
	case gen_tcp:accept(Listen) of
		{ ok, Socket } -> 
			%% if we get a socket, wait for the headers
			wait_headers(self(), <<"">>),
			{ ok, #websocket{ 
				uuid = uuid:id(), 
				socket = Socket, 
				headers = [], 
				data = [],
				module = Module,
				function = Function,
				connecting = true
			}};
		{ error, closed } ->
			%% if we don't get a socket, just bail!
			{ stop, closed }
	end.

handle_call({ send, Data } , _From, WebSocket = #websocket{ protocol = Protocol, socket = Socket }) ->
	ok = gen_tcp:send(Socket,Protocol:frame(Data)),
	{ reply, ok, WebSocket };

handle_call( uuid, _From, WebSocket = #websocket{ uuid = UUID }) ->
	{ reply, UUID, WebSocket };

handle_call( socket, _From, WebSocket = #websocket{ socket = Socket }) ->
	{ reply, Socket, WebSocket };

handle_call( headers, _From, WebSocket = #websocket{ headers = Headers }) ->
	{ reply, Headers, WebSocket };

handle_call( path, _From, WebSocket = #websocket{ headers = Headers }) ->
	{ path, Path } = lists:keyfind(path,1,Headers),
	{ reply, Path, WebSocket };

handle_call( method, _From, WebSocket = #websocket{ headers = Headers }) ->
	{ method, Method } = lists:keyfind(method,1,Headers),
	{ reply, Method, WebSocket };

handle_call( protocol, _From, WebSocket = #websocket{ protocol = Protocol }) ->
	{ reply, Protocol, WebSocket }.

handle_cast({ bind, Module, Function }, WebSocket) ->
	{ noreply, WebSocket#websocket{ module = Module, function = Function }};

handle_cast( stop, WebSocket ) ->
	{ stop, normal, WebSocket };

handle_cast({ wait_headers, Seen }, WebSocket) ->
	{ noreply, WebSocket#websocket{ data = Seen, connecting = true }};

handle_cast({ upgrade, Data }, WebSocket = #websocket{ socket = Socket, module = Module, function = Function }) ->
	%% parse the headers
	Headers = parse_headers(Data),
	%% determine which protocol to use, draft00 is now deprecated
	Protocol =  case proplists:get_value(<<"Sec-WebSocket-Version">>,Headers) of
		<<"13">> -> 
			io:format("using rfc6455~n"),
			websocket_rfc6455;
		_ ->
			io:format("Protocol not supported")
	end,
	%% generate the server handshake
	Handshake = Protocol:handshake(Headers,Data),
	%% find if we have any websocket data already in hand
	%% send the handshake
	case gen_tcp:send(Socket,Handshake) of
		ok -> 
			spawn(Module,Function,[self(),connected]),
			{ noreply, WebSocket#websocket{ 
				protocol = Protocol,
				handler = Protocol:handler(self(),Socket),
				headers = Headers, 
				data = [],
				connecting = false
			}};
		{ error, Reason } ->	
			io:format("Handshake failed ~p~n", [ Reason ]),
			{ stop, handshake_failed, WebSocket }
	end.

handle_info({tcp, _Socket, Data}, WebSocket = #websocket{ connecting = true, data = Seen }) ->
	case contains_blank_line(<<Seen/binary,Data/binary>>) of
		yes -> 
			upgrade(self(),<<Seen/binary,Data/binary>>),
			{ noreply, WebSocket };
		_ -> 
			wait_headers(self(),<<Seen/binary,Data/binary>>),
			{ noreply, WebSocket }
	end;	

handle_info({tcp_closed, Socket }, WebSocket = #websocket{ data = Seen, connecting = true }) ->
	io:format("Closed socket ~p with data ~p ~n", [ Socket, Seen ]),
	{ stop, normal, WebSocket };

handle_info({ message, Data }, WebSocket = #websocket{ module = Module, function = Function, uuid = UUID }) ->	
io:format("[websocket] Got message ~p~n", [ Data ]),
	io:format("invoking ~p:~p for ~p~n", [ Module, Function, UUID ]),
	spawn(Module,Function,[ self(), Data ] ),
	{ noreply, WebSocket#websocket{ data = [] }};

handle_info({ send, Data }, WebSocket = #websocket { protocol = Protocol, socket = Socket }) ->
	ok = gen_tcp:send(Socket,Protocol:frame(Data)),
	{ noreply, WebSocket };

handle_info({ ping, _Socket }, WebSocket = #websocket{ protocol = Protocol, socket = Socket }) -> 
	gen_tcp:send(Socket,Protocol:frame(<<"pong">>,10)),	%% send pong
	{ noreply, WebSocket };

handle_info({ pong, _Socket },WebSocket) ->
	{ noreply, WebSocket };

handle_info({ unknown, Any }, WebSocket) ->
	io:format("Unknown message ~p~n", [ Any ]),
	{ stop, unknown_message, WebSocket };

handle_info({ close, _Socket }, WebSocket = #websocket{ }) ->
	%% after a second stop the websocket
	timer:apply_after(1000, ?MODULE, stop, [ self() ]),
	{ noreply, WebSocket };

handle_info({tcp, Socket, NewData}, WebSocket = #websocket{ handler = Handler, socket = Socket }) ->
	Handler ! NewData,
	{ noreply, WebSocket };

handle_info({tcp_closed, _Socket }, WebSocket = #websocket{ module = Module, function = Function}) ->
	spawn(Module,Function,[self(),closed]),
	timer:apply_after(1000, ?MODULE, stop, [ self() ]),
	{ noreply, WebSocket };

handle_info( Message, WebSocket = #websocket{ protocol = Protocol, socket = Socket, data = Data }) ->
	io:format("[websocket] message  ~p~n", [ Message ]),
	NewData = Protocol:handle(self(),Socket,Message,Data),
	io:format("[websocket] new data ~p~n", [ NewData ]),
	{ noreply, WebSocket#websocket{ data = NewData }}.

terminate( normal, #websocket{ uuid = UUID, socket = Socket, module = Module, function = Function }) ->
	spawn(Module,Function,[UUID, closed]),
	gen_tcp:close(Socket),
	io:format("Closed socket ~p~n", [ UUID ]),
	ok;

terminate( Reason, #websocket{ uuid = UUID, socket = Socket, module = Module, function = Function }) ->
	io:format("Terminating socket ~p with reason ~p~n", [ UUID, Reason ]),
	spawn(Module,Function,[UUID, closed]),
	gen_tcp:close(Socket),
	ok.

{'fraction_non_alphanumeric': 0.1763995977204157, 'fraction_numerical': 0.006838752933288636, 'mean_word_length': 4.851706551588858, 'pattern_counts': {'":': 0, '<': 41, '<?xml version=': 0, '>': 131, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4180896', 'n_tokens_mistral': 5164, 'n_tokens_neox': 4855, 'n_words': 1263}