我在Erlang套接字关闭中实现websocket时出错

时间:2013-03-12 11:24:39

标签: sockets ssl erlang websocket rfc

我是Erlang的新手。我需要在Erlang中写一个RFC6455(websocket)的实现作为服务器,我写的所有可以用HTTP 101响应,我认为接受头是好的,因为我尝试用RFC6455的例子和维基百科的例子计算,结果是一样的。当我在浏览器运行事件中发送HTTP 101以及websocket和服务器中的错误时,我不会关闭套接字但我看到套接字已关闭。我有这段代码:

edumserver.erl

-module(edumserver).
-export([start/0]).

start() ->
    ssl:start(),
    crypto:start(),
    edumhttpsserver:start(8883).

edumhttpsserver.erl

-module(edumhttpsserver).
-export([start/1]).


-define(RESP_404, <<"HTTP/1.1 404 Not Found
Server: eDum
Connection: Close

">>).

-define(RESP_200, <<"HTTP/1.1 200 OK
Server: eDum
Connection: Close
Content-type: ">>).


start(Puerto) ->
    spawn(fun() -> edum_https_init(Puerto) end).

edum_https_init(Puerto) ->
    Op = [{certfile,"priv/server.crt"},
          {keyfile, "priv/server.key"},
          {reuseaddr, true}, {active, false}, {packet, http}],
    {ok, Socket} = ssl:listen(Puerto, Op),
    edum_https_bucle(Socket).

edum_https_bucle(Socket) ->
    {ok, SockCli} = ssl:transport_accept(Socket),
    Pid = spawn(fun() -> edum_https_cliente(SockCli, []) end),
    ssl:controlling_process(SockCli, Pid),
    ssl:setopts(SockCli, [{active, true}]),
    edum_https_bucle(Socket).

edum_https_cliente(Socket, Estado) ->
    receive
        {ssl, Socket, {http_request, Metodo, {abs_path, Ruta}, _}} ->
            edum_https_cliente(Socket, Estado ++ [
                {method, Metodo}, {path, Ruta}, {secure, true}
            ]);
        {ssl, Socket, {http_header, _, Clave, _, Valor}} ->
            edum_https_cliente(Socket, Estado ++ [{Clave, Valor}]);
        {ssl, Socket, http_eoh} ->
            io:format("Estado: ~p~n", [Estado]),
            case proplists:get_value('Upgrade', Estado) of
                "websocket" ->
                    edumwebsocketserver:edum_websocket_cliente(Socket, Estado);
                _ ->
                    Respuesta = edum_https_send_file(Estado),
                    ssl:send(Socket, Respuesta),
                    ssl:close(Socket)
            end;
        {ssl_closed, Socket} ->
            ssl:close(Socket);
        {ssl, _, {http_error, Msg}} ->
            io:format("Error HTTP: ~s~n", [Msg]);
        Any ->
            io:format("No reconocido ~p~n", [Any]),
            ssl:close(Socket)
    end.

edum_https_send_file(Peticion) ->
    "/" ++ Ruta = proplists:get_value(path, Peticion, "/"),
    {ok, CWD} = file:get_cwd(),
    JRuta = filename:join(CWD, "priv/html"),
    RRuta = filename:join(JRuta, Ruta),
    case file:read_file(RRuta) of
        {ok, Contenido} ->
            Peso = list_to_binary(
                io_lib:format("~p", [byte_size(Contenido)])
            ),
            Tipo = edum_https_tipomime(Ruta),
            <<
                ?RESP_200/binary, Tipo/binary,
                "\nContent-lenght: ", Peso/binary,
                "\r\n\r\n", Contenido/binary
            >>;
        {error, _} ->
            ?RESP_404
    end.

edum_https_tipomime(Archivo) ->
    case filename:extension(string:to_lower(Archivo)) of
        ".htm" -> <<"text/html">>;
        ".html" -> <<"text/html">>;
        ".js" -> <<"application/javascript">>;
        ".css" -> <<"text/css">>;
        _ -> <<"text/plain">>
    end.

edumwebsocketserver.erl

-module(edumwebsocketserver).
-export([edum_websocket_cliente/2]).


-define(RESP_101, <<"HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: ">>).

-define(WS_UUID, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").


edum_websocket_cliente(Socket, Estado) ->
    WSClave = proplists:get_value("Sec-Websocket-Key", Estado),
    WSAceptar = base64:encode(crypto:sha(WSClave ++ ?WS_UUID)),
    Respuesta = <<?RESP_101/binary, WSAceptar/binary,"\r\n\r\n">>,
    ssl:setopts(Socket, [{keepalive, true}]),
    ssl:send(Socket, Respuesta),
    edum_websocket_cliente_bucle(Socket).

edum_websocket_cliente_bucle(Socket) ->
    EstadoSocket = ssl:connection_info(Socket),
    io:format("WS ~p ~p~n", [self(), EstadoSocket]),
    case EstadoSocket of
        {error, Razon} -> ssl:close(Socket), exit(Razon);
        _ -> ok
    end,
    receive
        {ssl_closed, Socket} ->
            ssl:close(Socket);
        Any ->
            io:format("WS: ~p~n", [Any])
    after 5000 ->
        io:format("Nada~n")
    end,
    edum_websocket_cliente_bucle(Socket).

在行edum_websocket_cliente_bucle(Socket)中的函数io:format("WS ~p ~p~n", [self(), EstadoSocket])中,我首先看好套接字,然后在下一个递归调用中看起来错误:{error,closed}

但是我没有关闭,也不明白为什么关闭,我用以下方法接受标题:

WSAceptar = base64:encode(crypto:sha(WSClave ++ ?WS_UUID)),

我用这个例子测试:

  

具体而言,如上例所示,| Sec-WebSocket-Key |头   字段的值为“dGhlIHNhbXBsZSBub25jZQ ==”,服务器会   将字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”连接起来   字符串   “dGhlIHNhbXBsZSBub25jZQ == 258EAFA5-E914-47DA-95CA-C5AB0DC85B11”。该   然后服务器将获取此的SHA-1哈希值,给出值0xb3   0x7a 0x4f 0x2c 0xc0 0x62 0x4f 0x16 0x90 0xf6 0x46 0x06 0xcf 0x38 0x59   0x45 0xb2 0xbe 0xc4 0xea。然后对该值进行base64编码(参见   [RFC4648]的第4节),给出了值   “s3pPLMBiTxaQ9kYGzzhZRbK + XOO =”。然后将回显此值   | Sec-WebSocket-Accept |标题字段。

  

GET / mychat HTTP / 1.1主机:server.example.com升级:websocket   连接:升级Sec-WebSocket-Key:x3JJHMbDL1EzLkh9GBhXDw ==   Sec-WebSocket-Protocol:chat Sec-WebSocket-Version:13来源:   http://example.com

     

HTTP / 1.1 101切换协议升级:websocket连接:   升级Sec-WebSocket-Accept:HSmrc0sMlYUkAGmm5OPpG2HaGWk =   Sec-WebSocket-Protocol:chat

错误是什么?我是Erlang的新手。在chrome控制台中说出这个错误:“状态行不以CRLF结束”。

1 个答案:

答案 0 :(得分:0)

我解决了。只在第一行添加响应\ r \ n。