anchor.diff:                   _ when is_integer(Reason) -> Reason;
                    _ -> ?NORMAL_WS_STATUS
                end,
  -    send(WSState, {close, <<Status:16/big>>}),
  -    exit(Reason).
  +    do_send(WSState, {close, <<Status:16/big>>}).
   
   basic_messages(FrameInfos, {FragType, FragAcc}) ->
  -    {Messages, NewFragType, NewFragAcc}
  -        = lists:foldl(fun handle_message/2, {[], FragType, FragAcc}, FrameInfos),
  +    {Messages, NewFragType, NewFragAcc} =
  +        lists:foldl(fun handle_message/2, {[], FragType, FragAcc}, FrameInfos),
       {Messages, {NewFragType, NewFragAcc}}.
   
   %% start of a fragmented message
  
positive.code:      "Sec-WebSocket-Accept: ", AcceptHash , "\r\n",
       "\r\n"].
  
  loop(CallbackMod, #ws_state{sock=Socket}=WSState, CallbackState, CallbackType) ->
      receive
          {send, {Type, Data}} ->
              send(WSState, {Type, Data}),
              loop(CallbackMod, WSState, CallbackState, CallbackType);
          {tcp, Socket, FirstPacket} ->
              FrameInfos = unframe_active_once(WSState, FirstPacket),
              case CallbackType of
                  basic ->
                      {BasicMessages, NewCallbackState} =
                          basic_messages(FrameInfos, CallbackState),
                      CallbackResults = lists:map(
                                          fun(M) ->
                                                  CallbackMod:handle_message(M)
                                          end,
                                          BasicMessages),
                      lists:map(handle_result_fun(WSState), CallbackResults);
                  {advanced,_} ->
                      NewCallbackState = lists:foldl(
                                           do_callback_fun(WSState, CallbackMod),
                                           CallbackState,
                                           FrameInfos)
              end,
              Last = lists:last(FrameInfos),
              NewWSState = Last#ws_frame_info.ws_state,
              loop(CallbackMod, NewWSState, NewCallbackState, CallbackType);
          {tcp_closed, Socket} ->
              %% The only way we should get here is due to an abnormal close.
              %% Section 7.1.5 of RFC 6455 specifies 1006 as the connection
              %% close code for abnormal closure. It's also described in
              %% section 7.4.1.
              CloseStatus = 1006,
              case CallbackType of
                  basic ->
                      CallbackMod:handle_message({close, CloseStatus, <<>>});
                  {advanced, _} ->
                      ClosePayload = <<CloseStatus:16/big>>,
                      CloseWSState = WSState#ws_state{sock=undefined,
                                                      frag_type=none},
                      CloseFrameInfo = #ws_frame_info{fin=1,
                                                      rsv=0,
                                                      opcode=close,
                                                      masked=0,
                                                      masking_key=0,
                                                      length=byte_size(ClosePayload),
                                                      payload=ClosePayload,
                                                      ws_state=CloseWSState},
                      CallbackMod:handle_message(CloseFrameInfo, CallbackState)
              end,
              ok;
          _Any ->
              loop(CallbackMod, WSState, CallbackState, CallbackType)
      end.
  
  handle_result_fun(WSState) ->
      fun(Result) ->
              case Result of
                  {reply, {Type, Data}} ->
                      send(WSState, {Type, Data});
                  noreply ->
                      ok;
                  {close, Reason} ->
                      do_close(WSState, Reason)
              end
      end.
  
  do_callback_fun(WSState, CallbackMod) ->
      fun(FrameInfo, CallbackState) ->
              case CallbackMod:handle_message(FrameInfo, CallbackState) of
                  {reply, {Type, Data}, NewCallbackState} ->
                      send(WSState, {Type, Data}),
                      NewCallbackState;
                  {noreply, NewCallbackState} ->
                      NewCallbackState;
                  {close, Reason} ->
                      %% the following does not return
                      do_close(WSState, Reason)
              end
      end.
  
  
