memory leak problems with tetris poop
This commit is contained in:
parent
4bd279798c
commit
882a416831
@ -23,3 +23,10 @@
|
|||||||
-type body() :: {partial, binary()} | {multipart, [body_part()]} | zj:value() | binary().
|
-type body() :: {partial, binary()} | {multipart, [body_part()]} | zj:value() | binary().
|
||||||
-type body_part() :: {Field :: binary(), Data :: binary()}
|
-type body_part() :: {Field :: binary(), Data :: binary()}
|
||||||
| {Field :: binary(), Name :: binary(), Data :: binary()}.
|
| {Field :: binary(), Name :: binary(), Data :: binary()}.
|
||||||
|
|
||||||
|
|
||||||
|
-type request() :: #request{}.
|
||||||
|
-type response() :: #response{}.
|
||||||
|
-type tcp_error() :: closed
|
||||||
|
| {timeout, RestData :: binary() | erlang:iovec()}
|
||||||
|
| inet:posix().
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Chat with Websockets</title>
|
<title>Chat with Websockets</title>
|
||||||
<link rel="stylesheet" href="./default.css">
|
<link rel="stylesheet" href="/css/default.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
|||||||
@ -3,15 +3,28 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>WF Compiler Demo</title>
|
<title>WF Compiler Demo</title>
|
||||||
<link rel="stylesheet" href="./default.css">
|
<link rel="stylesheet" href="/css/default.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<h1 class="content-title">WFC Demo</h1>
|
<h1 class="content-title">WFC Demo</h1>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/chat.html">Websocket Chatroom</a></li>
|
<li>
|
||||||
<li><a href="/ws-test-echo.html">Websocket Echo Test</a></li>
|
Websocket demos that work
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><a href="/ws-test-echo.html">Echo</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Don't work
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><a href="/chat.html">Chatroom</a></li>
|
||||||
|
<li><a href="/tetris.html">Tetris</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="content-body">
|
<div class="content-body">
|
||||||
|
|||||||
4
priv/static/js/dist/libfewd.d.ts
vendored
4
priv/static/js/dist/libfewd.d.ts
vendored
@ -3,4 +3,6 @@
|
|||||||
*
|
*
|
||||||
* @module
|
* @module
|
||||||
*/
|
*/
|
||||||
export {};
|
export { auto_resize, auto_scroll_to_bottom };
|
||||||
|
declare function auto_resize(checkbox_element: HTMLInputElement, target_element: HTMLTextAreaElement, max_height: number): void;
|
||||||
|
declare function auto_scroll_to_bottom(checkbox_element: HTMLInputElement, target_element: HTMLTextAreaElement): void;
|
||||||
|
|||||||
19
priv/static/js/dist/libfewd.js
vendored
19
priv/static/js/dist/libfewd.js
vendored
@ -3,5 +3,22 @@
|
|||||||
*
|
*
|
||||||
* @module
|
* @module
|
||||||
*/
|
*/
|
||||||
export {};
|
export { auto_resize, auto_scroll_to_bottom };
|
||||||
|
function auto_resize(checkbox_element, target_element, max_height) {
|
||||||
|
// if the user has manually resized their output, we do nothing
|
||||||
|
if (checkbox_element.checked) {
|
||||||
|
let target_height = target_element.scrollHeight;
|
||||||
|
// resize it automagically up to 500px
|
||||||
|
if (target_height < max_height)
|
||||||
|
target_element.style.height = String(target_height) + 'px';
|
||||||
|
else
|
||||||
|
target_element.style.height = String(max_height) + 'px';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function auto_scroll_to_bottom(checkbox_element, target_element) {
|
||||||
|
if (checkbox_element.checked) {
|
||||||
|
// scroll to bottom
|
||||||
|
target_element.scrollTop = target_element.scrollHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
//# sourceMappingURL=libfewd.js.map
|
//# sourceMappingURL=libfewd.js.map
|
||||||
2
priv/static/js/dist/libfewd.js.map
vendored
2
priv/static/js/dist/libfewd.js.map
vendored
@ -1 +1 @@
|
|||||||
{"version":3,"file":"libfewd.js","sourceRoot":"","sources":["../ts/libfewd.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
{"version":3,"file":"libfewd.js","sourceRoot":"","sources":["../ts/libfewd.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACH,WAAW,EACX,qBAAqB,EACxB,CAAC;AAGF,SACA,WAAW,CACN,gBAAmC,EACnC,cAAsC,EACtC,UAAyB;IAG1B,+DAA+D;IAC/D,IAAI,gBAAgB,CAAC,OAAO,EAAE,CAAC;QAC3B,IAAI,aAAa,GAAW,cAAc,CAAC,YAAY,CAAC;QACxD,sCAAsC;QACtC,IAAI,aAAa,GAAG,UAAU;YAC1B,cAAc,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC;;YAE3D,cAAc,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC;IAChE,CAAC;AACL,CAAC;AAGD,SACA,qBAAqB,CAChB,gBAAmC,EACnC,cAAsC;IAGvC,IAAI,gBAAgB,CAAC,OAAO,EAAE,CAAC;QAC3B,mBAAmB;QACnB,cAAc,CAAC,SAAS,GAAG,cAAc,CAAC,YAAY,CAAC;IAC3D,CAAC;AACL,CAAC"}
|
||||||
6
priv/static/js/dist/tetris.d.ts
vendored
Normal file
6
priv/static/js/dist/tetris.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* Tetris
|
||||||
|
*
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
export {};
|
||||||
30
priv/static/js/dist/tetris.js
vendored
Normal file
30
priv/static/js/dist/tetris.js
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* Tetris
|
||||||
|
*
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
main();
|
||||||
|
async function main() {
|
||||||
|
let ws = new WebSocket("/ws/tetris");
|
||||||
|
let elt_tetris_state = document.getElementById('tetris-state');
|
||||||
|
ws.onmessage =
|
||||||
|
(e) => {
|
||||||
|
handle_evt(e, elt_tetris_state);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------
|
||||||
|
// Tetris
|
||||||
|
//-----------------------------------------------------
|
||||||
|
/**
|
||||||
|
* take the entire tetris state, render the html elements
|
||||||
|
*
|
||||||
|
* then fish out the element in the document, and replace it
|
||||||
|
*
|
||||||
|
* blitting basically
|
||||||
|
*/
|
||||||
|
async function handle_evt(e, oelt) {
|
||||||
|
let state_str = e.data;
|
||||||
|
oelt.value = state_str;
|
||||||
|
}
|
||||||
|
export {};
|
||||||
|
//# sourceMappingURL=tetris.js.map
|
||||||
1
priv/static/js/dist/tetris.js.map
vendored
Normal file
1
priv/static/js/dist/tetris.js.map
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"tetris.js","sourceRoot":"","sources":["../ts/tetris.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,IAAI,EAAE,CAAC;AAEP,KAAK,UACL,IAAI;IAIA,IAAI,EAAE,GAAuC,IAAI,SAAS,CAAC,YAAY,CAAC,CAAqC;IAC7G,IAAI,gBAAgB,GAAyB,QAAQ,CAAC,cAAc,CAAC,cAAc,CAAwB,CAAE;IAE7G,EAAE,CAAC,SAAS;QACR,CAAC,CAAe,EAAE,EAAE;YAChB,UAAU,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC;QACpC,CAAC,CAAA;AACT,CAAC;AAED,uDAAuD;AACvD,SAAS;AACT,uDAAuD;AAGvD;;;;;;GAMG;AACH,KAAK,UACL,UAAU,CACL,CAAmB,EACnB,IAA0B;IAG3B,IAAI,SAAS,GAAY,CAAC,CAAC,IAAc,CAAC;IAC1C,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;AAC3B,CAAC"}
|
||||||
@ -5,6 +5,39 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
auto_resize,
|
||||||
|
auto_scroll_to_bottom
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function
|
||||||
|
auto_resize
|
||||||
|
(checkbox_element : HTMLInputElement,
|
||||||
|
target_element : HTMLTextAreaElement,
|
||||||
|
max_height : number)
|
||||||
|
: void
|
||||||
|
{
|
||||||
|
// if the user has manually resized their output, we do nothing
|
||||||
|
if (checkbox_element.checked) {
|
||||||
|
let target_height: number = target_element.scrollHeight;
|
||||||
|
// resize it automagically up to 500px
|
||||||
|
if (target_height < max_height)
|
||||||
|
target_element.style.height = String(target_height) + 'px';
|
||||||
|
else
|
||||||
|
target_element.style.height = String(max_height) + 'px';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function
|
||||||
|
auto_scroll_to_bottom
|
||||||
|
(checkbox_element : HTMLInputElement,
|
||||||
|
target_element : HTMLTextAreaElement)
|
||||||
|
: void
|
||||||
|
{
|
||||||
|
if (checkbox_element.checked) {
|
||||||
|
// scroll to bottom
|
||||||
|
target_element.scrollTop = target_element.scrollHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
49
priv/static/js/ts/tetris.ts
Normal file
49
priv/static/js/ts/tetris.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/**
|
||||||
|
* Tetris
|
||||||
|
*
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
|
||||||
|
export {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
import * as libfewd from './libfewd.js';
|
||||||
|
|
||||||
|
main();
|
||||||
|
|
||||||
|
async function
|
||||||
|
main
|
||||||
|
()
|
||||||
|
: Promise<void>
|
||||||
|
{
|
||||||
|
let ws : WebSocket = new WebSocket("/ws/tetris") ;
|
||||||
|
let elt_tetris_state : HTMLTextAreaElement = document.getElementById('tetris-state') as HTMLTextAreaElement ;
|
||||||
|
|
||||||
|
ws.onmessage =
|
||||||
|
(e: MessageEvent) => {
|
||||||
|
handle_evt(e, elt_tetris_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------
|
||||||
|
// Tetris
|
||||||
|
//-----------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* take the entire tetris state, render the html elements
|
||||||
|
*
|
||||||
|
* then fish out the element in the document, and replace it
|
||||||
|
*
|
||||||
|
* blitting basically
|
||||||
|
*/
|
||||||
|
async function
|
||||||
|
handle_evt
|
||||||
|
(e : MessageEvent,
|
||||||
|
oelt : HTMLTextAreaElement)
|
||||||
|
: Promise<void>
|
||||||
|
{
|
||||||
|
let state_str : string = e.data as string;
|
||||||
|
oelt.value = state_str;
|
||||||
|
}
|
||||||
18
priv/static/tetris.html
Normal file
18
priv/static/tetris.html
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Tetris with Websockets</title>
|
||||||
|
<link rel="stylesheet" href="/css/default.css">
|
||||||
|
<link rel="stylesheet" href="/css/tetris.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="content">
|
||||||
|
<h1 class="content-title">Tetris</h1>
|
||||||
|
|
||||||
|
<textarea id="tetris-state"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="module" src="./js/dist/tetris.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Websockets echo test</title>
|
<title>Websockets echo test</title>
|
||||||
<link rel="stylesheet" href="./default.css">
|
<link rel="stylesheet" href="/css/default.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
|||||||
@ -32,7 +32,7 @@
|
|||||||
|
|
||||||
|
|
||||||
-record(s, {socket = none :: none | gen_tcp:socket(),
|
-record(s, {socket = none :: none | gen_tcp:socket(),
|
||||||
next = none :: none | binary()}).
|
next = <<>> :: binary()}).
|
||||||
|
|
||||||
|
|
||||||
%% An alias for the state record above. Aliasing state can smooth out annoyances
|
%% An alias for the state record above. Aliasing state can smooth out annoyances
|
||||||
@ -128,27 +128,20 @@ listen(Parent, Debug, ListenSocket) ->
|
|||||||
%% The service loop itself. This is the service state. The process blocks on receive
|
%% The service loop itself. This is the service state. The process blocks on receive
|
||||||
%% of Erlang messages, TCP segments being received themselves as Erlang messages.
|
%% of Erlang messages, TCP segments being received themselves as Erlang messages.
|
||||||
|
|
||||||
loop(Parent, Debug, State = #s{socket = Socket, next = Next}) ->
|
loop(Parent, Debug, State = #s{socket = Socket, next = Next0}) ->
|
||||||
ok = inet:setopts(Socket, [{active, once}]),
|
ok = inet:setopts(Socket, [{active, once}]),
|
||||||
receive
|
receive
|
||||||
{tcp, Socket, Message} ->
|
{tcp, Socket, Message} ->
|
||||||
tell("~p Next = ~p", [?LINE, Next]),
|
Received = <<Next0/binary, Message/binary>>,
|
||||||
Received =
|
|
||||||
case Next of
|
|
||||||
none -> Message;
|
|
||||||
_ -> <<Next/binary, Message/binary>>
|
|
||||||
end,
|
|
||||||
tell("qhl_parse(Socket, ~tp)", [Received]),
|
|
||||||
case qhl:parse(Socket, Received) of
|
case qhl:parse(Socket, Received) of
|
||||||
{ok, Req, NewNext} ->
|
{ok, Req, Next1} ->
|
||||||
tell("qhl return: {ok, ~p, ~p}", [Req, NewNext]),
|
Next2 = handle_request(Socket, Req, Next1),
|
||||||
handle_request(Socket, Req),
|
NewState = State#s{next = Next2},
|
||||||
NewState = State#s{next = NewNext},
|
|
||||||
loop(Parent, Debug, NewState);
|
loop(Parent, Debug, NewState);
|
||||||
Error ->
|
Error ->
|
||||||
%% should trigger bad request
|
%% should trigger bad request
|
||||||
io:format("~p QHL parse error: ~tp", [?LINE, Error]),
|
tell(error, "~p QHL parse error: ~tp", [?LINE, Error]),
|
||||||
io:format("~p bad request:~n~ts", [?LINE, Received]),
|
tell(error, "~p bad request:~n~ts", [?LINE, Received]),
|
||||||
http_err(Socket, 400),
|
http_err(Socket, 400),
|
||||||
gen_tcp:shutdown(Socket, read_write),
|
gen_tcp:shutdown(Socket, read_write),
|
||||||
exit(normal)
|
exit(normal)
|
||||||
@ -217,29 +210,57 @@ system_replace_state(StateFun, State) ->
|
|||||||
|
|
||||||
%%% http request handling
|
%%% http request handling
|
||||||
|
|
||||||
handle_request(Sock, R = #request{method = M, path = P}) when M =/= undefined, P =/= undefined ->
|
-spec handle_request(Sock, Request, Received) -> NewReceived
|
||||||
tell("~p ~ts", [M, P]),
|
when Sock :: gen_tcp:socket(),
|
||||||
route(Sock, M, P, R).
|
Request :: request(),
|
||||||
|
Received :: binary(),
|
||||||
|
NewReceived :: binary().
|
||||||
|
|
||||||
|
handle_request(Sock, R = #request{method = M, path = P}, Received) when M =/= undefined, P =/= undefined ->
|
||||||
|
tell("~tp ~tp ~ts", [self(), M, P]),
|
||||||
|
route(Sock, M, P, R, Received).
|
||||||
|
|
||||||
|
|
||||||
route(Sock, get, Route, Request) ->
|
|
||||||
|
-spec route(Sock, Method, Route, Request, Received) -> NewReceived
|
||||||
|
when Sock :: gen_tcp:socket(),
|
||||||
|
Method :: get | post,
|
||||||
|
Route :: binary(),
|
||||||
|
Request :: request(),
|
||||||
|
Received :: binary(),
|
||||||
|
NewReceived :: binary().
|
||||||
|
|
||||||
|
route(Sock, get, Route, Request, Received) ->
|
||||||
case Route of
|
case Route of
|
||||||
<<"/ws/echo">> -> ws_echo(Sock, Request);
|
<<"/ws/tetris">> -> ws_tetris(Sock, Request, Received);
|
||||||
<<"/">> -> route_static(Sock, <<"/index.html">>);
|
<<"/ws/echo">> -> ws_echo(Sock, Request) , Received;
|
||||||
_ -> route_static(Sock, Route)
|
<<"/">> -> route_static(Sock, <<"/index.html">>) , Received;
|
||||||
|
_ -> route_static(Sock, Route) , Received
|
||||||
end;
|
end;
|
||||||
route(Sock, post, Route, Request) ->
|
route(Sock, post, Route, Request, Received) ->
|
||||||
case Route of
|
case Route of
|
||||||
<<"/wfcin">> -> wfcin(Sock, Request);
|
<<"/wfcin">> -> wfcin(Sock, Request) , Received;
|
||||||
_ -> http_err(Sock, 404)
|
_ -> http_err(Sock, 404) , Received
|
||||||
end;
|
end;
|
||||||
route(Sock, _, _, _) ->
|
route(Sock, _, _, _, Received) ->
|
||||||
http_err(Sock, 404).
|
http_err(Sock, 404),
|
||||||
|
Received.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-spec route_static(Socket, Route) -> ok
|
||||||
|
when Socket :: gen_tcp:socket(),
|
||||||
|
Route :: binary().
|
||||||
|
|
||||||
route_static(Sock, Route) ->
|
route_static(Sock, Route) ->
|
||||||
respond_static(Sock, fd_sfc:query(Route)).
|
respond_static(Sock, fd_sfc:query(Route)).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-spec respond_static(Sock, MaybeEty) -> ok
|
||||||
|
when Sock :: gen_tcp:socket(),
|
||||||
|
MaybeEty :: fd_sfc:maybe_entry().
|
||||||
|
|
||||||
respond_static(Sock, {found, Entry}) ->
|
respond_static(Sock, {found, Entry}) ->
|
||||||
% -record(e, {fs_path :: file:filename(),
|
% -record(e, {fs_path :: file:filename(),
|
||||||
% last_modified :: file:date_time(),
|
% last_modified :: file:date_time(),
|
||||||
@ -259,6 +280,91 @@ respond_static(Sock, not_found) ->
|
|||||||
http_err(Sock, 404).
|
http_err(Sock, 404).
|
||||||
|
|
||||||
|
|
||||||
|
%% ------------------------------
|
||||||
|
%% tetris
|
||||||
|
%% ------------------------------
|
||||||
|
|
||||||
|
-spec ws_tetris(Sock, Request, Received) -> NewReceived
|
||||||
|
when Sock :: gen_tcp:socket(),
|
||||||
|
Request :: request(),
|
||||||
|
Received :: binary(),
|
||||||
|
NewReceived :: binary().
|
||||||
|
|
||||||
|
ws_tetris(Sock, Request, Received) ->
|
||||||
|
try
|
||||||
|
ws_tetris2(Sock, Request, Received)
|
||||||
|
catch
|
||||||
|
X:Y:Z ->
|
||||||
|
tell(error, "CRASH ws_tetris: ~tp:~tp:~tp", [X, Y, Z]),
|
||||||
|
http_err(Sock, 500)
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-spec ws_tetris2(Sock, Request, Received) -> NewReceived
|
||||||
|
when Sock :: gen_tcp:socket(),
|
||||||
|
Request :: request(),
|
||||||
|
Received :: binary(),
|
||||||
|
NewReceived :: binary().
|
||||||
|
|
||||||
|
ws_tetris2(Sock, Request, Received) ->
|
||||||
|
%tell("~p: ws_tetris request: ~tp", [?LINE, Request]),
|
||||||
|
case fd_ws:handshake(Request) of
|
||||||
|
{ok, Response} ->
|
||||||
|
respond(Sock, Response),
|
||||||
|
tetris_init(Sock),
|
||||||
|
ws_tetris_loop(Sock, [], Received);
|
||||||
|
Error ->
|
||||||
|
tell("ws_tetris: error: ~tp", [Error]),
|
||||||
|
http_err(Sock, 400)
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
-spec ws_tetris_loop(Sock, Frames, Received) -> NewReceived
|
||||||
|
when Sock :: gen_tcp:socket(),
|
||||||
|
Frames :: [fd_ws:frame()],
|
||||||
|
Received :: binary(),
|
||||||
|
NewReceived :: binary().
|
||||||
|
|
||||||
|
ws_tetris_loop(Sock, Frames, Received) ->
|
||||||
|
tell("~p:ws_tetris_loop(Sock, ~p, ~p)", [?MODULE, Frames, Received]),
|
||||||
|
%% create tetris state
|
||||||
|
case inet:setopts(Sock, [{active, once}]) of
|
||||||
|
ok ->
|
||||||
|
receive
|
||||||
|
{tcp, Sock, Bin} ->
|
||||||
|
Rcv1 = <<Received/binary, Bin/binary>>,
|
||||||
|
tell("~p:~p rcv1: ~tp", [?MODULE, ?LINE, Rcv1]),
|
||||||
|
ws_tetris_loop(Sock, Frames, <<>>);
|
||||||
|
{tetris, State} ->
|
||||||
|
tell("tetris: ~p", [State]),
|
||||||
|
fd_ws:send(Sock, {text, State}),
|
||||||
|
ws_tetris_loop(Sock, Frames, Received);
|
||||||
|
{tcp_closed, Sock} -> {error, tcp_closed};
|
||||||
|
{tcp_error, Sock, Reason} -> {error, {tcp_error, Reason}}
|
||||||
|
after 30_000 ->
|
||||||
|
{error, timeout}
|
||||||
|
end;
|
||||||
|
{error, Reason} ->
|
||||||
|
{error, {inet, Reason}}
|
||||||
|
end.
|
||||||
|
|
||||||
|
tetris_init(_Sock) ->
|
||||||
|
tell("~p tetris_init", [self()]),
|
||||||
|
Parent = self(),
|
||||||
|
_Child = spawn_link(fun() -> poop(Parent) end),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
poop(Parent) ->
|
||||||
|
tell("~p poop(~p)", [self(), Parent]),
|
||||||
|
Parent ! {tetris, "poop\n"},
|
||||||
|
timer:sleep(3_000),
|
||||||
|
poop(Parent).
|
||||||
|
|
||||||
|
%% ------------------------------
|
||||||
|
%% echo
|
||||||
|
%% ------------------------------
|
||||||
|
|
||||||
ws_echo(Sock, Request) ->
|
ws_echo(Sock, Request) ->
|
||||||
try
|
try
|
||||||
ws_echo2(Sock, Request)
|
ws_echo2(Sock, Request)
|
||||||
@ -269,12 +375,9 @@ ws_echo(Sock, Request) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
ws_echo2(Sock, Request) ->
|
ws_echo2(Sock, Request) ->
|
||||||
tell("~p: ws_echo request: ~tp", [?LINE, Request]),
|
|
||||||
case fd_ws:handshake(Request) of
|
case fd_ws:handshake(Request) of
|
||||||
{ok, Response} ->
|
{ok, Response} ->
|
||||||
tell("~p: ws_echo response: ~tp", [?LINE, Response]),
|
|
||||||
respond(Sock, Response),
|
respond(Sock, Response),
|
||||||
tell("~p: ws_echo: entering loop", [?LINE]),
|
|
||||||
ws_echo_loop(Sock);
|
ws_echo_loop(Sock);
|
||||||
Error ->
|
Error ->
|
||||||
tell("ws_echo: error: ~tp", [Error]),
|
tell("ws_echo: error: ~tp", [Error]),
|
||||||
@ -285,15 +388,15 @@ ws_echo_loop(Sock) ->
|
|||||||
ws_echo_loop(Sock, [], <<>>).
|
ws_echo_loop(Sock, [], <<>>).
|
||||||
|
|
||||||
ws_echo_loop(Sock, Frames, Received) ->
|
ws_echo_loop(Sock, Frames, Received) ->
|
||||||
tell("~p: ws_echo_loop: entering loop", [?LINE]),
|
tell("~p ws_echo_loop(Sock, ~tp, ~tp)", [self(), Frames, Received]),
|
||||||
case fd_ws:recv(Sock, Received, 5*fd_ws:min(), Frames) of
|
case fd_ws:recv(Sock, Received, 5*fd_ws:min(), Frames) of
|
||||||
Result = {ok, Message, NewFrames, NewReceived} ->
|
Result = {ok, Message, NewFrames, NewReceived} ->
|
||||||
tell("~p: ws_echo_loop ok: ~tp", [?LINE, Result]),
|
tell("~p echo message: ~tp", [self(), Message]),
|
||||||
% send the same message back
|
% send the same message back
|
||||||
ok = fd_ws:send(Sock, Message),
|
ok = fd_ws:send(Sock, Message),
|
||||||
ws_echo_loop(Sock, NewFrames, NewReceived);
|
ws_echo_loop(Sock, NewFrames, NewReceived);
|
||||||
Error ->
|
Error ->
|
||||||
tell("ws_echo_loop: error: ~tp", [Error]),
|
tell(error, "ws_echo_loop: error: ~tp", [Error]),
|
||||||
fd_ws:send(Sock, {close, <<>>}),
|
fd_ws:send(Sock, {close, <<>>}),
|
||||||
error(Error)
|
error(Error)
|
||||||
end.
|
end.
|
||||||
@ -333,6 +436,12 @@ wfcin(Sock, Request) ->
|
|||||||
http_err(Sock, 400).
|
http_err(Sock, 400).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-spec ctx(Cookies) -> {Cookie, Context}
|
||||||
|
when Cookies :: #{binary() := Cookie},
|
||||||
|
Cookie :: binary(),
|
||||||
|
Context :: wfc_eval_context:context().
|
||||||
|
|
||||||
ctx(#{<<"wfc">> := Cookie}) ->
|
ctx(#{<<"wfc">> := Cookie}) ->
|
||||||
case fd_cache:query(Cookie) of
|
case fd_cache:query(Cookie) of
|
||||||
{ok, Context} -> {Cookie, Context};
|
{ok, Context} -> {Cookie, Context};
|
||||||
@ -341,17 +450,40 @@ ctx(#{<<"wfc">> := Cookie}) ->
|
|||||||
ctx(_) ->
|
ctx(_) ->
|
||||||
{new_cookie(), wfc_eval_context:default()}.
|
{new_cookie(), wfc_eval_context:default()}.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-spec new_cookie() -> Cookie
|
||||||
|
when Cookie :: binary().
|
||||||
|
|
||||||
new_cookie() ->
|
new_cookie() ->
|
||||||
binary:encode_hex(crypto:strong_rand_bytes(8)).
|
binary:encode_hex(crypto:strong_rand_bytes(8)).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-spec jsgud(JSON) -> Encodable
|
||||||
|
when JSON :: zj:value(),
|
||||||
|
Encodable :: JSON.
|
||||||
|
|
||||||
jsgud(X) ->
|
jsgud(X) ->
|
||||||
#{"ok" => true,
|
#{"ok" => true,
|
||||||
"result" => X}.
|
"result" => X}.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-spec jsbad(JSON) -> JSBad
|
||||||
|
when JSON :: zj:value(),
|
||||||
|
JSBad :: zj:value().
|
||||||
|
|
||||||
jsbad(X) ->
|
jsbad(X) ->
|
||||||
#{"ok" => false,
|
#{"ok" => false,
|
||||||
"error" => X}.
|
"error" => X}.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-spec http_err(Socket, ErrorCode) -> ok
|
||||||
|
when Socket :: gen_tcp:socket(),
|
||||||
|
ErrorCode :: integer().
|
||||||
|
|
||||||
http_err(Sock, N) ->
|
http_err(Sock, N) ->
|
||||||
Slogan = qhl:slogan(N),
|
Slogan = qhl:slogan(N),
|
||||||
Body = ["<!doctype html>"
|
Body = ["<!doctype html>"
|
||||||
@ -372,10 +504,21 @@ http_err(Sock, N) ->
|
|||||||
respond(Sock, Resp).
|
respond(Sock, Resp).
|
||||||
|
|
||||||
|
|
||||||
respond(Sock, Response) ->
|
|
||||||
|
-spec respond(Sock, Response) -> ok
|
||||||
|
when Sock :: gen_tcp:socket(),
|
||||||
|
Response :: response().
|
||||||
|
|
||||||
|
respond(Sock, Response = #response{code = Code}) ->
|
||||||
|
tell("~tp ~tp ~ts", [self(), Code, qhl:slogan(Code)]),
|
||||||
gen_tcp:send(Sock, fmtresp(Response)).
|
gen_tcp:send(Sock, fmtresp(Response)).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-spec fmtresp(Response) -> Formatted
|
||||||
|
when Response :: response(),
|
||||||
|
Formatted :: iolist().
|
||||||
|
|
||||||
fmtresp(#response{type = page, %% no idea what {data, String} is
|
fmtresp(#response{type = page, %% no idea what {data, String} is
|
||||||
version = http11,
|
version = http11,
|
||||||
code = Code,
|
code = Code,
|
||||||
@ -389,6 +532,12 @@ fmtresp(#response{type = page, %% no idea what {data, String} is
|
|||||||
Body].
|
Body].
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-spec add_headers(Existing, Body) -> Hdrs
|
||||||
|
when Existing :: [{iolist(), iolist()}],
|
||||||
|
Body :: iolist(),
|
||||||
|
Hdrs :: [{iolist(), iolist()}].
|
||||||
|
|
||||||
%% body needed just for size
|
%% body needed just for size
|
||||||
add_headers(Hs, Body) ->
|
add_headers(Hs, Body) ->
|
||||||
Defaults = default_headers(Body),
|
Defaults = default_headers(Body),
|
||||||
@ -396,6 +545,11 @@ add_headers(Hs, Body) ->
|
|||||||
proplists:from_map(maps:merge(Defaults, Hs2)).
|
proplists:from_map(maps:merge(Defaults, Hs2)).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-spec default_headers(Body) -> HdrsMap
|
||||||
|
when Body :: iolist(),
|
||||||
|
HdrsMap :: #{iolist() := iolist()}.
|
||||||
|
|
||||||
default_headers(Body) ->
|
default_headers(Body) ->
|
||||||
BodySize = byte_size(iolist_to_binary(Body)),
|
BodySize = byte_size(iolist_to_binary(Body)),
|
||||||
#{"Server" => "fewd 0.1.0",
|
#{"Server" => "fewd 0.1.0",
|
||||||
|
|||||||
@ -3,6 +3,11 @@
|
|||||||
|
|
||||||
-behavior(gen_server).
|
-behavior(gen_server).
|
||||||
|
|
||||||
|
-export_type([
|
||||||
|
entry/0,
|
||||||
|
maybe_entry/0
|
||||||
|
]).
|
||||||
|
|
||||||
-export([
|
-export([
|
||||||
%% caller context
|
%% caller context
|
||||||
base_path/0,
|
base_path/0,
|
||||||
@ -16,6 +21,9 @@
|
|||||||
|
|
||||||
-include("$zx_include/zx_logger.hrl").
|
-include("$zx_include/zx_logger.hrl").
|
||||||
|
|
||||||
|
-type entry() :: fd_sfc_entry:entry().
|
||||||
|
-type maybe_entry() :: {found, fd_sfc_entry:entry()} | not_found.
|
||||||
|
|
||||||
|
|
||||||
-record(s, {base_path = base_path() :: file:filename(),
|
-record(s, {base_path = base_path() :: file:filename(),
|
||||||
cache = fd_sfc_cache:new(base_path()) :: fd_sfc_cache:cache(),
|
cache = fd_sfc_cache:new(base_path()) :: fd_sfc_cache:cache(),
|
||||||
@ -27,15 +35,22 @@
|
|||||||
%% caller context
|
%% caller context
|
||||||
%%-----------------------------------------------------------------------------
|
%%-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-spec base_path() -> file:filename().
|
||||||
base_path() ->
|
base_path() ->
|
||||||
filename:join([zx:get_home(), "priv", "static"]).
|
filename:join([zx:get_home(), "priv", "static"]).
|
||||||
|
|
||||||
|
-spec renew() -> ok.
|
||||||
renew() ->
|
renew() ->
|
||||||
gen_server:cast(?MODULE, renew).
|
gen_server:cast(?MODULE, renew).
|
||||||
|
|
||||||
|
|
||||||
|
-spec query(HttpPath) -> MaybeEntry
|
||||||
|
when HttpPath :: binary(),
|
||||||
|
MaybeEntry :: maybe_entry().
|
||||||
query(Path) ->
|
query(Path) ->
|
||||||
gen_server:call(?MODULE, {query, Path}).
|
gen_server:call(?MODULE, {query, Path}).
|
||||||
|
|
||||||
|
|
||||||
start_link() ->
|
start_link() ->
|
||||||
gen_server:start_link({local, ?MODULE}, ?MODULE, none, []).
|
gen_server:start_link({local, ?MODULE}, ?MODULE, none, []).
|
||||||
|
|
||||||
|
|||||||
@ -57,18 +57,11 @@ new2(BasePath) ->
|
|||||||
true -> filename:absname(BasePath);
|
true -> filename:absname(BasePath);
|
||||||
false -> filename:absname(filename:dirname(BasePath))
|
false -> filename:absname(filename:dirname(BasePath))
|
||||||
end,
|
end,
|
||||||
%% hacky, fuck you
|
|
||||||
RemovePrefix =
|
|
||||||
fun (Prefix, Size, From) ->
|
|
||||||
<<Prefix:Size/bytes, Rest/bytes>> = From,
|
|
||||||
Rest
|
|
||||||
end,
|
|
||||||
BBaseDir = unicode:characters_to_binary(BaseDir),
|
BBaseDir = unicode:characters_to_binary(BaseDir),
|
||||||
BBS = byte_size(BBaseDir),
|
|
||||||
HandlePath =
|
HandlePath =
|
||||||
fun(AbsPath, AccCache) ->
|
fun(AbsPath, AccCache) ->
|
||||||
BAbsPath = unicode:characters_to_binary(AbsPath),
|
BAbsPath = unicode:characters_to_binary(AbsPath),
|
||||||
HttpPath = RemovePrefix(BBaseDir, BBS, BAbsPath),
|
HttpPath = remove_prefix(BBaseDir, BAbsPath),
|
||||||
NewCache =
|
NewCache =
|
||||||
case fd_sfc_entry:new(AbsPath) of
|
case fd_sfc_entry:new(AbsPath) of
|
||||||
{found, Entry} -> maps:put(HttpPath, Entry, AccCache);
|
{found, Entry} -> maps:put(HttpPath, Entry, AccCache);
|
||||||
@ -81,3 +74,8 @@ new2(BasePath) ->
|
|||||||
_recursive = true,
|
_recursive = true,
|
||||||
_fun = HandlePath,
|
_fun = HandlePath,
|
||||||
_init_acc = #{}).
|
_init_acc = #{}).
|
||||||
|
|
||||||
|
remove_prefix(Prefix, From) ->
|
||||||
|
Size = byte_size(Prefix),
|
||||||
|
<<Prefix:Size/bytes, Rest/bytes>> = From,
|
||||||
|
Rest.
|
||||||
|
|||||||
@ -21,11 +21,6 @@
|
|||||||
-include("http.hrl").
|
-include("http.hrl").
|
||||||
-include("$zx_include/zx_logger.hrl").
|
-include("$zx_include/zx_logger.hrl").
|
||||||
|
|
||||||
-type request() :: #request{}.
|
|
||||||
-type response() :: #response{}.
|
|
||||||
-type tcp_error() :: closed
|
|
||||||
| {timeout, RestData :: binary() | erlang:iovec()}
|
|
||||||
| inet:posix().
|
|
||||||
|
|
||||||
-define(MAX_PAYLOAD_SIZE, ((1 bsl 63) - 1)).
|
-define(MAX_PAYLOAD_SIZE, ((1 bsl 63) - 1)).
|
||||||
|
|
||||||
|
|||||||
29
src/qhl.erl
29
src/qhl.erl
@ -49,62 +49,52 @@ parse(Socket, Received) ->
|
|||||||
%% socket.
|
%% socket.
|
||||||
|
|
||||||
parse(Socket, Received, M = #request{method = undefined}) ->
|
parse(Socket, Received, M = #request{method = undefined}) ->
|
||||||
io:format("~p parse(~p, ~p, ~p)~n", [?LINE, Socket, Received, M]),
|
|
||||||
case read_method(Socket, Received) of
|
case read_method(Socket, Received) of
|
||||||
{ok, Method, Rest} -> parse(Socket, Rest, M#request{method = Method});
|
{ok, Method, Rest} -> parse(Socket, Rest, M#request{method = Method});
|
||||||
Error -> Error
|
Error -> Error
|
||||||
end;
|
end;
|
||||||
parse(Socket, Received, M = #request{path = undefined}) ->
|
parse(Socket, Received, M = #request{path = undefined}) ->
|
||||||
io:format("~p parse(~p, ~p, ~p)~n", [?LINE, Socket, Received, M]),
|
|
||||||
case read_path(Socket, Received) of
|
case read_path(Socket, Received) of
|
||||||
{ok, Path, Rest} -> parse(Socket, Rest, M#request{path = Path});
|
{ok, Path, Rest} -> parse(Socket, Rest, M#request{path = Path});
|
||||||
Error -> Error
|
Error -> Error
|
||||||
end;
|
end;
|
||||||
parse(Socket, Received, M = #request{qargs = undefined}) ->
|
parse(Socket, Received, M = #request{qargs = undefined}) ->
|
||||||
io:format("~p parse(~p, ~p, ~p)~n", [?LINE, Socket, Received, M]),
|
|
||||||
case read_qargs(Socket, Received) of
|
case read_qargs(Socket, Received) of
|
||||||
{ok, Qargs, Rest} -> parse(Socket, Rest, M#request{qargs = Qargs});
|
{ok, Qargs, Rest} -> parse(Socket, Rest, M#request{qargs = Qargs});
|
||||||
Error -> Error
|
Error -> Error
|
||||||
end;
|
end;
|
||||||
parse(Socket, Received, M = #request{fragment = undefined}) ->
|
parse(Socket, Received, M = #request{fragment = undefined}) ->
|
||||||
io:format("~p parse(~p, ~p, ~p)~n", [?LINE, Socket, Received, M]),
|
|
||||||
case read_fragment(Socket, Received) of
|
case read_fragment(Socket, Received) of
|
||||||
{ok, Fragment, Rest} -> parse(Socket, Rest, M#request{fragment = Fragment});
|
{ok, Fragment, Rest} -> parse(Socket, Rest, M#request{fragment = Fragment});
|
||||||
Error -> Error
|
Error -> Error
|
||||||
end;
|
end;
|
||||||
parse(Socket, Received, M = #request{version = undefined}) ->
|
parse(Socket, Received, M = #request{version = undefined}) ->
|
||||||
io:format("~p parse(~p, ~p, ~p)~n", [?LINE, Socket, Received, M]),
|
|
||||||
case read_version(Socket, Received) of
|
case read_version(Socket, Received) of
|
||||||
{ok, Version, Rest} -> parse(Socket, Rest, M#request{version = Version});
|
{ok, Version, Rest} -> parse(Socket, Rest, M#request{version = Version});
|
||||||
Error -> Error
|
Error -> Error
|
||||||
end;
|
end;
|
||||||
parse(Socket, Received, M = #request{headers = undefined}) ->
|
parse(Socket, Received, M = #request{headers = undefined}) ->
|
||||||
io:format("~p parse(~p, ~p, ~p)~n", [?LINE, Socket, Received, M]),
|
|
||||||
case read_headers(Socket, Received) of
|
case read_headers(Socket, Received) of
|
||||||
{ok, Headers, Rest} -> parse(Socket, Rest, M#request{headers = Headers});
|
{ok, Headers, Rest} -> parse(Socket, Rest, M#request{headers = Headers});
|
||||||
Error -> Error
|
Error -> Error
|
||||||
end;
|
end;
|
||||||
parse(Socket, Received, M = #request{enctype = undefined}) ->
|
parse(Socket, Received, M = #request{enctype = undefined}) ->
|
||||||
io:format("~p parse(~p, ~p, ~p)~n", [?LINE, Socket, Received, M]),
|
|
||||||
case read_enctype(M) of
|
case read_enctype(M) of
|
||||||
{ok, Enctype} -> parse(Socket, Received, M#request{enctype = Enctype});
|
{ok, Enctype} -> parse(Socket, Received, M#request{enctype = Enctype});
|
||||||
Error -> Error
|
Error -> Error
|
||||||
end;
|
end;
|
||||||
parse(Socket, Received, M = #request{cookies = undefined}) ->
|
parse(Socket, Received, M = #request{cookies = undefined}) ->
|
||||||
io:format("~p parse(~p, ~p, ~p)~n", [?LINE, Socket, Received, M]),
|
|
||||||
case read_cookies(M) of
|
case read_cookies(M) of
|
||||||
{ok, Cookies} -> parse(Socket, Received, M#request{cookies = Cookies});
|
{ok, Cookies} -> parse(Socket, Received, M#request{cookies = Cookies});
|
||||||
Error -> Error
|
Error -> Error
|
||||||
end;
|
end;
|
||||||
parse(Socket, Received, M = #request{size = undefined}) ->
|
parse(Socket, Received, M = #request{size = undefined}) ->
|
||||||
io:format("~p parse(~p, ~p, ~p)~n", [?LINE, Socket, Received, M]),
|
|
||||||
case read_size(M) of
|
case read_size(M) of
|
||||||
{ok, 0} -> {ok, M#request{size = 0}, none};
|
{ok, 0} -> {ok, M#request{size = 0}, none};
|
||||||
{ok, Size} -> parse(Socket, Received, M#request{size = Size});
|
{ok, Size} -> parse(Socket, Received, M#request{size = Size});
|
||||||
Error -> Error
|
Error -> Error
|
||||||
end;
|
end;
|
||||||
parse(Socket, Received, M = #request{method = get, body = undefined, size = Size}) ->
|
parse(Socket, Received, M = #request{method = get, body = undefined, size = Size}) ->
|
||||||
io:format("~p parse(~p, ~p, ~p)~n", [?LINE, Socket, Received, M]),
|
|
||||||
case read_body(Received, Size) of
|
case read_body(Received, Size) of
|
||||||
{ok, Body} -> {ok, M#request{body = Body}, none};
|
{ok, Body} -> {ok, M#request{body = Body}, none};
|
||||||
{ok, Body, Next} -> {ok, M#request{body = Body}, Next};
|
{ok, Body, Next} -> {ok, M#request{body = Body}, Next};
|
||||||
@ -117,7 +107,6 @@ parse(Socket,
|
|||||||
method = post,
|
method = post,
|
||||||
enctype = urlencoded,
|
enctype = urlencoded,
|
||||||
size = Size}) ->
|
size = Size}) ->
|
||||||
io:format("~p parse(~p, ~p, ~p)~n", [?LINE, Socket, Received, M]),
|
|
||||||
case read_body(Received, Size) of
|
case read_body(Received, Size) of
|
||||||
{ok, Body} ->
|
{ok, Body} ->
|
||||||
{ok, M#request{body = parts_to_map(posted(Body))}, none};
|
{ok, M#request{body = parts_to_map(posted(Body))}, none};
|
||||||
@ -141,7 +130,6 @@ parse(Socket,
|
|||||||
method = post,
|
method = post,
|
||||||
enctype = {multipart, Boundary},
|
enctype = {multipart, Boundary},
|
||||||
size = Size}) ->
|
size = Size}) ->
|
||||||
io:format("~p parse(~p, ~p, ~p)~n", [?LINE, Socket, Received, M]),
|
|
||||||
case read_multipart(Socket, Received, Boundary, Size) of
|
case read_multipart(Socket, Received, Boundary, Size) of
|
||||||
{ok, Parts} -> {ok, M#request{body = parts_to_map(Parts)}, none};
|
{ok, Parts} -> {ok, M#request{body = parts_to_map(Parts)}, none};
|
||||||
{ok, Parts, Next} -> {ok, M#request{body = parts_to_map(Parts)}, Next};
|
{ok, Parts, Next} -> {ok, M#request{body = parts_to_map(Parts)}, Next};
|
||||||
@ -153,12 +141,10 @@ parse(Socket,
|
|||||||
method = post,
|
method = post,
|
||||||
enctype = json,
|
enctype = json,
|
||||||
size = Size}) ->
|
size = Size}) ->
|
||||||
io:format("~p parse(~p, ~p, ~p)~n", [?LINE, Socket, Received, M]),
|
|
||||||
case read_body(Received, Size) of
|
case read_body(Received, Size) of
|
||||||
{ok, Body} -> read_json(M#request{body = Body}, none);
|
{ok, Body} -> read_json(M#request{body = Body}, none);
|
||||||
{ok, Body, Next} -> read_json(M#request{body = Body}, Next);
|
{ok, Body, Next} -> read_json(M#request{body = Body}, Next);
|
||||||
{incomplete, Body} ->
|
{incomplete, Body} ->
|
||||||
io:format("~p {incomplete, ~p}~n", [?LINE, Body]),
|
|
||||||
case accumulate(Socket, M#request{body = Body}) of
|
case accumulate(Socket, M#request{body = Body}) of
|
||||||
{ok, NewM = #request{body = NewBody}} ->
|
{ok, NewM = #request{body = NewBody}} ->
|
||||||
read_json(NewM#request{body = NewBody}, none);
|
read_json(NewM#request{body = NewBody}, none);
|
||||||
@ -528,7 +514,6 @@ read_size(#request{method = options}) ->
|
|||||||
|
|
||||||
|
|
||||||
read_body(Received, Size) ->
|
read_body(Received, Size) ->
|
||||||
io:format("~p read_body(~p, ~p)~n", [?LINE, Received, Size]),
|
|
||||||
case Received of
|
case Received of
|
||||||
<<Bin:Size/binary>> ->
|
<<Bin:Size/binary>> ->
|
||||||
{ok, Bin};
|
{ok, Bin};
|
||||||
@ -826,11 +811,9 @@ accumulate(Socket, M = #request{size = Size, body = Body}) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
accumulate(Socket, Remaining, Received) when Remaining > 0 ->
|
accumulate(Socket, Remaining, Received) when Remaining > 0 ->
|
||||||
io:format("~p accumulate(~p, ~p, ~p)~n", [?LINE, Socket, Remaining, Received]),
|
|
||||||
ok = inet:setopts(Socket, [{active, once}]),
|
ok = inet:setopts(Socket, [{active, once}]),
|
||||||
receive
|
receive
|
||||||
{tcp, Socket, Bin} ->
|
{tcp, Socket, Bin} ->
|
||||||
io:format("~p~n", [?LINE]),
|
|
||||||
Size = byte_size(Bin),
|
Size = byte_size(Bin),
|
||||||
if
|
if
|
||||||
Size == Remaining ->
|
Size == Remaining ->
|
||||||
@ -845,16 +828,12 @@ accumulate(Socket, Remaining, Received) when Remaining > 0 ->
|
|||||||
{ok, NewReceived, Next}
|
{ok, NewReceived, Next}
|
||||||
end;
|
end;
|
||||||
{tcp_closed, Socket} ->
|
{tcp_closed, Socket} ->
|
||||||
io:format("~p~n", [?LINE]),
|
|
||||||
{error, tcp_closed};
|
{error, tcp_closed};
|
||||||
{tcp_error, Socket, Reason} ->
|
{tcp_error, Socket, Reason} ->
|
||||||
io:format("~p~n", [?LINE]),
|
{error, {tcp_error, Reason}}
|
||||||
{error, {tcp_error, Reason}};
|
%X ->
|
||||||
X ->
|
after 3_000 ->
|
||||||
io:format("~p raseevd: ~p~n", [?LINE, X])
|
{error, timeout}
|
||||||
after 10_000 ->
|
|
||||||
io:format("~p~n", [?LINE]),
|
|
||||||
{error, timeout}
|
|
||||||
end;
|
end;
|
||||||
accumulate(_, 0, Received) ->
|
accumulate(_, 0, Received) ->
|
||||||
{ok, Received};
|
{ok, Received};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user