├── .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 |
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 |
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 |
--------------------------------------------------------------------------------