├── .gitignore ├── CHANGELOG.md ├── EXPORTS.md ├── LICENSE.txt ├── Makefile ├── README.md ├── examples ├── application │ ├── misultin_app_example.app │ ├── misultin_app_example.erl │ ├── misultin_app_example_server.erl │ └── misultin_app_example_sup.erl ├── misultin_access_log.erl ├── misultin_body_recv.erl ├── misultin_chunked.erl ├── misultin_comet_iframe.erl ├── misultin_comet_iframe_event.erl ├── misultin_comet_long_polling.erl ├── misultin_compress.erl ├── misultin_cookies_example.erl ├── misultin_echo.erl ├── misultin_file.erl ├── misultin_file_upload.erl ├── misultin_get_variable.erl ├── misultin_hello_world.erl ├── misultin_hello_world_nameless.erl ├── misultin_multiple_servers_custom_name.erl ├── misultin_proxy_protocol.erl ├── misultin_redirect.erl ├── misultin_rest.erl ├── misultin_rest_utf8.erl ├── misultin_sessions_example.erl ├── misultin_ssl.erl ├── misultin_static.erl ├── misultin_stream.erl ├── misultin_unicode.erl ├── misultin_websocket_event_example.erl ├── misultin_websocket_event_example2.erl ├── misultin_websocket_example.erl ├── misultin_websocket_example_ssl.erl └── misultin_websocket_sessions_example.erl ├── include └── misultin.hrl ├── make.bat ├── priv ├── README.txt ├── test_certificate.pem └── test_privkey.pem ├── src ├── misultin.app.src ├── misultin.erl ├── misultin_acceptor.erl ├── misultin_acceptors_sup.erl ├── misultin_cookies.erl ├── misultin_http.erl ├── misultin_req.erl ├── misultin_server.erl ├── misultin_sessions.erl ├── misultin_socket.erl ├── misultin_utility.erl ├── misultin_websocket.erl ├── misultin_websocket_draft-hixie-68.erl ├── misultin_websocket_draft-hixie-76.erl ├── misultin_websocket_draft-hybi-10.erl ├── misultin_websocket_draft-hybi-10_17.erl ├── misultin_websocket_draft-hybi-17.erl └── misultin_ws.erl └── test └── misultin_SUITE.erl /.gitignore: -------------------------------------------------------------------------------- 1 | ebin 2 | logs 3 | erl_crash.dump 4 | *.beam 5 | 6 | # -------\/------- OSX ignores 7 | 8 | # store 9 | .DS_Store 10 | 11 | # thumbs 12 | ._* 13 | 14 | # files that might appear on external disk 15 | .Spotlight-V100 16 | .Trashes 17 | 18 | # -------\/------- OSX ignores -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ### 0.9: 4 | * added SESSIONS state, persistent across requests 5 | * added access log callback function, so that main application can log HTTP access 6 | * added streaming input for big files or endless input, using a manual ```body_recv function``` in conjunction with the ```{auto_recv_body, false}``` option 7 | * added static directory support, so that GET requests to /static/* can automatically send files from a specified directory (thanks to egobrain suggestion) 8 | * added request redirection helper method 9 | * consistently improved memory usage by not copying by default to handler processes the full request or websocket record 10 | * added configuration option to set which websocket versions must be supported by the server 11 | * added support for websocket draft-hybi-10 12 | * added support for websocket draft-hybi-17 (thanks to RJ) 13 | * added support for websockets on FireFox (thanks to Egobrain) 14 | * added support for 'If-Modified-Since' headers in file sending (thanks to davidgaleano) 15 | * added support for websockets when behind stunnel with ```{external_ssl, boolean()}``` option (thanks to RJ) 16 | * added support to see the correct client IP when behind stunnel, according to http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt (thanks to RJ) 17 | * added support for OPTIONS method (thanks to majek) 18 | * rebar-ized makefile 19 | * corrected minor bugs (thank you all - you know who you are!) 20 | 21 | ### 0.8: 22 | * Misultin has been redesigned to use supervisor behaviours where appropriate, to be more OTP compliant 23 | * added Cookie support 24 | * added preliminary support multipart/form-data and a file upload example (thanks to Max Lapshin) 25 | * added Date and Server headers 26 | * added support for headers being specified as binaries (thanks to akaspin) 27 | * added an example on how to properly embed misultin in your application 28 | * ```Req:get(peer_addr)``` now properly extracts peer information from headers "X-Real-Ip" or "X-Forwarded-For" if these are available (thanks to Max Lapshin) 29 | * solved bug on large data being sent over via websockets (thanks to omarkj) 30 | * corrected binary sending bug in websockets which would fail binaries on io_lib format (thanks to normanb) 31 | * added recbuf advanced option (issue #40) 32 | * added preliminary test suite 33 | * various optimizations using binary when needed 34 | 35 | ### 0.7.1: 36 | * considerably improved stability under heavy load 37 | * misultin now accepts incoming connections with a pool of acceptors instead of a single one 38 | * Misultin can now be used both with parametrized modules and with pure erlang code too (thanks to yrashk, nox and essen) 39 | * added support for HEAD, PUT, DELETE, TRACE and CONNECT methods 40 | * now websockets are on ```{active, once}``` mode to avoid malicious clients overflooding (thanks to essen) 41 | * ensured that body of request can be read on all methods except TRACE as per http specs 42 | * added support for iolist() in chunked resposes (thanks to RJ) 43 | 44 | ### 0.7: 45 | * added ```max_connections options``` parameter, which specifies maximum concurrent open connections accepted by the server 46 | * added ```post_max_size``` options parameter, which sets the maximum size of POST data 47 | * added ```get_url_max_size``` options parameter, which sets the maximum length of URI 48 | * added CHUNKED support, both for incoming requests and outgoing responses (thanks to yrashk suggestion) 49 | * added trapping of client closing a browser in Comet applications (thanks to yrashk) 50 | * added SSL support for websockets (enhancement track #25, thanks to viplifes) 51 | * Misultin can now be started without a registered name or with a different name, so that multiple versions of misultin can be started on a single node 52 | * added support for IP address specified in tuple format (thanks to okeuday suggestion) 53 | * added support to extract plain uri unquoted as a list() (thanks to okeuday) 54 | * added Comet Long Polling example 55 | * added Comet iFrame example 56 | * added the killing of alive processes on server shutdown 57 | * the GET uri parameters are now also available on POST requests 58 | * added custom headers on file sending to browser (thanks to josevalim) 59 | * additional minor adjustments 60 | 61 | ### 0.6.2: 62 | * refactored to considerably improve sending of static files 63 | * minor bug corrections 64 | 65 | ### 0.6.1: 66 | * added support to websocket protocol hixie draft 76 (thanks to sergio veiga) 67 | * added support to multiple websocket draft protocols (for backwards compatibility) 68 | * added ```ws_autoexit option``` which allows to get an event on websocket controlling processes (issue track #15, suggestion of esente) 69 | * added headers also in misultin websockets (thanks to jlirochon) 70 | * made it basho's rebar friendly (thanks to mrinalwadhwa) 71 | 72 | ### 0.6: 73 | * added HTTP compression option 74 | * refactoring of the main server loop, so that it is now isolated from the HTTP functionality 75 | * removed unnecessary compilation warnings 76 | * replaced ```proplists:get_value``` with much faster utility function 77 | 78 | ### 0.5: 79 | * added SSL support 80 | * refactoring of the acceptor loop 81 | 82 | ### 0.4: 83 | * added preliminary websocket support 84 | 85 | ### 0.3.4: 86 | * added Req support to return the socket handling the request 87 | * bug correction on Content-Length: 0 header causing timeout on POST requests (issue track #12, thanks to gdamjan) 88 | 89 | ### 0.3.3: 90 | * added echoing of the Connection header (issue track #7, thanks to thijsterlouw) 91 | * bug correction on acceptor respawning (issue track #10, thanks to thijsterlouw) 92 | 93 | ### 0.3.2: 94 | * optimized error handling (issue track #5, thanks to Max Lapshin) 95 | 96 | ### 0.3.1: 97 | * added flow control using inet options {active, once} (issue track #4, thanks to Max Lapshin) 98 | * added support to standard http headers response 99 | * added http 400 bad request error in socket handling 100 | * bug correction: removed erroneous sending of response timeout on listening open connections 101 | * added stream_support optimization option 102 | 103 | ### 0.3: 104 | * reengineering of the listener process, using active instead of passive mode in request parsing, except for BODY where passive is still used (thanks to Lev Walkin) 105 | * added better support for request timeout 106 | 107 | ### 0.2.2: 108 | * added .app file (thanks to Essien Ita Essien) 109 | * simplified get_options (thanks to Essien Ita Essien) 110 | * added ip address option (thanks to Essien Ita Essien) 111 | * added ipv6 support 112 | * added ```recv_timeout``` option 113 | * bug correction: requests peer address and port are now not reset on open connections multiple requests 114 | 115 | ### 0.2.1: 116 | * added support for Content-Type that specifies charset in POST data (thanks to Tuncer Ayaz) 117 | * added support for iolist in ```misultin_req:ok/1,2``` and ```misultin_req:respond/2,3``` 118 | * code optimized taking out unnecessary binary conversion and lists:flatten (thanks to Feng Yu) 119 | 120 | ### 0.2: 121 | * added trap exit for acceptor failure 122 | * added backlog option 123 | * added fallback if no connection header is present (issue track #1, thanks to Ciconia) 124 | * added limit for parsing headers to avoid malicious attacks (thanks to Mazen Harake) 125 | * minor bug corrections 126 | 127 | ### 0.1: 128 | * initial release. 129 | 130 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | ========================================================================================================== 2 | MISULTIN - An Erlang library for building fast lightweight HTTP servers. 3 | 4 | 5 | >-|-|-(°> 6 | 7 | Copyright (C) 2011, Roberto Ostinelli , Joe Armstrong, Sean Hinde, 8 | Bob Ippolito for Mochi Media, Inc. 9 | 10 | All rights reserved. 11 | 12 | Code portions from Joe Armstrong have been originally taken under MIT license at the address: 13 | 14 | 15 | Code portions from Sean Hinde have been originally taken under BSD license from Trapexit at the address: 16 | 17 | 18 | Code portions from Bob Ippolito have been originally taken under MIT license from MOCHIWEB: 19 | 20 | ========================================================================================================== 21 | 22 | BSD License 23 | 24 | Redistribution and use in source and binary forms, with or without modification, are permitted provided 25 | that the following conditions are met: 26 | 27 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the 28 | following disclaimer. 29 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 30 | the following disclaimer in the documentation and/or other materials provided with the distribution. 31 | * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 32 | products derived from this software without specific prior written permission. 33 | 34 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 35 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 36 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 37 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 38 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 39 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 40 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 41 | POSSIBILITY OF SUCH DAMAGE. 42 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | REBAR_CONFIG:=$(PWD)/rebar.config 2 | INCLUDE_DIR:=include 3 | SRC_DIR:=src 4 | 5 | all: compile 6 | 7 | compile: clean 8 | @rebar compile 9 | 10 | clean: 11 | @rebar clean 12 | @find $(PWD)/. -name "erl_crash\.dump" | xargs rm -f 13 | 14 | tests: compile 15 | @rebar ct 16 | 17 | debug: clean 18 | @if test -f $(REBAR_CONFIG); then mv $(REBAR_CONFIG) $(REBAR_CONFIG).bak; fi; 19 | @echo {erl_opts, [{d, log_debug}]}. > $(REBAR_CONFIG) 20 | @rebar debug_info=true compile 21 | @rm $(REBAR_CONFIG) 22 | @if test -f $(REBAR_CONFIG).bak; then mv $(REBAR_CONFIG).bak $(REBAR_CONFIG); fi; 23 | 24 | dialyze: 25 | @dialyzer -n -I $(INCLUDE_DIR) --src $(SRC_DIR)/*.erl 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | *** 2 | *** 3 | *** 4 | *** 5 | 6 | Misultin development has been discontinued. 7 | 8 | There currently are three main webserver _libraries_ which basically do similar things: 9 | 10 | * [Mochiweb](https://github.com/mochi/mochiweb) 11 | * [Cowboy](https://github.com/extend/cowboy) 12 | * [Misultin](https://github.com/ostinelli/misultin) 13 | 14 | **Mochiweb** has been around the block for a while and it's proven solid in production, I can only recommend it for all basic webserver needs you might have. 15 | **Cowboy** has a very interesting approach since it allows to use multiple TCP and UDP protocols on top of a common acceptor pool. It is a very modern approach, is very actively maintained and many projects are starting to be built around it. 16 | 17 | Especially since the recent heavy development of Cowboy's HTTP server, I believe there is way too much duplication of efforts going on here. This is why Misultin's current 'state of the art' has been frozen in the latest tag, [v0.9](https://github.com/ostinelli/misultin/tree/misultin-0.9), to support all the companies currently using Misultin in their production environment. I'm here to provide help, if needed, in moving away from it. Thus, this server should be robust and stable enough to continue serving your needs for some time. 18 | 19 | Instead of letting this library stand here without this notice, and getting developers still use this project, I have preferred to explicitly state to gradually move away from it, so that efforts can be concentrated around one server library only. It's hard enough to let one 'child' like this one go, but I believe it's best for the whole Erlang community. 20 | 21 | Thank you to everyone that has been supporting Misultin in these years. Hopefully its **code usability**, which I still believe to be unmatched (well, I have developed it so how could I feel differently about this ^^_), will provide inspiration for some library interfaces. 22 | 23 | Best to you all, 24 | 25 | r. 26 | 27 | *** 28 | *** 29 | *** 30 | *** 31 | 32 | # MISULTIN 33 | 34 | Misultin is an HTTP(S) library which can easily be embedded in your own application. 35 | 36 | https://github.com/ostinelli/misultin 37 | 38 | `>-|-|-(°>` 39 | 40 | 41 | # Features 42 | 43 | * _Very_ fast 44 | * HTTP and HTTPS 45 | * Supports multiple **Websocket** protocols (draft-hixie-68, draft-hixie-76, draft-hybi-10 and draft-hybi-17) 46 | * **Cookies** 47 | * **Session** Variables 48 | * Allows for Chunked and Byte **streaming responses** 49 | * Allows for **streaming file upload** (via Chunked Encoding) 50 | * Can serves static files from a ```static``` directory (though in production you should consider a specific server such as [nginx](http://nginx.org/) to do so) 51 | * Has Many customization options (maximum allowed connections, maximum body size, ...) 52 | * Has a callback function for your **logging** needs 53 | * Supports **Unicode** 54 | * Can start multiple servers on a single node 55 | * Can be used with or without Parametrized Modules 56 | * Can traps the client closing a browser in Comet applications 57 | * It's very easy to use 58 | 59 | 60 | # Quick Start 61 | 62 | The typical 'Hello World" example code is: 63 | 64 | ```erlang 65 | 66 | -module(misultin_hello_world). 67 | -export([start/0, stop/0]). 68 | 69 | % start misultin http server 70 | start() -> 71 | misultin:start_link([{port, 8080}, {loop, fun(Req) -> handle_http(Req) end}]). 72 | 73 | % stop misultin 74 | stop() -> 75 | misultin:stop(). 76 | 77 | % callback on request received 78 | handle_http(Req) -> 79 | Req:ok("Hello World."). 80 | ``` 81 | 82 | Issuing the ```start/0``` command will start an HTTP server on port 8080, which will respond to every request with an "Hello World" text. 83 | 84 | # Examples 85 | 86 | Misultin comes [packed with examples](https://github.com/ostinelli/misultin/tree/master/examples/). 87 | 88 | #### Simple Examples 89 | 90 | * [Hello World](https://github.com/ostinelli/misultin/tree/master/examples/misultin_hello_world.erl) 91 | * [Querystring Variables](https://github.com/ostinelli/misultin/tree/master/examples/misultin_get_variable.erl) 92 | * [Querystring and POST Variables](https://github.com/ostinelli/misultin/tree/master/examples/misultin_echo.erl) 93 | * [REST](https://github.com/ostinelli/misultin/tree/master/examples/misultin_rest.erl) 94 | * [Set and Get Cookies](https://github.com/ostinelli/misultin/tree/master/examples/misultin_cookies_example.erl) 95 | * [Set and Get Session Variables](https://github.com/ostinelli/misultin/tree/master/examples/misultin_sessions_example.erl) 96 | * [Serve a Static file for download](https://github.com/ostinelli/misultin/tree/master/examples/misultin_file.erl) 97 | * [Serving files from a Static directory](https://github.com/ostinelli/misultin/tree/master/examples/misultin_static.erl) 98 | * [File Upload](https://github.com/ostinelli/misultin/tree/master/examples/misultin_file_upload.erl) 99 | * [HTTPS example](https://github.com/ostinelli/misultin/tree/master/examples/misultin_ssl.erl) 100 | * [Performing a simple redirection](https://github.com/ostinelli/misultin/tree/master/examples/misultin_redirect.erl) 101 | * [Serving compressed content](https://github.com/ostinelli/misultin/tree/master/examples/misultin_compress.erl) 102 | * [Logging Access](https://github.com/ostinelli/misultin/tree/master/examples/misultin_access_log.erl) 103 | 104 | #### Websockets 105 | 106 | * [Simple Websocket](https://github.com/ostinelli/misultin/tree/master/examples/misultin_websocket_example.erl) 107 | * [Simple Websocket on SSL](https://github.com/ostinelli/misultin/tree/master/examples/misultin_websocket_example_ssl.erl) 108 | * [Websocket exposing the close event](https://github.com/ostinelli/misultin/tree/master/examples/misultin_websocket_event_example.erl) 109 | * [Websocket exposing the close event, example 2](https://github.com/ostinelli/misultin/tree/master/examples/misultin_websocket_event_example2.erl) 110 | * [Access Session Variables from Websockets](https://github.com/ostinelli/misultin/tree/master/examples/misultin_websocket_sessions_example.erl) 111 | 112 | 113 | #### Comets 114 | 115 | * [Long Polling](https://github.com/ostinelli/misultin/tree/master/examples/misultin_comet_long_polling.erl) 116 | * [iFrame Technique](https://github.com/ostinelli/misultin/tree/master/examples/misultin_comet_iframe.erl) 117 | * [iFrame Technique using the close event](https://github.com/ostinelli/misultin/tree/master/examples/misultin_comet_iframe_event.erl) 118 | 119 | 120 | #### More Advanced 121 | 122 | * [Sending Chunked Content](https://github.com/ostinelli/misultin/tree/master/examples/misultin_chunked.erl) 123 | * [Sending Byte Streaming Content](https://github.com/ostinelli/misultin/tree/master/examples/misultin_stream.erl) 124 | * [Receiving endless streaming Upload](https://github.com/ostinelli/misultin/tree/master/examples/misultin_body_recv.erl) 125 | * [Unicode](https://github.com/ostinelli/misultin/tree/master/examples/misultin_unicode.erl) 126 | * [REST with UTF-8](https://github.com/ostinelli/misultin/tree/master/examples/misultin_rest_utf8.erl) 127 | * [Starting a nameless server](https://github.com/ostinelli/misultin/tree/master/examples/misultin_hello_world_nameless.erl) 128 | * [Starting multiple servers on a same node, with a custom name](https://github.com/ostinelli/misultin/tree/master/examples/misultin_multiple_servers_custom_name.erl) 129 | * [Using the HAProxy protocol](https://github.com/ostinelli/misultin/tree/master/examples/misultin_proxy_protocol.erl) 130 | 131 | # Module Exports 132 | 133 | The complete list of module exports can be found [here](https://github.com/ostinelli/misultin/tree/master/EXPORTS.md). 134 | 135 | # Parametrized modules 136 | 137 | Some developers hate them, some love them. Misultin allows you to choose if you want to use them or not. The same **Hello World** example shown here above, but without parametrized modules, looks like this: 138 | 139 | ```erlang 140 | 141 | -module(misultin_hello_world). 142 | -export([start/0, stop/0]). 143 | 144 | % start misultin http server 145 | start() -> 146 | misultin:start_link([{port, 8080}, {loop, fun(Req) -> handle_http(Req) end}]). 147 | 148 | % stop misultin 149 | stop() -> 150 | misultin:stop(). 151 | 152 | % callback on request received 153 | handle_http(Req) -> 154 | misultin_req:ok("Hello World.", Req). 155 | ``` 156 | 157 | # Dependencies 158 | 159 | You will need: 160 | 161 | * [Erlang](http://www.erlang.org/download.html) >= R14B01 162 | * [Rebar](https://github.com/basho/rebar) to compile 163 | 164 | # Under the hood 165 | Misultin is built using the OTP principles. When you start it using the ```misultin:start_link/1``` command, you are actually starting a supervisor which handles all of Misultin's servers and modules. 166 | 167 | Therefore, in real life applications you should always embed it in your own application. An easy example on how this can be done can be found in the Application Example [here](https://github.com/ostinelli/misultin/tree/master/examples/application). 168 | 169 | # SSL Notes 170 | 171 | If you are running misultin behind an SSL terminator such as stunnel or stud, and are using websockets, to make the websocket handshakes work, set in the starting options: 172 | 173 | ```erlang 174 | {ws_force_ssl, true} 175 | ``` 176 | 177 | If you are using stunnel to terminate, to make misultin expect a PROXY.. line as per the [proxy protocol](http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt) you can also set in the starting options: 178 | ```erlang 179 | {proxy_protocol, true} 180 | ``` 181 | 182 | Newer versions of stunnel support this with the "protocol = proxy" config option. 183 | -------------------------------------------------------------------------------- /examples/application/misultin_app_example.app: -------------------------------------------------------------------------------- 1 | {application, misultin_app_example, 2 | [ 3 | {description, "Misultin Application Example"}, 4 | {vsn, "0.1"}, 5 | {modules, [ 6 | misultin_app_example, 7 | misultin_app_example_sup, 8 | misultin_app_example_server, 9 | misultin, 10 | misultin_acceptor, 11 | misultin_acceptors_sup, 12 | misultin_http, 13 | misultin_req, 14 | misultin_server, 15 | misultin_socket, 16 | misultin_utility, 17 | misultin_websocket, 18 | misultin_ws 19 | ]}, 20 | {registered, []}, 21 | {mod, {misultin_app_example, []}}, 22 | {env, []}, 23 | {applications, [kernel, stdlib]} 24 | ]}. 25 | -------------------------------------------------------------------------------- /examples/application/misultin_app_example.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Application based on Misultin - MAIN APPLICATION 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli , Example taken from 7 | % 8 | % All rights reserved. 9 | % 10 | % BSD License 11 | % 12 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 13 | % that the following conditions are met: 14 | % 15 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 16 | % following disclaimer. 17 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 18 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 19 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 20 | % products derived from this software without specific prior written permission. 21 | % 22 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 23 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 24 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 25 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 26 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | % POSSIBILITY OF SUCH DAMAGE. 30 | % ========================================================================================================== 31 | -module(misultin_app_example). 32 | -behaviour(application). 33 | 34 | % application callbacks 35 | -export([start/2, stop/1]). 36 | 37 | % ============================ \/ API ====================================================================== 38 | 39 | % ============================ /\ API ====================================================================== 40 | 41 | 42 | % ============================ \/ APPLICATION CALLBACKS ==================================================== 43 | 44 | % ---------------------------------------------------------------------------------------------------------- 45 | % Function: -> {ok, Pid} | {ok, Pid, State} | {error, Reason} 46 | % Description: Starts the application 47 | % ---------------------------------------------------------------------------------------------------------- 48 | start(_Type, _StartArgs) -> 49 | % start main application supervisor 50 | Options = [ 51 | {port, 8080}, 52 | {loop, fun(Req) -> misultin_app_example_server:handle_http(Req) end} 53 | ], 54 | misultin_app_example_sup:start_link(Options). 55 | 56 | % ---------------------------------------------------------------------------------------------------------- 57 | % Function: stop(State) -> void() 58 | % Description: Stops the application 59 | % ---------------------------------------------------------------------------------------------------------- 60 | stop(_State) -> 61 | ok. 62 | 63 | % ============================ /\ APPLICATION CALLBACKS ==================================================== 64 | 65 | 66 | % ============================ \/ INTERNAL FUNCTIONS ======================================================= 67 | 68 | % ============================ /\ INTERNAL FUNCTIONS ======================================================= 69 | 70 | 71 | -------------------------------------------------------------------------------- /examples/application/misultin_app_example_server.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Application based on Misultin - MAIN APPLICATION GEN_SERVER 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_app_example_server). 31 | -behaviour(gen_server). 32 | 33 | % gen_server callbacks 34 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). 35 | 36 | % API 37 | -export([start_link/0, handle_http/1]). 38 | 39 | 40 | % ============================ \/ API ====================================================================== 41 | 42 | % Function: {ok,Pid} | ignore | {error, Error} 43 | % Description: Starts the server. 44 | start_link() -> 45 | gen_server:start_link(?MODULE, [], []). 46 | 47 | % ============================ /\ API ====================================================================== 48 | 49 | 50 | % ============================ \/ GEN_SERVER CALLBACKS ===================================================== 51 | 52 | % ---------------------------------------------------------------------------------------------------------- 53 | % Function: -> {ok, State} | {ok, State, Timeout} | ignore | {stop, Reason} 54 | % Description: Initiates the server. 55 | % ---------------------------------------------------------------------------------------------------------- 56 | init([]) -> 57 | {ok, {}}. 58 | 59 | % ---------------------------------------------------------------------------------------------------------- 60 | % Function: handle_call(Request, From, State) -> {reply, Reply, State} | {reply, Reply, State, Timeout} | 61 | % {noreply, State} | {noreply, State, Timeout} | 62 | % {stop, Reason, Reply, State} | {stop, Reason, State} 63 | % Description: Handling call messages. 64 | % ---------------------------------------------------------------------------------------------------------- 65 | 66 | % handle_call generic fallback 67 | handle_call(_Request, _From, State) -> 68 | {reply, undefined, State}. 69 | 70 | % ---------------------------------------------------------------------------------------------------------- 71 | % Function: handle_cast(Msg, State) -> {noreply, State} | {noreply, State, Timeout} | {stop, Reason, State} 72 | % Description: Handling cast messages. 73 | % ---------------------------------------------------------------------------------------------------------- 74 | 75 | % handle_cast generic fallback (ignore) 76 | handle_cast(_Msg, State) -> 77 | {noreply, State}. 78 | 79 | % ---------------------------------------------------------------------------------------------------------- 80 | % Function: handle_info(Info, State) -> {noreply, State} | {noreply, State, Timeout} | {stop, Reason, State} 81 | % Description: Handling all non call/cast messages. 82 | % ---------------------------------------------------------------------------------------------------------- 83 | 84 | % handle_info generic fallback (ignore) 85 | handle_info(_Info, State) -> 86 | {noreply, State}. 87 | 88 | % ---------------------------------------------------------------------------------------------------------- 89 | % Function: terminate(Reason, State) -> void() 90 | % Description: This function is called by a gen_server when it is about to terminate. When it returns, 91 | % the gen_server terminates with Reason. The return value is ignored. 92 | % ---------------------------------------------------------------------------------------------------------- 93 | terminate(_Reason, _State) -> 94 | terminated. 95 | 96 | % ---------------------------------------------------------------------------------------------------------- 97 | % Func: code_change(OldVsn, State, Extra) -> {ok, NewState} 98 | % Description: Convert process state when code is changed. 99 | % ---------------------------------------------------------------------------------------------------------- 100 | code_change(_OldVsn, State, _Extra) -> 101 | {ok, State}. 102 | 103 | % ============================ /\ GEN_SERVER CALLBACKS ===================================================== 104 | 105 | 106 | % ============================ \/ INTERNAL FUNCTIONS ======================================================= 107 | 108 | % ---------------------------- \/ misultin requests -------------------------------------------------------- 109 | 110 | handle_http(Req) -> 111 | % get params depending on method 112 | Method = Req:get(method), 113 | case Method of 114 | 'GET' -> 115 | Args = Req:parse_qs(); 116 | 'POST' -> 117 | Args = Req:parse_post() 118 | end, 119 | % build an XML with all parameters and values 120 | BuildXml = fun({Param, Value}, Acc) -> 121 | [lists:flatten(io_lib:format("~s~s", [Param, Value]))|Acc] 122 | end, 123 | Xml = lists:flatten(lists:reverse(lists:foldl(BuildXml, [], Args))), 124 | % output 125 | Req:ok([{"Content-Type", "text/xml"}], "~s~s", [Method, Xml]). 126 | 127 | % ---------------------------- /\ misultin requests -------------------------------------------------------- 128 | 129 | % ============================ /\ INTERNAL FUNCTIONS ======================================================= 130 | 131 | -------------------------------------------------------------------------------- /examples/application/misultin_app_example_sup.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Application based on Misultin - MAIN APPLICATION SUPERVISOR 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli , Example taken from 7 | % 8 | % All rights reserved. 9 | % 10 | % BSD License 11 | % 12 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 13 | % that the following conditions are met: 14 | % 15 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 16 | % following disclaimer. 17 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 18 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 19 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 20 | % products derived from this software without specific prior written permission. 21 | % 22 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 23 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 24 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 25 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 26 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | % POSSIBILITY OF SUCH DAMAGE. 30 | % ========================================================================================================== 31 | -module(misultin_app_example_sup). 32 | -behaviour(supervisor). 33 | 34 | % API 35 | -export([start_link/1]). 36 | 37 | % supervisor callbacks 38 | -export([init/1]). 39 | 40 | % ============================ \/ API ====================================================================== 41 | 42 | % ---------------------------------------------------------------------------------------------------------- 43 | % Function: start_link() -> {ok,Pid} | ignore | {error,Error} 44 | % Description: Starts the supervisor 45 | % ---------------------------------------------------------------------------------------------------------- 46 | start_link(Options) -> 47 | supervisor:start_link(?MODULE, [Options]). 48 | 49 | % ============================ /\ API ====================================================================== 50 | 51 | 52 | % ============================ \/ SUPERVISOR CALLBACKS ===================================================== 53 | 54 | % ---------------------------------------------------------------------------------------------------------- 55 | % Function: -> {ok, {SupFlags, [ChildSpec]}} | ignore | {error, Reason} 56 | % Description: Starts the supervisor 57 | % ---------------------------------------------------------------------------------------------------------- 58 | init([Options]) -> 59 | % misultin specs 60 | MisultinSpecs = {misultin, 61 | {misultin, start_link, [Options]}, 62 | permanent, infinity, supervisor, [misultin] 63 | }, 64 | % application gen server specs 65 | ServerSpecs = {misultin_app_example_server, 66 | {misultin_app_example_server, start_link, []}, 67 | permanent, 60000, worker, [misultin_app_example_server] 68 | }, 69 | % spawn 70 | {ok, {{one_for_all, 5, 30}, [MisultinSpecs, ServerSpecs]}}. 71 | 72 | % ============================ /\ SUPERVISOR CALLBACKS ===================================================== 73 | 74 | 75 | % ============================ \/ INTERNAL FUNCTIONS ======================================================= 76 | 77 | % ============================ /\ INTERNAL FUNCTIONS ======================================================= 78 | -------------------------------------------------------------------------------- /examples/misultin_access_log.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Access Log. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_access_log). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {access_log, fun(AccessInfo) -> access_log(AccessInfo) end}, {loop, fun(Req) -> handle_http(Req) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback on request received 42 | handle_http(Req) -> 43 | Req:ok("Hello World logged."). 44 | 45 | % callback on access log 46 | access_log({PeerAddr, DateTime, RequestLine, HttpCode, ContentLength}) -> 47 | io:format("~s - - [~s] \"~s\" ~p ~p~n", [PeerAddr, DateTime, RequestLine, HttpCode, ContentLength]). 48 | 49 | -------------------------------------------------------------------------------- /examples/misultin_body_recv.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: allow for body reading. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_body_recv). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {auto_recv_body, false}, {loop, fun(Req) -> handle_http(Req) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback on request received 42 | handle_http(Req) -> 43 | body_recv(Req, <<>>). 44 | 45 | body_recv(Req, Acc) -> 46 | case Req:body_recv() of 47 | {ok, Body} -> 48 | % received a full body, no streaming 49 | Req:ok([], "received full body: ~p", [Body]); 50 | {chunk, Chunk} -> 51 | % received a body chunk: append it, and loop to receive next ones 52 | body_recv(Req, <>); 53 | end_of_chunks -> 54 | % received all body chunks, output to body 55 | Req:ok([], "done receiving all body chunks: ~p", [Acc]); 56 | {error, Reason} -> 57 | Req:ok([], "error reading all body: ~p", [Reason]) 58 | end. 59 | -------------------------------------------------------------------------------- /examples/misultin_chunked.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Chunk. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_chunked). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback on request received 42 | handle_http(Req) -> 43 | % send headers 44 | Req:chunk(head, [{"Content-Type", "text/html"}]), 45 | % send chunk 46 | Req:chunk("Sending CHUNK 1
"), 47 | timer:sleep(2000), 48 | % send chunk 49 | Req:chunk("Sending CHUNK 2
"), 50 | timer:sleep(2000), 51 | % send chunk 52 | Req:chunk("Sending CHUNK 3
"), 53 | % close 54 | Req:chunk(done). 55 | 56 | 57 | -------------------------------------------------------------------------------- /examples/misultin_comet_iframe.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Comet - iFrame Method 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli , Example taken from 7 | % 8 | % All rights reserved. 9 | % 10 | % BSD License 11 | % 12 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 13 | % that the following conditions are met: 14 | % 15 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 16 | % following disclaimer. 17 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 18 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 19 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 20 | % products derived from this software without specific prior written permission. 21 | % 22 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 23 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 24 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 25 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 26 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | % POSSIBILITY OF SUCH DAMAGE. 30 | % ========================================================================================================== 31 | -module(misultin_comet_iframe). 32 | -export([start/1, stop/0]). 33 | 34 | % start misultin http server 35 | start(Port) -> 36 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req, Port) end}]). 37 | 38 | % stop misultin 39 | stop() -> 40 | misultin:stop(). 41 | 42 | handle_http(Req, Port) -> 43 | % dispatch to rest 44 | handle(Req:get(method), Req:resource([lowercase, urldecode]), Req, Port). 45 | 46 | % handle a GET on / 47 | handle('GET', [], Req, Port) -> 48 | % output 49 | Req:ok([{"Content-Type", "text/html"}], 50 | [" 51 | 52 | 53 | Comet demo 54 | 55 | 56 | 57 | 58 |
The server time will be shown here in 5 seconds.
59 | 117 | 118 | 119 | "]); 120 | 121 | % handle a GET on /comet 122 | handle('GET', ["comet"], Req, _Port) -> 123 | % set comet true, this will allow trapping client closing the connection 124 | Req:options([{comet, true}]), 125 | % send headers 126 | Req:stream(head, [{"Content-Type", "text/html"}, {"Cache-Control", "no-cache, must-revalidate"}, {"Expires", "Mon, 26 Jul 1997 05:00:00 GMT"}]), 127 | % start the page 128 | Req:stream(" 129 | 130 | 131 | Comet php backend 132 | 133 | 134 | 135 | 148 | "), 149 | % enter notification loop 150 | notify(Req); 151 | 152 | % handle the 404 page not found 153 | handle(_, _, Req, _Port) -> 154 | Req:ok([{"Content-Type", "text/plain"}], "Page not found."). 155 | 156 | % notification loop 157 | notify(Req) -> 158 | % send a message every 5 seconds 159 | timer:sleep(5000), 160 | % get server local time 161 | {_Date, {Hour, Minutes, Seconds}} = erlang:localtime(), 162 | % send 163 | Req:stream([" 164 | 167 | "]), 168 | % loop 169 | notify(Req). 170 | 171 | 172 | -------------------------------------------------------------------------------- /examples/misultin_comet_iframe_event.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Comet - iFrame Method 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli , Example taken from 7 | % 8 | % All rights reserved. 9 | % 10 | % BSD License 11 | % 12 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 13 | % that the following conditions are met: 14 | % 15 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 16 | % following disclaimer. 17 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 18 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 19 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 20 | % products derived from this software without specific prior written permission. 21 | % 22 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 23 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 24 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 25 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 26 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | % POSSIBILITY OF SUCH DAMAGE. 30 | % ========================================================================================================== 31 | -module(misultin_comet_iframe_event). 32 | -export([start/1, stop/0]). 33 | 34 | % start misultin http server 35 | start(Port) -> 36 | misultin:start_link([{port, Port}, {autoexit, false}, {loop, fun(Req) -> handle_http(Req, Port) end}]). 37 | 38 | % stop misultin 39 | stop() -> 40 | misultin:stop(). 41 | 42 | handle_http(Req, Port) -> 43 | % dispatch to rest 44 | handle(Req:get(method), Req:resource([lowercase, urldecode]), Req, Port). 45 | 46 | % handle a GET on / 47 | handle('GET', [], Req, Port) -> 48 | % output 49 | Req:ok([{"Content-Type", "text/html"}], 50 | [" 51 | 52 | 53 | Comet demo 54 | 55 | 56 | 57 | 58 |
The server time will be shown here in 5 seconds.
59 | 117 | 118 | 119 | "]); 120 | 121 | % handle a GET on /comet 122 | handle('GET', ["comet"], Req, _Port) -> 123 | % set comet true, this will allow trapping client closing the connection 124 | Req:options([{comet, true}]), 125 | % send headers 126 | Req:stream(head, [{"Content-Type", "text/html"}, {"Cache-Control", "no-cache, must-revalidate"}, {"Expires", "Mon, 26 Jul 1997 05:00:00 GMT"}]), 127 | % start the page 128 | Req:stream(" 129 | 130 | 131 | Comet php backend 132 | 133 | 134 | 135 | 148 | "), 149 | % enter notification loop 150 | notify(Req); 151 | 152 | % handle the 404 page not found 153 | handle(_, _, Req, _Port) -> 154 | Req:ok([{"Content-Type", "text/plain"}], "Page not found."). 155 | 156 | % notification loop 157 | notify(Req) -> 158 | % send 159 | receive 160 | closed -> 161 | % IMPORTANT: since we specified the {autoexit, false} option, we need to manually ensure that this process exits 162 | % [otherwise it will become a zombie] 163 | io:format("The client closed the connection, exiting process!~n"); 164 | _Ignore -> 165 | notify(Req) 166 | after 5000 -> 167 | % get server local time 168 | {_Date, {Hour, Minutes, Seconds}} = erlang:localtime(), 169 | % send 170 | Req:stream([" 171 | 174 | "]), 175 | % loop 176 | notify(Req) 177 | end. 178 | -------------------------------------------------------------------------------- /examples/misultin_comet_long_polling.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Comet - Long Polling Method 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_comet_long_polling). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req, Port) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | handle_http(Req, Port) -> 42 | % dispatch to rest 43 | handle(Req:get(method), Req:resource([lowercase, urldecode]), Req, Port). 44 | 45 | % handle a GET on / 46 | handle('GET', [], Req, Port) -> 47 | % output 48 | Req:ok([{"Content-Type", "text/html"}], 49 | [" 50 | 51 | 52 | 53 | 67 | 68 | 69 | Long Polling example, please wait 10 seconds for incoming data.

