Skip to content

Commit

Permalink
Fix issue erlyaws#88
Browse files Browse the repository at this point in the history
2 changes here:

 * Revert "Flush remaining data when dynamic content is delivered"

This reverts commit d09ed3d.
Systematically flushing data is not a good idea.

 * Add "flush" as possible return value of the out/1 function

Some clients (eg. Firefox and google-chome...) do not handle connection
close gracefully when the connection is closed during sending data; they
do not read the server response. So, when the response is important, to
warn the end-user that an error occured for example, we must be fair by
flushing data before closing the connection.
  • Loading branch information
Christopher Faulet committed Feb 15, 2012
1 parent 280e38e commit f163227
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 28 deletions.
2 changes: 2 additions & 0 deletions doc/yaws.tex
Original file line number Diff line number Diff line change
Expand Up @@ -1647,6 +1647,8 @@ \section{All out/1 return values}

\item \verb+ok+ Do nothing.

\item \verb+flush+ Flush remaining data sent by the client.

\item \verb+{redirect, Url}+ Erase all previous headers and accumulate
a single HTTP \verb+Location+ header. Set the status code to 302.

Expand Down
4 changes: 4 additions & 0 deletions man/yaws_api.5
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,10 @@ in the yaws file.
\fBok\fR
Do nothing.

.TP
\fBflush\fR
Flush remaining data sent by the client.


.TP
\fB{redirect, Url}\fR
Expand Down
46 changes: 19 additions & 27 deletions src/yaws_server.erl
Original file line number Diff line number Diff line change
Expand Up @@ -2662,65 +2662,49 @@ get_chunked_client_data(CliSock,SSL) ->