negative.code:                                 {advanced, InitialState} ->
                                      InitialState
                              end,
              NState = State#state{wsstate=WSState,
                                   cbstate=CallbackState,
                                   cbtype=CallbackType},
              {noreply, NState}
      end;
  handle_cast({send, {Type, Data}}, #state{wsstate=WSState}=State) ->
      do_send(WSState, {Type, Data}),
      {noreply, State};
  handle_cast(_Msg, State) ->
      {noreply, State}.
  
  handle_info({tcp, Socket, FirstPacket}, #state{wsstate=#ws_state{sock=Socket}}=State) ->
      #state{cbmod=CallbackMod, wsstate=WSState,
             cbstate=CallbackState, cbtype=CallbackType} = State,
      FrameInfos = unframe_active_once(WSState, FirstPacket),
      {Results, NewCallbackState} =
          case CallbackType of
              basic ->
                  {BasicMessages, NewCallbackState0} =
                      basic_messages(FrameInfos, CallbackState),
                  CallbackResults = lists:map(
                                      fun(M) ->
                                              CallbackMod:handle_message(M)
                                      end,
                                      BasicMessages),
                  {catch lists:foldl(handle_result_fun(WSState), ok, CallbackResults),
                   NewCallbackState0};
              {advanced,_} ->
                  catch lists:foldl(do_callback_fun(WSState, CallbackMod),
                                    {ok, CallbackState},
                                    FrameInfos)
          end,
      case Results of
          {close, Reason} ->
              do_close(WSState, Reason),
              {stop, Reason, State#state{cbstate=NewCallbackState}};
          _ ->
              Last = lists:last(FrameInfos),
              NewWSState = Last#ws_frame_info.ws_state,
              {noreply, State#state{wsstate=NewWSState, cbstate=NewCallbackState}}
      end;
  handle_info({tcp_closed, Socket}, #state{wsstate=#ws_state{sock=Socket}}=State) ->
      %% The only way we should get here is due to an abnormal close.
      %% Section 7.1.5 of RFC 6455 specifies 1006 as the connection
      %% close code for abnormal closure. It's also described in
      %% section 7.4.1.
      #state{cbmod=CallbackMod, wsstate=WSState,
             cbstate=CallbackState, cbtype=CallbackType} = State,
      CloseStatus = 1006,
      case CallbackType of
          basic ->
              CallbackMod:handle_message({close, CloseStatus, <<>>});
          {advanced, _} ->
              ClosePayload = <<CloseStatus:16/big>>,
              CloseWSState = WSState#ws_state{sock=undefined,
                                              frag_type=none},
              CloseFrameInfo = #ws_frame_info{fin=1,
                                              rsv=0,
                                              opcode=close,
                                              masked=0,
                                              masking_key=0,
                                              length=byte_size(ClosePayload),
                                              payload=ClosePayload,
                                              ws_state=CloseWSState},
              CallbackMod:handle_message(CloseFrameInfo, CallbackState)
      end,
      {stop, normal, State};
  handle_info(_Info, State) ->
      {noreply, State}.
  
  terminate(_Reason, _State) ->
      ok.
  
  code_change(_OldVsn, Data, _Extra) ->
      {ok, Data}.
  
  %% internal functions
  
  do_send(#ws_state{sock=Socket, vsn=ProtoVsn}, {Type, Data}) ->
      DataFrame = frame(ProtoVsn, Type,  Data),
      case Socket of
          {sslsocket,_,_} ->
              ssl:send(Socket, DataFrame);
          _ ->
              gen_tcp:send(Socket, DataFrame)
      end.
  
  preprocess_opts(GivenOpts) ->
      Fun = fun({Key, Default}, Opts) ->
                    case lists:keyfind(Key, 1, Opts) of
                        false ->
                            [{Key, Default}|Opts];
                        _ -> Opts
                    end
            end,
      Defaults = [{origin, any},
                  {callback, basic}],
      lists:foldl(Fun, GivenOpts, Defaults).
  
  origin_check(_Origin, {origin, any}) ->
      ok;
  