Question

I read about Yaws: streaming data to the client. I created a simple example shown below, but it doesn't work. I get an error and the process is dying.

Here is my yaws-file:

<erl>
out(A) ->
    Self = self(),  
    spawn(fun() ->
        yaws_api:stream_chunk_deliver_blocking(Self, "Hello"),
        yaws_api:stream_chunk_end(Self, "Thanks"),
        exit(normal)
    end),
    {streamcontent, "text/html; charset=utf-8", "First\r\n"}.
</erl>

I also tried with yaws_api:stream_chunk_deliver/2 but I got the same error. Here is the error message I get in the command prompt:

=ERROR REPORT==== 13-Feb-2012::09:23:30 ===
Error in process <0.117.0> with exit value: {undef,[{yaws_api,stream_chunk_end,[
\n"],[]}]}"Thanks

1>
=ERROR REPORT==== 13-Feb-2012::09:24:00 ===
Yaws process died: {stream_timeout,
                       [{yaws_server,stream_loop_send,5,
                            [{file,"yaws_server.erl"},{line,2821}]},
                        {yaws_server,aloop,3,
                            [{file,"yaws_server.erl"},{line,1167}]},
                        {yaws_server,acceptor0,2,
                            [{file,"yaws_server.erl"},{line,1025}]},
                        {proc_lib,init_p_do_apply,3,
                            [{file,"proc_lib.erl"},{line,227}]}]}
1>

The last chunk doesn't seem to be sent to the client:

HTTP/1.1 200 OK
Server: Yaws 1.92
Date: Mon, 13 Feb 2012 07:31:18 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked

7
First

7
Hello

Here is the JavaScript client code I use (only working with IE8 and IE9), using XDomainRequest:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<script>
window.onload = function() {
    var el = document.getElementById("requestbutton");
    if(el.attachEvent) {
        el.attachEvent('onclick', doXDR);
    }
}

function writeStatus(message) {
    var html = document.createElement("div");
    html.setAttribute("class", "message");
    html.innerHTML = message;
    document.getElementById("status").appendChild(html);
}

function doXDR() {
    if(!window.XDomainRequest) {
        writeStatus("No XDR support in your browser");
        return;
    }

    writeStatus("Beginning XDR");
    try {
        var xdr = new XDomainRequest();
        xdr.open("GET", "http://localhost:8090/stream.yaws");
        xdr.send();
        xdr.onload = function() {
            writeStatus("XDR response: " + xdr.responseText);
        };
        xdr.onerror = function() {
            writeStatus("XDR error");
        };
        xdr.onprogress = function() {
            writeStatus("XDR intermediate: " + xdr.responseText);
        };
    } catch (e) {
        writeStatus("XDR Exception: " + e);
    }
}
</script>
</head>
<body>
<h1>Test page</h1>
<button id="requestbutton">Send request</button><br>
<div id="status"></div>
</body>
</html>

On the JavaScript client, the xdr.onerror = function() method is called. The client should not show any data in this example since it need a 2k prelude, but it should be sent, as how I understand it.


Update

After fixing the Erlang issues pointed out by Steve Vinoski and removing \r\n on my data, the Yaws server send the correct data. But I still get xdr.onerror = function() error on the JavaScript client. And it seems that I need to add another header to the response Access-Control-Allow-Origin: * as documented in XDomainRequest Object:

The document will request data from the domain's server by sending an Origin header with the value of the origin. It will only complete the connection if the server responds with an Access-Control-Allow-Origin header of either * or the exact URL of the requesting document . This behavior is part of the World Wide Web Consortium (W3C)'s Web Application Working Group's draft framework on client-side cross-domain communication that the XDomainRequest object integrates with.

How do I add this header to the HTTP response? It looks like I only can set the MIME type in the return value: {streamcontent, MimeType, FirstChunk}?

Was it helpful?

Solution

Anytime you see an undef error, like this one you showed above:

Error in process <0.117.0> with exit value: {undef,[{yaws_api,stream_chunk_end,

it means you're either calling a function in a module that isn't on your load path, or you're calling a function that doesn't exist. In your code, you're calling yaws_api:stream_chunK_end/2, which doesn't exist. What you want instead is yaws_api:stream_chunk_end/1. Change your code to the following:

<erl>
  out(A) ->
    Self = self(),  
      spawn(fun() ->
        yaws_api:stream_chunk_deliver_blocking(Self, "Hello\r\n"),
        yaws_api:stream_chunk_deliver_blocking(Self, "Thanks\r\n"),
        yaws_api:stream_chunk_end(Self),
        exit(normal)
      end),
    {streamcontent, "text/html; charset=utf-8", "First\r\n"}.
</erl>

and then I think your example will work fine.

Update:

To answer the new part of your question, you use a list return value rather than just a single tuple, making sure the streamcontent tuple is last in the list:

[{header, {"Access-Control-Allow-Origin", "*"}},
 {streamcontent, "text/html; charset=utf-8", "First"}].
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top