70 |
71 | 72 | 73 | "]); 74 | 75 | % handle a GET on /comet 76 | handle('GET', ["comet"], Req, _Port) -> 77 | % set comet true, this will allow trapping client closing the connection 78 | Req:options([{comet, true}]), 79 | % simulate a long polling with timer 80 | timer:sleep(10000), 81 | Req:ok([{"Content-Type", "text/plain"}], ["Message received from Long Polling, next message in 10 seconds."]); 82 | 83 | % handle the 404 page not found 84 | handle(_, _, Req, _Port) -> 85 | Req:ok([{"Content-Type", "text/plain"}], "Page not found."). 86 | -------------------------------------------------------------------------------- /examples/misultin_compress.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Hello World Compressed. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_compress). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {compress, true}, {loop, fun(Req) -> handle_http(Req) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback on request received 42 | handle_http(Req) -> 43 | Req:ok("Hello World Compressed."). 44 | -------------------------------------------------------------------------------- /examples/misultin_cookies_example.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Show how to set/retrieve cookies. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_cookies_example). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback on request received 42 | handle_http(Req) -> 43 | % get cookies 44 | Cookies = Req:get_cookies(), 45 | case Req:get_cookie_value("misultin_test_cookie", Cookies) of 46 | undefined -> 47 | % no cookies preexists, create one that will expire in 365 days 48 | Req:set_cookie("misultin_test_cookie", "value of the test cookie", [{max_age, 365*24*3600}]), 49 | Req:ok("A cookie has been set. Refresh the browser to see it."); 50 | CookieVal -> 51 | Req:delete_cookie("misultin_test_cookie"), 52 | Req:ok(["The set cookie value was set to \"", CookieVal,"\", and has now been removed. Refresh the browser to see this."]) 53 | end. 54 | -------------------------------------------------------------------------------- /examples/misultin_echo.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Echoes inputted GET variables into an XML. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_echo). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback on request received 42 | handle_http(Req) -> 43 | % get params depending on method 44 | Method = Req:get(method), 45 | Args = case Method of 46 | 'GET' -> 47 | Req:parse_qs(); 48 | 'POST' -> 49 | Req:parse_post() 50 | end, 51 | % build an XML with all parameters and values 52 | BuildXml = fun({Param, Value}, Acc) -> 53 | [lists:flatten(io_lib:format("~s~s", [Param, Value]))|Acc] 54 | end, 55 | Xml = lists:flatten(lists:reverse(lists:foldl(BuildXml, [], Args))), 56 | % output 57 | Req:ok([{"Content-Type", "text/xml"}], "~s~s", [Method, Xml]). 58 | 59 | -------------------------------------------------------------------------------- /examples/misultin_file.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Sends file as attachment. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_file). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback on request received 42 | handle_http(Req) -> 43 | Req:file(attachment, "1.png"). 44 | -------------------------------------------------------------------------------- /examples/misultin_file_upload.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: File Upload. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_file_upload). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback on request received 42 | handle_http(Req) -> 43 | % dispatch to rest 44 | handle(Req:get(method), Req:resource([lowercase, urldecode]), Req). 45 | 46 | % ---------------------------- \/ handle rest -------------------------------------------------------------- 47 | 48 | % handle a GET on / 49 | handle('GET', [], Req) -> 50 | DestPath = get_destination_path(), 51 | Req:ok([{"Content-Type", "text/html"}], [" 52 | 53 | Misultin File Upload 54 | 55 | 56 |

57 | Upload a File. This file will be saved in \"", DestPath, "\", please ensure that the appropriate writing permissions have been set. 58 |

59 |
60 | 61 | 62 |
63 | 64 | "]); 65 | 66 | % handle a POST on / -> file received 67 | handle('POST', [], Req) -> 68 | case Req:parse_post() of 69 | [{_Tag, Attributes, FileData}] -> 70 | % build destination file path 71 | DestPath = get_destination_path(), 72 | FileName = misultin_utility:get_key_value("filename", Attributes), 73 | DestFile = filename:join(DestPath, FileName), 74 | % save file 75 | case file:write_file(DestFile, FileData) of 76 | ok -> 77 | Req:ok(["File has been successfully saved to \"", DestFile, "\"."]); 78 | {error, _Reason} -> 79 | Req:respond(500) 80 | end; 81 | _ -> 82 | Req:respond(500) 83 | end; 84 | 85 | % handle the 404 page not found 86 | handle(_, _, Req) -> 87 | Req:ok([{"Content-Type", "text/plain"}], "Page not found."). 88 | 89 | % ---------------------------- /\ handle rest -------------------------------------------------------------- 90 | 91 | % gets the destination path 92 | get_destination_path() -> 93 | filename:dirname(code:which(?MODULE)). 94 | -------------------------------------------------------------------------------- /examples/misultin_get_variable.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Gets the GET variable 'value' and prints it out as XML if found. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_get_variable). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback on request received 42 | handle_http(Req) -> 43 | % get params 44 | Args = Req:parse_qs(), 45 | case Req:get_variable("value", Args) of 46 | undefined -> 47 | Req:ok([{"Content-Type", "text/xml"}], "no value specified"); 48 | Value -> 49 | Req:ok([{"Content-Type", "text/xml"}], "~s", [Value]) 50 | end. 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /examples/misultin_hello_world.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Hello World. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_hello_world). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback on request received 42 | handle_http(Req) -> 43 | Req:ok("Hello World."). 44 | -------------------------------------------------------------------------------- /examples/misultin_hello_world_nameless.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Hello World with a non-registered misultin server. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_hello_world_nameless). 31 | -export([start/1, stop/1]). 32 | 33 | % Description: Start misultin http server 34 | % For example: 35 | % {ok, Pid} = misultin_hello_world_nameless:start(8080). 36 | start(Port) -> 37 | misultin:start_link([{port, Port}, {name, false}, {loop, fun(Req) -> handle_http(Req) end}]). 38 | 39 | % Description: Stop misultin 40 | % Continuing the above example: 41 | % misultin_hello_world_nameless:stop(Pid). 42 | stop(ServerPid) -> 43 | misultin:stop(ServerPid). 44 | 45 | % callback on request received 46 | handle_http(Req) -> 47 | Req:ok("Hello World."). 48 | -------------------------------------------------------------------------------- /examples/misultin_multiple_servers_custom_name.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Hello World with two custom name registered misultin servers. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_multiple_servers_custom_name). 31 | -export([start/2, stop/0]). 32 | 33 | % Description: Start misultin http servers 34 | start(Port1, Port2) -> 35 | % start misultin1 36 | misultin:start_link([{port, Port1}, {name, misultin1}, {loop, fun(Req) -> handle_http(Req, misultin1) end}]), 37 | % start misultin2 38 | misultin:start_link([{port, Port2}, {name, misultin2}, {loop, fun(Req) -> handle_http(Req, misultin2) end}]). 39 | 40 | % Description: Stop misultin servers 41 | stop() -> 42 | misultin:stop(misultin1), 43 | misultin:stop(misultin2). 44 | 45 | % callback on request received 46 | handle_http(Req, RegName) -> 47 | Req:ok(["Hello World from ", atom_to_list(RegName)]). 48 | -------------------------------------------------------------------------------- /examples/misultin_proxy_protocol.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Proxy protocol 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Richard Jones 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | % 31 | % This test assumes you are accessing misultin via something that supports the 32 | % haproxy proxy protocol, such as recent versions of stunnel. 33 | % see: http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt 34 | -module(misultin_proxy_protocol). 35 | -export([start/1, stop/0]). 36 | 37 | % start misultin http server 38 | start(Port) -> 39 | misultin:start_link([{proxy_protocol, true}, {port, Port}, {loop, fun(Req) -> handle_http(Req) end}]). 40 | 41 | % stop misultin 42 | stop() -> 43 | misultin:stop(). 44 | 45 | % callback on request received 46 | handle_http(Req) -> 47 | {A,B,C,D} = Req:get(peer_addr), 48 | Msg = io_lib:format("Hello ~B.~B.~B.~B", [A,B,C,D]), 49 | Req:ok(Msg). 50 | -------------------------------------------------------------------------------- /examples/misultin_redirect.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Redirect. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_redirect). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback function called on incoming http request 42 | handle_http(Req) -> 43 | % dispatch to rest 44 | handle(Req:get(method), Req:resource([lowercase, urldecode]), Req). 45 | 46 | % handle a get on / 47 | handle('GET', [], Req) -> 48 | Req:redirect("/redirected"); 49 | 50 | % handle a get on /redirected 51 | handle('GET', ["redirected"], Req) -> 52 | Req:ok("You got temporarily redirected here."); 53 | 54 | % handle the 404 page not found 55 | handle(_, _, Req) -> 56 | Req:ok([{"Content-Type", "text/plain"}], "Page not found."). 57 | -------------------------------------------------------------------------------- /examples/misultin_rest.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: RESTful support. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_rest). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback function called on incoming http request 42 | handle_http(Req) -> 43 | % dispatch to rest 44 | handle(Req:get(method), Req:resource([lowercase, urldecode]), Req). 45 | 46 | % ---------------------------- \/ handle rest -------------------------------------------------------------- 47 | 48 | % handle a GET on / 49 | handle('GET', [], Req) -> 50 | Req:ok([{"Content-Type", "text/plain"}], "Main home page."); 51 | 52 | % handle a GET on /users 53 | handle('GET', ["users"], Req) -> 54 | Req:ok([{"Content-Type", "text/plain"}], "Main users root."); 55 | 56 | % handle a GET on /users/{username} 57 | handle('GET', ["users", UserName], Req) -> 58 | Req:ok([{"Content-Type", "text/plain"}], "This is ~s's page.", [UserName]); 59 | 60 | % handle a GET on /users/{username}/messages 61 | handle('GET', ["users", UserName, "messages"], Req) -> 62 | Req:ok([{"Content-Type", "text/plain"}], "This is ~s's messages page.", [UserName]); 63 | 64 | % handle the 404 page not found 65 | handle(_, _, Req) -> 66 | Req:ok([{"Content-Type", "text/plain"}], "Page not found."). 67 | 68 | % ---------------------------- /\ handle rest -------------------------------------------------------------- 69 | -------------------------------------------------------------------------------- /examples/misultin_rest_utf8.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: RESTful support. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | 31 | % point your browser to http://localhost:8080/users/こんにちは and you should see: 32 | % This is こんにちは's page. 33 | 34 | -module(misultin_rest_utf8). 35 | -export([start/1, stop/0]). 36 | 37 | % start misultin http server 38 | start(Port) -> 39 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}]). 40 | 41 | % stop misultin 42 | stop() -> 43 | misultin:stop(). 44 | 45 | % callback function called on incoming http request 46 | handle_http(Req) -> 47 | % dispatch to rest 48 | handle(Req:get(method), Req:resource([lowercase, urldecode]), Req). 49 | 50 | % ---------------------------- \/ handle rest -------------------------------------------------------------- 51 | 52 | % handle a GET on / 53 | handle('GET', [], Req) -> 54 | template(Req, "Main home page."); 55 | 56 | % handle a GET on /users 57 | handle('GET', ["users"], Req) -> 58 | template(Req, "Main users root."); 59 | 60 | % handle a GET on /users/{username} 61 | handle('GET', ["users", UserName], Req) -> 62 | template(Req, ["This is ", UserName, "'s page."]); 63 | 64 | % handle a GET on /users/{username}/messages 65 | handle('GET', ["users", UserName, "messages"], Req) -> 66 | template(Req, ["This is ", UserName, "'s messages page."]); 67 | 68 | % handle the 404 page not found 69 | handle(_, _, Req) -> 70 | template(Req, "Page not found."). 71 | 72 | % ---------------------------- /\ handle rest -------------------------------------------------------------- 73 | 74 | 75 | 76 | % ---------------------------- \/ template ----------------------------------------------------------------- 77 | 78 | % returns the template 79 | template(Req, Content) -> 80 | Req:ok([{"Content-Type", "text/html"}], [" 81 | 82 | ", Content, ""]). 83 | 84 | % ---------------------------- /\ template ----------------------------------------------------------------- 85 | -------------------------------------------------------------------------------- /examples/misultin_sessions_example.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Sessions Example. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_sessions_example). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {sessions_expire, 60}, {loop, fun(Req) -> handle_http(Req) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback on request received 42 | handle_http(Req) -> 43 | % get session info 44 | {SessionId, SessionState} = Req:session(), 45 | % check state 46 | Count = case SessionState of 47 | [] -> 48 | % no state set previously, init counter 49 | 1; 50 | N -> 51 | % increment counter 52 | N + 1 53 | end, 54 | % save counter as session's state. a more complex state can easily be saved here, such as proplist() 55 | Req:save_session_state(SessionId, Count), 56 | % reply 57 | Req:ok([], "Session Id is: ~p, counter is: ~p", [SessionId, Count]). 58 | -------------------------------------------------------------------------------- /examples/misultin_ssl.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Hello World SSL. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_ssl). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}, 36 | {ssl, [ 37 | {certfile, filename:join([filename:dirname(code:which(?MODULE)), "..", "priv", "test_certificate.pem"])}, 38 | {keyfile, filename:join([filename:dirname(code:which(?MODULE)), "..", "priv", "test_privkey.pem"])}, 39 | {password, "misultin"} 40 | ]} 41 | ]). 42 | 43 | % stop misultin 44 | stop() -> 45 | misultin:stop(). 46 | 47 | % callback on request received 48 | handle_http(Req) -> 49 | % output 50 | Req:ok("Hello World SSL."). 51 | 52 | -------------------------------------------------------------------------------- /examples/misultin_static.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: static directory support. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_static). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server, with a static option 34 | % all files under the specified directory /var/www/static will be automatically served. 35 | start(Port) -> 36 | misultin:start_link([ 37 | {port, Port}, 38 | {static, "/var/www/static"}, 39 | {loop, fun(Req) -> handle_http(Req) end} 40 | ]). 41 | 42 | % stop misultin 43 | stop() -> 44 | misultin:stop(). 45 | 46 | % callback function called on incoming http request, if not a static request 47 | handle_http(Req) -> 48 | Req:ok("Not a static file request."). 49 | -------------------------------------------------------------------------------- /examples/misultin_stream.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Stream data gradually. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_stream). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback on request received 42 | handle_http(Req) -> 43 | % send headers 44 | Req:stream(head, [{"Content-Type", "text/plain"}]), 45 | % send stream 46 | Req:stream("1"), 47 | timer:sleep(2000), 48 | % send stream 49 | Req:stream("2"), 50 | timer:sleep(2000), 51 | % send stream 52 | Req:stream("3"), 53 | % close socket 54 | Req:stream(close). 55 | 56 | -------------------------------------------------------------------------------- /examples/misultin_unicode.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Unicode strings. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_unicode). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback function called on incoming http request 42 | handle_http(Req) -> 43 | % dispatch to rest 44 | handle(Req:get(method), Req:resource([lowercase, urldecode]), Req). 45 | 46 | % handle a GET on / 47 | handle('GET', [], Req) -> 48 | Req:ok([{"Content-Type", "text/html"}], [" 49 | 50 | 51 | 52 | 53 |
54 | 55 | 56 |
57 | 58 | "]); 59 | 60 | % handle POST submit on / 61 | handle('POST', [], Req) -> 62 | Args = Req:parse_post(unicode), 63 | case Req:get_variable("data", Args) of 64 | undefined -> 65 | Req:ok("No data value submitted."); 66 | Value -> 67 | % we have specified an unicode encoding, so ensure we convert the list back to binary before sending it down the socket 68 | ValueBin = unicode:characters_to_binary(Value), 69 | Req:ok([{"Content-Type", "text/html"}], [" 70 | 71 | 72 | 73 | 74 |

