Search code examples
socketserlangyaws

How to use yaws_api:stream_process_deliver(Socket, IoList) properly


I need to stream database data/string to client using Erlang/Yaws. I found this documentation to achieve this but this example uses open_port to send data.

this is the example from [http://yaws.hyber.org/stream.yaws][1]

out(A) ->
    %% Create a random number
    {_A1, A2, A3} = now(),
    random:seed(erlang:phash(node(), 100000),
                erlang:phash(A2, A3),
                A3),
    Sz = random:uniform(100000),

    Pid = spawn(fun() ->
                        %% Read random junk
                        S="dd if=/dev/urandom count=1 bs=" ++
                            integer_to_list(Sz) ++ " 2>/dev/null",
                        P = open_port({spawn, S}, [binary,stream, eof]),
                        rec_loop(A#arg.clisock, P)
                end),

    [{header, {content_length, Sz}},
     {streamcontent_from_pid, "application/octet-stream", Pid}].


rec_loop(Sock, P) ->
    receive
        {discard, YawsPid} ->
            yaws_api:stream_process_end(Sock, YawsPid);
        {ok, YawsPid} ->
            rec_loop(Sock, YawsPid, P)
    end,
    port_close(P),
    exit(normal).

rec_loop(Sock, YawsPid, P) ->
    receive
        {P, {data, BinData}} ->
            yaws_api:stream_process_deliver(Sock, BinData),
            rec_loop(Sock, YawsPid, P);
        {P, eof} ->
            yaws_api:stream_process_end(Sock, YawsPid)
    end.

I need to stream string, I managed to understand the process until here except port_close(p)-which obviously closes the port.

rec_loop(Sock, P) ->
    receive
        {discard, YawsPid} ->
            yaws_api:stream_process_end(Sock, YawsPid);
        {ok, YawsPid} -> rec_loop(Sock, YawsPid, P)
    end,
    port_close(P),
    exit(normal).

What I do not understand is this part.

rec_loop(Sock, YawsPid, P) ->
    receive
        {P, {data, BinData}} ->
            yaws_api:stream_process_deliver(Sock, BinData),
            rec_loop(Sock, YawsPid, P);
        {P, eof} ->
            yaws_api:stream_process_end(Sock, YawsPid)
    end.

Now, There is no documentation on {P, {data, BinData}} -> nor {P, eof} -> and I need to change the content type {streamcontent_from_pid, "application/octet-stream", Pid}. to {streamcontent_from_pid, "text/html; charset=utf-8", Pid}

So the question is How do I stream text/string without using port?


Solution

  • The Yaws example creates an OS process to read from /dev/urandom, a special file that delivers pseudorandom values, and it uses a port to communicate with that process. It runs the port within an Erlang process that serves as the content streamer.

    The content streamer process first awaits directions from Yaws by receiving either {discard, YawsPid} or {ok, YawsPid}. If it gets the discard message, it has no work to do, otherwise it calls rec_loop/3, which loops recursively, taking in data from the port and streaming it to a Yaws HTTP socket. When rec_loop/3 gets an end-of-file indication from the port, it terminates its streaming by calling yaws_api:stream_process_end/2 and then returns to rec_loop/2, which in turn closes the port and exits normally.

    For your application, you need a streaming process that, just like the Yaws example, first handles either {discard, YawsPid} or {ok, YawsPid}. If it gets {ok, YawsPid}, it should then go into a loop receiving messages that supply the text you want to stream to the HTTP client. When there's no more text to send, it should receive some sort of message telling it to stop, after which it should exit normally.