deliver_dyn_part(CliSock, % essential params
LineNo, YawsFile, % for diagnostic output
CliDataPos, % for `get_more'
CliDataPos0, % for `get_more' and `flush'
Arg,UT,
YawsFun, % call YawsFun(Arg)
DeliverCont % call DeliverCont(Arg)
% to continue normally
) ->
put(yaws_ut, UT),
put(yaws_arg, Arg),
put(client_data_pos, CliDataPos0),
Res = (catch YawsFun(Arg)),
case handle_out_reply(Res, LineNo, YawsFile, UT, Arg) of
{get_more, Cont, State} when element(1, Arg#arg.clidata) == partial ->
More = get_more_post_data(CliDataPos, Arg),
CliDataPos1 = get(client_data_pos),
More = get_more_post_data(CliDataPos1, Arg),
A2 = Arg#arg{clidata=More, cont=Cont, state=State},
deliver_dyn_part(
CliSock, LineNo, YawsFile, CliDataPos+size(un_partial(More)),
CliSock, LineNo, YawsFile, CliDataPos1+size(un_partial(More)),
A2, UT, YawsFun, DeliverCont
);
break ->
flush(CliSock, CliDataPos, (Arg#arg.headers)#headers.content_length,
(Arg#arg.headers)#headers.transfer_encoding),
finish_up_dyn_file(Arg, CliSock);
{page, Page} ->
flush(CliSock, CliDataPos, (Arg#arg.headers)#headers.content_length,
(Arg#arg.headers)#headers.transfer_encoding),
{page, Page};
Arg2 = #arg{} ->
flush(CliSock, CliDataPos, (Arg#arg.headers)#headers.content_length,
(Arg#arg.headers)#headers.transfer_encoding),
DeliverCont(Arg2);
{streamcontent, _, _} ->
flush(CliSock, CliDataPos, (Arg#arg.headers)#headers.content_length,
(Arg#arg.headers)#headers.transfer_encoding),
Priv = deliver_accumulated(Arg, CliSock, decide, undefined, stream),
stream_loop_send(Priv, CliSock, 30000);
%% For other timeout values (other than 30 second)
{streamcontent_with_timeout, _, _, TimeOut} ->
flush(CliSock, CliDataPos, (Arg#arg.headers)#headers.content_length,
(Arg#arg.headers)#headers.transfer_encoding),
Priv = deliver_accumulated(Arg, CliSock, decide, undefined, stream),
stream_loop_send(Priv, CliSock, TimeOut);
{streamcontent_with_size, Sz, _, _} ->
flush(CliSock, CliDataPos, (Arg#arg.headers)#headers.content_length,
(Arg#arg.headers)#headers.transfer_encoding),
Priv = deliver_accumulated(Arg, CliSock, decide, Sz, stream),
stream_loop_send(Priv, CliSock, 30000);
{streamcontent_from_pid, _, Pid} ->
flush(CliSock, CliDataPos, (Arg#arg.headers)#headers.content_length,
(Arg#arg.headers)#headers.transfer_encoding),
Priv = deliver_accumulated(Arg, CliSock, no, undefined, stream),
wait_for_streamcontent_pid(Priv, CliSock, Pid);
{websocket, CallbackMod, Opts} ->
flush(CliSock, CliDataPos, (Arg#arg.headers)#headers.content_length,
(Arg#arg.headers)#headers.transfer_encoding),
%% The handshake passes control over the socket to OwnerPid
%% and terminates the Yaws worker!
yaws_websockets:start(Arg, CallbackMod, Opts);
_ ->
flush(CliSock, CliDataPos, (Arg#arg.headers)#headers.content_length,
(Arg#arg.headers)#headers.transfer_encoding),
DeliverCont(Arg)
end.

Expand Down Expand Up @@ -3293,6 +3277,14 @@ handle_out_reply({get_more, Cont, State}, _LineNo, _YawsFile, _UT, _ARG) ->
handle_out_reply(Arg = #arg{}, _LineNo, _YawsFile, _UT, _ARG) ->
Arg;

handle_out_reply(flush, _LineNo, _YawsFile, _UT, ARG) ->
CliDataPos0 = get(client_data_pos),
CliDataPos1 = flush(ARG#arg.clisock, CliDataPos0,
(ARG#arg.headers)#headers.content_length,
(ARG#arg.headers)#headers.transfer_encoding),
put(client_data_pos, CliDataPos1),
ok;

handle_out_reply(Reply, LineNo, YawsFile, _UT, ARG) ->
L = ?F("yaws code at ~s:~p crashed or "
"ret bad val:~p ~nReq: ~p",
Expand Down Expand Up @@ -4649,11 +4641,11 @@ flush(Sock, Sz, TransferEncoding) ->
flush(Sock, Pos, undefined, "chunked") ->
SC = get(sc),
case get_chunked_client_data(Sock, yaws:is_ssl(SC)) of
{partial, _} -> flush(Sock, Pos, undefined, "chunked");
_ -> ok
{partial, Bin} -> flush(Sock, Pos+size(Bin), undefined, "chunked");
_ -> Pos
end;
flush(_Sock, _Pos, undefined, _) ->
ok;
flush(_Sock, Pos, undefined, _) ->
Pos;
flush(Sock, Pos, Sz, TE) when is_list(Sz) ->
flush(Sock, Pos, strip_list_to_integer(Sz), TE);
flush(Sock, Pos, Sz, _) ->
Expand All @@ -4662,11 +4654,11 @@ flush(Sock, Pos, Sz, _) ->
flush(_Sock, Sz, Sz, _SSL, _PPS) ->
ok;
Sz;
flush(Sock, Pos, Sz, SSL, PPS) ->
case yaws:do_recv(Sock, erlang:min(Sz - Pos, PPS), SSL) of
{ok, Bin} -> flush(Sock, Pos + size(Bin), Sz, SSL, PPS);
_ -> ok
_ -> Pos
end.
Expand Down
2 changes: 1 addition & 1 deletion test/t2/flushtest.erl
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
-include("../../include/yaws_api.hrl").

out(_Arg) ->
[{status, 200}, {html, "break"}, break].
[{status, 200}, {html, "break"}, flush, break].

0 comments on commit f163227

Please sign in to comment.