0

I've been having this problem for months now, and I've scrolled all over the web, and got no answer. Right now, I am running Yaws version 2.0.6, and Erlang OTP 19 for my project. I know to establish a websocket connection, the client needs to make a request to the server for a handshake. I have my client side written in javascript, and my server side written in erlang. However, when I try to run the program, I get this vague Yaws error message stating, "Invalid connection header". It doesn't tell me specifically what I need to change, but somehow I have a feeling it has one or two things to do with the header. Number one, the header somehow didn't upgrade from http protocol to websocket correctly, or two, there is something wrong with websocket connection. I don't know what it is, but I'll show you the snippets of code on both sides, plus the error message, and if you can show me a different way of writing code for either side, that would be very helpful. Thanks.

Client side!!!

$(function() {
  var WebSocket = window.WebSocket || window.MozWebSocket;
  var socket = new WebSocket("ws://127.0.0.1:8080/sign_up.yaws");

  // wait for socket to open
  socket.onopen = function() {

    $('input#echo').on('keypress', function(event) {
      if (event.which == 13) {
        event.preventDefault();
        var msg = $(this).val();

        socket.send(JSON.stringify({
          message: msg
        }));
      }
    });

    socket.onmessage = function(msg) {
      var message = $.parseJSON(msg.data);
      var html = $('div#messages').html() + message.message + "<br>\n";
      $('div#message').html(html);
    }
  }

});

server-side

-module(sign_up).
-include("yaws_api.hrl").
-export([handle_message/2]).

-record(state, {frag_type = none,
                acc = <<>>}).

handle_message(#ws_frame_info{fin=0,
                              opcode=FragType,
                              data=Data},
               #state{frag_type=none, acc = <<>>}) ->
    {noreply, #state{frag_type=FragType, acc=Data}};


handle_message(#ws_frame_info{fin=0,
                  opcode=continuation,
                              data=Data},
               #state{frag_type = FragType, acc = Acc}) ->
    {noreply, #state{frag_type=FragType, acc = <<Acc/binary,Data/binary>>}};

handle_message(#ws_frame_info{fin=1,
                              opcode=continuation,
                              data=Data},
               #state{frag_type=text, acc=Acc}) ->
    Unfragged = <<Acc/binary, Data/binary>>,
    {reply, {text, Unfragged}, #state{frag_type=none, acc = <<>>}};

handle_message(#ws_frame_info{opcode=text, data=Data}, State) ->
    {reply, {text, Data}, State};

handle_message(#ws_frame_info{fin=1,
                              opcode=continuation,
                              data=Data},
               #state{frag_type=binary, acc=Acc}) ->
    Unfragged = <<Acc/binary, Data/binary>>,
    io:format("echoing back binary message~n", []),
    {reply, {binary, Unfragged}, #state{frag_type=none, acc = <<>>}};

handle_message(#ws_frame_info{opcode=binary,
                              data=Data},
               State) ->
    io:format("echoing back binary message~n", []),
    {reply, {binary, Data}, State};

handle_message(#ws_frame_info{opcode=pong}, State) ->
    io:format("ignoring unsolicited pong~n", []),
    {noreply, State};

handle_message(#ws_frame_info{}=FrameInfo, State) ->
    io:format("WS Endpoint Unhandled message: ~p~n~p~n", [FrameInfo, State]),
    {close, {error, {unhandled_message, FrameInfo}}}.

html

<script language="Javascript" type="text/javascript" src="jquery.min.map"></script><script language="Javascript" type="text/javascript" src="socket.js"></script>
<erl>
get_upgrade_header(#headers{other=L}) ->
lists:foldl(fun({http_header,_,KO,_,V}, undefined) ->
                    K = case is_atom(KO) of
                            true ->
                                 atom_to_list(KO);
                            false ->
                                 KO
                        end,
                    case string:to_lower(K) of
                        "upgrade" ->
                            true;
                       _ ->
                            false
                    end;
               (_, ACC) ->
                    ACC
           end, undefined, L).
%%------------------------------------------------------------------------------
out(Arg) ->
   case get_upgrade_header(Arg#arg.headers) of
   true ->
       error_logger:warning_msg("Not a web socket client~n"),
       {content, "text/plain", "You're not a web sockets client! Go away!"};
   false ->
       error_logger:info_msg("Starting web socket~n"),
       {websocket, sign_up, []}
   end.
</erl>

Yaws code crashed error message

=INFO REPORT==== 13-Jan-2022::15:04:24 ===
Starting web socket

=ERROR REPORT==== 13-Jan-2022::15:04:24 ===
Invalid connection header

I know I've asked this question before, and I'm sorry for asking again. I don't think I was as explicit before as I am today, but if anyone can show me a different way to solve this problem, like rewrite the server side code, or do some editing to the erlang code on the html page, I would very appreciate it. Thanks again.

Emiel Zuurbier
  • 19,095
  • 3
  • 17
  • 32
  • Question: is there any particular reason you're using Erlang/OTP 19, which is over 5 years old, and Yaws 2.0.6, which is over 3 years old? I ask because IMO it's easier to get support on latest versions (currently OTP 24 and Yaws 2.1.0); I also ask because I'm currently the primary maintainer of Yaws. – Steve Vinoski Jan 23 '22 at 14:05
  • Looking at the code, it's hard to know what you're trying to accomplish. It seems like you want to have an input element where you read a string and then have that echoed back to you from the websocket server? I don't see anywhere that you're providing the actual HTML of the client page. Have you tried viewing the `websockets.yaws` page and the code underneath it from your local Yaws server, since it contains a basic echo example? – Steve Vinoski Jan 23 '22 at 16:09
  • I started working on this project back in 2018-2019, and I believe at the time Erlang/OTP 19, and Yaws 2.0.6 was the latest versions running at the time. You're definitely right about the technology I'm using. It's obsolete, and I need a huge upgrade. If I upgrade to the latest versions, would I have to rewrite all code for the backend??? – Mykal Marsh Jan 27 '22 at 14:03
  • Also, the code you see above, I was trying to accomplish a websocket connection so that I can pass audio data between client, and server. Please tell what I'm doing wrong if I'm doing something wrong. Also, just curious, to establish a valid websocket connection between client, and server has to be done between two webpages??? – Mykal Marsh Jan 27 '22 at 14:17
  • We're pretty careful with Yaws as far as backward compatibility goes, so no, you shouldn't need to rewrite anything to use version 2.1.0, except the minimum Erlang/OTP version it supports is 21.3, so you'll need to update that as well, and I suggest going up to the current latest, 24.2. Meanwhile, I'll see if I can come up with an example for what you're trying to accomplish. – Steve Vinoski Jan 27 '22 at 15:13
  • A couple things: 1) your callback module uses the advanced callback approach, but you're registering it as a basic callback module; 2) in `get_upgrade_header` you're looking for an upgrade header, but the `yaws_websockets` module handles that. Your JS code already calls `sign_up.yaws` when creating a `WebSocket`, so unconditionally return `{websocket, sign_up, [{callback, {advanced, {state, none, <<>>}}}]}` from the `out/1` function in `sign_up.yaws` and don't bother looking for an `Upgrade` header. – Steve Vinoski Jan 31 '22 at 03:22
  • Ok, will do!!!! – Mykal Marsh Feb 01 '22 at 18:57

0 Answers0