Unicode string is: \"", ValueBin, "\", and its length is ", integer_to_list(length(Value)), " 75 | 76 | "]) 77 | end; 78 | 79 | % handle the 404 page not found 80 | handle(_, _, Req) -> 81 | Req:ok("Page not found."). 82 | 83 | -------------------------------------------------------------------------------- /examples/misultin_websocket_event_example.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Shows misultin Websocket With an event support. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_websocket_event_example). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([ 36 | {port, Port}, {loop, fun(Req) -> handle_http(Req, Port) end}, 37 | {ws_loop, fun(Ws) -> handle_websocket(Ws) end}, {ws_autoexit, false} 38 | ]). 39 | 40 | % stop misultin 41 | stop() -> 42 | misultin:stop(). 43 | 44 | % callback on request received 45 | handle_http(Req, Port) -> 46 | % output 47 | Req:ok([{"Content-Type", "text/html"}], 48 | [" 49 | 50 | 51 | 86 | 87 | 88 |

89 | 90 | "]). 91 | 92 | % callback on received websockets data 93 | handle_websocket(Ws) -> 94 | receive 95 | {browser, Data} -> 96 | Ws:send(["received '", Data, "'"]), 97 | handle_websocket(Ws); 98 | closed -> 99 | % IMPORTANT: since we specified the {ws_autoexit, false} option, we need to manually ensure that this process exits 100 | % [otherwise it will become a zombie] 101 | io:format("The WebSocket was CLOSED!~n"); 102 | _Ignore -> 103 | handle_websocket(Ws) 104 | after 5000 -> 105 | Ws:send("pushing!"), 106 | handle_websocket(Ws) 107 | end. 108 | -------------------------------------------------------------------------------- /examples/misultin_websocket_event_example2.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Shows misultin Websocket With an event support. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2010, Leandro Silva 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_websocket_event_example2). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([ 36 | {port, Port}, {loop, fun(Req) -> handle_http(Req, Port) end}, 37 | {ws_loop, fun(Ws) -> handle_websocket(Ws) end}, {ws_autoexit, false} 38 | ]). 39 | 40 | % stop misultin 41 | stop() -> 42 | misultin:stop(). 43 | 44 | % callback on request received 45 | handle_http(Req, Port) -> 46 | % output 47 | Req:ok([{"Content-Type", "text/html"}], 48 | [" 49 | 50 | 51 | 93 | 94 | 95 | 96 | 97 | 98 | 99 |
100 | 101 | "]). 102 | 103 | % callback on received websockets data 104 | handle_websocket(Ws) -> 105 | receive 106 | {browser, Data} -> 107 | Ws:send(["received '", Data, "'"]), 108 | handle_websocket(Ws); 109 | closed -> 110 | % IMPORTANT: since we specified the {ws_autoexit, false} option, we need to manually ensure that this process exits 111 | % [otherwise it will become a zombie] 112 | io:format("The WebSocket was CLOSED!~n"), 113 | closed; 114 | _Ignore -> 115 | handle_websocket(Ws) 116 | after 5000 -> 117 | Ws:send("pushing!"), 118 | handle_websocket(Ws) 119 | end. 120 | -------------------------------------------------------------------------------- /examples/misultin_websocket_example.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Shows misultin Websocket support. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_websocket_example). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req, Port) end}, {ws_loop, fun(Ws) -> handle_websocket(Ws) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback on request received 42 | handle_http(Req, Port) -> 43 | % output 44 | Req:ok([{"Content-Type", "text/html"}], 45 | [" 46 | 47 | 48 | 83 | 84 | 85 |
86 | 87 | "]). 88 | 89 | % callback on received websockets data 90 | handle_websocket(Ws) -> 91 | receive 92 | {browser, Data} -> 93 | Ws:send(["received '", Data, "'"]), 94 | handle_websocket(Ws); 95 | _Ignore -> 96 | handle_websocket(Ws) 97 | after 5000 -> 98 | Ws:send("pushing!"), 99 | handle_websocket(Ws) 100 | end. 101 | -------------------------------------------------------------------------------- /examples/misultin_websocket_example_ssl.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Shows misultin SSL Websocket support. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_websocket_example_ssl). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req, Port) end}, {ws_loop, fun(Ws) -> handle_websocket(Ws) end}, 36 | {ssl, [ 37 | {certfile, "../priv/test_certificate.pem"}, 38 | {keyfile, "../priv/test_privkey.pem"}, 39 | {password, "misultin"} 40 | ]}]). 41 | 42 | % stop misultin 43 | stop() -> 44 | misultin:stop(). 45 | 46 | % callback on request received 47 | handle_http(Req, Port) -> 48 | % output 49 | Req:ok([{"Content-Type", "text/html"}], 50 | [" 51 | 52 | 53 | 88 | 89 | 90 |
91 | 92 | "]). 93 | 94 | % callback on received websockets data 95 | handle_websocket(Ws) -> 96 | receive 97 | {browser, Data} -> 98 | Ws:send(["received '", Data, "'"]), 99 | handle_websocket(Ws); 100 | _Ignore -> 101 | handle_websocket(Ws) 102 | after 5000 -> 103 | Ws:send("pushing!"), 104 | handle_websocket(Ws) 105 | end. 106 | -------------------------------------------------------------------------------- /examples/misultin_websocket_sessions_example.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Websocket Sessions Example. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_websocket_sessions_example). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req, Port) end}, {ws_loop, fun(Ws) -> handle_websocket(Ws) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback on request received 42 | handle_http(Req, Port) -> 43 | % get session info 44 | {SessionId, _SessionState} = Req:session(), 45 | % save user's peer_addr and a resetted counter as session's state. a more complex state can easily be saved here, such as proplist() 46 | Req:save_session_state(SessionId, {Req:get(peer_addr), 1}), 47 | % output 48 | Req:ok([{"Content-Type", "text/html"}], 49 | [" 50 | 51 | 52 | 87 | 88 | 89 |
90 | 91 | "]). 92 | 93 | % callback on received websockets data 94 | handle_websocket(Ws) -> 95 | % get session info 96 | {SessionId, {UserPeerAddr, Count}} = Ws:session(), 97 | receive 98 | {browser, Data} -> 99 | Ws:send(["received '", Data, "'"]), 100 | handle_websocket(Ws); 101 | _Ignore -> 102 | handle_websocket(Ws) 103 | after 5000 -> 104 | % increase pushed counter and save new sessin state 105 | Ws:save_session_state(SessionId, {UserPeerAddr, Count + 1}), 106 | % build push message 107 | Pushmessage = lists:flatten(io_lib:format("pushed ~p time(s) for user with session IP: ~p", [Count, UserPeerAddr])), 108 | Ws:send(Pushmessage), 109 | handle_websocket(Ws) 110 | end. 111 | -------------------------------------------------------------------------------- /include/misultin.hrl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Include file 3 | % 4 | % Copyright (C) 2011, Sean Hinde, Roberto Ostinelli 5 | % All rights reserved. 6 | % 7 | % BSD License 8 | % 9 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 10 | % that the following conditions are met: 11 | % 12 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 13 | % following disclaimer. 14 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 15 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 16 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 17 | % products derived from this software without specific prior written permission. 18 | % 19 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 20 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 21 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 23 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 25 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | % POSSIBILITY OF SUCH DAMAGE. 27 | % ========================================================================================================== 28 | 29 | 30 | % ============================ \/ LOG ====================================================================== 31 | -ifdef(log_debug). 32 | -define(LOG_DEBUG(Str, Args), erlang:apply(error_logger, info_msg, [lists:concat(["[DEBUG] pid: ", pid_to_list(self()), "~n module: ", ?MODULE, "~n line: ", ?LINE, "~n", Str, "~n"]), Args])). 33 | -define(LOG_INFO(Str, Args), erlang:apply(error_logger, info_msg, [lists:concat([" module: ", ?MODULE, "~n line: ", ?LINE, "~n", Str, "~n"]), Args])). 34 | -define(LOG_WARNING(Str, Args), erlang:apply(error_logger, warning_msg, [lists:concat([" module: ", ?MODULE, "~n line: ", ?LINE, "~n", Str, "~n"]), Args])). 35 | -define(LOG_ERROR(Str, Args), erlang:apply(error_logger, error_msg, [lists:concat([" module: ", ?MODULE, "~n line: ", ?LINE, "~n", Str, "~n"]), Args])). 36 | -else. 37 | -ifdef(log_info). 38 | -define(LOG_DEBUG(Str, Args), ok). 39 | -define(LOG_INFO(Str, Args), erlang:apply(error_logger, info_msg, [lists:concat([" module: ", ?MODULE, "~n line: ", ?LINE, "~n", Str, "~n"]), Args])). 40 | -define(LOG_WARNING(Str, Args), erlang:apply(error_logger, warning_msg, [lists:concat([" module: ", ?MODULE, "~n line: ", ?LINE, "~n", Str, "~n"]), Args])). 41 | -define(LOG_ERROR(Str, Args), erlang:apply(error_logger, error_msg, [lists:concat([" module: ", ?MODULE, "~n line: ", ?LINE, "~n", Str, "~n"]), Args])). 42 | -else. 43 | -ifdef(log_error). 44 | -define(LOG_DEBUG(Str, Args), ok). 45 | -define(LOG_INFO(Str, Args), ok). 46 | -define(LOG_WARNING(Str, Args), ok). 47 | -define(LOG_ERROR(Str, Args), erlang:apply(error_logger, error_msg, [lists:concat([" module: ", ?MODULE, "~n line: ", ?LINE, "~n", Str, "~n"]), Args])). 48 | -else. 49 | % default to warning level 50 | -define(LOG_DEBUG(Str, Args), ok). 51 | -define(LOG_INFO(Str, Args), ok). 52 | -define(LOG_WARNING(Str, Args), erlang:apply(error_logger, warning_msg, [lists:concat([" module: ", ?MODULE, "~n line: ", ?LINE, "~n", Str, "~n"]), Args])). 53 | -define(LOG_ERROR(Str, Args), erlang:apply(error_logger, error_msg, [lists:concat([" module: ", ?MODULE, "~n line: ", ?LINE, "~n", Str, "~n"]), Args])). 54 | -endif. 55 | -endif. 56 | -endif. 57 | % ============================ /\ LOG ====================================================================== 58 | 59 | 60 | % ============================ \/ TYPES ==================================================================== 61 | 62 | % ---------------------------- \/ MISULTIN ----------------------------------------------------------------- 63 | -type misultin_option_tcp() :: 64 | {ip, string() | tuple()} | 65 | {port, non_neg_integer()} | 66 | {backlog, non_neg_integer()} | 67 | {acceptors_poolsize, non_neg_integer()} | 68 | {recv_timeout, non_neg_integer()} | 69 | {max_connections, non_neg_integer()} | 70 | {ssl, gen_proplist()} | 71 | {recbuf, non_neg_integer()}. 72 | -type misultin_option_server() :: 73 | {name, atom()} | 74 | {post_max_size, non_neg_integer()} | 75 | {get_url_max_size, non_neg_integer()} | 76 | {compress, boolean()} | 77 | {loop, function()} | 78 | {autoexit, boolean()} | 79 | {ws_loop, undefined | function()} | 80 | {ws_autoexit, boolean()} | 81 | {ws_versions, [websocket_version()]} | 82 | {sessions_expire, non_neg_integer()} | 83 | {access_log, undefined | function()} | 84 | {auto_recv_body, boolean()}. 85 | -type misultin_option() :: misultin_option_tcp() | misultin_option_server(). 86 | % ---------------------------- /\ MISULTIN ----------------------------------------------------------------- 87 | 88 | % ---------------------------- \/ HTTP --------------------------------------------------------------------- 89 | -type http_version() :: {Maj::non_neg_integer(), Min::non_neg_integer()}. 90 | 91 | -type http_header() :: 'Cache-Control' | 'Connection' | 'Date' | 'Pragma' | 'Transfer-Encoding' | 'Upgrade' | 92 | 'Via' | 'Accept' | 'Accept-Charset' | 'Accept-Encoding' | 'Accept-Language' | 'Authorization' | 'From' | 93 | 'Host' | 'If-Modified-Since' | 'If-Match' | 'If-None-Match' | 'If-Range' | 'If-Unmodified-Since' | 94 | 'Max-Forwards' | 'Proxy-Authorization' | 'Range' | 'Referer' | 'User-Agent' | 'Age' | 'Location' | 95 | 'Proxy-Authenticate' | 'Public' | 'Retry-After' | 'Server' | 'Vary' | 'Warning' | 'Www-Authenticate' | 96 | 'Allow' | 'Content-Base' | 'Content-Encoding' | 'Content-Language' | 'Content-Length' | 'Content-Location' | 97 | 'Content-Md5' | 'Content-Range' | 'Content-Type' | 'Etag' | 'Expires' | 'Last-Modified' | 'Accept-Ranges' | 98 | 'Set-Cookie' | 'Set-Cookie2' | 'X-Forwarded-For' | 'Cookie' | 'Keep-Alive' | 'Proxy-Connection' | 99 | list() | binary(). 100 | -type http_headers() :: list({http_header(), list() | binary() | integer() | atom()}). 101 | 102 | -type http_method() :: 'GET' | 'POST' | 'HEAD' | 'PUT' | 'DELETE' | 'TRACE' | 'CONNECT'. 103 | 104 | -type http_connection() :: close | keep_alive. 105 | 106 | -type http_uri() :: 107 | {abs_path, Path::list()} | 108 | {absoluteURI, Path::list()} | 109 | {absoluteURI, http | https | atom(), Host::binary(), Port::non_neg_integer(), Path::list()} | 110 | {scheme, Scheme::list(), RequestString::list()}. 111 | 112 | -type http_supported_encoding() :: deflate | gzip. 113 | % ---------------------------- /\ HTTP --------------------------------------------------------------------- 114 | 115 | % ---------------------------- \/ OTHER -------------------------------------------------------------------- 116 | -type websocket_version() :: 117 | 'draft-hybi-10' | 118 | 'draft-hybi-17' | 119 | 'draft-hixie-68' | 120 | 'draft-hixie-76'. 121 | 122 | -type socketmode() :: http | ssl. 123 | -type socket() :: inet:socket() | term(). % unfortunately ssl does not export the socket equivalent, we could use {sslsocket, term(), term()} but this is relying on internals. 124 | 125 | -type cookies_options() :: [ 126 | {max_age, undefined | integer()} | 127 | {local_time, undefined | date_tuple()} | 128 | {secure, true | term()} | 129 | {domain, undefined | string()} | 130 | {path, undefined | string()} | 131 | {http_only, true | term()} 132 | ]. 133 | 134 | -type gen_proplist() :: [{Tag::atom()|list()|binary(), Value::term()}]. 135 | -type gen_proplist_options() :: [{Tag::atom()|list()|binary(), Value::term()} | atom()]. 136 | 137 | -type date_tuple() :: {{non_neg_integer(), 1..12, 1..31}, {0..24, 0..60, 0..60}}. 138 | % ---------------------------- /\ OTHER -------------------------------------------------------------------- 139 | 140 | % ============================ /\ TYPES ==================================================================== 141 | 142 | 143 | % ============================ \/ RECORDS ================================================================== 144 | 145 | % misultin server Options 146 | -record(custom_opts, { 147 | post_max_size = undefined :: undefined | non_neg_integer(), % maximum post size in bytes, defaults to 4 MB 148 | get_url_max_size = undefined :: undefined | non_neg_integer(), % maximum GET url size in bytes, defaults to 2000 149 | compress = false :: boolean(), % send compressed output if supported by browser 150 | loop = undefined :: undefined | function(), % the fun handling requests 151 | autoexit = true :: boolean(), % shoud the http process be automatically killed? 152 | ws_loop = undefined :: undefined | function(), % the loop handling websockets 153 | ws_autoexit = true :: boolean(), % shoud the ws process be automatically killed? 154 | ws_versions = undefined :: [websocket_version()], % list of supported ws versions 155 | access_log = undefined :: undefined | function(), % access log function 156 | ws_force_ssl = false :: boolean(), % if we are deployed behind stunnel, or other ssl proxy 157 | proxy_protocol = false :: boolean(), % upstream proxy is sending us http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt 158 | auto_recv_body = true :: boolean(), % if set to false, body has to be manually read in loop 159 | static = false :: boolean() % if set to a directory, then all files in it will be automatically sent to the browser. 160 | }). 161 | 162 | % Request 163 | -record(req, { 164 | socket = undefined :: undefined | socket(), % the socket handling the request 165 | socket_mode = http :: socketmode(), 166 | peer_addr = undefined :: undefined | inet:ip_address(), % peer IP | undefined: this is the peer address of the connection, may differ from the client 167 | peer_port = undefined :: undefined | non_neg_integer(), % peer port | undefined: this is the peer port of the connection, may differ from the client 168 | peer_cert = undefined :: undefined | term(), % the DER encoded peer certificate that can be decoded with public_key:pkix_decode_cert/2 169 | connection = close :: keep_alive | close, 170 | content_length = undefined :: undefined | non_neg_integer(), 171 | vsn = {1, 1} :: http_version(), % defaults to HTTP/1.1 172 | method = undefined :: undefined | http_method(), 173 | uri = undefined :: undefined | http_uri(), 174 | args = "" :: list(), % Part of URI after ? 175 | headers = [] :: http_headers(), 176 | body = <<>> :: binary(), 177 | ws_force_ssl = false :: boolean() % if we are deployed behind stunnel, or other ssl proxy 178 | }). 179 | 180 | % Websocket Request 181 | -record(ws, { 182 | socket = undefined :: undefined | socket(), % the socket handling the request 183 | socket_mode = http :: socketmode(), 184 | ws_autoexit = true :: boolean(), % shoud the ws process be automatically killed? 185 | peer_addr = undefined :: undefined | inet:ip_address(), % peer IP | undefined: this is the peer address of the connection, may differ from the client 186 | peer_port = undefined :: undefined | non_neg_integer(), % peer port | undefined: this is the peer port of the connection, may differ from the client 187 | peer_cert = undefined :: undefined | term(), % the DER encoded peer certificate that can be decoded with public_key:pkix_decode_cert/2 188 | vsn = undefined :: undefined | websocket_version(), 189 | origin = undefined :: undefined | list(), % the originator 190 | host = undefined :: undefined | list(), % the host 191 | path = undefined :: undefined | list(), % the websocket GET request path 192 | headers = [] :: http_headers() 193 | }). 194 | 195 | % ============================ /\ RECORDS ================================================================== 196 | -------------------------------------------------------------------------------- /make.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM ========================================================================================================== 3 | REM MISULTIN - compile 4 | REM 5 | REM >-|-|-<°> 6 | REM 7 | REM Copyright (C) 2009, Roberto Ostinelli 8 | REM All rights reserved. 9 | REM 10 | REM BSD License 11 | REM 12 | REM Redistribution and use in source and binary forms, with or without modification, are permitted provided 13 | REM that the following conditions are met: 14 | REM 15 | REM * Redistributions of source code must retain the above copyright notice, this list of conditions and the 16 | REM following disclaimer. 17 | REM * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 18 | REM the following disclaimer in the documentation and/or other materials provided with the distribution. 19 | REM * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 20 | REM products derived from this software without specific prior written permission. 21 | REM 22 | REM THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 23 | REM WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 24 | REM PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 25 | REM ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 26 | REM TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | REM HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | REM NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | REM POSSIBILITY OF SUCH DAMAGE. 30 | REM ========================================================================================================== 31 | 32 | :BEGIN 33 | IF "%1"=="debug" GOTO SETDEBUG 34 | IF "%1"=="example" GOTO EXAMPLES 35 | IF "%1"=="clean" GOTO CLEAN 36 | GOTO COMPILE 37 | 38 | :SETDEBUG 39 | SET command=-D log_debug 40 | GOTO COMPILE 41 | 42 | :EXAMPLES 43 | mkdir ebin 44 | FOR %%f in (examples\*.erl) DO erlc -W %command% -pa ebin -I include -o ebin "%%f" 45 | 46 | :COMPILE 47 | mkdir ebin 48 | FOR %%f in (src\*.erl) DO erlc -W %command% -pa ebin -I include -o ebin "%%f" 49 | copy src\misultin.app.src ebin\misultin.app /Y 50 | GOTO END 51 | 52 | :CLEAN 53 | FOR %%f in (ebin\*) DO del "%%f" 54 | 55 | :END 56 | -------------------------------------------------------------------------------- /priv/README.txt: -------------------------------------------------------------------------------- 1 | ========================================================================================================== 2 | DISCLAIMER 3 | ========================================================================================================== 4 | 5 | Please note that the included certificate 'test_certificate.pem' and private key 'test_privkey.pem' are 6 | publicly available via the Misultin repositories, and should NOT be used for any secure application. These 7 | have been provided here for your testing comfort only. 8 | 9 | You may consider getting your copy of OpenSSL and generate your own certificate 10 | and private key by issuing a command similar to: 11 | 12 | openssl req -new -x509 -newkey rsa:1024 -days 365 -keyout test_privkey.pem -out test_certificate.pem 13 | -------------------------------------------------------------------------------- /priv/test_certificate.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDhzCCAvCgAwIBAgIJAPFFl046vv7vMA0GCSqGSIb3DQEBBQUAMIGKMQswCQYD 3 | VQQGEwJJVDENMAsGA1UECBMEQ29tbzENMAsGA1UEBxMEQ29tbzERMA8GA1UEChMI 4 | TWlzdWx0aW4xETAPBgNVBAsTCE1pc3VsdGluMREwDwYDVQQDEwhNaXN1bHRpbjEk 5 | MCIGCSqGSIb3DQEJARYVcm9iZXJ0b0Bvc3RpbmVsbGkubmV0MB4XDTEwMDQyMDE3 6 | NDczOFoXDTIwMDQxNzE3NDczOFowgYoxCzAJBgNVBAYTAklUMQ0wCwYDVQQIEwRD 7 | b21vMQ0wCwYDVQQHEwRDb21vMREwDwYDVQQKEwhNaXN1bHRpbjERMA8GA1UECxMI 8 | TWlzdWx0aW4xETAPBgNVBAMTCE1pc3VsdGluMSQwIgYJKoZIhvcNAQkBFhVyb2Jl 9 | cnRvQG9zdGluZWxsaS5uZXQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM8A 10 | BT4OzNCcgrfVnzmEp8VdyR+O0ubsSBunX8J/BTKbWgZVrrGrY9fO8AkVmD1VFm8n 11 | w/yLlz/Ow24j40UCY82Y9gMDgADa3BqcDPn1lPdGpOHhaMXMRFKnrVOfwMPE0wfx 12 | kpr9/I5rAPAnkX1WtvOWMK0V5yGsuIMBd2S4VzmrAgMBAAGjgfIwge8wHQYDVR0O 13 | BBYEFItpRD/8fT21N/pLeSWKexZHWP/3MIG/BgNVHSMEgbcwgbSAFItpRD/8fT21 14 | N/pLeSWKexZHWP/3oYGQpIGNMIGKMQswCQYDVQQGEwJJVDENMAsGA1UECBMEQ29t 15 | bzENMAsGA1UEBxMEQ29tbzERMA8GA1UEChMITWlzdWx0aW4xETAPBgNVBAsTCE1p 16 | c3VsdGluMREwDwYDVQQDEwhNaXN1bHRpbjEkMCIGCSqGSIb3DQEJARYVcm9iZXJ0 17 | b0Bvc3RpbmVsbGkubmV0ggkA8UWXTjq+/u8wDAYDVR0TBAUwAwEB/zANBgkqhkiG 18 | 9w0BAQUFAAOBgQA5ePVuJo6LcHegSKfD1GlTu2Ffkom547e9PYmKBaDyuNhCfP4F 19 | YB4GMi1SZeCsaYzJpEOCY9JEJD8hJO7xPnyKnwc3FhT7KfYHWO7FNZdmdMxE99mi 20 | lolCiDfglJkQCPihtxK5TKBDK+lzMub+1Xmc3VOvIuonzjh+VkSz1rCciQ== 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /priv/test_privkey.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | Proc-Type: 4,ENCRYPTED 3 | DEK-Info: DES-EDE3-CBC,E6F891BC96EC9B16 4 | 5 | kBpeVQGiACcY96k5ix5H+/Htt6XIGB2cyUhBMZlgBPQSAttU9B94YV+nXZmIVi8f 6 | r0cBDqGZ7uv+YjQom1HWw/NilWJr5x67VOhBGvg3kb2wYe3aMeGrmqRpPqNi6K2U 7 | t9QuragXj7tyFu8+sYnW0SeI7GN9aghF2S9bVKasOFDBHGDg1igb7PzBsctrQh1Q 8 | Hhkl2/Ql3+j18yUNZ2vCZgvGE2UfX5TJ79irpUCFiSgbf31EU7WCePZfSuNZfJB7 9 | hjQM5q/vfEmgqVCdVR1W8wFxdalPJOA819gKwpKBBgpfWPn2Gvyw/yAhm3FPLasG 10 | OgrY9PgsAcfozXgCOJP6NEP8IHzvb7kWoTussgBCd8P2Vv+YTp8WkBQDtpAlQExf 11 | 03sKUE9+pDXnzLnq4Pore6xBlzcZ4hu35WUo854UApRT/OQB5+Kmth9pqax43bbp 12 | 9Lfg6Zg9NXDuGHbRdK3U48uXLa7lDi5TMJ3LHuJ+DxZq4WNCTbMA3YZTFnjGiD/N 13 | NvTy54oQThjn67N7BQe3PeWI2ryGEWJAXShnc0ZTaxQaQn+18zVAe2tQOFUPhbbA 14 | Bq7zx49gea1tlJC1DHLktmw72v0g5W3HZ2fP1m+9socH9n4iORGEpicwuMgf9Tlb 15 | T0mFP3hL0y1wEdBoohF6Euk1Y33P44tbXsYn6bP2/mVmWphVA3wWOocMYw/UgSxM 16 | pzpC+z6y19dhqYNJyywgsMv6GQdrovW1DF2udmjx1Mv7qbwdJlH0GyLdyBL8aZFb 17 | WBXI2PjWWtZS/F1U7QsELzV3mM1U8n+K5hZuBPtvLzohpq2W59tPkg== 18 | -----END RSA PRIVATE KEY----- 19 | -------------------------------------------------------------------------------- /src/misultin.app.src: -------------------------------------------------------------------------------- 1 | {application, misultin, 2 | [ 3 | {description, "Lightweight HTTP(s) and Websockets Server Library"}, 4 | {vsn, "0.9"}, 5 | {modules, [ 6 | misultin, 7 | misultin_acceptor, 8 | misultin_acceptors_sup, 9 | misultin_cookies, 10 | misultin_http, 11 | misultin_req, 12 | misultin_server, 13 | misultin_sessions, 14 | misultin_socket, 15 | misultin_utility, 16 | misultin_websocket, 17 | 'misultin_websocket_draft-hixie-68', 18 | 'misultin_websocket_draft-hixie-76', 19 | 'misultin_websocket_draft-hybi-10_17', 20 | 'misultin_websocket_draft-hybi-10', 21 | 'misultin_websocket_draft-hybi-17', 22 | misultin_ws 23 | ]}, 24 | {registered, []}, 25 | {env, []}, 26 | {applications, [kernel, stdlib]} 27 | ]}. 28 | -------------------------------------------------------------------------------- /src/misultin_acceptor.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Acceptor 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli , Sean Hinde. 7 | % All rights reserved. 8 | % 9 | % Code portions from Sean Hinde have been originally taken under BSD license from Trapexit at the address: 10 | % 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_acceptor). 34 | -vsn("0.9"). 35 | 36 | % API 37 | -export([start_link/6]). 38 | 39 | % internal 40 | -export([init/6,acceptor/8]). 41 | 42 | % includes 43 | -include("../include/misultin.hrl"). 44 | 45 | 46 | % ============================ \/ API ====================================================================== 47 | 48 | % Starts the acceptor. 49 | -spec start_link( 50 | MainSupRef::pid(), 51 | ListenSocket::socket(), 52 | ListenPort::non_neg_integer(), 53 | RecvTimeout::non_neg_integer(), 54 | SocketMode::socketmode(), 55 | CustomOpts::#custom_opts{}) -> {ok, Pid::pid()}. 56 | start_link(MainSupRef, ListenSocket, ListenPort, RecvTimeout, SocketMode, CustomOpts) -> 57 | Pid = proc_lib:spawn_link(?MODULE, init, [MainSupRef, ListenSocket, ListenPort, RecvTimeout, SocketMode, CustomOpts]), 58 | {ok, Pid}. 59 | 60 | % init 61 | -spec init( 62 | MainSupRef::pid(), 63 | ListenSocket::socket(), 64 | ListenPort::non_neg_integer(), 65 | RecvTimeout::non_neg_integer(), 66 | SocketMode::socketmode(), 67 | CustomOpts::#custom_opts{}) -> {error, Reason::term()}. 68 | init(MainSupRef, ListenSocket, ListenPort, RecvTimeout, SocketMode, CustomOpts) -> 69 | ?LOG_DEBUG("starting new acceptor with pid ~p", [self()]), 70 | % get pid of misultin server 71 | Childrens = supervisor:which_children(MainSupRef), 72 | case lists:keyfind(server, 1, Childrens) of 73 | {server, ServerRef, _, _} -> 74 | ?LOG_DEBUG("got misultin server pid: ~p", [ServerRef]), 75 | % get rfc table ref 76 | TableDateRef = misultin_server:get_table_date_ref(ServerRef), 77 | ?LOG_DEBUG("got misultin table date reference: ~p", [TableDateRef]), 78 | % get pid of sessions server 79 | case lists:keyfind(sessions, 1, Childrens) of 80 | {sessions, SessionsRef, _, _} -> 81 | ?LOG_DEBUG("got misultin sessions pid: ~p", [SessionsRef]), 82 | acceptor(ServerRef, SessionsRef, TableDateRef, ListenSocket, ListenPort, RecvTimeout, SocketMode, CustomOpts); 83 | _ -> 84 | {error, could_not_get_sessionsref} 85 | end; 86 | _ -> 87 | {error, could_not_get_serverref} 88 | end. 89 | 90 | % Starts the socket. 91 | -spec acceptor( 92 | ServerRef::pid(), 93 | SessionsRef::pid(), 94 | TableDateRef::ets:tid(), 95 | ListenSocket::socket(), 96 | ListenPort::non_neg_integer(), 97 | RecvTimeout::non_neg_integer(), 98 | SocketMode::socketmode(), 99 | CustomOpts::#custom_opts{}) -> []. 100 | acceptor(ServerRef, SessionsRef, TableDateRef, ListenSocket, ListenPort, RecvTimeout, SocketMode, CustomOpts) -> 101 | case catch misultin_socket:accept(ListenSocket, SocketMode) of 102 | {ok, Sock} when SocketMode =:= http -> 103 | ?LOG_DEBUG("received a new http request, spawning a controlling process",[]), 104 | Pid = spawn(fun() -> 105 | activate_controller_process(ServerRef, SessionsRef, TableDateRef, Sock, ListenPort, RecvTimeout, SocketMode, CustomOpts) 106 | end), 107 | % set controlling process 108 | case misultin_socket:controlling_process(Sock, Pid, SocketMode) of 109 | ok -> 110 | Pid ! set; 111 | {error, _Reason} -> 112 | ?LOG_ERROR("could not set controlling process: ~p, closing socket", [_Reason]), 113 | misultin_socket:close(Sock, SocketMode) 114 | end, 115 | % get back to accept loop 116 | ?MODULE:acceptor(ServerRef, SessionsRef, TableDateRef, ListenSocket, ListenPort, RecvTimeout, SocketMode, CustomOpts); 117 | {ok, Sock} -> 118 | ?LOG_DEBUG("received a new https request, spawning a controlling process",[]), 119 | Pid = spawn(fun() -> 120 | case ssl:ssl_accept(Sock, 60000) of 121 | ok -> 122 | activate_controller_process(ServerRef, SessionsRef, TableDateRef, Sock, ListenPort, RecvTimeout, SocketMode, CustomOpts); 123 | {ok, NewSock} -> 124 | activate_controller_process(ServerRef, SessionsRef, TableDateRef, NewSock, ListenPort, RecvTimeout, SocketMode, CustomOpts); 125 | {error, _Reason} -> 126 | % could not negotiate a SSL transaction, leave process 127 | ?LOG_WARNING("could not negotiate a SSL transaction: ~p", [_Reason]), 128 | misultin_socket:close(Sock, SocketMode) 129 | end 130 | end), 131 | % set controlling process 132 | case misultin_socket:controlling_process(Sock, Pid, SocketMode) of 133 | ok -> 134 | Pid ! set; 135 | {error, _Reason} -> 136 | ?LOG_ERROR("could not set controlling process: ~p, closing socket", [_Reason]), 137 | misultin_socket:close(Sock, SocketMode) 138 | end, 139 | % get back to accept loop 140 | ?MODULE:acceptor(ServerRef, SessionsRef, TableDateRef, ListenSocket, ListenPort, RecvTimeout, SocketMode, CustomOpts); 141 | {error, _Error} -> 142 | ?LOG_WARNING("accept failed with error: ~p", [_Error]), 143 | % get back to accept loop 144 | ?MODULE:acceptor(ServerRef, SessionsRef, TableDateRef, ListenSocket, ListenPort, RecvTimeout, SocketMode, CustomOpts); 145 | {'EXIT', Error} -> 146 | ?LOG_ERROR("accept exited with error: ~p, quitting process", [Error]), 147 | exit({error, {accept_failed, Error}}) 148 | end. 149 | 150 | % ============================ /\ API ====================================================================== 151 | 152 | 153 | % ============================ \/ INTERNAL FUNCTIONS ======================================================= 154 | 155 | % activate the controller pid 156 | -spec activate_controller_process( 157 | ServerRef::pid(), 158 | SessionsRef::pid(), 159 | TableDateRef::ets:tid(), 160 | Sock::socket(), 161 | ListenPort::non_neg_integer(), 162 | RecvTimeout::non_neg_integer(), 163 | SocketMode::socketmode(), 164 | CustomOpts::#custom_opts{}) -> ok. 165 | activate_controller_process(ServerRef, SessionsRef, TableDateRef, Sock, ListenPort, RecvTimeout, SocketMode, CustomOpts) -> 166 | receive 167 | set -> 168 | ?LOG_DEBUG("activated controlling process ~p", [self()]), 169 | open_connections_switch(ServerRef, SessionsRef, TableDateRef, Sock, ListenPort, RecvTimeout, SocketMode, CustomOpts) 170 | after 60000 -> 171 | ?LOG_ERROR("timeout waiting for set in controlling process, closing socket", []), 172 | misultin_socket:close(Sock, SocketMode) 173 | end. 174 | 175 | % manage open connection 176 | -spec open_connections_switch( 177 | ServerRef::pid(), 178 | SessionsRef::pid(), 179 | TableDateRef::ets:tid(), 180 | Sock::socket(), 181 | ListenPort::non_neg_integer(), 182 | RecvTimeout::non_neg_integer(), 183 | SocketMode::socketmode(), 184 | CustomOpts::#custom_opts{}) -> ok. 185 | open_connections_switch(ServerRef, SessionsRef, TableDateRef, Sock, ListenPort, RecvTimeout, SocketMode, CustomOpts) -> 186 | case misultin_server:http_pid_ref_add(ServerRef, self()) of 187 | ok -> 188 | % get peer address and port 189 | case get_peer_addr_port(Sock, SocketMode, CustomOpts) of 190 | {ok, {PeerAddr, PeerPort}} -> 191 | ?LOG_DEBUG("remote peer is ~p", [{PeerAddr, PeerPort}]), 192 | % get peer certificate, if any 193 | PeerCert = misultin_socket:peercert(Sock, SocketMode), 194 | ?LOG_DEBUG("remote peer certificate is ~p", [PeerCert]), 195 | % jump to external callback 196 | ?LOG_DEBUG("jump to connection logic", []), 197 | misultin_http:handle_data(ServerRef, SessionsRef, TableDateRef, Sock, SocketMode, ListenPort, PeerAddr, PeerPort, PeerCert, RecvTimeout, CustomOpts), 198 | ok; 199 | {error, Msg} -> 200 | ?LOG_DEBUG("error reading PROXY line for IP address info",[]), 201 | send_error_message_and_close(502, Msg, Sock, SocketMode, TableDateRef, CustomOpts) 202 | end; 203 | {error, _Reason} -> 204 | % too many open connections, send error and close [spawn to avoid locking] 205 | ?LOG_DEBUG("~p, refusing new request", [_Reason]), 206 | send_error_message_and_close(503, "Server is experiencing heavy load, please try again in a few minutes.", Sock, SocketMode, TableDateRef, CustomOpts) 207 | end. 208 | 209 | % ============================ /\ INTERNAL FUNCTIONS ======================================================= 210 | 211 | % send error message 212 | -spec send_error_message_and_close(HttpCode::non_neg_integer(), Msg::iolist(), Sock::socket(), SocketMode::socketmode(), TableDateRef::ets:tid(), CustomOpts::#custom_opts{}) -> ok. 213 | send_error_message_and_close(HttpCode, Msg, Sock, SocketMode, TableDateRef, CustomOpts) -> 214 | {PeerAddr, PeerPort} = misultin_socket:peername(Sock, SocketMode), 215 | Msg0 = misultin_http:build_error_message(HttpCode, #req{peer_addr = PeerAddr, peer_port = PeerPort, connection = close}, TableDateRef, CustomOpts#custom_opts.access_log, Msg), 216 | misultin_socket:send(Sock, Msg0, SocketMode), 217 | misultin_socket:close(Sock, SocketMode). 218 | 219 | % get peer address 220 | -spec get_peer_addr_port(Sock::socket(), SocketMode::socketmode(), CustomOpts::#custom_opts{}) -> {ok, {PeerAddr::inet:ip_address(), PeerPort::non_neg_integer()}} | {error, term()}. 221 | get_peer_addr_port(Sock, SocketMode, CustomOpts) -> 222 | case CustomOpts#custom_opts.proxy_protocol of 223 | false -> 224 | % no proxy, return info 225 | {ok, misultin_socket:peername(Sock, SocketMode)}; 226 | true -> 227 | % proxy, read info 228 | peername_from_proxy_line(Sock, SocketMode) 229 | end. 230 | 231 | % receive the first line, and extract peer address details as per http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt 232 | -spec peername_from_proxy_line(Sock::socket(), SocketMode::socketmode()) -> {ok, {PeerAddr::inet:ip_address(), PeerPort::non_neg_integer()}} | {error, term()}. 233 | peername_from_proxy_line(Sock, SocketMode) -> 234 | %% Temporary socket options for reading PROXY line: 235 | misultin_socket:setopts(Sock, [{active, once}, {packet, line}, list], SocketMode), 236 | Val = parse_peername_from_proxy_line(Sock), 237 | %% Set socket options back to previous values, set in misultin.erl 238 | misultin_socket:setopts(Sock, [{active, false}, {packet, raw}, binary], SocketMode), 239 | Val. 240 | 241 | -spec parse_peername_from_proxy_line(Sock::socket()) -> {ok, {PeerAddr::inet:ip_address(), PeerPort::non_neg_integer()}} | {error, term()}. 242 | parse_peername_from_proxy_line(Sock) -> 243 | receive 244 | {TcpOrSsl, Sock, "PROXY " ++ ProxyLine} when TcpOrSsl =:= tcp; TcpOrSsl =:= ssl -> 245 | case string:tokens(ProxyLine, "\r\n ") of 246 | [_Proto, SrcAddrStr, _DestAddr, SrcPortStr, _DestPort] -> 247 | {SrcPort, _} = string:to_integer(SrcPortStr), 248 | {ok, SrcAddr} = inet_parse:address(SrcAddrStr), 249 | ?LOG_DEBUG("got peer address from proxy line: ~p", [{SrcAddr, SrcPort}]), 250 | {ok, {SrcAddr, SrcPort}}; 251 | _ -> 252 | ?LOG_DEBUG("got malformed proxy line: ~p", [ProxyLine]), 253 | {error, "got malformed proxy line: ~p", [ProxyLine]} 254 | end; 255 | {_, Sock, FirstLine} -> 256 | ?LOG_DEBUG("first line not 'PROXY', but 'PROXY ...' expected due to config option; line was: '~s'", [FirstLine]), 257 | {error, ["

PROXY line expected

", 258 | "Misultin configured to expect PROXY line first, as per ", 259 | "the haproxy proxy protocol spec, ", 260 | "but first line received was:
\r\n",
261 | 				FirstLine,
262 | 				"\r\n
"]}; 263 | Other -> 264 | ?LOG_DEBUG("got from proxy unexpected: ~p", [Other]), 265 | {error, "got from proxy unexpected: ~p", [Other]} 266 | after 5000 -> 267 | ?LOG_DEBUG("timeout receiving PROXY line from upstream proxy, closing",[]), 268 | {error, "timeout on receiving proxy line from upstream proxy"} 269 | end. 270 | -------------------------------------------------------------------------------- /src/misultin_acceptors_sup.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Acceptors Supervisor 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli , Sean Hinde. 7 | % All rights reserved. 8 | % 9 | % Code portions from Sean Hinde have been originally taken under BSD license from Trapexit at the address: 10 | % 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_acceptors_sup). 34 | -behaviour(supervisor). 35 | -vsn("0.9"). 36 | 37 | % API 38 | -export([start_link/7]). 39 | 40 | % supervisor callbacks 41 | -export([init/1]). 42 | 43 | % includes 44 | -include("../include/misultin.hrl"). 45 | 46 | % ============================ \/ API ====================================================================== 47 | 48 | % ---------------------------------------------------------------------------------------------------------- 49 | % Starts the supervisor 50 | % ---------------------------------------------------------------------------------------------------------- 51 | -spec start_link( 52 | MainSupRef::pid(), 53 | Port::non_neg_integer(), 54 | OptionsTcp::[misultin_option_tcp()], 55 | AcceptorsPoolsize::non_neg_integer(), 56 | RecvTimeout::non_neg_integer(), 57 | SocketMode::socketmode(), 58 | CustomOpts::#custom_opts{}) -> {ok, Pid::pid()}. 59 | start_link(MainSupRef, Port, OptionsTcp, AcceptorsPoolsize, RecvTimeout, SocketMode, CustomOpts) -> 60 | supervisor:start_link(?MODULE, {MainSupRef, Port, OptionsTcp, AcceptorsPoolsize, RecvTimeout, SocketMode, CustomOpts}). 61 | 62 | % ============================ /\ API ====================================================================== 63 | 64 | 65 | % ============================ \/ SUPERVISOR CALLBACKS ===================================================== 66 | 67 | % ---------------------------------------------------------------------------------------------------------- 68 | % Function: -> {ok, {SupFlags, [ChildSpec]}} | ignore | {error, Reason} 69 | % Description: Starts the supervisor 70 | % ---------------------------------------------------------------------------------------------------------- 71 | -spec init({ 72 | MainSupRef::pid(), 73 | Port::non_neg_integer(), 74 | OptionsTcp::[misultin_option_tcp()], 75 | AcceptorsPoolsize::non_neg_integer(), 76 | RecvTimeout::non_neg_integer(), 77 | SocketMode::socketmode(), 78 | CustomOpts::#custom_opts{} }) -> {ok, term()} | {error, Reason::term()}. 79 | init({MainSupRef, Port, OptionsTcp, AcceptorsPoolsize, RecvTimeout, SocketMode, CustomOpts}) -> 80 | ?LOG_DEBUG("starting listening ~p socket with options ~p on port ~p", [SocketMode, OptionsTcp, Port]), 81 | case misultin_socket:listen(Port, OptionsTcp, SocketMode) of 82 | {ok, ListenSocket} -> 83 | Acceptors = [ 84 | {{acceptor, N}, {misultin_acceptor, start_link, [MainSupRef, ListenSocket, Port, RecvTimeout, SocketMode, CustomOpts]}, permanent, brutal_kill, worker, [misultin_acceptor]} 85 | || N <- lists:seq(1, AcceptorsPoolsize) 86 | ], 87 | {ok, {{one_for_one, 5, 10}, Acceptors}}; 88 | {error, Reason} -> 89 | % error 90 | {error, Reason} 91 | end. 92 | 93 | % ============================ /\ SUPERVISOR CALLBACKS ===================================================== 94 | 95 | 96 | % ============================ \/ INTERNAL FUNCTIONS ======================================================= 97 | 98 | % ============================ /\ INTERNAL FUNCTIONS ======================================================= 99 | -------------------------------------------------------------------------------- /src/misultin_cookies.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Various Utilities 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Emad El-Haraty for Mochi Media Inc., 7 | % Roberto Ostinelli . 8 | % All rights reserved. 9 | % 10 | % BSD License 11 | % 12 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 13 | % that the following conditions are met: 14 | % 15 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 16 | % following disclaimer. 17 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 18 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 19 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 20 | % products derived from this software without specific prior written permission. 21 | % 22 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 23 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 24 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 25 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 26 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | % POSSIBILITY OF SUCH DAMAGE. 30 | % ========================================================================================================== 31 | -module(misultin_cookies). 32 | -vsn("0.9"). 33 | 34 | % API 35 | -export([set_cookie/3, set_cookie/2, delete_cookie/1, parse_cookie/1]). 36 | 37 | % Macros 38 | -define(QUOTE, $\"). 39 | -define(IS_WHITESPACE(C), (C =:= $\s orelse C =:= $\t orelse C =:= $\r orelse C =:= $\n)). 40 | 41 | % RFC 2616 separators (called tspecials in RFC 2068) 42 | -define(IS_SEPARATOR(C), ( 43 | C < 32 orelse C =:= $\s orelse C =:= $\t orelse 44 | C =:= $( orelse C =:= $) orelse C =:= $< orelse C =:= $> orelse 45 | C =:= $@ orelse C =:= $, orelse C =:= $; orelse C =:= $: orelse 46 | C =:= $\\ orelse C =:= $\" orelse C =:= $/ orelse 47 | C =:= $[ orelse C =:= $] orelse C =:= $? orelse C =:= $= orelse 48 | C =:= ${ orelse C =:= $} 49 | )). 50 | 51 | % includes 52 | -include("../include/misultin.hrl"). 53 | 54 | 55 | % ============================ \/ API ====================================================================== 56 | 57 | %% @type proplist() = [{Key::string(), Value::string()}]. 58 | %% @type header() = {Name::string(), Value::string()}. 59 | %% @type int_seconds() = integer(). 60 | 61 | %% @spec set_cookie(Key::string(), Value::string()) -> header() 62 | %% @doc Short-hand for cookie(Key, Value, []). 63 | -spec set_cookie(Key::string(), Value::string()) -> {http_header(), string()}. 64 | -spec set_cookie(Key::string(), Value::string(), Options::cookies_options()) -> {http_header(), string()}. 65 | set_cookie(Key, Value) -> 66 | set_cookie(Key, Value, []). 67 | set_cookie(Key, Value, Options) -> 68 | cookie(Key, misultin_utility:quote_plus(Value), Options). 69 | 70 | %% Delete a cookie 71 | -spec delete_cookie(Key::string()) -> {http_header(), string()}. 72 | delete_cookie(Key) -> 73 | set_cookie(Key, "", [{max_age, -3600}]). 74 | 75 | %% @spec parse_cookie(string()) -> [{K::string(), V::string()}] 76 | %% @doc Parse the contents of a Cookie header field, ignoring cookie 77 | %% attributes, and return a simple property list. 78 | -spec parse_cookie(Cookie::string()) -> gen_proplist(). 79 | parse_cookie("") -> 80 | []; 81 | parse_cookie(Cookie) -> 82 | parse_cookie(Cookie, []). 83 | 84 | % ============================ /\ API ====================================================================== 85 | 86 | 87 | 88 | % ============================ \/ INTERNAL FUNCTIONS ======================================================= 89 | 90 | % SET COOKIE 91 | 92 | %% @spec cookie(Key::string(), Value::string(), Options::[Option]) -> header() 93 | %% where Option = {max_age, int_seconds()} | {local_time, {date(), time()}} 94 | %% | {domain, string()} | {path, string()} 95 | %% | {secure, true | false} | {http_only, true | false} 96 | %% 97 | %% @doc Generate a Set-Cookie header field tuple. 98 | -spec cookie(Key::string(), Value::string(), Options::cookies_options()) -> {http_header(), string()}. 99 | cookie(Key, Value, Options) -> 100 | Cookie = [any_to_list(Key), "=", quote(Value), "; Version=1"], 101 | %% Set-Cookie: 102 | %% Comment, Domain, Max-Age, Path, Secure, Version 103 | %% Set-Cookie2: 104 | %% Comment, CommentURL, Discard, Domain, Max-Age, Path, Port, Secure, 105 | %% Version 106 | ExpiresPart = case misultin_utility:get_key_value(max_age, Options) of 107 | undefined -> ""; 108 | RawAge -> 109 | When = case misultin_utility:get_key_value(local_time, Options) of 110 | undefined -> calendar:local_time(); 111 | LocalTime -> LocalTime 112 | end, 113 | Age = case RawAge < 0 of 114 | true -> 0; 115 | false -> RawAge 116 | end, 117 | ["; Expires=", age_to_cookie_date(Age, When), "; Max-Age=", quote(Age)] 118 | end, 119 | SecurePart = case misultin_utility:get_key_value(secure, Options) of 120 | true -> "; Secure"; 121 | _ -> "" 122 | end, 123 | DomainPart = case misultin_utility:get_key_value(domain, Options) of 124 | undefined -> ""; 125 | Domain -> ["; Domain=", quote(Domain)] 126 | end, 127 | PathPart = case misultin_utility:get_key_value(path, Options) of 128 | undefined -> ""; 129 | Path -> ["; Path=", quote(Path)] 130 | end, 131 | HttpOnlyPart = case misultin_utility:get_key_value(http_only, Options) of 132 | true -> "; HttpOnly"; 133 | _ -> "" 134 | end, 135 | CookieParts = [Cookie, ExpiresPart, SecurePart, DomainPart, PathPart, HttpOnlyPart], 136 | {"Set-Cookie", lists:flatten(CookieParts)}. 137 | 138 | %% Every major browser incorrectly handles quoted strings in a 139 | %% different and (worse) incompatible manner. Instead of wasting time 140 | %% writing redundant code for each browser, we restrict cookies to 141 | %% only contain characters that browsers handle compatibly. 142 | %% 143 | %% By replacing the definition of quote with this, we generate 144 | %% RFC-compliant cookies: 145 | %% 146 | %% quote(V) -> 147 | %% Fun = fun(?QUOTE, Acc) -> [$\\, ?QUOTE | Acc]; 148 | %% (Ch, Acc) -> [Ch | Acc] 149 | %% end, 150 | %% [?QUOTE | lists:foldr(Fun, [?QUOTE], V)]. 151 | 152 | %% Convert to a string and raise an error if quoting is required. 153 | -spec quote(Unquoted::term()) -> Quoted::string(). 154 | quote(V0) -> 155 | V = any_to_list(V0), 156 | lists:all(fun(Ch) -> Ch =:= $/ orelse not ?IS_SEPARATOR(Ch) end, V) 157 | orelse erlang:error({cookie_quoting_required, V}), 158 | V. 159 | 160 | %% Return a date in the form of: Wdy, DD-Mon-YYYY HH:MM:SS GMT 161 | %% See also: rfc2109: 10.1.2 162 | -spec rfc2109_cookie_expires_date(LocalTime::date_tuple()) -> string(). 163 | rfc2109_cookie_expires_date(LocalTime) -> 164 | {{YYYY,MM,DD},{Hour,Min,Sec}} = case calendar:local_time_to_universal_time_dst(LocalTime) of 165 | [Gmt] -> Gmt; 166 | [_,Gmt] -> Gmt 167 | end, 168 | DayNumber = calendar:day_of_the_week({YYYY,MM,DD}), 169 | lists:flatten( 170 | io_lib:format("~s, ~2.2.0w-~3.s-~4.4.0w ~2.2.0w:~2.2.0w:~2.2.0w GMT", 171 | [httpd_util:day(DayNumber),DD,httpd_util:month(MM),YYYY,Hour,Min,Sec]) 172 | ). 173 | 174 | -spec age_to_cookie_date(Age::integer(), LocalTime::date_tuple()) -> string(). 175 | age_to_cookie_date(Age, LocalTime) -> 176 | rfc2109_cookie_expires_date(add_seconds(Age, LocalTime)). 177 | -spec add_seconds(Secs::integer(), LocalTime::date_tuple()) -> date_tuple(). 178 | add_seconds(Secs, LocalTime) -> 179 | Greg = calendar:datetime_to_gregorian_seconds(LocalTime), 180 | calendar:gregorian_seconds_to_datetime(Greg + Secs). 181 | 182 | % PARSE COOKIE 183 | -spec parse_cookie(Cookie::string(), Acc::string()) -> gen_proplist(). 184 | parse_cookie([], Acc) -> 185 | lists:reverse(Acc); 186 | parse_cookie(String, Acc) -> 187 | {{Token, Value}, Rest} = read_pair(String), 188 | Acc1 = case Token of 189 | "" -> Acc; 190 | "$" ++ _ -> Acc; 191 | _ -> [{Token, Value} | Acc] 192 | end, 193 | parse_cookie(Rest, Acc1). 194 | 195 | read_pair(String) -> 196 | {Token, Rest} = read_token(skip_whitespace(String)), 197 | {Value, Rest1} = read_value(skip_whitespace(Rest)), 198 | {{Token, Value}, skip_past_separator(Rest1)}. 199 | 200 | read_value([$= | Value]) -> 201 | Value1 = skip_whitespace(Value), 202 | case Value1 of 203 | [?QUOTE | _] -> 204 | read_quoted(Value1); 205 | _ -> 206 | read_token(Value1) 207 | end; 208 | read_value(String) -> 209 | {"", String}. 210 | 211 | read_quoted([?QUOTE | String]) -> 212 | read_quoted(String, []). 213 | 214 | read_quoted([], Acc) -> 215 | {lists:reverse(Acc), []}; 216 | read_quoted([?QUOTE | Rest], Acc) -> 217 | {lists:reverse(Acc), Rest}; 218 | read_quoted([$\\, Any | Rest], Acc) -> 219 | read_quoted(Rest, [Any | Acc]); 220 | read_quoted([C | Rest], Acc) -> 221 | read_quoted(Rest, [C | Acc]). 222 | 223 | skip_whitespace(String) -> 224 | F = fun (C) -> ?IS_WHITESPACE(C) end, 225 | lists:dropwhile(F, String). 226 | 227 | read_token(String) -> 228 | F = fun (C) -> not ?IS_SEPARATOR(C) end, 229 | lists:splitwith(F, String). 230 | 231 | skip_past_separator([]) -> 232 | []; 233 | skip_past_separator([$; | Rest]) -> 234 | Rest; 235 | skip_past_separator([$, | Rest]) -> 236 | Rest; 237 | skip_past_separator([_ | Rest]) -> 238 | skip_past_separator(Rest). 239 | 240 | any_to_list(V) when is_list(V) -> 241 | V; 242 | any_to_list(V) when is_atom(V) -> 243 | atom_to_list(V); 244 | any_to_list(V) when is_binary(V) -> 245 | binary_to_list(V); 246 | any_to_list(V) when is_integer(V) -> 247 | integer_to_list(V). 248 | 249 | % ============================ /\ INTERNAL FUNCTIONS ======================================================= 250 | 251 | -------------------------------------------------------------------------------- /src/misultin_socket.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Socket 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli , Sean Hinde. 7 | % All rights reserved. 8 | % 9 | % Code portions from Sean Hinde have been originally taken under BSD license from Trapexit at the address: 10 | % 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_socket). 34 | -vsn("0.9"). 35 | 36 | % API 37 | -export([listen/3, accept/2, controlling_process/3, peername/2, peercert/2, setopts/3, recv/4, send/3, close/2]). 38 | 39 | % includes 40 | -include("../include/misultin.hrl"). 41 | 42 | 43 | % ============================ \/ API ====================================================================== 44 | 45 | % socket listen 46 | -spec listen(Port::non_neg_integer(), Options::gen_proplist(), socketmode()) -> {ok, ListenSock::socket()} | {error, Reason::term()}. 47 | listen(Port, Options, http) -> gen_tcp:listen(Port, Options); 48 | listen(Port, Options, ssl) -> ssl:listen(Port, Options). 49 | 50 | % socket accept 51 | -spec accept(ListenSocket::socket(), socketmode()) -> {ok, ListenSock::socket()} | {error, Reason::term()}. 52 | accept(ListenSocket, http) -> gen_tcp:accept(ListenSocket); 53 | accept(ListenSocket, ssl) -> 54 | try ssl:transport_accept(ListenSocket) 55 | catch 56 | error:{badmatch, {error, Reason}} -> 57 | {error, Reason} 58 | end. 59 | 60 | % socket controlling process 61 | -spec controlling_process(Sock::socket(), Pid::pid(), socketmode()) -> ok | {error, Reason::term()}. 62 | controlling_process(Sock, Pid, http) -> gen_tcp:controlling_process(Sock, Pid); 63 | controlling_process(Sock, Pid, ssl) -> ssl:controlling_process(Sock, Pid). 64 | 65 | % Get socket peername 66 | -spec peername(Sock::socket(), socketmode() | function()) -> {inet:ip_address(), non_neg_integer()}. 67 | peername(Sock, http) -> peername(Sock, fun inet:peername/1); 68 | peername(Sock, ssl) -> peername(Sock, fun ssl:peername/1); 69 | peername(Sock, F) -> 70 | case F(Sock) of 71 | {ok, {Addr, Port}} -> 72 | {Addr, Port}; 73 | {error, _Reason} -> 74 | {undefined, undefined} 75 | end. 76 | 77 | % Get socket certificate 78 | -spec peercert(Sock::socket(), socketmode()) -> Cert::term() | undefined. 79 | peercert(_Sock, http) -> undefined; 80 | peercert(Sock, ssl) -> 81 | case ssl:peercert(Sock) of 82 | {ok, Cert} -> Cert; 83 | {error, _Reason} -> undefined 84 | end. 85 | 86 | % socket set options 87 | -spec setopts(Sock::socket(), Options::gen_proplist_options(), socketmode()) -> ok | {error, Reason::term()}. 88 | setopts(Sock, Options, http) -> inet:setopts(Sock, Options); 89 | setopts(Sock, Options, ssl) -> ssl:setopts(Sock, Options). 90 | 91 | % socket receive 92 | -spec recv(Sock::socket(), Len::non_neg_integer(), RecvTimeout::non_neg_integer(), socketmode()) -> {ok, Data::list() | binary()} | {error, Reason::term()}. 93 | recv(Sock, Len, RecvTimeout, http) -> gen_tcp:recv(Sock, Len, RecvTimeout); 94 | recv(Sock, Len, RecvTimeout, ssl) -> ssl:recv(Sock, Len, RecvTimeout). 95 | 96 | % socket send 97 | -spec send(Sock::socket(), Data::binary() | iolist() | list(), socketmode() | function()) -> ok. 98 | send(Sock, Data, http) -> send(Sock, Data, fun gen_tcp:send/2); 99 | send(Sock, Data, ssl) -> send(Sock, Data, fun ssl:send/2); 100 | send(Sock, Data, F) -> 101 | ?LOG_DEBUG("sending data: ~p", [Data]), 102 | case F(Sock, Data) of 103 | ok -> 104 | ok; 105 | {error, _Reason} -> 106 | ?LOG_ERROR("error sending data: ~p", [_Reason]), 107 | exit(kill) 108 | end. 109 | 110 | % TCP close 111 | -spec close(Sock::socket(), socketmode() | function()) -> ok. 112 | close(Sock, http) -> close(Sock, fun gen_tcp:close/1); 113 | close(Sock, ssl) -> close(Sock, fun ssl:close/1); 114 | close(Sock, F) -> 115 | ?LOG_DEBUG("closing socket", []), 116 | case catch F(Sock) of 117 | ok -> 118 | ok; 119 | _Else -> 120 | ?LOG_WARNING("could not close socket: ~p", [_Else]), 121 | exit(kill) 122 | end. 123 | 124 | % ============================ /\ API ====================================================================== 125 | 126 | 127 | % ============================ \/ INTERNAL FUNCTIONS ======================================================= 128 | 129 | % ============================ /\ INTERNAL FUNCTIONS ======================================================= 130 | -------------------------------------------------------------------------------- /src/misultin_websocket_draft-hixie-68.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - WebSocket 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli , Joe Armstrong. 7 | % All rights reserved. 8 | % 9 | % Code portions from Joe Armstrong have been originally taken under MIT license at the address: 10 | % 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 | 37 | % API 38 | -export([check_websocket/1, handshake/3, handle_data/3, send_format/2]). 39 | 40 | % includes 41 | -include("../include/misultin.hrl"). 42 | 43 | 44 | % ============================ \/ API ====================================================================== 45 | 46 | % ---------------------------------------------------------------------------------------------------------- 47 | % Function: -> true | false 48 | % Description: Callback to check if the incoming request is a websocket request according to this protocol. 49 | % ---------------------------------------------------------------------------------------------------------- 50 | -spec check_websocket(Headers::http_headers()) -> boolean(). 51 | check_websocket(Headers) -> 52 | % set required headers 53 | RequiredHeaders = [ 54 | {'Upgrade', "WebSocket"}, {'Connection', "Upgrade"}, {'Host', ignore}, {'Origin', ignore} 55 | ], 56 | % check for headers existance 57 | case misultin_websocket:check_headers(Headers, RequiredHeaders) of 58 | true -> true; 59 | _RemainingHeaders -> 60 | ?LOG_DEBUG("not this protocol, remaining headers: ~p", [_RemainingHeaders]), 61 | false 62 | end. 63 | 64 | % ---------------------------------------------------------------------------------------------------------- 65 | % Function: -> iolist() | binary() 66 | % Description: Callback to build handshake data. 67 | % ---------------------------------------------------------------------------------------------------------- 68 | -spec handshake(Req::#req{}, Headers::http_headers(), {Path::string(), Origin::string(), Host::string()}) -> iolist(). 69 | handshake(#req{socket_mode = SocketMode, ws_force_ssl = WsForceSsl} = _Req, _Headers, {Path, Origin, Host}) -> 70 | % prepare handhsake response 71 | WsMode = case SocketMode of 72 | ssl -> "wss"; 73 | http when WsForceSsl =:= true -> "wss"; % behind stunnel or similar, client is using ssl 74 | http when WsForceSsl =:= false -> "ws" 75 | end, 76 | ["HTTP/1.1 101 Web Socket Protocol Handshake\r\n", 77 | "Upgrade: WebSocket\r\n", 78 | "Connection: Upgrade\r\n", 79 | "WebSocket-Origin: ", Origin , "\r\n", 80 | "WebSocket-Location: ", WsMode, "://", lists:concat([Host, Path]), "\r\n\r\n" 81 | ]. 82 | 83 | % ---------------------------------------------------------------------------------------------------------- 84 | % Function: -> websocket_close | {websocket_close, DataToSendBeforeClose::binary() | iolist()} | NewState 85 | % Description: Callback to handle incomed data. 86 | % ---------------------------------------------------------------------------------------------------------- 87 | -spec handle_data(Data::binary(), State::undefined | term(), {Socket::socket(), SocketMode::socketmode(), WsHandleLoopPid::pid()}) -> websocket_close | term(). 88 | handle_data(Data, undefined, {Socket, SocketMode, WsHandleLoopPid}) -> 89 | % init status 90 | handle_data(Data, {buffer, none}, {Socket, SocketMode, WsHandleLoopPid}); 91 | handle_data(Data, {buffer, B} = _State, {Socket, SocketMode, WsHandleLoopPid}) -> 92 | % read status 93 | i_handle_data(Data, B, {Socket, SocketMode, WsHandleLoopPid}). 94 | 95 | % ---------------------------------------------------------------------------------------------------------- 96 | % Function: -> binary() | iolist() 97 | % Description: Callback to format data before it is sent into the socket. 98 | % ---------------------------------------------------------------------------------------------------------- 99 | -spec send_format(Data::iolist(), State::term()) -> iolist(). 100 | send_format(Data, _State) -> 101 | [0, Data, 255]. 102 | 103 | % ============================ /\ API ====================================================================== 104 | 105 | 106 | % ============================ \/ INTERNAL FUNCTIONS ======================================================= 107 | 108 | % Buffering and data handling 109 | -spec i_handle_data( 110 | Data::binary(), 111 | Buffer::binary() | none, 112 | {Socket::socket(), SocketMode::socketmode(), WsHandleLoopPid::pid()}) -> websocket_close | term(). 113 | i_handle_data(<<0, T/binary>>, none, {Socket, SocketMode, WsHandleLoopPid}) -> 114 | i_handle_data(T, <<>>, {Socket, SocketMode, WsHandleLoopPid}); 115 | i_handle_data(<<>>, none, {_Socket, _SocketMode, _WsHandleLoopPid}) -> 116 | % return status 117 | {buffer, none}; 118 | i_handle_data(<<255, 0>>, _L, {Socket, SocketMode, _WsHandleLoopPid}) -> 119 | ?LOG_DEBUG("websocket close message received from client, closing websocket with pid ~p", [self()]), 120 | misultin_socket:send(Socket, <<255, 0>>, SocketMode), 121 | % return command 122 | websocket_close; 123 | i_handle_data(<<255, T/binary>>, L, {Socket, SocketMode, WsHandleLoopPid}) -> 124 | misultin_websocket:send_to_browser(WsHandleLoopPid, binary_to_list(L)), 125 | i_handle_data(T, none, {Socket, SocketMode, WsHandleLoopPid}); 126 | i_handle_data(<>, L, {Socket, SocketMode, WsHandleLoopPid}) -> 127 | i_handle_data(T, <>, {Socket, SocketMode, WsHandleLoopPid}); 128 | i_handle_data(<<>>, L, {_Socket, _SocketMode, _WsHandleLoopPid}) -> 129 | {buffer, L}. 130 | 131 | % ============================ /\ INTERNAL FUNCTIONS ======================================================= 132 | -------------------------------------------------------------------------------- /src/misultin_websocket_draft-hixie-76.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - WebSocket 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli , Joe Armstrong. 7 | % All rights reserved. 8 | % 9 | % Code portions from Joe Armstrong have been originally taken under MIT license at the address: 10 | % 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-76'). 34 | -behaviour(misultin_websocket). 35 | -vsn("0.9"). 36 | 37 | % API 38 | -export([check_websocket/1, handshake/3, handle_data/3, send_format/2]). 39 | 40 | % includes 41 | -include("../include/misultin.hrl"). 42 | 43 | 44 | % ============================ \/ API ====================================================================== 45 | 46 | % ---------------------------------------------------------------------------------------------------------- 47 | % Function: -> true | false 48 | % Description: Callback to check if the incoming request is a websocket request according to this protocol. 49 | % ---------------------------------------------------------------------------------------------------------- 50 | -spec check_websocket(Headers::http_headers()) -> boolean(). 51 | check_websocket(Headers) -> 52 | % set required headers 53 | RequiredHeaders = [ 54 | {'Upgrade', "WebSocket"}, {'Connection', "Upgrade"}, {'Host', ignore}, {'Origin', ignore}, 55 | {'Sec-WebSocket-Key1', ignore}, {'Sec-WebSocket-Key2', ignore} 56 | ], 57 | % check for headers existance 58 | case misultin_websocket:check_headers(Headers, RequiredHeaders) of 59 | true -> 60 | % return 61 | true; 62 | _RemainingHeaders -> 63 | ?LOG_DEBUG("not this protocol, remaining headers: ~p", [_RemainingHeaders]), 64 | false 65 | end. 66 | 67 | % ---------------------------------------------------------------------------------------------------------- 68 | % Function: -> iolist() | binary() 69 | % Description: Callback to build handshake data. 70 | % ---------------------------------------------------------------------------------------------------------- 71 | -spec handshake(Req::#req{}, Headers::http_headers(), {Path::string(), Origin::string(), Host::string()}) -> iolist(). 72 | handshake(#req{socket = Sock, socket_mode = SocketMode, ws_force_ssl = WsForceSsl}, Headers, {Path, Origin, Host}) -> 73 | % build data 74 | Key1 = misultin_utility:header_get_value('Sec-WebSocket-Key1', Headers), 75 | Key2 = misultin_utility:header_get_value('Sec-WebSocket-Key2', Headers), 76 | % handshake needs body of the request, still need to read it [TODO: default recv timeout hard set, will be exported when WS protocol is final] 77 | misultin_socket:setopts(Sock, [{packet, raw}, {active, false}], SocketMode), 78 | Body = case misultin_socket:recv(Sock, 8, 30*1000, SocketMode) of 79 | {ok, Bin} -> Bin; 80 | {error, timeout} -> 81 | ?LOG_WARNING("timeout in reading websocket body", []), 82 | <<>>; 83 | _Other -> 84 | ?LOG_ERROR("tcp error treating data: ~p", [_Other]), 85 | <<>> 86 | end, 87 | ?LOG_DEBUG("got content in body of websocket request: ~p", [Body]), 88 | % prepare handhsake response 89 | WsMode = case SocketMode of 90 | ssl -> "wss"; 91 | http when WsForceSsl =:= true -> "wss"; % behind stunnel or similar, client is using ssl 92 | http when WsForceSsl =:= false -> "ws" 93 | end, 94 | % build challenge 95 | Ikey1 = [D || D <- Key1, $0 =< D, D =< $9], 96 | Ikey2 = [D || D <- Key2, $0 =< D, D =< $9], 97 | Blank1 = length([D || D <- Key1, D =:= 32]), 98 | Blank2 = length([D || D <- Key2, D =:= 32]), 99 | Part1 = erlang:list_to_integer(Ikey1) div Blank1, 100 | Part2 = erlang:list_to_integer(Ikey2) div Blank2, 101 | Ckey = <>, 102 | Challenge = erlang:md5(Ckey), 103 | % format 104 | ["HTTP/1.1 101 WebSocket Protocol Handshake\r\n", 105 | "Upgrade: WebSocket\r\n", 106 | "Connection: Upgrade\r\n", 107 | "Sec-WebSocket-Origin: ", Origin, "\r\n", 108 | "Sec-WebSocket-Location: ", WsMode, "://", lists:concat([Host, Path]), "\r\n\r\n", 109 | Challenge 110 | ]. 111 | 112 | % ---------------------------------------------------------------------------------------------------------- 113 | % Function: -> websocket_close | {websocket_close, DataToSendBeforeClose::binary() | iolist()} | NewState 114 | % Description: Callback to handle incomed data. 115 | % ---------------------------------------------------------------------------------------------------------- 116 | -spec handle_data(Data::binary(), State::undefined | term(), {Socket::socket(), SocketMode::socketmode(), WsHandleLoopPid::pid()}) -> websocket_close | term(). 117 | handle_data(Data, undefined, {Socket, SocketMode, WsHandleLoopPid}) -> 118 | % init status 119 | handle_data(Data, {buffer, none}, {Socket, SocketMode, WsHandleLoopPid}); 120 | handle_data(Data, {buffer, B} = _State, {Socket, SocketMode, WsHandleLoopPid}) -> 121 | % read status 122 | i_handle_data(Data, B, {Socket, SocketMode, WsHandleLoopPid}). 123 | 124 | % ---------------------------------------------------------------------------------------------------------- 125 | % Function: -> binary() | iolist() 126 | % Description: Callback to format data before it is sent into the socket. 127 | % ---------------------------------------------------------------------------------------------------------- 128 | -spec send_format(Data::iolist(), State::term()) -> iolist(). 129 | send_format(Data, _State) -> 130 | [0, Data, 255]. 131 | 132 | % ============================ /\ API ====================================================================== 133 | 134 | 135 | % ============================ \/ INTERNAL FUNCTIONS ======================================================= 136 | 137 | % Buffering and data handling 138 | -spec i_handle_data( 139 | Data::binary(), 140 | Buffer::binary() | none, 141 | {Socket::socket(), SocketMode::socketmode(), WsHandleLoopPid::pid()}) -> websocket_close | term(). 142 | i_handle_data(<<0, T/binary>>, none, {Socket, SocketMode, WsHandleLoopPid}) -> 143 | i_handle_data(T, <<>>, {Socket, SocketMode, WsHandleLoopPid}); 144 | i_handle_data(<<>>, none, {_Socket, _SocketMode, _WsHandleLoopPid}) -> 145 | % return status 146 | {buffer, none}; 147 | i_handle_data(<<255, 0>>, _L, {Socket, SocketMode, _WsHandleLoopPid}) -> 148 | ?LOG_DEBUG("websocket close message received from client, closing websocket with pid ~p", [self()]), 149 | misultin_socket:send(Socket, <<255, 0>>, SocketMode), 150 | % return command 151 | websocket_close; 152 | i_handle_data(<<255, T/binary>>, L, {Socket, SocketMode, WsHandleLoopPid}) -> 153 | misultin_websocket:send_to_browser(WsHandleLoopPid, binary_to_list(L)), 154 | i_handle_data(T, none, {Socket, SocketMode, WsHandleLoopPid}); 155 | i_handle_data(<>, L, {Socket, SocketMode, WsHandleLoopPid}) -> 156 | i_handle_data(T, <>, {Socket, SocketMode, WsHandleLoopPid}); 157 | i_handle_data(<<>>, L, {_Socket, _SocketMode, _WsHandleLoopPid}) -> 158 | {buffer, L}. 159 | 160 | % ============================ /\ INTERNAL FUNCTIONS ======================================================= 161 | -------------------------------------------------------------------------------- /src/misultin_websocket_draft-hybi-10.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - WebSocket - draft hybi 10 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli . 7 | % All rights reserved. 8 | % 9 | % Code portions from Joe Armstrong have been originally taken under MIT license at the address: 10 | % 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-hybi-10'). 34 | -behaviour(misultin_websocket). 35 | -vsn("0.9"). 36 | 37 | % API 38 | -export([check_websocket/1, handshake/3, handle_data/3, send_format/2]). 39 | 40 | % macros 41 | -define(HYBI_COMMON, 'misultin_websocket_draft-hybi-10_17'). 42 | 43 | % includes 44 | -include("../include/misultin.hrl"). 45 | 46 | 47 | % ============================ \/ API ====================================================================== 48 | 49 | % ---------------------------------------------------------------------------------------------------------- 50 | % Function: -> true | false 51 | % Description: Callback to check if the incoming request is a websocket request according to this protocol. 52 | % ---------------------------------------------------------------------------------------------------------- 53 | -spec check_websocket(Headers::http_headers()) -> boolean(). 54 | check_websocket(Headers) -> 55 | % set required headers 56 | RequiredHeaders = [ 57 | {'Upgrade', "websocket"}, {'Connection', "Upgrade"}, {'Host', ignore}, {'Sec-Websocket-Origin', ignore}, 58 | {'Sec-Websocket-Key', ignore}, {'Sec-WebSocket-Version', "8"} 59 | ], 60 | ?HYBI_COMMON:check_websocket(Headers, RequiredHeaders). 61 | 62 | % ---------------------------------------------------------------------------------------------------------- 63 | % Function: -> iolist() | binary() 64 | % Description: Callback to build handshake data. 65 | % ---------------------------------------------------------------------------------------------------------- 66 | -spec handshake(Req::#req{}, Headers::http_headers(), {Path::string(), Origin::string(), Host::string()}) -> iolist(). 67 | handshake(Req, Headers, {Path, Origin, Host}) -> 68 | ?HYBI_COMMON:handshake(Req, Headers, {Path, Origin, Host}). 69 | 70 | % ---------------------------------------------------------------------------------------------------------- 71 | % Function: -> websocket_close | {websocket_close, DataToSendBeforeClose::binary() | iolist()} | NewState 72 | % Description: Callback to handle incomed data. 73 | % ---------------------------------------------------------------------------------------------------------- 74 | -spec handle_data(Data::binary(), 75 | State::undefined | term(), 76 | {Socket::socket(), SocketMode::socketmode(), WsHandleLoopPid::pid()}) -> websocket_close | {websocket_close, binary()} | term(). 77 | handle_data(Data, St, Tuple) -> 78 | ?HYBI_COMMON:handle_data(Data, St, Tuple). 79 | 80 | % ---------------------------------------------------------------------------------------------------------- 81 | % Function: -> binary() | iolist() 82 | % Description: Callback to format data before it is sent into the socket. 83 | % ---------------------------------------------------------------------------------------------------------- 84 | -spec send_format(Data::iolist(), State::term()) -> binary(). 85 | send_format(Data, State) -> 86 | ?HYBI_COMMON:send_format(Data, State). 87 | 88 | % ============================ /\ API ====================================================================== 89 | 90 | 91 | % ============================ \/ INTERNAL FUNCTIONS ======================================================= 92 | 93 | % ============================ /\ INTERNAL FUNCTIONS ======================================================= 94 | -------------------------------------------------------------------------------- /src/misultin_websocket_draft-hybi-17.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - WebSocket - draft hybi 17 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli . 7 | % All rights reserved. 8 | % 9 | % Code portions from Joe Armstrong have been originally taken under MIT license at the address: 10 | % 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-hybi-17'). 34 | -behaviour(misultin_websocket). 35 | -vsn("0.9"). 36 | 37 | % API 38 | -export([check_websocket/1, handshake/3, handle_data/3, send_format/2]). 39 | 40 | % macros 41 | -define(HYBI_COMMON, 'misultin_websocket_draft-hybi-10_17'). 42 | 43 | % includes 44 | -include("../include/misultin.hrl"). 45 | 46 | 47 | % ============================ \/ API ====================================================================== 48 | 49 | % ---------------------------------------------------------------------------------------------------------- 50 | % Function: -> true | false 51 | % Description: Callback to check if the incoming request is a websocket request according to this protocol. 52 | % ---------------------------------------------------------------------------------------------------------- 53 | -spec check_websocket(Headers::http_headers()) -> boolean(). 54 | check_websocket(Headers) -> 55 | % set required headers 56 | RequiredHeaders = [ 57 | {'Upgrade', "websocket"}, {'Connection', "Upgrade"}, {'Host', ignore}, 58 | {'Sec-Websocket-Key', ignore}, {'Sec-WebSocket-Version', "13"} 59 | ], 60 | ?HYBI_COMMON:check_websocket(Headers, RequiredHeaders). 61 | 62 | % ---------------------------------------------------------------------------------------------------------- 63 | % Function: -> iolist() | binary() 64 | % Description: Callback to build handshake data. 65 | % ---------------------------------------------------------------------------------------------------------- 66 | -spec handshake(Req::#req{}, Headers::http_headers(), {Path::string(), Origin::string(), Host::string()}) -> iolist(). 67 | handshake(Req, Headers, {Path, Origin, Host}) -> 68 | ?HYBI_COMMON:handshake(Req, Headers, {Path, Origin, Host}). 69 | 70 | % ---------------------------------------------------------------------------------------------------------- 71 | % Function: -> websocket_close | {websocket_close, DataToSendBeforeClose::binary() | iolist()} | NewState 72 | % Description: Callback to handle incomed data. 73 | % ---------------------------------------------------------------------------------------------------------- 74 | -spec handle_data(Data::binary(), 75 | State::undefined | term(), 76 | {Socket::socket(), SocketMode::socketmode(), WsHandleLoopPid::pid()}) -> websocket_close | {websocket_close, binary()} | term(). 77 | handle_data(Data, St, Tuple) -> 78 | ?HYBI_COMMON:handle_data(Data, St, Tuple). 79 | 80 | % ---------------------------------------------------------------------------------------------------------- 81 | % Function: -> binary() | iolist() 82 | % Description: Callback to format data before it is sent into the socket. 83 | % ---------------------------------------------------------------------------------------------------------- 84 | -spec send_format(Data::iolist(), State::term()) -> binary(). 85 | send_format(Data, State) -> 86 | ?HYBI_COMMON:send_format(Data, State). 87 | 88 | % ============================ /\ API ====================================================================== 89 | 90 | 91 | % ============================ \/ INTERNAL FUNCTIONS ======================================================= 92 | 93 | % ============================ /\ INTERNAL FUNCTIONS ======================================================= 94 | -------------------------------------------------------------------------------- /src/misultin_ws.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Websocket Request 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli . 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_ws). 31 | -vsn("0.9"). 32 | 33 | % API 34 | -export([raw/1, get/2, get_cookies/1, get_cookie_value/3, send/2]). 35 | -export([session/1, session/2, save_session_state/3]). 36 | 37 | % includes 38 | -include("../include/misultin.hrl"). 39 | 40 | % types 41 | -type wst() :: {misultin_ws, SocketPid::pid()}. 42 | 43 | 44 | % ============================ \/ API ====================================================================== 45 | 46 | % Returns raw websocket content. 47 | -spec raw(wst()) -> #ws{}. 48 | raw({misultin_ws, SocketPid}) -> 49 | misultin_websocket:get_wsinfo(SocketPid, raw). 50 | 51 | % Get websocket info. 52 | -spec get(WsInfo::atom(), wst()) -> term(). 53 | get(WsInfo, {misultin_ws, SocketPid}) when 54 | WsInfo =:= socket; 55 | WsInfo =:= socket_mode; 56 | WsInfo =:= peer_port; 57 | WsInfo =:= peer_cert; 58 | WsInfo =:= vsn; 59 | WsInfo =:= origin; 60 | WsInfo =:= host; 61 | WsInfo =:= path; 62 | WsInfo =:= headers -> 63 | misultin_websocket:get_wsinfo(SocketPid, WsInfo); 64 | get(peer_addr, {misultin_ws, SocketPid}) -> 65 | Headers = get(headers, {misultin_ws, SocketPid}), 66 | ConnectionPeerAddr = misultin_websocket:get_wsinfo(SocketPid, peer_addr), 67 | misultin_utility:get_peer(Headers, ConnectionPeerAddr). 68 | 69 | % ---------------------------- \/ Cookies ------------------------------------------------------------------ 70 | 71 | % Get all cookies. 72 | -spec get_cookies(wst()) -> gen_proplist(). 73 | get_cookies(WsT) -> 74 | Headers = get(headers, WsT), 75 | case misultin_utility:get_key_value('Cookie', Headers) of 76 | undefined -> []; 77 | CookieContent -> 78 | F = fun({Tag, Val}, Acc) -> 79 | [{misultin_utility:unquote(Tag), misultin_utility:unquote(Val)}|Acc] 80 | end, 81 | lists:foldl(F, [], misultin_cookies:parse_cookie(CookieContent)) 82 | end. 83 | 84 | % Get the value of a single cookie 85 | -spec get_cookie_value(CookieTag::string(), Cookies::gen_proplist(), wst()) -> undefined | string(). 86 | get_cookie_value(CookieTag, Cookies, _WsT) -> 87 | misultin_utility:get_key_value(CookieTag, Cookies). 88 | 89 | % ---------------------------- /\ Cookies ------------------------------------------------------------------ 90 | 91 | % ---------------------------- \/ Sessions ----------------------------------------------------------------- 92 | 93 | % get session id and state 94 | -spec session(WsT::wst()) -> {error, Reason::term()} | {SessionId::string(), SessionState::term()}. 95 | -spec session(Cookies::gen_proplist(), WsT::wst()) -> {error, Reason::term()} | {SessionId::string(), SessionState::term()}. 96 | session(WsT) -> 97 | Cookies = get_cookies(WsT), 98 | session(Cookies, WsT). 99 | session(Cookies, {misultin_ws, SocketPid}) -> 100 | misultin_websocket:session_cmd(SocketPid, {session, Cookies}). 101 | 102 | % save session state 103 | -spec save_session_state(SessionId::string(), SessionState::term(), WsT::wst()) -> {error, Reason::term()} | ok. 104 | save_session_state(SessionId, SessionState, {misultin_ws, SocketPid}) -> 105 | misultin_websocket:session_cmd(SocketPid, {save_session_state, SessionId, SessionState}). 106 | 107 | % ---------------------------- /\ Sessions ------------------------------------------------------------------ 108 | 109 | % send data 110 | -spec send(Data::list() | binary() | iolist(), wst()) -> term(). 111 | send(Data, {misultin_ws, SocketPid}) -> 112 | SocketPid ! {send, Data}. 113 | 114 | % ============================ /\ API ====================================================================== 115 | 116 | 117 | 118 | % ============================ \/ INTERNAL FUNCTIONS ======================================================= 119 | 120 | % ============================ /\ INTERNAL FUNCTIONS ======================================================= 121 | --------------------------------------------------------------------------------