mirror of
https://github.com/internetee/epp_proxy.git
synced 2025-08-21 22:50:48 +02:00
Merge pull request #6 from internetee/implement-new-logic-in-tcp-client-as-well
Implement large file logic in TCP socket
This commit is contained in:
commit
7f49098d96
1 changed files with 43 additions and 33 deletions
|
@ -22,6 +22,8 @@
|
||||||
|
|
||||||
-define(XMLErrorMessage, <<"Command syntax error.">>).
|
-define(XMLErrorMessage, <<"Command syntax error.">>).
|
||||||
|
|
||||||
|
-define(DefaultTimeout, 120000).
|
||||||
|
|
||||||
%% Initialize process
|
%% Initialize process
|
||||||
%% Assign an unique session id that will be passed on to http server as a cookie
|
%% Assign an unique session id that will be passed on to http server as a cookie
|
||||||
init(Socket) ->
|
init(Socket) ->
|
||||||
|
@ -95,8 +97,10 @@ handle_cast(process_command,
|
||||||
%% On logout, close the socket.
|
%% On logout, close the socket.
|
||||||
%% Else, go back to the beginning of the loop.
|
%% Else, go back to the beginning of the loop.
|
||||||
if Command =:= "logout" ->
|
if Command =:= "logout" ->
|
||||||
ok = gen_tcp:shutdown(Socket, read_write),
|
case gen_tcp:shutdown(Socket, read_write) of
|
||||||
{stop, normal, State};
|
ok -> {stop, normal, State};
|
||||||
|
{error, closed} -> {stop, normal, State}
|
||||||
|
end;
|
||||||
true ->
|
true ->
|
||||||
gen_server:cast(self(), process_command),
|
gen_server:cast(self(), process_command),
|
||||||
{noreply,
|
{noreply,
|
||||||
|
@ -111,30 +115,12 @@ code_change(_OldVersion, State, _Extra) -> {ok, State}.
|
||||||
write_line(Socket, Line) ->
|
write_line(Socket, Line) ->
|
||||||
ok = gen_tcp:send(Socket, Line).
|
ok = gen_tcp:send(Socket, Line).
|
||||||
|
|
||||||
read_length(Socket) ->
|
|
||||||
case gen_tcp:recv(Socket, 4) of
|
|
||||||
{ok, Data} ->
|
|
||||||
Length = binary:decode_unsigned(Data, big),
|
|
||||||
LengthToReceive =
|
|
||||||
epp_util:frame_length_to_receive(Length),
|
|
||||||
{ok, LengthToReceive};
|
|
||||||
{error, Reason} ->
|
|
||||||
io:format("Error: ~p~n", [Reason]), {error, Reason}
|
|
||||||
end.
|
|
||||||
|
|
||||||
read_frame(Socket, FrameLength) ->
|
|
||||||
case gen_tcp:recv(Socket, FrameLength) of
|
|
||||||
{ok, Data} -> {ok, Data};
|
|
||||||
{error, Reason} ->
|
|
||||||
io:format("Error: ~p~n", [Reason]), {error, Reason}
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% Wrap a message in EPP frame, and then send it to socket.
|
%% Wrap a message in EPP frame, and then send it to socket.
|
||||||
frame_to_socket(Message, Socket) ->
|
frame_to_socket(Message, Socket) ->
|
||||||
Length = epp_util:frame_length_to_send(Message),
|
Length = epp_util:frame_length_to_send(Message),
|
||||||
ByteSize = <<Length:32/big>>,
|
ByteSize = <<Length:32/big>>,
|
||||||
write_line(Socket, ByteSize),
|
CompleteMessage = <<ByteSize/binary, Message/binary>>,
|
||||||
write_line(Socket, Message).
|
write_line(Socket, CompleteMessage).
|
||||||
|
|
||||||
%% Extract state info from socket. Fail if you must.
|
%% Extract state info from socket. Fail if you must.
|
||||||
state_from_socket(Socket, State) ->
|
state_from_socket(Socket, State) ->
|
||||||
|
@ -147,18 +133,42 @@ state_from_socket(Socket, State) ->
|
||||||
[NewState]),
|
[NewState]),
|
||||||
NewState.
|
NewState.
|
||||||
|
|
||||||
%% First, listen for 4 bytes, then listen until the declared length.
|
|
||||||
%% Return the frame binary at the very end.
|
|
||||||
frame_from_socket(Socket, State) ->
|
frame_from_socket(Socket, State) ->
|
||||||
Length = case read_length(Socket) of
|
case gen_tcp:recv(Socket, 0, ?DefaultTimeout) of
|
||||||
{ok, Data} -> Data;
|
{ok, Data} ->
|
||||||
{error, _Details} -> {stop, normal, State}
|
EPPEnvelope = binary:part(Data, {0, 4}),
|
||||||
end,
|
ReportedLength = binary:decode_unsigned(EPPEnvelope,
|
||||||
Frame = case read_frame(Socket, Length) of
|
big),
|
||||||
{ok, FrameData} -> FrameData;
|
read_until_exhausted(Socket, ReportedLength, Data);
|
||||||
{error, _FrameDetails} -> {stop, normal, State}
|
{error, closed} -> log_and_exit(State);
|
||||||
end,
|
{error, timeout} -> log_on_timeout(State)
|
||||||
Frame.
|
end.
|
||||||
|
|
||||||
|
%% When an EPP message is long, it will be received in smaller chunks.
|
||||||
|
%% For example, first 4 bytes equal 800 000, but we'd receive only 200 000
|
||||||
|
%% in the first chunk. In such a case, we should listen for more.
|
||||||
|
%%
|
||||||
|
%% Note that there is no case for messages the exceed the reported length of a
|
||||||
|
%% frame. Those cases are invalid from the perspective of EPP protocol and
|
||||||
|
%% should not be supported.
|
||||||
|
read_until_exhausted(Socket, ExpectedLength, Frame) ->
|
||||||
|
if ExpectedLength =:= byte_size(Frame) ->
|
||||||
|
binary:part(Frame,
|
||||||
|
{byte_size(Frame), 4 - ExpectedLength});
|
||||||
|
ExpectedLength > byte_size(Frame) ->
|
||||||
|
{ok, NextFrame} = gen_tcp:recv(Socket, 0,
|
||||||
|
?DefaultTimeout),
|
||||||
|
NewFrame = <<Frame/binary, NextFrame/binary>>,
|
||||||
|
read_until_exhausted(Socket, ExpectedLength, NewFrame)
|
||||||
|
end.
|
||||||
|
|
||||||
|
log_and_exit(State) ->
|
||||||
|
lager:info("Client closed connection: [~p]~n", [State]),
|
||||||
|
exit(normal).
|
||||||
|
|
||||||
|
log_on_timeout(State) ->
|
||||||
|
lager:info("Client timed out: [~p]~n", [State]),
|
||||||
|
exit(normal).
|
||||||
|
|
||||||
%% Get status, XML record, command and clTRID if defined.
|
%% Get status, XML record, command and clTRID if defined.
|
||||||
%% Otherwise return an invalid frame with predefined error message and code.
|
%% Otherwise return an invalid frame with predefined error message and code.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue