PageRenderTime 49ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/src/misultin_websocket_draft-hixie-68.erl

http://github.com/ostinelli/misultin
Erlang | 131 lines | 56 code | 13 blank | 62 comment | 0 complexity | 88b006aaf5f66aef4a80916b90aaf17f MD5 | raw file
  1. % ==========================================================================================================
  2. % MISULTIN - WebSocket
  3. %
  4. % >-|-|-(°>
  5. %
  6. % Copyright (C) 2011, Roberto Ostinelli <roberto@ostinelli.net>, Joe Armstrong.
  7. % All rights reserved.
  8. %
  9. % Code portions from Joe Armstrong have been originally taken under MIT license at the address:
  10. % <http://armstrongonsoftware.blogspot.com/2009/12/comet-is-dead-long-live-websockets.html>
  11. %
  12. % BSD License
  13. %
  14. % Redistribution and use in source and binary forms, with or without modification, are permitted provided
  15. % that the following conditions are met:
  16. %
  17. % * Redistributions of source code must retain the above copyright notice, this list of conditions and the
  18. % following disclaimer.
  19. % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
  20. % the following disclaimer in the documentation and/or other materials provided with the distribution.
  21. % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote
  22. % products derived from this software without specific prior written permission.
  23. %
  24. % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
  25. % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
  26. % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
  27. % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
  28. % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  29. % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  30. % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  31. % POSSIBILITY OF SUCH DAMAGE.
  32. % ==========================================================================================================
  33. -module('misultin_websocket_draft-hixie-68').
  34. -behaviour(misultin_websocket).
  35. -vsn("0.9").
  36. % API
  37. -export([check_websocket/1, handshake/3, handle_data/3, send_format/2]).
  38. % includes
  39. -include("../include/misultin.hrl").
  40. % ============================ \/ API ======================================================================
  41. % ----------------------------------------------------------------------------------------------------------
  42. % Function: -> true | false
  43. % Description: Callback to check if the incoming request is a websocket request according to this protocol.
  44. % ----------------------------------------------------------------------------------------------------------
  45. -spec check_websocket(Headers::http_headers()) -> boolean().
  46. check_websocket(Headers) ->
  47. % set required headers
  48. RequiredHeaders = [
  49. {'Upgrade', "WebSocket"}, {'Connection', "Upgrade"}, {'Host', ignore}, {'Origin', ignore}
  50. ],
  51. % check for headers existance
  52. case misultin_websocket:check_headers(Headers, RequiredHeaders) of
  53. true -> true;
  54. _RemainingHeaders ->
  55. ?LOG_DEBUG("not this protocol, remaining headers: ~p", [_RemainingHeaders]),
  56. false
  57. end.
  58. % ----------------------------------------------------------------------------------------------------------
  59. % Function: -> iolist() | binary()
  60. % Description: Callback to build handshake data.
  61. % ----------------------------------------------------------------------------------------------------------
  62. -spec handshake(Req::#req{}, Headers::http_headers(), {Path::string(), Origin::string(), Host::string()}) -> iolist().
  63. handshake(#req{socket_mode = SocketMode, ws_force_ssl = WsForceSsl} = _Req, _Headers, {Path, Origin, Host}) ->
  64. % prepare handhsake response
  65. WsMode = case SocketMode of
  66. ssl -> "wss";
  67. http when WsForceSsl =:= true -> "wss"; % behind stunnel or similar, client is using ssl
  68. http when WsForceSsl =:= false -> "ws"
  69. end,
  70. ["HTTP/1.1 101 Web Socket Protocol Handshake\r\n",
  71. "Upgrade: WebSocket\r\n",
  72. "Connection: Upgrade\r\n",
  73. "WebSocket-Origin: ", Origin , "\r\n",
  74. "WebSocket-Location: ", WsMode, "://", lists:concat([Host, Path]), "\r\n\r\n"
  75. ].
  76. % ----------------------------------------------------------------------------------------------------------
  77. % Function: -> websocket_close | {websocket_close, DataToSendBeforeClose::binary() | iolist()} | NewState
  78. % Description: Callback to handle incomed data.
  79. % ----------------------------------------------------------------------------------------------------------
  80. -spec handle_data(Data::binary(), State::undefined | term(), {Socket::socket(), SocketMode::socketmode(), WsHandleLoopPid::pid()}) -> websocket_close | term().
  81. handle_data(Data, undefined, {Socket, SocketMode, WsHandleLoopPid}) ->
  82. % init status
  83. handle_data(Data, {buffer, none}, {Socket, SocketMode, WsHandleLoopPid});
  84. handle_data(Data, {buffer, B} = _State, {Socket, SocketMode, WsHandleLoopPid}) ->
  85. % read status
  86. i_handle_data(Data, B, {Socket, SocketMode, WsHandleLoopPid}).
  87. % ----------------------------------------------------------------------------------------------------------
  88. % Function: -> binary() | iolist()
  89. % Description: Callback to format data before it is sent into the socket.
  90. % ----------------------------------------------------------------------------------------------------------
  91. -spec send_format(Data::iolist(), State::term()) -> iolist().
  92. send_format(Data, _State) ->
  93. [0, Data, 255].
  94. % ============================ /\ API ======================================================================
  95. % ============================ \/ INTERNAL FUNCTIONS =======================================================
  96. % Buffering and data handling
  97. -spec i_handle_data(
  98. Data::binary(),
  99. Buffer::binary() | none,
  100. {Socket::socket(), SocketMode::socketmode(), WsHandleLoopPid::pid()}) -> websocket_close | term().
  101. i_handle_data(<<0, T/binary>>, none, {Socket, SocketMode, WsHandleLoopPid}) ->
  102. i_handle_data(T, <<>>, {Socket, SocketMode, WsHandleLoopPid});
  103. i_handle_data(<<>>, none, {_Socket, _SocketMode, _WsHandleLoopPid}) ->
  104. % return status
  105. {buffer, none};
  106. i_handle_data(<<255, 0>>, _L, {Socket, SocketMode, _WsHandleLoopPid}) ->
  107. ?LOG_DEBUG("websocket close message received from client, closing websocket with pid ~p", [self()]),
  108. misultin_socket:send(Socket, <<255, 0>>, SocketMode),
  109. % return command
  110. websocket_close;
  111. i_handle_data(<<255, T/binary>>, L, {Socket, SocketMode, WsHandleLoopPid}) ->
  112. misultin_websocket:send_to_browser(WsHandleLoopPid, binary_to_list(L)),
  113. i_handle_data(T, none, {Socket, SocketMode, WsHandleLoopPid});
  114. i_handle_data(<<H, T/binary>>, L, {Socket, SocketMode, WsHandleLoopPid}) ->
  115. i_handle_data(T, <<L/binary, H>>, {Socket, SocketMode, WsHandleLoopPid});
  116. i_handle_data(<<>>, L, {_Socket, _SocketMode, _WsHandleLoopPid}) ->
  117. {buffer, L}.
  118. % ============================ /\ INTERNAL FUNCTIONS =======================================================