├── .gitignore ├── LICENSE ├── Makefile ├── NOTICE ├── README.md ├── doc ├── README.md ├── barrel.md ├── barrel_acceptor.md ├── barrel_app.md ├── barrel_connections.md ├── barrel_deps.md ├── barrel_listener.md ├── barrel_server.md ├── barrel_ssl.md ├── barrel_sup.md ├── barrel_tcp.md ├── barrel_util.md └── overview.edoc ├── example ├── echo │ ├── README.md │ ├── rebar.config │ ├── src │ │ ├── echo.app.src │ │ ├── echo.erl │ │ ├── echo_app.erl │ │ ├── echo_handler.erl │ │ └── echo_sup.erl │ └── start.sh └── echo_p │ ├── README.md │ ├── rebar.config │ ├── src │ ├── echo.app.src │ ├── echo.erl │ ├── echo_app.erl │ ├── echo_handler.erl │ └── echo_sup.erl │ └── start.sh ├── rebar ├── rebar.config ├── rebar_dev.config └── src ├── barrel.app.src ├── barrel.erl ├── barrel_acceptor.erl ├── barrel_app.erl ├── barrel_connections.erl ├── barrel_deps.erl ├── barrel_listener.erl ├── barrel_server.erl ├── barrel_ssl.erl ├── barrel_sup.erl ├── barrel_tcp.erl └── barrel_util.erl /.gitignore: -------------------------------------------------------------------------------- 1 | ebin 2 | *.swp 3 | *.dump 4 | edoc-info 5 | .DS_Store 6 | deps/ 7 | doc/*.html 8 | doc/*.css 9 | doc/erlang.png 10 | doc/edoc-info 11 | t/*.beam 12 | examples/*.beam 13 | .eunit/* 14 | log 15 | 16 | examples/echo/log 17 | examples/echo/deps 18 | examples/echo/ebin 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2013 (c) Benoît Chesneau 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | REBAR?=./rebar 2 | 3 | all: build 4 | 5 | dev: devbuild 6 | 7 | doc: dev 8 | $(REBAR) -C rebar_dev.config doc 9 | 10 | clean: 11 | $(REBAR) clean 12 | 13 | distclean: clean 14 | @rm -rf deps 15 | 16 | build: deps 17 | $(REBAR) compile 18 | 19 | deps: 20 | $(REBAR) get-deps 21 | 22 | 23 | # development 24 | # 25 | devclean: 26 | $(REBAR) -C rebar_dev.config clean 27 | 28 | devbuild: devdeps 29 | $(REBAR) -C rebar_dev.config compile 30 | 31 | devdeps: 32 | $(REBAR) -C rebar_dev.config get-deps 33 | 34 | 35 | .PHONY: doc deps 36 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | barrel 2 | ------ 3 | 4 | 2013 (c) Benoît Chesneau 5 | 6 | barrel is released under the MIT license. See the LICENSE 7 | file for the complete license. 8 | 9 | 10 | Third parties 11 | ------------- 12 | 13 | *) barrel_tcp, barrel_ssl under ISC license: 14 | Copyright (c) 2011-2012, Loïc Hoguin 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # barrel - generic TCP acceptor pool # 4 | 5 | Copyright (c) 2013 Benoît Chesneau. 6 | 7 | __Version:__ 2.1 8 | 9 | 10 | barrel is a **generic TCP acceptor pool** with low latency in Erlang. 11 | 12 | **Main Features**: 13 | 14 | - start/stop TCP and SSL listener 15 | - can be use with different transports based on an "accept" model. (can 16 | be stcp, uTCP...) 17 | - Low latency when accepting connection 18 | - Any protocol can be used, can be HTTP, IRC or anything you want. 19 | - Graceful reload of protocol configurations 20 | - Scale the number of concurrent connections accepted at the same time 21 | - Scale the number of concurrent connections handled by a listener 22 | 23 | ## Design 24 | 25 | > The design of barrel differs from 26 | > [ranch](http://github.com/extend/ranch). Instead of spawning 27 | > a new handler for each request, then making it control the socket - which 28 | > can be slow - barrel spawns new acceptors. The accepted 29 | > socket will continue to be used in the same process that accepted it 30 | > previously. Optionally you can launch a process to handle the 31 | > accepted socket if you want. The choice is yours. 32 | 33 | ## Usage 34 | 35 | ### Create a simple TCP echo server. 36 | 37 | 1. Create a simple echo handler 38 | 39 | ``` 40 | -module(echo_handler). 41 | 42 | -export([init/3]). 43 | 44 | init(_ListenerRef, Transport, Socket, _Opts) -> 45 | wait_request(Transport, Socket). 46 | 47 | wait_request(Transport, Socket) -> 48 | case Transport:recv(Socket, 0, 5000) of 49 | {ok, <<".\r\n">>} -> 50 | io:format("remote exited", []), 51 | Transport:close(Socket); 52 | {ok, Data} -> 53 | io:format("got ~p~n", [Data]), 54 | Transport:send(Socket, Data), 55 | wait_request(Transport, Socket); 56 | {error, _Reason} -> 57 | Transport:close(Socket) 58 | end. 59 | ``` 60 | 61 | `init/4` is the function that receives the transport (`barrel_tcp` if TCP or 62 | `barrel_ssl` if SSL) socket. 63 | 64 | Note that, by default, barrel uses the process accepting 65 | the connection to handle the request. In other words, the acceptor process 66 | becomes the request process. In the other case you may want to launch a new 67 | process instead and pass the control of the socket to it (which is the 68 | only way to do it in ranch). In this case instead of `init/4` use a 69 | `start_link/4` function. Eg. : 70 | 71 | ``` 72 | start_link(Ref, Transport, Socket, _Opts) -> 73 | Pid = spawn_link(?MODULE, init, [Ref, Transport, Socket]), 74 | {ok, Pid}. 75 | 76 | init(Ref, Transport, Socket) -> 77 | ok = barrel:accept_ack(Ref), 78 | wait_request(Transport, Socket). 79 | 80 | ... 81 | ``` 82 | 83 | To start the listener do the following: 84 | 85 | ``` 86 | Ref = echo, 87 | NbAcceptors = 100, 88 | Transport = barrel_tcp, 89 | TransporOptions = [{port, 10001}], 90 | Protocol = echo_handler, 91 | ProtocolOptions = [], 92 | barrel:start_listener(Ref, NbAcceptors, Transport, TransportOptions, 93 | Protocol, ProtocolOptions). 94 | ``` 95 | 96 | A Ref can be any Erlang term used to identify a listener. A listener is 97 | a gen_server that manages all the acceptors workers and handles connection 98 | shutdowns. 99 | 100 | A `Protocol` is the protocol used to handle a connection. It can be any 101 | module following the protocol behaviour. Currently Barrel handles the TCP 102 | (`barel_tcp`) and SSL/TLS (`barel_ssl`) protocols. 103 | 104 | A `Protocol` is what will be used to handle the data coming from the 105 | socket. You can pass `ProtocolOpts` to it. 106 | 107 | Additionaly you can pass custom options to the listener. This is where 108 | you pass the SSL options, for example. 109 | 110 | The full example can be found in the [example folder](http://github.com/benoitc/barrel/tree/master/example/echo). 111 | 112 | ### Scale 113 | 114 | #### Number of acceptors 115 | 116 | There are 2 way of scaling a listener in barrel. One is to increase the 117 | number of acceptors. By default the the number of acceptors is 100. 118 | 119 | To do it use the `barrel:set_nb_acceptors/2` function. 120 | 121 | Increasing the number of acceptors will increase the number of 122 | concurrent connections you can **accept** at the same time 123 | 124 | ### Number of clients 125 | 126 | While increasing the number of acceptors increase the number of 127 | concurrent connections you **accept** at the same time; you can also fix 128 | the number of concurrent conncections (clients) you are willing to handle. 129 | This can be used to limit the usage of the resources (memory and 130 | file descriptors). 131 | 132 | To do it use the `barrel:set_max_client/2` function. 133 | 134 | ### Load a new protocol configuration 135 | 136 | Barrel allows you to either change the protocol handler or its 137 | configuration without closing the active connections. 138 | 139 | What happens here is that once you pass a new protocol configuration 140 | using the function `barrel:set_protocol_conf/4` to the 141 | listener, new acceptors will be launched with the new configuration and 142 | old acceptors will get killed right after. Once it's done, a graceful 143 | shutdown will be sent to the remaining connections. If the graceful timeout is 144 | not defined, the connections will continue to run until they die, otherwise 145 | the connections will get killed after that time passes. This behaviour is 146 | similar to the one you can find in 147 | [nginx](http://wiki.nginx.org/CommandLine#Loading_a_New_Configuration_Using_Signals). 148 | 149 | ## Contribute 150 | 151 | For issues, comments or feedback please [create an 152 | issue](http://github.com/benoitc/barrel/issues). 153 | 154 | ### Notes for developers 155 | 156 | If you want to contribute patches or improve the docs, you will need to 157 | build Barrel using the `rebar_dev.config` file. It can also be built 158 | using the **Makefile**: 159 | 160 | ``` 161 | $ make dev ; # compile & get deps 162 | $ make devclean ; # clean all files 163 | ``` 164 | 165 | 166 | 167 | ## Modules ## 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 |
barrel
barrel_acceptor
barrel_app
barrel_connections
barrel_deps
barrel_listener
barrel_server
barrel_ssl
barrel_sup
barrel_tcp
barrel_util
182 | 183 | -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # barrel - generic TCP acceptor pool # 4 | 5 | Copyright (c) 2013 Benoît Chesneau. 6 | 7 | __Version:__ 2.1 8 | 9 | 10 | barrel is a **generic TCP acceptor pool** with low latency in Erlang. 11 | 12 | **Main Features**: 13 | 14 | - start/stop TCP and SSL listener 15 | - can be use with different transports based on an "accept" model. (can 16 | be stcp, uTCP...) 17 | - Low latency when accepting connection 18 | - Any protocol can be used, can be HTTP, IRC or anything you want. 19 | - Graceful reload of protocol configurations 20 | - Scale the number of concurrent connections accepted at the same time 21 | - Scale the number of concurrent connections handled by a listener 22 | 23 | ## Design 24 | 25 | > The design of barrel differs from 26 | > [ranch](http://github.com/extend/ranch). Instead of spawning 27 | > a new handler for each request, then making it control the socket - which 28 | > can be slow - barrel spawns new acceptors. The accepted 29 | > socket will continue to be used in the same process that accepted it 30 | > previously. Optionally you can launch a process to handle the 31 | > accepted socket if you want. The choice is yours. 32 | 33 | ## Usage 34 | 35 | ### Create a simple TCP echo server. 36 | 37 | 1. Create a simple echo handler 38 | 39 | ``` 40 | -module(echo_handler). 41 | 42 | -export([init/3]). 43 | 44 | init(_ListenerRef, Transport, Socket, _Opts) -> 45 | wait_request(Transport, Socket). 46 | 47 | wait_request(Transport, Socket) -> 48 | case Transport:recv(Socket, 0, 5000) of 49 | {ok, <<".\r\n">>} -> 50 | io:format("remote exited", []), 51 | Transport:close(Socket); 52 | {ok, Data} -> 53 | io:format("got ~p~n", [Data]), 54 | Transport:send(Socket, Data), 55 | wait_request(Transport, Socket); 56 | {error, _Reason} -> 57 | Transport:close(Socket) 58 | end. 59 | ``` 60 | 61 | `init/4` is the function that receives the transport (`barrel_tcp` if TCP or 62 | `barrel_ssl` if SSL) socket. 63 | 64 | Note that, by default, barrel uses the process accepting 65 | the connection to handle the request. In other words, the acceptor process 66 | becomes the request process. In the other case you may want to launch a new 67 | process instead and pass the control of the socket to it (which is the 68 | only way to do it in ranch). In this case instead of `init/4` use a 69 | `start_link/4` function. Eg. : 70 | 71 | ``` 72 | start_link(Ref, Transport, Socket, _Opts) -> 73 | Pid = spawn_link(?MODULE, init, [Ref, Transport, Socket]), 74 | {ok, Pid}. 75 | 76 | init(Ref, Transport, Socket) -> 77 | ok = barrel:accept_ack(Ref), 78 | wait_request(Transport, Socket). 79 | 80 | ... 81 | ``` 82 | 83 | To start the listener do the following: 84 | 85 | ``` 86 | Ref = echo, 87 | NbAcceptors = 100, 88 | Transport = barrel_tcp, 89 | TransporOptions = [{port, 10001}], 90 | Protocol = echo_handler, 91 | ProtocolOptions = [], 92 | barrel:start_listener(Ref, NbAcceptors, Transport, TransportOptions, 93 | Protocol, ProtocolOptions). 94 | ``` 95 | 96 | A Ref can be any Erlang term used to identify a listener. A listener is 97 | a gen_server that manages all the acceptors workers and handles connection 98 | shutdowns. 99 | 100 | A `Protocol` is the protocol used to handle a connection. It can be any 101 | module following the protocol behaviour. Currently Barrel handles the TCP 102 | (`barel_tcp`) and SSL/TLS (`barel_ssl`) protocols. 103 | 104 | A `Protocol` is what will be used to handle the data coming from the 105 | socket. You can pass `ProtocolOpts` to it. 106 | 107 | Additionaly you can pass custom options to the listener. This is where 108 | you pass the SSL options, for example. 109 | 110 | The full example can be found in the [example folder](http://github.com/benoitc/barrel/tree/master/example/echo). 111 | 112 | ### Scale 113 | 114 | #### Number of acceptors 115 | 116 | There are 2 way of scaling a listener in barrel. One is to increase the 117 | number of acceptors. By default the the number of acceptors is 100. 118 | 119 | To do it use the `barrel:set_nb_acceptors/2` function. 120 | 121 | Increasing the number of acceptors will increase the number of 122 | concurrent connections you can **accept** at the same time 123 | 124 | ### Number of clients 125 | 126 | While increasing the number of acceptors increase the number of 127 | concurrent connections you **accept** at the same time; you can also fix 128 | the number of concurrent conncections (clients) you are willing to handle. 129 | This can be used to limit the usage of the resources (memory and 130 | file descriptors). 131 | 132 | To do it use the `barrel:set_max_client/2` function. 133 | 134 | ### Load a new protocol configuration 135 | 136 | Barrel allows you to either change the protocol handler or its 137 | configuration without closing the active connections. 138 | 139 | What happens here is that once you pass a new protocol configuration 140 | using the function `barrel:set_protocol_conf/4` to the 141 | listener, new acceptors will be launched with the new configuration and 142 | old acceptors will get killed right after. Once it's done, a graceful 143 | shutdown will be sent to the remaining connections. If the graceful timeout is 144 | not defined, the connections will continue to run until they die, otherwise 145 | the connections will get killed after that time passes. This behaviour is 146 | similar to the one you can find in 147 | [nginx](http://wiki.nginx.org/CommandLine#Loading_a_New_Configuration_Using_Signals). 148 | 149 | ## Contribute 150 | 151 | For issues, comments or feedback please [create an 152 | issue](http://github.com/benoitc/barrel/issues). 153 | 154 | ### Notes for developers 155 | 156 | If you want to contribute patches or improve the docs, you will need to 157 | build Barrel using the `rebar_dev.config` file. It can also be built 158 | using the **Makefile**: 159 | 160 | ``` 161 | $ make dev ; # compile & get deps 162 | $ make devclean ; # clean all files 163 | ``` 164 | 165 | 166 | 167 | ## Modules ## 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 |
barrel
barrel_acceptor
barrel_app
barrel_connections
barrel_deps
barrel_listener
barrel_server
barrel_ssl
barrel_sup
barrel_tcp
barrel_util
182 | 183 | -------------------------------------------------------------------------------- /doc/barrel.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Module barrel # 4 | * [Data Types](#types) 5 | * [Function Index](#index) 6 | * [Function Details](#functions) 7 | 8 | 9 | 10 | 11 | 12 | ## Data Types ## 13 | 14 | 15 | 16 | 17 | ### info_key() ### 18 | 19 | 20 | 21 |

 22 | info_key() = ip | port | open_reqs | nb_acceptors | max_clients
 23 | 
24 | 25 | 26 | 27 | 28 | 29 | ### info_keys() ### 30 | 31 | 32 | 33 |

 34 | info_keys() = [info_key()]
 35 | 
36 | 37 | 38 | 39 | 40 | 41 | ### ref() ### 42 | 43 | 44 | 45 |

 46 | ref() = any()
 47 | 
48 | 49 | 50 | 51 | 52 | ## Function Index ## 53 | 54 | 55 |
accept_ack/1 used to start to handle the connection in a spawned protocol process.
child_spec/2return a child spec suitable for embeding your listener in the 56 | supervisor.
get_max_clients/1get max number of concurrent clients.
get_nb_acceptors/1get the number of acceptors set for a listener.
get_port/1get current port of a listener.
get_protocol_conf/1get the protocol configuration.
info/1get all infos of a listener.
info/2get info for some keys.
remove_connection/2remove a connection from the connection manager 57 | Useful when you want to keep this connection open but don't want to 58 | count in the concurrent connections managed by a listener.
set_max_clients/2set max number of concurrent clients.
set_nb_acceptors/2set the number of acceptors for a listener.
set_protocol_conf/3update the protocol configuration and kill the connections after 59 | 30s.
set_protocol_conf/4update the protocol configuration and kill the connections after 60 | a timeout.
start/0Start the barrel application.
start_listener/6start a listener.
start_listener/7
stop/0Start the coffer application.
stop_listener/1stop a listener 61 | All connections and acceptors for this listener are killed 62 | immediately.
63 | 64 | 65 | 66 | 67 | ## Function Details ## 68 | 69 | 70 | 71 | ### accept_ack/1 ### 72 | 73 | `accept_ack(Ref) -> any()` 74 | 75 | used to start to handle the connection in a spawned protocol process. 76 | It is needed to use this function first so the control of the socket 77 | is given to the process. 78 | 79 | 80 | ### child_spec/2 ### 81 | 82 | 83 |

 84 | child_spec(Ref::barrel:ref(), Options::any()) -> any()
 85 | 
86 | 87 |

88 | 89 | 90 | return a child spec suitable for embeding your listener in the 91 | supervisor 92 | 93 | 94 | ### get_max_clients/1 ### 95 | 96 | `get_max_clients(Ref) -> any()` 97 | 98 | get max number of concurrent clients 99 | 100 | 101 | ### get_nb_acceptors/1 ### 102 | 103 | `get_nb_acceptors(Ref) -> any()` 104 | 105 | get the number of acceptors set for a listener 106 | 107 | 108 | ### get_port/1 ### 109 | 110 | 111 |

112 | get_port(Ref::barrel:ref()) -> integer()
113 | 
114 | 115 |

116 | 117 | 118 | 119 | get current port of a listener 120 | 121 | 122 | Ref = term() 123 | 124 | 125 | ### get_protocol_conf/1 ### 126 | 127 | `get_protocol_conf(Ref) -> any()` 128 | 129 | get the protocol configuration 130 | 131 | 132 | ### info/1 ### 133 | 134 | 135 |

136 | info(Ref::barrel:ref()) -> any()
137 | 
138 | 139 |

140 | 141 | 142 | 143 | get all infos of a listener 144 | 145 | 146 | %% Ref = term() 147 | 148 | 149 | ### info/2 ### 150 | 151 | 152 |

153 | info(Ref::barrel:ref(), Key::info_keys()) -> any()
154 | 
155 | 156 |

157 | 158 | 159 | 160 | get info for some keys 161 | 162 | 163 | Ref = term() 164 | Key = ip | port | open_reqs | nb_acceptors | max_clients 165 | 166 | 167 | ### remove_connection/2 ### 168 | 169 | `remove_connection(Ref, Pid) -> any()` 170 | 171 | remove a connection from the connection manager 172 | Useful when you want to keep this connection open but don't want to 173 | count in the concurrent connections managed by a listener 174 | 175 | 176 | ### set_max_clients/2 ### 177 | 178 | `set_max_clients(Ref, MaxClients) -> any()` 179 | 180 | set max number of concurrent clients 181 | 182 | 183 | ### set_nb_acceptors/2 ### 184 | 185 | `set_nb_acceptors(Ref, Nb) -> any()` 186 | 187 | set the number of acceptors for a listener. By default 100. 188 | 189 | 190 | ### set_protocol_conf/3 ### 191 | 192 | `set_protocol_conf(Ref, Handler, Options) -> any()` 193 | 194 | update the protocol configuration and kill the connections after 195 | 30s. 196 | 197 | 198 | ### set_protocol_conf/4 ### 199 | 200 | `set_protocol_conf(Ref, Handler, Options, GracefulTimeout) -> any()` 201 | 202 | update the protocol configuration and kill the connections after 203 | a timeout. If timeout is none then the connections will continue 204 | until they die. 205 | 206 | 207 | ### start/0 ### 208 | 209 | `start() -> any()` 210 | 211 | Start the barrel application. Useful when testing using the shell. 212 | 213 | 214 | ### start_listener/6 ### 215 | 216 | 217 |

218 | start_listener(Ref::barrel:ref(), NbAcceptors::integer(), Transport::any(), TransOpts::any(), Protocol::any(), ProtocolOpts::any()) -> {ok, pid()} | {error, term()}
219 | 
220 | 221 |

222 | 223 | 224 | 225 | start a listener 226 | 227 | 228 | 229 | ``` 230 | Ref = term() 231 | NbAcceptors = integer() 232 | Transport = barrel_tcp | barrel_ssl | any 233 | TransOpts = any() 234 | Protocol = any() 235 | ProtocolOpts = any() 236 | ListenerOpts - any(), 237 | ``` 238 | 239 | 240 | 241 | A Ref can be any Erlang term used to identify a listener. A listener 242 | is a gen_server that manage all acceptors workers and handle 243 | connections shutdown. 244 | 245 | 246 | 247 | A protocol is the protocol used to handle a connection. It can be 248 | any module following the protocol behaviour. Barrel offers 249 | to handle the TCP ans SSL/TLS protocol for now. 250 | 251 | 252 | 253 | A protocol is what will be used to handle the data coming from the 254 | socket. You can pass to it some options (ProtocolOpts). 255 | 256 | 257 | Optionnaly you can pass custom options to the listener. This is where 258 | you pass the SSL options for example. 259 | 260 | 261 | ### start_listener/7 ### 262 | 263 | 264 |

265 | start_listener(Ref::barrel:ref(), NbAcceptors::integer(), Transport::any(), TransOpts::any(), Protocol::any(), ProtoOpts::any(), ListenerOpts0::any()) -> {ok, pid()} | {error, term()}
266 | 
267 | 268 |

269 | 270 | 271 | 272 | 273 | 274 | ### stop/0 ### 275 | 276 | `stop() -> any()` 277 | 278 | Start the coffer application. Useful when testing using the shell. 279 | 280 | 281 | ### stop_listener/1 ### 282 | 283 | 284 |

285 | stop_listener(Ref::barrel:ref()) -> ok | {error, term()}
286 | 
287 | 288 |

289 | 290 | 291 | stop a listener 292 | All connections and acceptors for this listener are killed 293 | immediately. 294 | -------------------------------------------------------------------------------- /doc/barrel_acceptor.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Module barrel_acceptor # 4 | * [Function Index](#index) 5 | * [Function Details](#functions) 6 | 7 | 8 | 9 | 10 | ## Function Index ## 11 | 12 | 13 |
accept/6
start_link/5
14 | 15 | 16 | 17 | 18 | ## Function Details ## 19 | 20 | 21 | 22 | ### accept/6 ### 23 | 24 | `accept(Listener, Ref, Transport, ListenSocket, Opts, Protocol) -> any()` 25 | 26 | 27 | 28 | 29 | ### start_link/5 ### 30 | 31 | `start_link(Listener, Transport, ListenSocket, Opts, Protocol) -> any()` 32 | 33 | 34 | -------------------------------------------------------------------------------- /doc/barrel_app.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Module barrel_app # 4 | * [Function Index](#index) 5 | * [Function Details](#functions) 6 | 7 | __Behaviours:__ [`application`](application.md). 8 | 9 | 10 | ## Function Index ## 11 | 12 | 13 |
ensure_deps_started/0
start/2
stop/1
14 | 15 | 16 | 17 | 18 | ## Function Details ## 19 | 20 | 21 | 22 | ### ensure_deps_started/0 ### 23 | 24 | `ensure_deps_started() -> any()` 25 | 26 | 27 | 28 | 29 | ### start/2 ### 30 | 31 | `start(StartType, StartArgs) -> any()` 32 | 33 | 34 | 35 | 36 | ### stop/1 ### 37 | 38 | `stop(State) -> any()` 39 | 40 | 41 | -------------------------------------------------------------------------------- /doc/barrel_connections.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Module barrel_connections # 4 | * [Function Index](#index) 5 | * [Function Details](#functions) 6 | 7 | __Behaviours:__ [`gen_server`](gen_server.md). 8 | 9 | 10 | ## Function Index ## 11 | 12 | 13 |
add_connection/2
code_change/3
handle_call/3
handle_cast/2
handle_info/2
init/1
shutdown/2
start/0
stop/1
terminate/2
14 | 15 | 16 | 17 | 18 | ## Function Details ## 19 | 20 | 21 | 22 | ### add_connection/2 ### 23 | 24 | `add_connection(Pid, ConnPid) -> any()` 25 | 26 | 27 | 28 | 29 | ### code_change/3 ### 30 | 31 | `code_change(OldVsn, State, Extra) -> any()` 32 | 33 | 34 | 35 | 36 | ### handle_call/3 ### 37 | 38 | `handle_call(Msg, From, State) -> any()` 39 | 40 | 41 | 42 | 43 | ### handle_cast/2 ### 44 | 45 | `handle_cast(Msg, State) -> any()` 46 | 47 | 48 | 49 | 50 | ### handle_info/2 ### 51 | 52 | `handle_info(Info, State) -> any()` 53 | 54 | 55 | 56 | 57 | ### init/1 ### 58 | 59 | `init(X1) -> any()` 60 | 61 | 62 | 63 | 64 | ### shutdown/2 ### 65 | 66 | `shutdown(Pid, Timeout) -> any()` 67 | 68 | 69 | 70 | 71 | ### start/0 ### 72 | 73 | `start() -> any()` 74 | 75 | 76 | 77 | 78 | ### stop/1 ### 79 | 80 | `stop(Pid) -> any()` 81 | 82 | 83 | 84 | 85 | ### terminate/2 ### 86 | 87 | `terminate(Reason, State) -> any()` 88 | 89 | 90 | -------------------------------------------------------------------------------- /doc/barrel_deps.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Module barrel_deps # 4 | * [Function Index](#index) 5 | * [Function Details](#functions) 6 | 7 | 8 | 9 | 10 | ## Function Index ## 11 | 12 | 13 |
deps_on_path/0List of project dependencies on the path.
ensure/0Ensure that the ebin and include paths for dependencies of 14 | this application are on the code path.
ensure/1Ensure that all ebin and include paths for dependencies 15 | of the application for Module are on the code path.
get_base_dir/0Return the application directory for this application.
get_base_dir/1Return the application directory for Module.
local_path/1Return an application-relative directory for this application.
local_path/2Return an application-relative directory from Module's application.
new_siblings/1Find new siblings paths relative to Module that aren't already on the 16 | code path.
17 | 18 | 19 | 20 | 21 | ## Function Details ## 22 | 23 | 24 | 25 | ### deps_on_path/0 ### 26 | 27 | 28 |

 29 | deps_on_path() -> [ProjNameAndVers]
 30 | 
31 | 32 |

33 | 34 | 35 | List of project dependencies on the path. 36 | 37 | 38 | ### ensure/0 ### 39 | 40 | 41 |

 42 | ensure() -> ok
 43 | 
44 | 45 |

46 | 47 | 48 | Ensure that the ebin and include paths for dependencies of 49 | this application are on the code path. Equivalent to 50 | ensure(?Module). 51 | 52 | 53 | ### ensure/1 ### 54 | 55 | 56 |

 57 | ensure(Module) -> ok
 58 | 
59 | 60 |

61 | 62 | 63 | Ensure that all ebin and include paths for dependencies 64 | of the application for Module are on the code path. 65 | 66 | 67 | ### get_base_dir/0 ### 68 | 69 | 70 |

 71 | get_base_dir() -> string()
 72 | 
73 | 74 |

75 | 76 | 77 | Return the application directory for this application. Equivalent to 78 | get_base_dir(?MODULE). 79 | 80 | 81 | ### get_base_dir/1 ### 82 | 83 | 84 |

 85 | get_base_dir(Module) -> string()
 86 | 
87 | 88 |

89 | 90 | 91 | Return the application directory for Module. It assumes Module is in 92 | a standard OTP layout application in the ebin or src directory. 93 | 94 | 95 | ### local_path/1 ### 96 | 97 | 98 |

 99 | local_path(Components) -> string()
100 | 
101 | 102 |

103 | 104 | 105 | Return an application-relative directory for this application. 106 | Equivalent to local_path(Components, ?MODULE). 107 | 108 | 109 | ### local_path/2 ### 110 | 111 | 112 |

113 | local_path(Components::[string()], Module) -> string()
114 | 
115 | 116 |

117 | 118 | 119 | Return an application-relative directory from Module's application. 120 | 121 | 122 | ### new_siblings/1 ### 123 | 124 | 125 |

126 | new_siblings(Module) -> [Dir]
127 | 
128 | 129 |

130 | 131 | 132 | Find new siblings paths relative to Module that aren't already on the 133 | code path. 134 | -------------------------------------------------------------------------------- /doc/barrel_listener.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Module barrel_listener # 4 | * [Function Index](#index) 5 | * [Function Details](#functions) 6 | 7 | __Behaviours:__ [`gen_server`](gen_server.md). 8 | 9 | 10 | ## Function Index ## 11 | 12 | 13 |
code_change/3
get_max_clients/1get max number of concurrent clients.
get_nb_acceptors/1get the number of acceptors.
get_port/1get current port.
get_protocol_conf/1get the protocol configuration.
handle_call/3
handle_cast/2
handle_info/2
info/1get all infos.
info/2get info for some keys.
init/1
remove_connection/2remove a connection from the connection manager.
set_max_clients/2set max number of concurrent clients.
set_nb_acceptors/2set the number of acceptors.
set_protocol_conf/4update the protocol configuration and kill after a timeout.
start_accepting/1internal api, tell to the acceptor if he can start to accept a new 14 | connection.
start_link/1
terminate/2
15 | 16 | 17 | 18 | 19 | ## Function Details ## 20 | 21 | 22 | 23 | ### code_change/3 ### 24 | 25 | `code_change(OldVsn, State, Extra) -> any()` 26 | 27 | 28 | 29 | 30 | ### get_max_clients/1 ### 31 | 32 | `get_max_clients(Ref) -> any()` 33 | 34 | get max number of concurrent clients 35 | 36 | 37 | ### get_nb_acceptors/1 ### 38 | 39 | `get_nb_acceptors(Ref) -> any()` 40 | 41 | get the number of acceptors 42 | 43 | 44 | ### get_port/1 ### 45 | 46 | `get_port(Ref) -> any()` 47 | 48 | get current port 49 | 50 | 51 | ### get_protocol_conf/1 ### 52 | 53 | `get_protocol_conf(Ref) -> any()` 54 | 55 | get the protocol configuration 56 | 57 | 58 | ### handle_call/3 ### 59 | 60 | `handle_call(Msg, From, State) -> any()` 61 | 62 | 63 | 64 | 65 | ### handle_cast/2 ### 66 | 67 | `handle_cast(Msg, State) -> any()` 68 | 69 | 70 | 71 | 72 | ### handle_info/2 ### 73 | 74 | `handle_info(X1, State) -> any()` 75 | 76 | 77 | 78 | 79 | ### info/1 ### 80 | 81 | `info(Ref) -> any()` 82 | 83 | get all infos 84 | 85 | 86 | ### info/2 ### 87 | 88 | `info(Ref, Keys) -> any()` 89 | 90 | get info for some keys 91 | 92 | 93 | ### init/1 ### 94 | 95 | `init(X1) -> any()` 96 | 97 | 98 | 99 | 100 | ### remove_connection/2 ### 101 | 102 | `remove_connection(Ref, Pid) -> any()` 103 | 104 | remove a connection from the connection manager 105 | 106 | 107 | ### set_max_clients/2 ### 108 | 109 | `set_max_clients(Ref, Nb) -> any()` 110 | 111 | set max number of concurrent clients 112 | 113 | 114 | ### set_nb_acceptors/2 ### 115 | 116 | `set_nb_acceptors(Ref, Nb) -> any()` 117 | 118 | set the number of acceptors 119 | 120 | 121 | ### set_protocol_conf/4 ### 122 | 123 | `set_protocol_conf(Ref, Handler, Opts, GracefulTimeout) -> any()` 124 | 125 | update the protocol configuration and kill after a timeout 126 | 127 | 128 | ### start_accepting/1 ### 129 | 130 | `start_accepting(Ref) -> any()` 131 | 132 | internal api, tell to the acceptor if he can start to accept a new 133 | connection. 134 | 135 | 136 | ### start_link/1 ### 137 | 138 | `start_link(Options) -> any()` 139 | 140 | 141 | 142 | 143 | ### terminate/2 ### 144 | 145 | `terminate(Reason, State) -> any()` 146 | 147 | 148 | -------------------------------------------------------------------------------- /doc/barrel_server.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Module barrel_server # 4 | * [Function Index](#index) 5 | * [Function Details](#functions) 6 | 7 | __Behaviours:__ [`gen_server`](gen_server.md). 8 | 9 | 10 | ## Function Index ## 11 | 12 | 13 |
get_listener/1Return the listener associated to the ref.
handle_call/3
init/1
set_listener/2Set the listener associated to the ref.
start_link/0Start the barell_sever.
14 | 15 | 16 | 17 | 18 | ## Function Details ## 19 | 20 | 21 | 22 | ### get_listener/1 ### 23 | 24 | 25 |

26 | get_listener(Ref::ranch:ref()) -> pid()
27 | 
28 | 29 |

30 | 31 | 32 | Return the listener associated to the ref. 33 | 34 | 35 | ### handle_call/3 ### 36 | 37 | `handle_call(Request, From, State) -> any()` 38 | 39 | 40 | 41 | 42 | ### init/1 ### 43 | 44 | `init(X1) -> any()` 45 | 46 | 47 | 48 | 49 | ### set_listener/2 ### 50 | 51 | 52 |

53 | set_listener(Ref::barrel:ref(), Pid::pid()) -> ok
54 | 
55 | 56 |

57 | 58 | 59 | Set the listener associated to the ref. 60 | 61 | 62 | ### start_link/0 ### 63 | 64 | 65 |

66 | start_link() -> {ok, pid()}
67 | 
68 | 69 |

70 | 71 | 72 | Start the barell_sever. 73 | -------------------------------------------------------------------------------- /doc/barrel_ssl.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Module barrel_ssl # 4 | * [Function Index](#index) 5 | * [Function Details](#functions) 6 | 7 | 8 | 9 | 10 | ## Function Index ## 11 | 12 | 13 |
accept/2Accept connections with the given listening socket.
close/1Close a TCP socket.
connect/3
connect/4
controlling_process/2Assign a new controlling process Pid to Socket.
listen/1Listen for connections on the given port number.
listen/2
name/0Name of this transport, tcp.
peername/1Return the address and port for the other end of a connection.
recv/2
recv/3Receive a packet from a socket in passive mode.
send/2Send a packet on a socket.
setopts/2Set one or more options for a socket.
sockname/1Get the local address and port of a socket.
14 | 15 | 16 | 17 | 18 | ## Function Details ## 19 | 20 | 21 | 22 | ### accept/2 ### 23 | 24 | 25 |

 26 | accept(LSocket::ssl:sslsocket(), Timeout::timeout()) -> {ok, ssl:sslsocket()} | {error, closed | timeout | atom() | tuple()}
 27 | 
28 | 29 |

30 | 31 | 32 | 33 | Accept connections with the given listening socket. 34 | 35 | 36 | Note that this function does both the transport accept and 37 | the SSL handshake. The returned socket is thus fully connected. 38 | 39 | 40 | __See also:__ [ssl:ssl_accept/2](ssl.md#ssl_accept-2), [ssl:transport_accept/2](ssl.md#transport_accept-2). 41 | 42 | 43 | ### close/1 ### 44 | 45 | 46 |

 47 | close(Socket::ssl:sslsocket()) -> ok
 48 | 
49 | 50 |

51 | 52 | 53 | Close a TCP socket. 54 | 55 | __See also:__ [ssl:close/1](ssl.md#close-1). 56 | 57 | 58 | ### connect/3 ### 59 | 60 | `connect(Host, Port, Opts) -> any()` 61 | 62 | 63 | 64 | 65 | ### connect/4 ### 66 | 67 | `connect(Host, Port, Opts, Timeout) -> any()` 68 | 69 | 70 | 71 | 72 | ### controlling_process/2 ### 73 | 74 | 75 |

 76 | controlling_process(Socket::ssl:sslsocket(), Pid::pid()) -> ok | {error, closed | not_owner | atom()}
 77 | 
78 | 79 |

80 | 81 | 82 | Assign a new controlling process _Pid_ to _Socket_. 83 | 84 | __See also:__ [ssl:controlling_process/2](ssl.md#controlling_process-2). 85 | 86 | 87 | ### listen/1 ### 88 | 89 | `listen(Opts) -> any()` 90 | 91 | Listen for connections on the given port number. 92 | 93 | __See also:__ [ssl:listen/2](ssl.md#listen-2). 94 | 95 | 96 | ### listen/2 ### 97 | 98 | `listen(Port, Opts) -> any()` 99 | 100 | 101 | 102 | 103 | ### name/0 ### 104 | 105 | `name() -> any()` 106 | 107 | Name of this transport, _tcp_. 108 | 109 | 110 | ### peername/1 ### 111 | 112 | 113 |

114 | peername(Socket::ssl:sslsocket()) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, atom()}
115 | 
116 | 117 |

118 | 119 | 120 | Return the address and port for the other end of a connection. 121 | 122 | __See also:__ [ssl:peername/1](ssl.md#peername-1). 123 | 124 | 125 | ### recv/2 ### 126 | 127 | `recv(Socket, Length) -> any()` 128 | 129 | 130 | 131 | 132 | ### recv/3 ### 133 | 134 | 135 |

136 | recv(Socket::ssl:sslsocket(), Length::non_neg_integer(), Timeout::timeout()) -> {ok, any()} | {error, closed | atom()}
137 | 
138 | 139 |

140 | 141 | 142 | Receive a packet from a socket in passive mode. 143 | 144 | __See also:__ [ssl:recv/3](ssl.md#recv-3). 145 | 146 | 147 | ### send/2 ### 148 | 149 | 150 |

151 | send(Socket::ssl:sslsocket(), Packet::iolist()) -> ok | {error, atom()}
152 | 
153 | 154 |

155 | 156 | 157 | Send a packet on a socket. 158 | 159 | __See also:__ [ssl:send/2](ssl.md#send-2). 160 | 161 | 162 | ### setopts/2 ### 163 | 164 | 165 |

166 | setopts(Socket::ssl:sslsocket(), Opts::list()) -> ok | {error, atom()}
167 | 
168 | 169 |

170 | 171 | 172 | Set one or more options for a socket. 173 | 174 | __See also:__ [ssl:setopts/2](ssl.md#setopts-2). 175 | 176 | 177 | ### sockname/1 ### 178 | 179 | 180 |

181 | sockname(Socket::ssl:sslsocket()) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, atom()}
182 | 
183 | 184 |

185 | 186 | 187 | Get the local address and port of a socket 188 | 189 | __See also:__ [ssl:sockname/1](ssl.md#sockname-1). 190 | -------------------------------------------------------------------------------- /doc/barrel_sup.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Module barrel_sup # 4 | * [Function Index](#index) 5 | * [Function Details](#functions) 6 | 7 | __Behaviours:__ [`supervisor`](supervisor.md). 8 | 9 | 10 | ## Function Index ## 11 | 12 | 13 |
init/1
start_link/0
14 | 15 | 16 | 17 | 18 | ## Function Details ## 19 | 20 | 21 | 22 | ### init/1 ### 23 | 24 | `init(X1) -> any()` 25 | 26 | 27 | 28 | 29 | ### start_link/0 ### 30 | 31 | `start_link() -> any()` 32 | 33 | 34 | -------------------------------------------------------------------------------- /doc/barrel_tcp.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Module barrel_tcp # 4 | * [Function Index](#index) 5 | * [Function Details](#functions) 6 | 7 | 8 | 9 | 10 | ## Function Index ## 11 | 12 | 13 |
accept/2Accept connections with the given listening socket.
close/1Close a TCP socket.
connect/3
connect/4
controlling_process/2Assign a new controlling process Pid to Socket.
listen/1Listen for connections on the given port number.
listen/2
name/0Name of this transport, tcp.
peername/1Return the address and port for the other end of a connection.
recv/2
recv/3Receive a packet from a socket in passive mode.
send/2Send a packet on a socket.
setopts/2Set one or more options for a socket.
sockname/1Get the local address and port of a socket.
14 | 15 | 16 | 17 | 18 | ## Function Details ## 19 | 20 | 21 | 22 | ### accept/2 ### 23 | 24 | 25 |

 26 | accept(LSocket::inet:socket(), Timeout::timeout()) -> {ok, inet:socket()} | {error, closed | timeout | atom()}
 27 | 
28 | 29 |

30 | 31 | 32 | Accept connections with the given listening socket. 33 | 34 | __See also:__ [gen_tcp:accept/2](gen_tcp.md#accept-2). 35 | 36 | 37 | ### close/1 ### 38 | 39 | 40 |

 41 | close(Socket::inet:socket()) -> ok
 42 | 
43 | 44 |

45 | 46 | 47 | Close a TCP socket. 48 | 49 | __See also:__ [gen_tcp:close/1](gen_tcp.md#close-1). 50 | 51 | 52 | ### connect/3 ### 53 | 54 | `connect(Host, Port, Opts) -> any()` 55 | 56 | 57 | 58 | 59 | ### connect/4 ### 60 | 61 | `connect(Host, Port, Opts, Timeout) -> any()` 62 | 63 | 64 | 65 | 66 | ### controlling_process/2 ### 67 | 68 | 69 |

 70 | controlling_process(Socket::inet:socket(), Pid::pid()) -> ok | {error, closed | not_owner | atom()}
 71 | 
72 | 73 |

74 | 75 | 76 | Assign a new controlling process _Pid_ to _Socket_. 77 | 78 | __See also:__ [gen_tcp:controlling_process/2](gen_tcp.md#controlling_process-2). 79 | 80 | 81 | ### listen/1 ### 82 | 83 | `listen(Opts) -> any()` 84 | 85 | Listen for connections on the given port number. 86 | 87 | __See also:__ [gen_tcp:listen/2](gen_tcp.md#listen-2). 88 | 89 | 90 | ### listen/2 ### 91 | 92 | `listen(Port, Opts) -> any()` 93 | 94 | 95 | 96 | 97 | ### name/0 ### 98 | 99 | `name() -> any()` 100 | 101 | Name of this transport, _tcp_. 102 | 103 | 104 | ### peername/1 ### 105 | 106 | 107 |

108 | peername(Socket::inet:socket()) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, atom()}
109 | 
110 | 111 |

112 | 113 | 114 | Return the address and port for the other end of a connection. 115 | 116 | __See also:__ [inet:peername/1](inet.md#peername-1). 117 | 118 | 119 | ### recv/2 ### 120 | 121 | `recv(Socket, Length) -> any()` 122 | 123 | 124 | 125 | 126 | ### recv/3 ### 127 | 128 | 129 |

130 | recv(Socket::inet:socket(), Length::non_neg_integer(), Timeout::timeout()) -> {ok, any()} | {error, closed | atom()}
131 | 
132 | 133 |

134 | 135 | 136 | Receive a packet from a socket in passive mode. 137 | 138 | __See also:__ [gen_tcp:recv/3](gen_tcp.md#recv-3). 139 | 140 | 141 | ### send/2 ### 142 | 143 | 144 |

145 | send(Socket::inet:socket(), Packet::iolist()) -> ok | {error, atom()}
146 | 
147 | 148 |

149 | 150 | 151 | Send a packet on a socket. 152 | 153 | __See also:__ [gen_tcp:send/2](gen_tcp.md#send-2). 154 | 155 | 156 | ### setopts/2 ### 157 | 158 | 159 |

160 | setopts(Socket::inet:socket(), Opts::list()) -> ok | {error, atom()}
161 | 
162 | 163 |

164 | 165 | 166 | Set one or more options for a socket. 167 | 168 | __See also:__ [inet:setopts/2](inet.md#setopts-2). 169 | 170 | 171 | ### sockname/1 ### 172 | 173 | 174 |

175 | sockname(Socket::inet:socket()) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, atom()}
176 | 
177 | 178 |

179 | 180 | 181 | Get the local address and port of a socket 182 | 183 | __See also:__ [inet:sockname/1](inet.md#sockname-1). 184 | -------------------------------------------------------------------------------- /doc/barrel_util.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Module barrel_util # 4 | * [Function Index](#index) 5 | * [Function Details](#functions) 6 | 7 | 8 | 9 | 10 | ## Function Index ## 11 | 12 | 13 |
filter_props/2filter a list of properties and removed n.
filter_props/3
fix_ip/1
ipv6_supported/0
propmerge/2Update a proplist with values of the second.
require/1Start the given applications if they were not already started.
14 | 15 | 16 | 17 | 18 | ## Function Details ## 19 | 20 | 21 | 22 | ### filter_props/2 ### 23 | 24 | `filter_props(Props, Allowed) -> any()` 25 | 26 | filter a list of properties and removed n 27 | 28 | 29 | ### filter_props/3 ### 30 | 31 | `filter_props(Rest, Allowed, Acc) -> any()` 32 | 33 | 34 | 35 | 36 | ### fix_ip/1 ### 37 | 38 | `fix_ip(Opts) -> any()` 39 | 40 | 41 | 42 | 43 | ### ipv6_supported/0 ### 44 | 45 | `ipv6_supported() -> any()` 46 | 47 | 48 | 49 | 50 | ### propmerge/2 ### 51 | 52 | `propmerge(L1, L2) -> any()` 53 | 54 | Update a proplist with values of the second. In case the same 55 | key is in 2 proplists, the value from the first are kept. 56 | 57 | 58 | ### require/1 ### 59 | 60 | 61 |

62 | require(Rest::[module()]) -> ok
63 | 
64 | 65 |

66 | 67 | 68 | Start the given applications if they were not already started. 69 | -------------------------------------------------------------------------------- /doc/overview.edoc: -------------------------------------------------------------------------------- 1 | %%============================================================================== 2 | %% See the NOTICE for more information. 3 | %% 4 | %% Copyright (c) 2013 Benoît Chesneau 5 | %%============================================================================== 6 | 7 | @copyright 2013 Benoît Chesneau. 8 | @version 2.1 9 | @title barrel - generic TCP acceptor pool 10 | 11 | @doc 12 | barrel is a **generic TCP acceptor pool** with low latency in Erlang. 13 | 14 | **Main Features**: 15 | 16 | - start/stop TCP and SSL listener 17 | - can be use with different transports based on an "accept" model. (can 18 | be stcp, uTCP...) 19 | - Low latency when accepting connection 20 | - Any protocol can be used, can be HTTP, IRC or anything you want. 21 | - Graceful reload of protocol configurations 22 | - Scale the number of concurrent connections accepted at the same time 23 | - Scale the number of concurrent connections handled by a listener 24 | 25 | ## Design 26 | 27 | > The design of barrel differs from 28 | > [ranch](http://github.com/extend/ranch). Instead of spawning 29 | > a new handler for each request, then making it control the socket - which 30 | > can be slow - barrel spawns new acceptors. The accepted 31 | > socket will continue to be used in the same process that accepted it 32 | > previously. Optionally you can launch a process to handle the 33 | > accepted socket if you want. The choice is yours. 34 | 35 | ## Usage 36 | 37 | ### Create a simple TCP echo server. 38 | 39 | 1. Create a simple echo handler 40 | 41 | ``` 42 | -module(echo_handler). 43 | 44 | -export([init/3]). 45 | 46 | init(_ListenerRef, Transport, Socket, _Opts) -> 47 | wait_request(Transport, Socket). 48 | 49 | wait_request(Transport, Socket) -> 50 | case Transport:recv(Socket, 0, 5000) of 51 | {ok, <<".\r\n">>} -> 52 | io:format("remote exited", []), 53 | Transport:close(Socket); 54 | {ok, Data} -> 55 | io:format("got ~p~n", [Data]), 56 | Transport:send(Socket, Data), 57 | wait_request(Transport, Socket); 58 | {error, _Reason} -> 59 | Transport:close(Socket) 60 | end. 61 | ''' 62 | 63 | `init/4' is the function that receives the transport (`barrel_tcp' if TCP or 64 | `barrel_ssl' if SSL) socket. 65 | 66 | Note that, by default, barrel uses the process accepting 67 | the connection to handle the request. In other words, the acceptor process 68 | becomes the request process. In the other case you may want to launch a new 69 | process instead and pass the control of the socket to it (which is the 70 | only way to do it in ranch). In this case instead of `init/4' use a 71 | `start_link/4' function. Eg. : 72 | 73 | ``` 74 | start_link(Ref, Transport, Socket, _Opts) -> 75 | Pid = spawn_link(?MODULE, init, [Ref, Transport, Socket]), 76 | {ok, Pid}. 77 | 78 | init(Ref, Transport, Socket) -> 79 | ok = barrel:accept_ack(Ref), 80 | wait_request(Transport, Socket). 81 | 82 | ... 83 | 84 | ''' 85 | 86 | 87 | To start the listener do the following: 88 | 89 | ``` 90 | Ref = echo, 91 | NbAcceptors = 100, 92 | Transport = barrel_tcp, 93 | TransporOptions = [{port, 10001}], 94 | Protocol = echo_handler, 95 | ProtocolOptions = [], 96 | barrel:start_listener(Ref, NbAcceptors, Transport, TransportOptions, 97 | Protocol, ProtocolOptions). 98 | ''' 99 | 100 | A Ref can be any Erlang term used to identify a listener. A listener is 101 | a gen_server that manages all the acceptors workers and handles connection 102 | shutdowns. 103 | 104 | A `Protocol' is the protocol used to handle a connection. It can be any 105 | module following the protocol behaviour. Currently Barrel handles the TCP 106 | (`barel_tcp') and SSL/TLS (`barel_ssl') protocols. 107 | 108 | A `Protocol' is what will be used to handle the data coming from the 109 | socket. You can pass `ProtocolOpts' to it. 110 | 111 | Additionaly you can pass custom options to the listener. This is where 112 | you pass the SSL options, for example. 113 | 114 | 115 | The full example can be found in the [example folder](http://github.com/benoitc/barrel/tree/master/example/echo). 116 | 117 | ### Scale 118 | 119 | #### Number of acceptors 120 | 121 | There are 2 way of scaling a listener in barrel. One is to increase the 122 | number of acceptors. By default the the number of acceptors is 100. 123 | 124 | To do it use the `barrel:set_nb_acceptors/2' function. 125 | 126 | Increasing the number of acceptors will increase the number of 127 | concurrent connections you can **accept** at the same time 128 | 129 | ### Number of clients 130 | 131 | While increasing the number of acceptors increase the number of 132 | concurrent connections you **accept** at the same time; you can also fix 133 | the number of concurrent conncections (clients) you are willing to handle. 134 | This can be used to limit the usage of the resources (memory and 135 | file descriptors). 136 | 137 | To do it use the `barrel:set_max_client/2' function. 138 | 139 | 140 | ### Load a new protocol configuration 141 | 142 | Barrel allows you to either change the protocol handler or its 143 | configuration without closing the active connections. 144 | 145 | What happens here is that once you pass a new protocol configuration 146 | using the function `barrel:set_protocol_conf/4' to the 147 | listener, new acceptors will be launched with the new configuration and 148 | old acceptors will get killed right after. Once it's done, a graceful 149 | shutdown will be sent to the remaining connections. If the graceful timeout is 150 | not defined, the connections will continue to run until they die, otherwise 151 | the connections will get killed after that time passes. This behaviour is 152 | similar to the one you can find in 153 | [nginx](http://wiki.nginx.org/CommandLine#Loading_a_New_Configuration_Using_Signals). 154 | 155 | 156 | ## Contribute 157 | 158 | For issues, comments or feedback please [create an 159 | issue](http://github.com/benoitc/barrel/issues). 160 | 161 | ### Notes for developers 162 | 163 | If you want to contribute patches or improve the docs, you will need to 164 | build Barrel using the `rebar_dev.config' file. It can also be built 165 | using the **Makefile**: 166 | 167 | ``` 168 | $ make dev ; # compile & get deps 169 | $ make devclean ; # clean all files 170 | ''' 171 | -------------------------------------------------------------------------------- /example/echo/README.md: -------------------------------------------------------------------------------- 1 | # ishtar echo example 2 | 3 | To compile this example : 4 | 5 | $ ../../rebar get-deps compile 6 | 7 | 8 | You can then start the erlang node with the following command: 9 | 10 | $ ./start.sh 11 | 12 | Then telnet localhotst 10001, and start to type, the server should echo 13 | the result. 14 | 15 | > Note: type "." to EXIT. 16 | -------------------------------------------------------------------------------- /example/echo/rebar.config: -------------------------------------------------------------------------------- 1 | %% -*- tab-width: 4;erlang-indent-level: 4;indent-tabs-mode: nil -*- 2 | %% ex: ft=erlang ts=4 sw=4 et 3 | 4 | {erl_opts, [warnings_as_errors]}. 5 | 6 | {cover_enabled, true}. 7 | {cover_print_enabled, true}. 8 | {eunit_opts, [verbose]}. 9 | 10 | {require_otp_vsn, "R15|R16|17"}. 11 | {clean_files, ["*~","*/*~","*/*.xfm","test/*.beam"]}. 12 | 13 | {deps, [ 14 | {barrel, ".*", {git, "http://github.com/benoitc/barrel.git", 15 | {branch, "master"}}} 16 | ]}. 17 | 18 | -------------------------------------------------------------------------------- /example/echo/src/echo.app.src: -------------------------------------------------------------------------------- 1 | {application, echo, 2 | [ 3 | {description, ""}, 4 | {vsn, "1"}, 5 | {registered, []}, 6 | {applications, [ 7 | kernel, 8 | stdlib 9 | ]}, 10 | {mod, { echo_app, []}}, 11 | {env, []} 12 | ]}. 13 | -------------------------------------------------------------------------------- /example/echo/src/echo.erl: -------------------------------------------------------------------------------- 1 | -module(echo). 2 | 3 | -export([start/0]). 4 | 5 | start() -> 6 | ok = barrel:start(), 7 | ok = application:start(echo). 8 | -------------------------------------------------------------------------------- /example/echo/src/echo_app.erl: -------------------------------------------------------------------------------- 1 | -module(echo_app). 2 | 3 | -behaviour(application). 4 | 5 | %% Application callbacks 6 | -export([start/2, stop/1]). 7 | 8 | %% =================================================================== 9 | %% Application callbacks 10 | %% =================================================================== 11 | 12 | start(_StartType, _StartArgs) -> 13 | barrel:start_listener(echo, 100, barrel_tcp, 14 | [{port, 10001}], echo_handler, []), 15 | echo_sup:start_link(). 16 | 17 | stop(_State) -> 18 | ok. 19 | -------------------------------------------------------------------------------- /example/echo/src/echo_handler.erl: -------------------------------------------------------------------------------- 1 | -module(echo_handler). 2 | 3 | -export([init/4]). 4 | 5 | init(_Ref, Transport, Socket, _Opts) -> 6 | wait_request(Transport, Socket). 7 | 8 | wait_request(Transport, Socket) -> 9 | case Transport:recv(Socket, 0, 30000) of 10 | {ok, <<".\r\n">>} -> 11 | io:format("remote exited", []), 12 | Transport:close(Socket); 13 | {ok, Data} -> 14 | io:format("got ~p~n", [Data]), 15 | Transport:send(Socket, Data), 16 | wait_request(Transport, Socket); 17 | {error, _Reason} -> 18 | Transport:close(Socket) 19 | end. 20 | -------------------------------------------------------------------------------- /example/echo/src/echo_sup.erl: -------------------------------------------------------------------------------- 1 | 2 | -module(echo_sup). 3 | 4 | -behaviour(supervisor). 5 | 6 | %% API 7 | -export([start_link/0]). 8 | 9 | %% Supervisor callbacks 10 | -export([init/1]). 11 | 12 | %% Helper macro for declaring children of supervisor 13 | -define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). 14 | 15 | %% =================================================================== 16 | %% API functions 17 | %% =================================================================== 18 | 19 | start_link() -> 20 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 21 | 22 | %% =================================================================== 23 | %% Supervisor callbacks 24 | %% =================================================================== 25 | 26 | init([]) -> 27 | {ok, { {one_for_one, 5, 10}, []} }. 28 | 29 | -------------------------------------------------------------------------------- /example/echo/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | erl -Ww -pa ebin deps/*/ebin -s echo 3 | -------------------------------------------------------------------------------- /example/echo_p/README.md: -------------------------------------------------------------------------------- 1 | # barrel echo example 2 | 3 | Like the echo example but instead we spawn a new process to handle the 4 | request and pass the control of the socket to it. 5 | 6 | To compile this example : 7 | 8 | $ ../../rebar get-deps compile 9 | 10 | 11 | You can then start the erlang node with the following command: 12 | 13 | $ ./start.sh 14 | 15 | Then telnet localhotst 10001, and start to type, the server should echo 16 | the result. 17 | 18 | > Note: type "." to EXIT. 19 | -------------------------------------------------------------------------------- /example/echo_p/rebar.config: -------------------------------------------------------------------------------- 1 | %% -*- tab-width: 4;erlang-indent-level: 4;indent-tabs-mode: nil -*- 2 | %% ex: ft=erlang ts=4 sw=4 et 3 | 4 | {erl_opts, [warnings_as_errors]}. 5 | 6 | {cover_enabled, true}. 7 | {cover_print_enabled, true}. 8 | {eunit_opts, [verbose]}. 9 | 10 | {require_otp_vsn, "R15|R16|17"}. 11 | {clean_files, ["*~","*/*~","*/*.xfm","test/*.beam"]}. 12 | 13 | {deps, [ 14 | {barrel, ".*", {git, "http://github.com/benoitc/barrel.git", 15 | {branch, "master"}}} 16 | ]}. 17 | 18 | -------------------------------------------------------------------------------- /example/echo_p/src/echo.app.src: -------------------------------------------------------------------------------- 1 | {application, echo, 2 | [ 3 | {description, ""}, 4 | {vsn, "1"}, 5 | {registered, []}, 6 | {applications, [ 7 | kernel, 8 | stdlib 9 | ]}, 10 | {mod, { echo_app, []}}, 11 | {env, []} 12 | ]}. 13 | -------------------------------------------------------------------------------- /example/echo_p/src/echo.erl: -------------------------------------------------------------------------------- 1 | -module(echo). 2 | 3 | -export([start/0]). 4 | 5 | start() -> 6 | ok = barrel:start(), 7 | ok = application:start(echo). 8 | -------------------------------------------------------------------------------- /example/echo_p/src/echo_app.erl: -------------------------------------------------------------------------------- 1 | -module(echo_app). 2 | 3 | -behaviour(application). 4 | 5 | %% Application callbacks 6 | -export([start/2, stop/1]). 7 | 8 | %% =================================================================== 9 | %% Application callbacks 10 | %% =================================================================== 11 | 12 | start(_StartType, _StartArgs) -> 13 | barrel:start_listener(echo, 100, barrel_tcp, 14 | [{port, 10001}], echo_handler, []), 15 | echo_sup:start_link(). 16 | 17 | stop(_State) -> 18 | ok. 19 | -------------------------------------------------------------------------------- /example/echo_p/src/echo_handler.erl: -------------------------------------------------------------------------------- 1 | -module(echo_handler). 2 | 3 | 4 | -export([start_link/4]). 5 | -export([init/3]). 6 | 7 | 8 | start_link(Ref, Transport, Socket, _Opts) -> 9 | Pid = spawn_link(?MODULE, init, [Ref, Transport, Socket]), 10 | {ok, Pid}. 11 | 12 | init(Ref, Transport, Socket) -> 13 | ok = barrel:accept_ack(Ref), 14 | wait_request(Transport, Socket). 15 | 16 | wait_request(Transport, Socket) -> 17 | case Transport:recv(Socket, 0, 30000) of 18 | {ok, <<".\r\n">>} -> 19 | io:format("remote exited", []), 20 | Transport:close(Socket); 21 | {ok, Data} -> 22 | io:format("got ~p~n", [Data]), 23 | Transport:send(Socket, Data), 24 | wait_request(Transport, Socket); 25 | {error, _Reason} -> 26 | Transport:close(Socket) 27 | end. 28 | -------------------------------------------------------------------------------- /example/echo_p/src/echo_sup.erl: -------------------------------------------------------------------------------- 1 | 2 | -module(echo_sup). 3 | 4 | -behaviour(supervisor). 5 | 6 | %% API 7 | -export([start_link/0]). 8 | 9 | %% Supervisor callbacks 10 | -export([init/1]). 11 | 12 | %% Helper macro for declaring children of supervisor 13 | -define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). 14 | 15 | %% =================================================================== 16 | %% API functions 17 | %% =================================================================== 18 | 19 | start_link() -> 20 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 21 | 22 | %% =================================================================== 23 | %% Supervisor callbacks 24 | %% =================================================================== 25 | 26 | init([]) -> 27 | {ok, { {one_for_one, 5, 10}, []} }. 28 | 29 | -------------------------------------------------------------------------------- /example/echo_p/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | erl -Ww -pa ebin deps/*/ebin -s echo 3 | -------------------------------------------------------------------------------- /rebar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benoitc-attic/barrel_tcp/e689d0f4286b97462e0fda455f87501dc0ef4487/rebar -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | %% -*- tab-width: 4;erlang-indent-level: 4;indent-tabs-mode: nil -*- 2 | %% ex: ft=erlang ts=4 sw=4 et 3 | 4 | {erl_opts, [warnings_as_errors]}. 5 | {xref_checks, [undefined_function_calls]}. 6 | 7 | {cover_enabled, true}. 8 | {cover_print_enabled, true}. 9 | {eunit_opts, [verbose]}. 10 | 11 | {require_otp_vsn, "R15|R16|17|18"}. 12 | {clean_files, ["*~","*/*~","*/*.xfm","test/*.beam"]}. 13 | -------------------------------------------------------------------------------- /rebar_dev.config: -------------------------------------------------------------------------------- 1 | %% -*- tab-width: 4;erlang-indent-level: 4;indent-tabs-mode: nil -*- 2 | %% ex: ft=erlang ts=4 sw=4 et 3 | 4 | {erl_opts, [debug_info]}. 5 | {xref_checks, [undefined_function_calls]}. 6 | 7 | {cover_enabled, true}. 8 | {eunit_opts, [verbose]}. 9 | 10 | {clean_files, ["*~","*/*~","*/*.xfm","test/*.beam"]}. 11 | 12 | {deps, [ 13 | {edown, ".*", {git, "git://github.com/esl/edown.git", 14 | "HEAD"}} 15 | ]}. 16 | 17 | {edoc_opts, [{doclet, edown_doclet}, 18 | {exclude_packages, [goldrush, edown, lager]}, 19 | {top_level_readme, 20 | {"./README.md", "http://github.com/benoitc/barrel"}}]}. 21 | -------------------------------------------------------------------------------- /src/barrel.app.src: -------------------------------------------------------------------------------- 1 | %% -*- tab-width: 4;erlang-indent-level: 4;indent-tabs-mode: nil -*- 2 | %% ex: ft=erlang ts=4 sw=4 et 3 | 4 | {application, barrel, 5 | [ 6 | {description, "generic TCP acceptor pool"}, 7 | {vsn, "2.1"}, 8 | {registered, []}, 9 | {applications, [ 10 | kernel, 11 | stdlib 12 | ]}, 13 | {mod, { barrel_app, []}}, 14 | {env, []} 15 | ]}. 16 | -------------------------------------------------------------------------------- /src/barrel.erl: -------------------------------------------------------------------------------- 1 | %%% -*- erlang -*- 2 | %%% 3 | %%% This file is part of barrel released under the MIT license. 4 | %%% See the NOTICE for more information. 5 | 6 | -module(barrel). 7 | 8 | -export([start/0, stop/0]). 9 | -export([start_listener/6, start_listener/7, 10 | stop_listener/1, 11 | child_spec/2, 12 | get_port/1, 13 | info/1, info/2, 14 | set_max_clients/2, get_max_clients/1, 15 | set_nb_acceptors/2, get_nb_acceptors/1, 16 | set_protocol_conf/3, set_protocol_conf/4, 17 | get_protocol_conf/1, 18 | remove_connection/2]). 19 | 20 | -export([accept_ack/1]). 21 | 22 | -type ref() :: any(). 23 | -export_type([ref/0]). 24 | 25 | -type info_key() :: ip | port | open_reqs | nb_acceptors | max_clients. 26 | -export_type([info_key/0]). 27 | 28 | -type info_keys() :: [info_key()]. 29 | -export_type([info_keys/0]). 30 | 31 | % --- Application --- 32 | 33 | %% @doc Start the barrel application. Useful when testing using the shell. 34 | start() -> 35 | barrel_deps:ensure(), 36 | application:load(barrel), 37 | barrel_app:ensure_deps_started(), 38 | application:start(barrel). 39 | 40 | %% @doc Start the coffer application. Useful when testing using the shell. 41 | stop() -> 42 | application:stop(barrel). 43 | 44 | 45 | % --- barrel API --- 46 | 47 | -spec start_listener(barrel:ref(), integer(), any(), any(), any(), 48 | any()) -> {ok, pid()} | {error, term()}. 49 | %% @doc start a listener 50 | %% 51 | %% ``` 52 | %% Ref = term() 53 | %% NbAcceptors = integer() 54 | %% Transport = barrel_tcp | barrel_ssl | any 55 | %% TransOpts = any() 56 | %% Protocol = any() 57 | %% ProtocolOpts = any() 58 | %% ListenerOpts - any(), 59 | %% ''' 60 | %% 61 | %% A Ref can be any Erlang term used to identify a listener. A listener 62 | %% is a gen_server that manage all acceptors workers and handle 63 | %% connections shutdown. 64 | %% 65 | %% A protocol is the protocol used to handle a connection. It can be 66 | %% any module following the protocol behaviour. Barrel offers 67 | %% to handle the TCP ans SSL/TLS protocol for now. 68 | %% 69 | %% A protocol is what will be used to handle the data coming from the 70 | %% socket. You can pass to it some options (ProtocolOpts). 71 | %% 72 | %% Optionnaly you can pass custom options to the listener. This is where 73 | %% you pass the SSL options for example. 74 | start_listener(Ref, NbAcceptors, Transport, TransOpts, Protocol, 75 | ProtocolOpts) -> 76 | start_listener(Ref, NbAcceptors, Transport, TransOpts, Protocol, 77 | ProtocolOpts, []). 78 | 79 | -spec start_listener(barrel:ref(), integer(), any(), any(), any(), 80 | any(), any()) -> {ok, pid()} | {error, term()}. 81 | start_listener(Ref, NbAcceptors, Transport, TransOpts, Protocol, 82 | ProtoOpts, ListenerOpts0) -> 83 | _ = code:ensure_loaded(Transport), 84 | case erlang:function_exported(Transport, name, 0) of 85 | false -> 86 | {error, badarg}; 87 | true -> 88 | ListenerOpts = [{ref, Ref} | ListenerOpts0], 89 | Socket = proplists:get_value(socket, TransOpts), 90 | Spec = child_spec(Ref, [NbAcceptors, Transport, TransOpts, 91 | Protocol, ProtoOpts, ListenerOpts]), 92 | 93 | case supervisor:start_child(barrel_sup, Spec) of 94 | {ok, Pid} when Socket /= undefined -> 95 | %% pass the control of the socket to the listener. 96 | Transport:controlling_process(Socket, Pid), 97 | {ok, Pid}; 98 | Else -> 99 | Else 100 | end 101 | end. 102 | 103 | %% @doc stop a listener 104 | %% All connections and acceptors for this listener are killed 105 | %% immediately. 106 | -spec stop_listener(barrel:ref()) -> ok | {error, term()}. 107 | stop_listener(Ref) -> 108 | case supervisor:terminate_child(barrel_sup, Ref) of 109 | ok -> 110 | supervisor:delete_child(barrel_sup, Ref); 111 | Error -> 112 | Error 113 | end. 114 | 115 | %% @doc return a child spec suitable for embeding your listener in the 116 | %% supervisor 117 | 118 | -spec child_spec(barrel:ref(), any()) -> any(). 119 | child_spec(Ref, Options) -> 120 | {Ref, {barrel_listener, start_link, [Options]}, 121 | permanent, 5000, worker, [Ref]}. 122 | 123 | %% @doc get current port of a listener 124 | %% 125 | %% Ref = term() 126 | -spec get_port(barrel:ref()) -> integer(). 127 | get_port(Ref) -> 128 | ListenerPid = barrel_server:get_listener(Ref), 129 | barrel_listener:get_port(ListenerPid). 130 | 131 | %% @doc get all infos of a listener 132 | %% 133 | %% %% Ref = term() 134 | -spec info(barrel:ref()) -> any(). 135 | info(Ref) -> 136 | ListenerPid = barrel_server:get_listener(Ref), 137 | barrel_listener:info(ListenerPid). 138 | 139 | %% @doc get info for some keys 140 | %% 141 | %% Ref = term() 142 | %% Key = ip | port | open_reqs | nb_acceptors | max_clients 143 | -spec info(barrel:ref(), info_keys()) -> any(). 144 | info(Ref, Key) -> 145 | ListenerPid = barrel_server:get_listener(Ref), 146 | barrel_listener:info(ListenerPid, Key). 147 | 148 | %% @doc set max number of concurrent clients 149 | set_max_clients(Ref, MaxClients) -> 150 | ListenerPid = barrel_server:get_listener(Ref), 151 | barrel_listener:set_max_clients(ListenerPid, MaxClients). 152 | 153 | %% @doc get max number of concurrent clients 154 | get_max_clients(Ref) -> 155 | ListenerPid = barrel_server:get_listener(Ref), 156 | barrel_listener:get_max_clients(ListenerPid). 157 | 158 | %% @doc set the number of acceptors for a listener. By default 100. 159 | set_nb_acceptors(Ref, Nb) -> 160 | ListenerPid = barrel_server:get_listener(Ref), 161 | barrel_listener:set_nb_acceptors(ListenerPid, Nb). 162 | 163 | %% @doc get the number of acceptors set for a listener 164 | get_nb_acceptors(Ref) -> 165 | ListenerPid = barrel_server:get_listener(Ref), 166 | barrel_listener:get_nb_acceptors(ListenerPid). 167 | 168 | %% @doc update the protocol configuration and kill the connections after 169 | %% 30s. 170 | set_protocol_conf(Ref, Handler, Options) -> 171 | set_protocol_conf(Ref, Handler, Options, 30000). 172 | 173 | %% @doc update the protocol configuration and kill the connections after 174 | %% a timeout. If timeout is none then the connections will continue 175 | %% until they die. 176 | set_protocol_conf(Ref, Handler, Options, GracefulTimeout) -> 177 | ListenerPid = barrel_server:get_listener(Ref), 178 | barrel_listener:set_protocol_conf(ListenerPid, Handler, Options, 179 | GracefulTimeout). 180 | 181 | %% @doc get the protocol configuration 182 | get_protocol_conf(Ref) -> 183 | ListenerPid = barrel_server:get_listener(Ref), 184 | barrel_listener:get_protocol_conf(ListenerPid). 185 | 186 | %% @doc used to start to handle the connection in a spawned protocol process. 187 | %% It is needed to use this function first so the control of the socket 188 | %% is given to the process. 189 | accept_ack(Ref) -> 190 | receive {accept_ack, Ref} -> ok end. 191 | 192 | 193 | %% @doc remove a connection from the connection manager 194 | %% Useful when you want to keep this connection open but don't want to 195 | %% count in the concurrent connections managed by a listener 196 | remove_connection(Ref, Pid) -> 197 | ListenerPid = barrel_server:get_listener(Ref), 198 | barrel_listener:remove_connection(ListenerPid, Pid). 199 | -------------------------------------------------------------------------------- /src/barrel_acceptor.erl: -------------------------------------------------------------------------------- 1 | %%% -*- erlang -*- 2 | %%% 3 | %%% This file is part of barrel released under the MIT license. 4 | %%% See the NOTICE for more information. 5 | 6 | -module(barrel_acceptor). 7 | 8 | -export([start_link/5]). 9 | 10 | -export([accept/6]). 11 | 12 | 13 | start_link(Listener, Transport, ListenSocket, Opts, Protocol) -> 14 | Ref = proplists:get_value(ref, Opts), 15 | 16 | spawn_link(?MODULE, accept, [Listener, Ref, Transport, ListenSocket, Opts, 17 | Protocol]). 18 | 19 | %% accept on the socket until a client connect 20 | accept(Listener, Ref, Transport, ListenSocket, Opts, 21 | {ProtocolHandler, ProtoOpts, Spawn}=Protocol) -> 22 | 23 | %% here we call the listener to make sure we can handle the 24 | %% connection now. if not it will sleep until we get the 25 | %% answer. 26 | ok = barrel_listener:start_accepting(Listener), 27 | 28 | AcceptTimeout = proplists:get_value(accept_timeout, Opts, 10000), 29 | case catch Transport:accept(ListenSocket, AcceptTimeout) of 30 | {ok, Socket} when Spawn /= true -> 31 | gen_server:cast(Listener, {accepted, self()}), 32 | ProtocolHandler:init(Ref, Transport, Socket, ProtoOpts); 33 | {ok, Socket} -> 34 | %% The protocol want to spawn a new process instead of using 35 | %% the acceptor one so pass it the control of the socket 36 | case ProtocolHandler:start_link(Ref, Transport, Socket, 37 | ProtoOpts) of 38 | {ok, Pid} -> 39 | ok = Transport:controlling_process(Socket, Pid), 40 | gen_server:cast(Listener, {accepted, Pid}), 41 | Pid ! {accept_ack, Ref}; 42 | _ -> 43 | ok 44 | end, 45 | %% we don't have to exit here since we spawned a new process 46 | %% to handle the request 47 | ?MODULE:accept(Listener, Ref, Transport, ListenSocket, Opts, 48 | Protocol); 49 | {error, timeout} -> 50 | ?MODULE:accept(Listener, Ref, Transport, ListenSocket, Opts, 51 | Protocol); 52 | {error, emfile} -> 53 | %% rather than exiting the acceptor when we run out of file 54 | %% descriptors wait a little and retry to accept. 55 | receive after 100 -> ok end, 56 | ?MODULE:accept(Listener, Ref, Transport, ListenSocket, Opts, 57 | Protocol); 58 | {error, econnaborted} -> 59 | ?MODULE:accept(Listener, Ref, Transport, ListenSocket, Opts, 60 | Protocol); 61 | {error, esslaccept} -> 62 | exit(normal); 63 | {error, Other} -> 64 | exit({error, Other}) 65 | end. 66 | -------------------------------------------------------------------------------- /src/barrel_app.erl: -------------------------------------------------------------------------------- 1 | %%% -*- erlang -*- 2 | %%% 3 | %%% This file is part of barrel released under the MIT license. 4 | %%% See the NOTICE for more information. 5 | 6 | -module(barrel_app). 7 | 8 | -behaviour(application). 9 | 10 | %% Application callbacks 11 | -export([start/2, stop/1, 12 | ensure_deps_started/0]). 13 | 14 | %% =================================================================== 15 | %% Application callbacks 16 | %% =================================================================== 17 | 18 | start(_StartType, _StartArgs) -> 19 | barrel_deps:ensure(), 20 | ensure_deps_started(), 21 | barrel_sup:start_link(). 22 | 23 | stop(_State) -> 24 | ok. 25 | 26 | ensure_deps_started() -> 27 | {ok, Deps} = application:get_key(barrel, applications), 28 | true = lists:all(fun ensure_started/1, Deps). 29 | 30 | ensure_started(App) -> 31 | case application:start(App) of 32 | ok -> 33 | true; 34 | {error, {already_started, App}} -> 35 | true; 36 | Else -> 37 | error_logger:error_msg("Couldn't start ~p: ~p", [App, Else]), 38 | Else 39 | end. 40 | 41 | -------------------------------------------------------------------------------- /src/barrel_connections.erl: -------------------------------------------------------------------------------- 1 | %%% -*- erlang -*- 2 | %%% 3 | %%% This file is part of barrel released under the MIT licens. 4 | %%% See the NOTICE for more information. 5 | %%% 6 | -module(barrel_connections). 7 | 8 | -behaviour(gen_server). 9 | 10 | -record(state, { 11 | listener, 12 | age, 13 | reqs, 14 | reqs_by_age, 15 | status, 16 | graceful_timer}). 17 | 18 | 19 | -export([start/0]). 20 | -export([add_connection/2, 21 | stop/1, 22 | shutdown/2]). 23 | 24 | %% gen_server callbacks 25 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 26 | code_change/3, terminate/2]). 27 | 28 | 29 | add_connection(Pid, ConnPid) -> 30 | gen_server:cast(Pid, {add_connection, ConnPid}). 31 | 32 | stop(Pid) -> 33 | gen_server:call(Pid, stop). 34 | 35 | shutdown(Pid, Timeout) -> 36 | gen_server:call(Pid, {shutdown, Timeout}). 37 | 38 | start() -> 39 | Listener = self(), 40 | case gen_server:start_link(?MODULE, [Listener], []) of 41 | {ok, Pid} -> 42 | MRef = erlang:monitor(process, Pid), 43 | {ok, {Pid, MRef}}; 44 | Error -> 45 | Error 46 | end. 47 | 48 | init([Listener]) -> 49 | {ok, #state{listener=Listener, 50 | age = 0, 51 | reqs = dict:new(), 52 | reqs_by_age = gb_trees:empty(), 53 | status = active}}. 54 | 55 | 56 | handle_call({shutdown, Timeout}, _From, State) -> 57 | NewState = case Timeout of 58 | none -> 59 | State#state{status=stopping}; 60 | _ -> 61 | {ok, TRef} = timer:send_after(Timeout, shutdown), 62 | State#state{status=stopping, graceful_timer=TRef} 63 | end, 64 | {reply, ok, NewState}; 65 | handle_call(stop, _From, State) -> 66 | {stop, normal, ok, State}; 67 | 68 | handle_call(_Msg, _From, State) -> 69 | {reply, ok, State}. 70 | 71 | handle_cast({add_connection, Pid}, #state{reqs=Reqs, 72 | reqs_by_age=ReqsByAge, 73 | age=Age}=State) -> 74 | MRef = erlang:monitor(process, Pid), 75 | NewReqs = dict:store(Pid, {Age, MRef}, Reqs), 76 | ReqsByAge1 = gb_trees:enter(Age, {Pid, MRef}, ReqsByAge), 77 | {noreply, State#state{reqs=NewReqs, 78 | reqs_by_age=ReqsByAge1, 79 | age=Age+1}}; 80 | handle_cast({remove_connection, Pid}, #state{listener=ListenerPid, 81 | reqs=Reqs, 82 | reqs_by_age=ReqsByAge, 83 | age=Age}=State) -> 84 | case dict:find(Pid, Reqs) of 85 | {ok, {Age, MRef}} -> 86 | erlang:demonitor(MRef), 87 | Reqs1 = dict:erase(Pid, Reqs), 88 | ReqsByAge1 = gb_trees:delete_any(Age, ReqsByAge), 89 | 90 | %% tell the listener the connection is down 91 | ListenerPid ! {req_down, self()}, 92 | 93 | NewState = State#state{reqs=Reqs1, 94 | reqs_by_age=ReqsByAge1}, 95 | {noreply, NewState}; 96 | _ -> 97 | {noreply, State} 98 | end; 99 | 100 | 101 | handle_cast(_Msg, State) -> 102 | {noreply, State}. 103 | 104 | handle_info({'DOWN', _MRef, _, Pid, _}, #state{listener=ListenerPid, 105 | reqs=Reqs, 106 | reqs_by_age=ReqsByAge, 107 | status=Status}=State) -> 108 | case dict:find(Pid, Reqs) of 109 | {ok, {Age, _}} -> 110 | Reqs1 = dict:erase(Pid, Reqs), 111 | ReqsByAge1 = gb_trees:delete_any(Age, ReqsByAge), 112 | 113 | %% tell the listener the connection is down 114 | ListenerPid ! {req_down, self()}, 115 | 116 | NbReqs = gb_trees:size(ReqsByAge1), 117 | NewState = State#state{reqs=Reqs1, 118 | reqs_by_age=ReqsByAge1}, 119 | 120 | case Status of 121 | active -> 122 | {noreply, NewState}; 123 | stopping when NbReqs /= 0 -> 124 | {noreply, NewState}; 125 | _ -> 126 | timer:cancel(NewState#state.graceful_timer), 127 | {stop, NewState#state{graceful_timer=nil}} 128 | end; 129 | _ -> 130 | {noreply, State} 131 | end; 132 | 133 | handle_info(shutdown, State) -> 134 | {stop, normal, State}; 135 | 136 | handle_info(_Info, State) -> 137 | {noreply, State}. 138 | 139 | terminate(_Reason, #state{listener=ListenerPid, 140 | reqs_by_age=ReqsByAge}) -> 141 | Iterator = gb_trees:iterator(ReqsByAge), 142 | kill_connections(gb_trees:next(Iterator), ListenerPid), 143 | ok. 144 | 145 | code_change(_OldVsn, State, _Extra) -> 146 | {ok, State}. 147 | 148 | kill_connections(none, _ListenerPid) -> 149 | ok; 150 | kill_connections({_Age, {Pid, MRef}, Iterator}, ListenerPid) -> 151 | try 152 | catch exit(Pid, kill), 153 | receive 154 | {'DOWN', MRef, _, _, _} -> 155 | ok 156 | end 157 | after 158 | erlang:demonitor(MRef) 159 | end, 160 | if pid /= nil -> 161 | ListenerPid ! {req_down, self()} 162 | end, 163 | kill_connections(gb_trees:next(Iterator), ListenerPid). 164 | -------------------------------------------------------------------------------- /src/barrel_deps.erl: -------------------------------------------------------------------------------- 1 | %%% -*- erlang -*- 2 | %%% 3 | %%% This file is part of barrel released under the MIT license. 4 | %%% See the NOTICE for more information. 5 | 6 | -module(barrel_deps). 7 | -author('Justin Sheehy '). 8 | -author('Andy Gross '). 9 | 10 | -export([ensure/0, ensure/1]). 11 | -export([get_base_dir/0, get_base_dir/1]). 12 | -export([local_path/1, local_path/2]). 13 | -export([deps_on_path/0, new_siblings/1]). 14 | 15 | %% @spec deps_on_path() -> [ProjNameAndVers] 16 | %% @doc List of project dependencies on the path. 17 | deps_on_path() -> 18 | ordsets:from_list([filename:basename(filename:dirname(X)) || X <- code:get_path()]). 19 | 20 | %% @spec new_siblings(Module) -> [Dir] 21 | %% @doc Find new siblings paths relative to Module that aren't already on the 22 | %% code path. 23 | new_siblings(Module) -> 24 | Existing = deps_on_path(), 25 | SiblingEbin = [ X || X <- filelib:wildcard(local_path(["deps", "*", "ebin"], Module)), 26 | filename:basename(filename:dirname(X)) /= %% don't include self 27 | filename:basename(filename:dirname( 28 | filename:dirname( 29 | filename:dirname(X)))) ], 30 | Siblings = [filename:dirname(X) || X <- SiblingEbin, 31 | ordsets:is_element( 32 | filename:basename(filename:dirname(X)), 33 | Existing) =:= false], 34 | lists:filter(fun filelib:is_dir/1, 35 | lists:append([[filename:join([X, "ebin"]), 36 | filename:join([X, "include"])] || 37 | X <- Siblings])). 38 | 39 | 40 | %% @spec ensure(Module) -> ok 41 | %% @doc Ensure that all ebin and include paths for dependencies 42 | %% of the application for Module are on the code path. 43 | ensure(Module) -> 44 | code:add_paths(new_siblings(Module)), 45 | %% code:clash is annoying when you load couchbeam in a script. 46 | %% code:clash(), 47 | ok. 48 | 49 | %% @spec ensure() -> ok 50 | %% @doc Ensure that the ebin and include paths for dependencies of 51 | %% this application are on the code path. Equivalent to 52 | %% ensure(?Module). 53 | ensure() -> 54 | ensure(?MODULE). 55 | 56 | %% @spec get_base_dir(Module) -> string() 57 | %% @doc Return the application directory for Module. It assumes Module is in 58 | %% a standard OTP layout application in the ebin or src directory. 59 | get_base_dir(Module) -> 60 | {file, Here} = code:is_loaded(Module), 61 | filename:dirname(filename:dirname(Here)). 62 | 63 | %% @spec get_base_dir() -> string() 64 | %% @doc Return the application directory for this application. Equivalent to 65 | %% get_base_dir(?MODULE). 66 | get_base_dir() -> 67 | get_base_dir(?MODULE). 68 | 69 | %% @spec local_path([string()], Module) -> string() 70 | %% @doc Return an application-relative directory from Module's application. 71 | local_path(Components, Module) -> 72 | filename:join([get_base_dir(Module) | Components]). 73 | 74 | %% @spec local_path(Components) -> string() 75 | %% @doc Return an application-relative directory for this application. 76 | %% Equivalent to local_path(Components, ?MODULE). 77 | local_path(Components) -> 78 | local_path(Components, ?MODULE). 79 | -------------------------------------------------------------------------------- /src/barrel_listener.erl: -------------------------------------------------------------------------------- 1 | %%% -*- erlang -*- 2 | %%% 3 | %%% This file is part of barrel released under the MIT license. 4 | %%% See the NOTICE for more information. 5 | 6 | -module(barrel_listener). 7 | -behaviour(gen_server). 8 | 9 | -export([get_port/1, 10 | info/1, info/2, 11 | set_max_clients/2, get_max_clients/1, 12 | set_nb_acceptors/2, get_nb_acceptors/1, 13 | set_protocol_conf/4, get_protocol_conf/1, 14 | remove_connection/2]). 15 | 16 | 17 | %% internal API 18 | -export([start_link/1]). 19 | -export([start_accepting/1]). 20 | 21 | 22 | %% gen_server callbacks 23 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 24 | code_change/3, terminate/2]). 25 | 26 | 27 | -record(state, {socket, 28 | transport, 29 | transport_opts, 30 | nb_acceptors, 31 | acceptors = [], 32 | conn_managers = [], 33 | open_reqs = 0, 34 | max_clients=300000, 35 | sleepers=[], 36 | listener_opts, 37 | protocol}). 38 | 39 | %% @doc get current port 40 | get_port(Ref) -> 41 | gen_server:call(Ref, get_port). 42 | 43 | %% @doc get all infos 44 | info(Ref) -> 45 | info(Ref, [ip, port, open_reqs, nb_acceptors, max_clients]). 46 | 47 | %% @doc get info for some keys 48 | info(Ref, Keys) -> 49 | gen_server:call(Ref, {info, Keys}). 50 | 51 | %% @doc set max number of concurrent clients 52 | set_max_clients(Ref, Nb) -> 53 | gen_server:call(Ref, {set_max_clients, Nb}). 54 | 55 | %% @doc get max number of concurrent clients 56 | get_max_clients(Ref) -> 57 | [{max_clients, Max}] = info(Ref, [max_connection]), 58 | Max. 59 | 60 | %% @doc set the number of acceptors 61 | set_nb_acceptors(Ref, Nb) -> 62 | gen_server:call(Ref, {set_nb_acceptors, Nb}). 63 | 64 | %% @doc get the number of acceptors 65 | get_nb_acceptors(Ref) -> 66 | [{nb_acceptors, Nb}] = info(Ref, [nb_acceptors]), 67 | Nb. 68 | 69 | %% @doc update the protocol configuration and kill after a timeout 70 | set_protocol_conf(Ref, Handler, Opts, GracefulTimeout) -> 71 | gen_server:call(Ref, {set_protocol_conf, Handler, Opts, 72 | GracefulTimeout}). 73 | 74 | %% @doc get the protocol configuration 75 | get_protocol_conf(Ref) -> 76 | gen_server:call(Ref, get_protocol_conf). 77 | 78 | %% @doc remove a connection from the connection manager 79 | remove_connection(Ref, Pid) -> 80 | gen_server:call(Ref, {remove_connection, Pid}). 81 | 82 | 83 | %% @doc internal api, tell to the acceptor if he can start to accept a new 84 | %% connection. 85 | start_accepting(Ref) -> 86 | gen_server:call(Ref, start_accepting, infinity). 87 | 88 | start_link([_, _, _, _, _, ListenerOpts] = Options) -> 89 | Ref = proplists:get_value(ref, ListenerOpts), 90 | case gen_server:start_link({local, Ref}, ?MODULE, Options, []) of 91 | {ok, Pid} -> 92 | ok = barrel_server:set_listener(Ref, Pid), 93 | {ok, Pid}; 94 | Error -> 95 | Error 96 | end. 97 | 98 | init([NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts, 99 | ListenerOpts]) -> 100 | 101 | process_flag(trap_exit, true), 102 | 103 | %% If we have a socket already listening then use it 104 | LSocket = case proplists:get_value(socket, TransOpts) of 105 | undefined -> 106 | {ok, Socket} = Transport:listen(TransOpts), 107 | Socket; 108 | Socket -> 109 | Socket 110 | end, 111 | 112 | %% launch acceptors 113 | Spawn = is_request_spawned(Protocol), 114 | Acceptors = [barrel_acceptor:start_link(self(), Transport, LSocket, 115 | ListenerOpts, 116 | {Protocol, ProtoOpts, Spawn}) 117 | || _ <- lists:seq(1, NbAcceptors)], 118 | 119 | %% Start the connection monitor 120 | {ok, ConnManager} = barrel_connections:start(), 121 | 122 | {ok, #state{socket = LSocket, 123 | transport = Transport, 124 | transport_opts = TransOpts, 125 | acceptors = Acceptors, 126 | nb_acceptors = NbAcceptors, 127 | conn_managers = [ConnManager], 128 | open_reqs = 0, 129 | max_clients = 300000, 130 | sleepers = [], 131 | listener_opts = ListenerOpts, 132 | protocol = {Protocol, ProtoOpts, Spawn}}}. 133 | 134 | 135 | handle_call(get_port, _From, #state{socket=S, transport=Transport}=State) -> 136 | case Transport:sockname(S) of 137 | {ok, {_, Port}} -> 138 | {reply, {ok, Port}, State}; 139 | Error -> 140 | {reply, Error, State} 141 | end; 142 | 143 | handle_call({info, Keys}, _From, State) -> 144 | Infos = get_infos(Keys, State), 145 | {reply, Infos, State}; 146 | 147 | handle_call({set_max_clients, Nb}, _From, State) -> 148 | {reply, ok, State#state{max_clients=Nb}}; 149 | 150 | handle_call({set_nb_acceptors, Nb}, _From, State) -> 151 | NewState = manage_acceptors(State#state{nb_acceptors=Nb}), 152 | {reply, ok, NewState}; 153 | 154 | handle_call(start_accepting, From, #state{open_reqs=NbReqs, 155 | max_clients=Max, 156 | sleepers=Sleepers}=State) 157 | when NbReqs =:= Max -> 158 | {noreply, State#state{sleepers=[From | Sleepers]}}; 159 | handle_call(start_accepting, _From, State) -> 160 | {reply, ok, State}; 161 | 162 | handle_call({set_protocol_conf, Handler, Opts, GracefulTimeout}, _From, 163 | #state{conn_managers=Managers, 164 | nb_acceptors=Nb, 165 | acceptors=Acceptors, 166 | sleepers=Sleepers}=State) -> 167 | 168 | [{Pid, _} | _] = Managers, 169 | State1 = State#state{protocol={Handler, Opts, 170 | is_request_spawned(Handler)}}, 171 | 172 | %% spawn new acceptors with the upgraded protocol 173 | NewAcceptors = spawn_acceptors(Nb, State1), 174 | 175 | %% kill old acceptors, 176 | [catch exit(AcceptorPid, normal) || AcceptorPid <- Acceptors], 177 | 178 | %% kill sleepers if any 179 | lists:foreach(fun({SleeperPid, _}) -> 180 | catch exit(SleeperPid, normal) 181 | end, Sleepers), 182 | 183 | 184 | {ok, NewConnMgr} = barrel_connections:start(), 185 | 186 | %% tell to the connections supervisor to shutdown ASAP 187 | ok = barrel_connections:shutdown(Pid, GracefulTimeout), 188 | 189 | {reply, ok, State1#state{acceptors=NewAcceptors, 190 | conn_managers=[NewConnMgr | Managers]}}; 191 | 192 | handle_call(get_protocol_conf, _From, 193 | #state{protocol={Handler,Opts, _}}=State) -> 194 | {reply, {Handler, Opts}, State}; 195 | 196 | handle_call({remove_connection, Pid}, _From, 197 | #state{conn_managers = [{MgrPid, _} | _]}=State) -> 198 | 199 | %% tell the manage to remove the connection 200 | gen_server:cast(MgrPid, {remove_connection, Pid}), 201 | 202 | %% decrease the number of open requests 203 | {reply, ok, State}; 204 | 205 | handle_call(_Msg, _From, State) -> 206 | {reply, ok, State}. 207 | 208 | handle_cast({accepted, Pid}, #state{acceptors=Acceptors}=State) -> 209 | %% accept a request and start a new acceptor 210 | %% 211 | NewState = case lists:member(Pid, Acceptors) of 212 | true -> 213 | start_new_acceptor(accept_request(Pid, State)); 214 | false -> 215 | %% acceptor isn't exited only monitor the spawned request 216 | %% process. 217 | monitor_request(Pid, State) 218 | end, 219 | {noreply, NewState}; 220 | 221 | handle_cast(_Msg, State) -> 222 | {noreply, State}. 223 | 224 | handle_info({'DOWN', _MRef, _, Pid, _}, 225 | #state{conn_managers=[{Pid, _} | Rest]}=State) -> 226 | error_logger:error_msg("connection supervisor down: ~p~n", [self()]), 227 | 228 | {ok, NewConnMgr} = barrel_connections:start(), 229 | {noreply, State#state{conn_managers=[NewConnMgr | Rest]}}; 230 | 231 | handle_info({'DOWN', _MRef, _, Pid, _}, 232 | #state{conn_managers=Managers}=State) -> 233 | 234 | case lists:keyfind(Pid, 1, Managers) of 235 | false -> 236 | {noreply, State}; 237 | _ -> 238 | {ok, NewConnMgr} = barrel_connections:start(), 239 | NewManagers = [NewConnMgr | lists:keydelete(Pid, 1, Managers)], 240 | {noreply, State#state{conn_managers=NewManagers}} 241 | end; 242 | 243 | 244 | handle_info({req_down, _Pid}, #state{open_reqs=NbReqs}=State) -> 245 | State1 = case State#state.sleepers of 246 | [] -> State; 247 | [Sleeper | Rest] -> 248 | gen_server:reply(Sleeper, ok), 249 | State#state{sleepers=Rest} 250 | end, 251 | 252 | {noreply, State1#state{open_reqs=NbReqs-1}}; 253 | 254 | handle_info({'EXIT', Pid, normal}, State) -> 255 | {noreply, remove_acceptor(State, Pid)}; 256 | 257 | handle_info({'EXIT', Pid, Reason}, State) -> 258 | error_logger:info_msg("request (pid ~p) unexpectedly crashed:~n~p~n", 259 | [Pid, Reason]), 260 | {noreply, remove_acceptor(State, Pid)}. 261 | 262 | terminate(_Reason, #state{conn_managers=Managers}) -> 263 | %% kill all connections managers 264 | %% 265 | lists:foreach(fun({Pid, MRef}) -> 266 | erlang:demonitor(MRef), 267 | barrel_connections:stop(Pid) 268 | end, Managers), 269 | ok. 270 | 271 | code_change(_OldVsn, State, _Extra) -> 272 | {ok, State}. 273 | 274 | 275 | %% internals 276 | %% 277 | %% 278 | monitor_request(Pid, #state{conn_managers=[{MgrPid, _} | _], 279 | open_reqs=NbReqs}=State) -> 280 | barrel_connections:add_connection(MgrPid, Pid), 281 | State#state{open_reqs=NbReqs+1}. 282 | 283 | accept_request(Pid, #state{acceptors=Acceptors}=State) -> 284 | %% remove acceptor from the list of acceptor and increase state 285 | unlink(Pid), 286 | 287 | %% trap premature exit 288 | receive 289 | {'EXIT', Pid, _} -> 290 | true 291 | after 0 -> 292 | true 293 | end, 294 | 295 | %% remove the acceptor from the list and start to monitor it as a 296 | %% request. 297 | monitor_request(Pid, State#state{acceptors=lists:delete(Pid, Acceptors)}). 298 | 299 | remove_acceptor(#state{acceptors=Acceptors, nb_acceptors=N}=State, Pid) 300 | when length(Acceptors) < N-> 301 | NewPid = barrel_acceptor:start_link(self(), State#state.transport, 302 | State#state.socket, 303 | State#state.listener_opts, 304 | State#state.protocol), 305 | Acceptors1 = [NewPid | lists:delete(Pid, Acceptors)], 306 | State#state{acceptors = Acceptors1}; 307 | remove_acceptor(State, Pid) -> 308 | State#state{acceptors = lists:delete(Pid, State#state.acceptors)}. 309 | 310 | start_new_acceptor(State) -> 311 | Pid = barrel_acceptor:start_link(self(), State#state.transport, 312 | State#state.socket, 313 | State#state.listener_opts, 314 | State#state.protocol), 315 | 316 | State#state{acceptors = [Pid | State#state.acceptors]}. 317 | 318 | manage_acceptors(#state{nb_acceptors=N, acceptors=Acceptors}=State) -> 319 | AcceptorsLen = length(Acceptors), 320 | if N > AcceptorsLen -> 321 | NewAcceptors = spawn_acceptors(N - AcceptorsLen, State), 322 | State#state{acceptors=Acceptors ++ NewAcceptors}; 323 | true -> 324 | NewAcceptors = murder_acceptors(lists:reverse(Acceptors), 325 | AcceptorsLen - N), 326 | State#state{acceptors=NewAcceptors} 327 | end. 328 | 329 | spawn_acceptors(Nb, State) -> 330 | [barrel_acceptor:start_link(self(), State#state.transport, 331 | State#state.socket, 332 | State#state.listener_opts, 333 | State#state.protocol) 334 | || _ <- lists:seq(1, Nb)]. 335 | 336 | 337 | murder_acceptors(Acceptors, 1) -> 338 | lists:reverse(Acceptors); 339 | murder_acceptors([], _N) -> 340 | []; 341 | murder_acceptors([Pid | Rest], N) -> 342 | catch exit(Pid, normal), 343 | murder_acceptors(Rest, N-1). 344 | 345 | get_infos(Keys, #state{transport=Transport, socket=Socket}=State) -> 346 | IpPort = case Transport:sockname(Socket) of 347 | {ok, IpPort1} -> 348 | IpPort1; 349 | Error -> 350 | {{error, Error}, {error, Error}} 351 | end, 352 | get_infos(Keys, IpPort, State, []). 353 | 354 | get_infos([], _IpPort, _State, Acc) -> 355 | lists:reverse(Acc); 356 | get_infos([ip|Rest], {Ip, _}=IpPort, State, Acc) -> 357 | get_infos(Rest, IpPort, State, [{ip, Ip}|Acc]); 358 | get_infos([port|Rest], {_, Port}=IpPort, State, Acc) -> 359 | get_infos(Rest, IpPort, State, [{port, Port}|Acc]); 360 | get_infos([open_reqs|Rest], IpPort, #state{open_reqs=NbReqs}=State, Acc) -> 361 | get_infos(Rest, IpPort, State, [{open_reqs, NbReqs}|Acc]); 362 | get_infos([nb_acceptors|Rest], IpPort, #state{acceptors=Acceptors}=State, 363 | Acc) -> 364 | get_infos(Rest, IpPort, State, [{acceptors, length(Acceptors)}|Acc]); 365 | get_infos([max_clients|Rest], IpPort, #state{max_clients=Max}=State, 366 | Acc) -> 367 | get_infos(Rest, IpPort, State, [{max_clients, Max} | Acc]). 368 | 369 | 370 | is_request_spawned(Handler) -> 371 | _ = code:ensure_loaded(Handler), 372 | 373 | case erlang:function_exported(Handler, start_link, 4) of 374 | true -> 375 | true; 376 | false -> 377 | case erlang:function_exported(Handler, init, 4) of 378 | true -> 379 | false; 380 | false -> 381 | throw({error, bad_proto}) 382 | end 383 | end. 384 | -------------------------------------------------------------------------------- /src/barrel_server.erl: -------------------------------------------------------------------------------- 1 | -module(barrel_server). 2 | 3 | -behaviour(gen_server). 4 | 5 | -export([start_link/0]). 6 | -export([set_listener/2, get_listener/1]). 7 | 8 | %% gen_server callbacks 9 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 10 | code_change/3, terminate/2]). 11 | 12 | 13 | -define(TAB, ?MODULE). 14 | -record(state, {monitors = [] }). 15 | 16 | 17 | %% @doc Set the listener associated to the ref. 18 | -spec set_listener(barrel:ref(), pid()) -> ok. 19 | set_listener(Ref, Pid) -> 20 | true = gen_server:call(?MODULE, {set_listener, Ref, Pid}), 21 | ok. 22 | 23 | %% @doc Return the listener associated to the ref. 24 | -spec get_listener(ranch:ref()) -> pid(). 25 | get_listener(Ref) -> 26 | ets:lookup_element(?TAB, {listeners, Ref}, 2). 27 | 28 | %% @doc Start the barell_sever. 29 | -spec start_link() -> {ok, pid()}. 30 | start_link() -> 31 | gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). 32 | 33 | init([]) -> 34 | Monitors = [{{erlang:monitor(process, Pid), Pid}, Ref} || 35 | [Ref, Pid] <- ets:match(?TAB, {{listeners, '$1'}, '$2'})], 36 | {ok, #state{monitors=Monitors}}. 37 | 38 | 39 | handle_call({set_listener, Ref, Pid}, _From, 40 | #state{monitors=Monitors}=State) -> 41 | case ets:insert_new(?TAB, {{listeners, Ref}, Pid}) of 42 | true -> 43 | MRef = erlang:monitor(process, Pid), 44 | {reply, true, 45 | State#state{monitors=[{{MRef, Pid}, Ref}|Monitors]}}; 46 | false -> 47 | {reply, false, State} 48 | end; 49 | 50 | handle_call(_Request, _From, State) -> 51 | {reply, ignore, State}. 52 | 53 | %% @private 54 | handle_cast(_Request, State) -> 55 | {noreply, State}. 56 | 57 | %% @private 58 | handle_info({'DOWN', MRef, process, Pid, _}, 59 | State=#state{monitors=Monitors}) -> 60 | {_, Ref} = lists:keyfind({MRef, Pid}, 1, Monitors), 61 | true = ets:delete(?TAB, {listeners, Ref}), 62 | Monitors2 = lists:keydelete({MRef, Pid}, 1, Monitors), 63 | {noreply, State#state{monitors=Monitors2}}; 64 | handle_info(_Info, State) -> 65 | {noreply, State}. 66 | 67 | %% @private 68 | terminate(_Reason, _State) -> 69 | ok. 70 | 71 | %% @private 72 | code_change(_OldVsn, State, _Extra) -> 73 | {ok, State}. 74 | -------------------------------------------------------------------------------- /src/barrel_ssl.erl: -------------------------------------------------------------------------------- 1 | %%% -*- erlang -*- 2 | %%% 3 | %%% This file is part of barrel released under the MIT license. 4 | %%% See the NOTICE for more information. 5 | %%% 6 | %%% Copyright (c) 2011-2012, Loïc Hoguin 7 | 8 | -module(barrel_ssl). 9 | -export([name/0, 10 | messages/0, 11 | listen/1, listen/2, 12 | accept/2, 13 | connect/3, connect/4, 14 | recv/3, recv/2, 15 | send/2, 16 | setopts/2, 17 | controlling_process/2, 18 | peername/1, 19 | close/1, 20 | sockname/1, 21 | sendfile/2]). 22 | 23 | %% @doc Name of this transport, tcp. 24 | name() -> ssl. 25 | 26 | %% @doc Atoms used to identify messages in {active, once | true} mode. 27 | messages() -> {ssl, ssl_closed, ssl_error}. 28 | 29 | %% @doc Listen for connections on the given port number. 30 | %% @see ssl:listen/2 31 | %% 32 | listen(Opts) -> 33 | listen(0, Opts). 34 | 35 | 36 | listen(Port, Opts) -> 37 | barrel_util:require([crypto, public_key, ssl]), 38 | BaseOpts = [binary, 39 | {backlog, 1024}, 40 | {active, false}, 41 | {packet, raw}, 42 | {reuseaddr, true}, 43 | {nodelay, true}], 44 | 45 | % do we have required options 46 | true = lists:keymember(cert, 1, Opts) 47 | orelse lists:keymember(certfile, 1, Opts), 48 | 49 | Opts1 = barrel_util:filter_props(Opts, [backlog, cacertfile, 50 | cacerts, cert, certfile, 51 | ciphers, fail_if_no_peer_cert, 52 | ip, key, keyfile, 53 | next_protocols_advertised, 54 | verify_fun, depth, 55 | nodelay, password, port, 56 | raw, verify], BaseOpts), 57 | ssl:listen(Port, barrel_util:fix_ip(Opts1)). 58 | 59 | 60 | %% @doc Accept connections with the given listening socket. 61 | %% 62 | %% Note that this function does both the transport accept and 63 | %% the SSL handshake. The returned socket is thus fully connected. 64 | %% 65 | %% @see ssl:transport_accept/2 66 | %% @see ssl:ssl_accept/2 67 | -spec accept(ssl:sslsocket(), timeout()) 68 | -> {ok, ssl:sslsocket()} | {error, closed | timeout | atom() | tuple()}. 69 | accept(LSocket, Timeout) -> 70 | case ssl:transport_accept(LSocket, Timeout) of 71 | {ok, CSocket} -> 72 | ssl_accept(CSocket, Timeout); 73 | {error, Reason} -> 74 | {error, Reason} 75 | end. 76 | 77 | connect(Host, Port, Opts) -> 78 | connect(Host, Port, Opts, infinity). 79 | 80 | connect(Host, Port, Opts, Timeout) when is_list(Host), is_integer(Port), 81 | (Timeout =:= infinity orelse is_integer(Timeout)) -> 82 | ssl:connect(Host, Port, 83 | Opts ++ [binary, {active, false}, {packet, raw}], Timeout). 84 | 85 | recv(Socket, Length) -> 86 | recv(Socket, Length, infinity). 87 | 88 | %% @doc Receive a packet from a socket in passive mode. 89 | %% @see ssl:recv/3 90 | -spec recv(ssl:sslsocket(), non_neg_integer(), timeout()) 91 | -> {ok, any()} | {error, closed | atom()}. 92 | recv(Socket, Length, Timeout) -> 93 | ssl:recv(Socket, Length, Timeout). 94 | 95 | %% @doc Send a packet on a socket. 96 | %% @see ssl:send/2 97 | -spec send(ssl:sslsocket(), iolist() | binary()) -> ok | {error, atom()}. 98 | send(Socket, Packet) -> 99 | ssl:send(Socket, Packet). 100 | 101 | %% @doc Set one or more options for a socket. 102 | %% @see ssl:setopts/2 103 | -spec setopts(ssl:sslsocket(), list()) -> ok | {error, atom()}. 104 | setopts(Socket, Opts) -> 105 | ssl:setopts(Socket, Opts). 106 | 107 | %% @doc Assign a new controlling process Pid to Socket. 108 | %% @see ssl:controlling_process/2 109 | -spec controlling_process(ssl:sslsocket(), pid()) 110 | -> ok | {error, closed | not_owner | atom()}. 111 | controlling_process(Socket, Pid) -> 112 | ssl:controlling_process(Socket, Pid). 113 | 114 | %% @doc Return the address and port for the other end of a connection. 115 | %% @see ssl:peername/1 116 | -spec peername(ssl:sslsocket()) 117 | -> {ok, {inet:ip_address(), inet:port_number()}} | {error, atom()}. 118 | peername(Socket) -> 119 | ssl:peername(Socket). 120 | 121 | %% @doc Close a TCP socket. 122 | %% @see ssl:close/1 123 | -spec close(ssl:sslsocket()) -> ok. 124 | close(Socket) -> 125 | ssl:close(Socket). 126 | 127 | %% @doc Get the local address and port of a socket 128 | %% @see ssl:sockname/1 129 | -spec sockname(ssl:sslsocket()) 130 | -> {ok, {inet:ip_address(), inet:port_number()}} | {error, atom()}. 131 | sockname(Socket) -> 132 | ssl:sockname(Socket). 133 | 134 | 135 | 136 | %% Internal 137 | %% 138 | %% This call always times out, either because a numeric timeout value 139 | %% was given, or because we've decided to use 5000ms instead of infinity. 140 | %% This value should be reasonable enough for the moment. 141 | -spec ssl_accept(ssl:sslsocket(), timeout()) 142 | -> {ok, ssl:sslsocket()} | {error, {ssl_accept, atom()}}. 143 | ssl_accept(Socket, infinity) -> 144 | ssl_accept(Socket, 5000); 145 | ssl_accept(Socket, Timeout) -> 146 | case ssl:ssl_accept(Socket, Timeout) of 147 | ok -> 148 | {ok, Socket}; 149 | {error, Reason} -> 150 | {error, {ssl_accept, Reason}} 151 | end. 152 | 153 | %% @doc Send a file on a socket. 154 | %% 155 | %% Unlike with TCP, no syscall can be used here, so sending files 156 | %% through SSL will be much slower in comparison. 157 | %% 158 | %% @see file:sendfile/2 159 | -spec sendfile(ssl:sslsocket(), file:name()) 160 | -> {ok, non_neg_integer()} | {error, atom()}. 161 | sendfile(Socket, Filepath) -> 162 | {ok, IoDevice} = file:open(Filepath, [read, binary, raw]), 163 | sendfile(Socket, IoDevice, 0). 164 | 165 | -spec sendfile(ssl:sslsocket(), file:io_device(), non_neg_integer()) 166 | -> {ok, non_neg_integer()} | {error, atom()}. 167 | sendfile(Socket, IoDevice, Sent) -> 168 | case file:read(IoDevice, 16#1FFF) of 169 | eof -> 170 | ok = file:close(IoDevice), 171 | {ok, Sent}; 172 | {ok, Bin} when is_binary(Bin) -> 173 | case send(Socket, Bin) of 174 | ok -> 175 | sendfile(Socket, IoDevice, Sent + byte_size(Bin)); 176 | {error, Reason} -> 177 | {error, Reason} 178 | end 179 | end. 180 | -------------------------------------------------------------------------------- /src/barrel_sup.erl: -------------------------------------------------------------------------------- 1 | %%% -*- erlang -*- 2 | %%% 3 | %%% This file is part of barrel released under the MIT license. 4 | %%% See the NOTICE for more information. 5 | 6 | -module(barrel_sup). 7 | 8 | -behaviour(supervisor). 9 | 10 | %% API 11 | -export([start_link/0]). 12 | 13 | %% Supervisor callbacks 14 | -export([init/1]). 15 | 16 | %% Helper macro for declaring children of supervisor 17 | -define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). 18 | 19 | %% =================================================================== 20 | %% API functions 21 | %% =================================================================== 22 | 23 | start_link() -> 24 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 25 | 26 | %% =================================================================== 27 | %% Supervisor callbacks 28 | %% =================================================================== 29 | 30 | init([]) -> 31 | %% initialize the table keeping infos on listeners. This table 32 | %% should be public so we will be able to monitor listeners already 33 | %% started if the server is stopped. 34 | barrel_server = ets:new(barrel_server, [ordered_set, public, 35 | named_table]), 36 | Server = {barrel_server, {barrel_server, start_link, []}, 37 | permanent, 5000, worker, [barrel_server]}, 38 | 39 | {ok, { {one_for_one, 10, 10}, [Server]} }. 40 | 41 | -------------------------------------------------------------------------------- /src/barrel_tcp.erl: -------------------------------------------------------------------------------- 1 | %%% -*- erlang -*- 2 | %%% 3 | %%% This file is part of barrel released under the MIT license. 4 | %%% See the NOTICE for more information. 5 | %%% 6 | %%% Copyright (c) 2011-2012, Loïc Hoguin 7 | %%% 8 | -module(barrel_tcp). 9 | -export([name/0, 10 | messages/0, 11 | listen/1,listen/2, 12 | accept/2, 13 | connect/3, connect/4, 14 | recv/2, recv/3, 15 | send/2, 16 | setopts/2, 17 | controlling_process/2, 18 | peername/1, 19 | close/1, 20 | sockname/1, 21 | sendfile/2]). 22 | 23 | %% @doc Name of this transport, tcp. 24 | name() -> tcp. 25 | 26 | %% @doc Atoms used to identify messages in {active, once | true} mod. 27 | messages() -> {tcp, tcp_closed, tcp_error}. 28 | 29 | %% @doc Listen for connections on the given port number. 30 | %% @see gen_tcp:listen/2 31 | %% 32 | 33 | listen(Opts) -> 34 | listen(0, Opts). 35 | 36 | listen(Port, Opts) -> 37 | BaseOpts = [binary, 38 | {backlog, 1024}, 39 | {active, false}, 40 | {packet, raw}, 41 | {reuseaddr, true}, 42 | {nodelay, true}], 43 | Opts1 = barrel_util:filter_props(Opts, [backlog, ip, nodelay, port, 44 | packet], BaseOpts), 45 | gen_tcp:listen(Port, barrel_util:fix_ip(Opts1)). 46 | 47 | %% @doc Accept connections with the given listening socket. 48 | %% @see gen_tcp:accept/2 49 | -spec accept(inet:socket(), timeout()) 50 | -> {ok, inet:socket()} | {error, closed | timeout | atom()}. 51 | accept(LSocket, Timeout) -> 52 | gen_tcp:accept(LSocket, Timeout). 53 | 54 | connect(Host, Port, Opts) -> 55 | connect(Host, Port, Opts, infinity). 56 | 57 | connect(Host, Port, Opts, Timeout) when is_list(Host), is_integer(Port), 58 | (Timeout =:= infinity orelse is_integer(Timeout)) -> 59 | gen_tcp:connect(Host, Port, 60 | Opts ++ [binary, {active, false}, {packet, raw}], Timeout). 61 | 62 | recv(Socket, Length) -> 63 | recv(Socket, Length, infinity). 64 | 65 | %% @doc Receive a packet from a socket in passive mode. 66 | %% @see gen_tcp:recv/3 67 | -spec recv(inet:socket(), non_neg_integer(), timeout()) 68 | -> {ok, any()} | {error, closed | atom()}. 69 | recv(Socket, Length, Timeout) -> 70 | gen_tcp:recv(Socket, Length, Timeout). 71 | 72 | 73 | %% @doc Send a packet on a socket. 74 | %% @see gen_tcp:send/2 75 | -spec send(inet:socket(), iolist()) -> ok | {error, atom()}. 76 | send(Socket, Packet) -> 77 | gen_tcp:send(Socket, Packet). 78 | 79 | %% @doc Set one or more options for a socket. 80 | %% @see inet:setopts/2 81 | -spec setopts(inet:socket(), list()) -> ok | {error, atom()}. 82 | setopts(Socket, Opts) -> 83 | inet:setopts(Socket, Opts). 84 | 85 | %% @doc Assign a new controlling process Pid to Socket. 86 | %% @see gen_tcp:controlling_process/2 87 | -spec controlling_process(inet:socket(), pid()) 88 | -> ok | {error, closed | not_owner | atom()}. 89 | controlling_process(Socket, Pid) -> 90 | gen_tcp:controlling_process(Socket, Pid). 91 | 92 | %% @doc Return the address and port for the other end of a connection. 93 | %% @see inet:peername/1 94 | -spec peername(inet:socket()) 95 | -> {ok, {inet:ip_address(), inet:port_number()}} | {error, atom()}. 96 | peername(Socket) -> 97 | inet:peername(Socket). 98 | 99 | %% @doc Close a TCP socket. 100 | %% @see gen_tcp:close/1 101 | -spec close(inet:socket()) -> ok. 102 | close(Socket) -> 103 | gen_tcp:close(Socket). 104 | 105 | %% @doc Get the local address and port of a socket 106 | %% @see inet:sockname/1 107 | -spec sockname(inet:socket()) 108 | -> {ok, {inet:ip_address(), inet:port_number()}} | {error, atom()}. 109 | sockname(Socket) -> 110 | inet:sockname(Socket). 111 | 112 | 113 | %% @doc Send a file on a socket. 114 | %% 115 | %% This is the optimal way to send files using TCP. It uses a syscall 116 | %% which means there is no context switch between opening the file 117 | %% and writing its contents on the socket. 118 | %% 119 | %% @see file:sendfile/2 120 | -spec sendfile(inet:socket(), file:name()) 121 | -> {ok, non_neg_integer()} | {error, atom()}. 122 | sendfile(Socket, Filename) -> 123 | try file:sendfile(Filename, Socket) of 124 | Result -> Result 125 | catch 126 | error:{badmatch, {error, enotconn}} -> 127 | %% file:sendfile/2 might fail by throwing a {badmatch, {error, enotconn}} 128 | %% this is because its internal implementation fails with a badmatch in 129 | %% prim_file:sendfile/10 if the socket is not connected. 130 | {error, closed} 131 | end. 132 | -------------------------------------------------------------------------------- /src/barrel_util.erl: -------------------------------------------------------------------------------- 1 | %%% -*- erlang -*- 2 | %%% 3 | %%% This file is part of barrel released under the MIT license. 4 | %%% See the NOTICE for more information. 5 | 6 | -module(barrel_util). 7 | 8 | -export([filter_props/2, filter_props/3, 9 | propmerge/2, 10 | fix_ip/1, 11 | ipv6_supported/0, 12 | require/1]). 13 | 14 | %% @doc filter a list of properties and removed n 15 | filter_props(Props, Allowed) -> 16 | filter_props(Props, Allowed, []). 17 | 18 | filter_props([], _Allowed, Acc) -> 19 | lists:reverse(Acc); 20 | filter_props([{K, _}=KV|Rest], Allowed, Acc) -> 21 | case lists:member(K, Allowed) of 22 | true -> 23 | filter_props(Rest, Allowed, [KV|Acc]); 24 | false -> 25 | filter_props(Rest, Allowed, Acc) 26 | end. 27 | 28 | 29 | %% @doc Update a proplist with values of the second. In case the same 30 | %% key is in 2 proplists, the value from the first are kept. 31 | propmerge(L1, L2) -> 32 | propmerge1(fun(_, V1, _) -> V1 end, L1, L2). 33 | 34 | propmerge1(F, L1, L2) -> 35 | dict:to_list(dict:merge(F, dict:from_list(L1), dict:from_list(L2))). 36 | 37 | 38 | fix_ip(Opts) -> 39 | {Opts1, ParsedIp} = case proplists:get_value(ip, Opts) of 40 | undefined -> 41 | {[{ip, any}|Opts], any}; 42 | any -> 43 | {Opts, any}; 44 | Ip when is_tuple(Ip) -> 45 | {Opts, Ip}; 46 | Ip when is_list(Ip) -> 47 | {ok, IpTuple} = inet_parse:address(Ip), 48 | {[{ip, IpTuple}|proplists:delete(ip, Opts)], IpTuple} 49 | end, 50 | 51 | case ParsedIp of 52 | any -> 53 | case ipv6_supported() of % IPv4, and IPv6 if supported 54 | true -> [inet, inet6 | Opts1]; 55 | _ -> Opts1 56 | end; 57 | {_, _, _, _} -> % IPv4 58 | [inet | Opts1]; 59 | {_, _, _, _, _, _, _, _} -> % IPv6 60 | [inet6 | Opts1] 61 | end. 62 | 63 | ipv6_supported() -> 64 | case (catch inet:getaddr("localhost", inet6)) of 65 | {ok, _Addr} -> 66 | true; 67 | {error, _} -> 68 | false 69 | end. 70 | 71 | 72 | %% @doc Start the given applications if they were not already started. 73 | -spec require(list(module())) -> ok. 74 | require([]) -> 75 | ok; 76 | require([App|Rest]) -> 77 | case application:start(App) of 78 | ok -> ok; 79 | {error, {already_started, App}} -> ok 80 | end, 81 | require(Rest). 82 | --------------------------------------------------------------------------------