├── .gitignore ├── README.md ├── composer.json ├── composer.lock ├── scripts └── install_nginx_pushstream_module.sh └── src └── Cmosguy └── Broadcasting ├── Broadcasters └── PushStreamBroadcaster.php └── PushStreamBroadcastManagerProvider.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | composer.phar 3 | composer.lock 4 | phpunit.xml 5 | .idea 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HTTP Push-Stream Nginx Module Laravel Broadcast Driver 2 | 3 | Thank your for your interest in the HTTP Push-stream Nginx broadcast driver for Laravel. If you are here, it is because you are trying to leverage the latest feature within the Laravel 5.1 broadcasts events for websockets. The [HTTP Pushstream Module for Nginx](https://github.com/wandenberg/nginx-push-stream-module) is a powerful Websocket system. 4 | 5 | 6 | # Why use HTTP Pushstream? 7 | If you want to really absorb the power of the capabilities of this module then check out what the dev-ops from [Disqus](http://disqus.com) thought in this link: 8 | 9 | http://highscalability.com/blog/2014/4/28/how-disqus-went-realtime-with-165k-messages-per-second-and-l.html 10 | 11 | # How does this driver work? 12 | Once you setup all your routes for the pub/sub requests to the HTTP routes in the `location` directives for *Nginx*, then you'll be able to quickly open a socket on your client use the **pushstream.js** and push your broadcasts out using websocket or long-polling. 13 | 14 | The *pub/sub* requests are internally called by the [GuzzleHttp](http://guzzle.readthedocs.org/en/latest/) package. The `broadcasting.php` config file will use the **pushstream** driver where you can control the HTTP requests to the to your pub/sub endpoints. 15 | 16 | You can lock down your pub/sub nginx endpoints using the [Access Key Module](http://wiki.nginx.org/HttpAccessKeyModule). Here you can configure the key. 17 | 18 | ## Requirements 19 | 20 | * **HTTP Pushstream Module:** You must compile the Nginx HTTP Pushstream module. Here are some instructions on how to do it here for the module itself on github: 21 | * [Github readme](https://github.com/wandenberg/nginx-push-stream-module#installation) 22 | * [Nginx module site](http://wiki.nginx.org/HttpPushStreamModule#instalation) 23 | * [I have made an Ubuntu install script](scripts/install_nginx_pushstream_module.sh) 24 | 25 | * **Acces Key Module** Also, if you want to use the `access_key` feature to the routes for the pub/sub then please also install the [Access Key Module](http://wiki.nginx.org/HttpAccessKeyModule). 26 | 27 | 28 | ## Installation 29 | 30 | 1. Do a composer require get this package: `composer require cmosguy/laravel-http-pushstream-broadcaster` 31 | 32 | 2. Next, go into your `config/broadcasting.php` file and add the following lines accordingly. `base_url` refers to websocket root for your HTTP requests pub/sub routes: 33 | 34 | 'default' => 'pushstream', 35 | 36 | 'pushstream' => [ 37 | 'driver' => 'pushstream', 38 | 'base_url' => 'http://localhost', 39 | 'access_key' => md5('foo'), 40 | 'cert' => null 41 | // or 'cert' => 'path/to/server.crt' for self-signed certificate 42 | ] 43 | 44 | 3. In your `config/app.php` add the following line to your `providers` array: 45 | 46 | 'Cmosguy\Broadcasting\PushStreamBroadcastManagerProvider' 47 | 48 | ## Sample Nginx Configuration 49 | 1. In your `/etc/nginx/nginx.conf` file add: 50 | 51 | push_stream_shared_memory_size 32M; 52 | 53 | 2. Edit your config file for your routes in the `server {` section. Obviously, you need to understand and modify the items below. The following config information is just meant to get you started. If you want a more thorough config check out [this](https://gist.github.com/dctrwatson/0b3b52050254e273ff11#file-nginx-v): 54 | 55 | location /channels-stats { 56 | # activate channels statistics mode for this location 57 | push_stream_channels_statistics; 58 | 59 | # query string based channel id 60 | push_stream_channels_path $arg_id; 61 | } 62 | 63 | location /pub { 64 | # activate publisher (admin) mode for this location 65 | push_stream_publisher admin; 66 | 67 | # query string based channel id 68 | push_stream_channels_path $arg_id; 69 | } 70 | 71 | location ~ /sub/(.*) { 72 | # activate subscriber (streaming) mode for this location 73 | push_stream_subscriber; 74 | 75 | # positional channel path 76 | push_stream_channels_path $1; 77 | } 78 | 79 | location ~ /ws/(.*) { 80 | # activate websocket mode for this location 81 | push_stream_subscriber websocket; 82 | 83 | 84 | # positional channel path 85 | push_stream_channels_path $1; 86 | if ($arg_tests = "on") { 87 | push_stream_channels_path "test_$1"; 88 | } 89 | 90 | # store messages in memory 91 | push_stream_store_messages on; 92 | 93 | push_stream_websocket_allow_publish on; 94 | 95 | if ($arg_qs = "on") { 96 | push_stream_last_received_message_time "$arg_time"; 97 | push_stream_last_received_message_tag "$arg_tag"; 98 | push_stream_last_event_id "$arg_eventid"; 99 | } 100 | } 101 | 102 | ### Locking down the pub/sub endpoint 103 | 104 | location /pub { 105 | # activate publisher (admin) mode for this location 106 | push_stream_publisher admin; 107 | accesskey on; 108 | accesskey_hashmethod md5; 109 | accesskey_arg "access_key"; 110 | accesskey_signature "foo" 111 | 112 | # query string based channel id 113 | push_stream_channels_path $arg_id; 114 | } 115 | 116 | ## Usage in your app 117 | 118 | So, once you are finally ready to trigger an event, you can do this easily now by just extending this `broadcastOn` in your event handler: 119 | 120 | ```php 121 | 122 | foo = $foo; 139 | } 140 | 141 | /** 142 | * Get the channels the event should be broadcast on. 143 | * 144 | * @return array 145 | */ 146 | public function broadcastOn() 147 | { 148 | return ['foochannel-'.$this->foo->uuid]; 149 | } 150 | } 151 | 152 | ``` 153 | 154 | ## The Client 155 | 156 | Please download the **pushstream.js** from either following locations: 157 | 158 | * [The wandenberg/nginx-push-stream-module repository](https://raw.githubusercontent.com/wandenberg/nginx-push-stream-module/master/misc/js/pushstream.js) 159 | * Or you can go the bower route: `bower install pushstream` 160 | 161 | 162 | ## Study the Push Stream Module 163 | 164 | At this time the only way to get more information from about the module and the capabilities is directly from the github repository, so do some reading here: 165 | 166 | * https://github.com/wandenberg/nginx-push-stream-module 167 | * See how the pushstream.js example works here: http://www.nginxpushstream.com/chat.html 168 | * Also read more about the pushstream.js here: https://github.com/wandenberg/nginx-push-stream-module/blob/master/docs/examples/websocket.textile#websocket- 169 | 170 | 171 | # Disclaimer 172 | 173 | This is by no means the only way to go about how this should work. You need to understand what all the options do and there is definitely a a 174 | 175 | # Help 176 | Please help me with updating this documentation. If it does not make sense or if you see something stupid let me know. Also, if there is a way to make this extend further and make it more flexible for others, please submit a PR to improve upon these. 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cmosguy/laravel-http-pushstream-broadcaster", 3 | "description": "Laravel broadcast driver for the HTTP Push-stream Nginx module", 4 | "require": { 5 | "php": ">=5.3.1", 6 | "guzzlehttp/guzzle": "~5.3", 7 | "illuminate/support": "~5.1" 8 | }, 9 | "autoload": { 10 | "psr-0": { 11 | "Cmosguy": "src/" 12 | } 13 | }, 14 | 15 | "license": "MIT", 16 | "authors": [ 17 | { 18 | "name": "Adam Klein", 19 | "email": "asklein@gmail.com" 20 | } 21 | ], 22 | "minimum-stability": "dev" 23 | } 24 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "714c1f0b6a9fdb13e39be54af77d1a96", 8 | "packages": [ 9 | { 10 | "name": "danielstjules/stringy", 11 | "version": "1.9.0", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/danielstjules/Stringy.git", 15 | "reference": "3cf18e9e424a6dedc38b7eb7ef580edb0929461b" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/danielstjules/Stringy/zipball/3cf18e9e424a6dedc38b7eb7ef580edb0929461b", 20 | "reference": "3cf18e9e424a6dedc38b7eb7ef580edb0929461b", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "ext-mbstring": "*", 25 | "php": ">=5.3.0" 26 | }, 27 | "require-dev": { 28 | "phpunit/phpunit": "~4.0" 29 | }, 30 | "type": "library", 31 | "autoload": { 32 | "psr-4": { 33 | "Stringy\\": "src/" 34 | }, 35 | "files": [ 36 | "src/Create.php" 37 | ] 38 | }, 39 | "notification-url": "https://packagist.org/downloads/", 40 | "license": [ 41 | "MIT" 42 | ], 43 | "authors": [ 44 | { 45 | "name": "Daniel St. Jules", 46 | "email": "danielst.jules@gmail.com", 47 | "homepage": "http://www.danielstjules.com" 48 | } 49 | ], 50 | "description": "A string manipulation library with multibyte support", 51 | "homepage": "https://github.com/danielstjules/Stringy", 52 | "keywords": [ 53 | "UTF", 54 | "helpers", 55 | "manipulation", 56 | "methods", 57 | "multibyte", 58 | "string", 59 | "utf-8", 60 | "utility", 61 | "utils" 62 | ], 63 | "time": "2015-02-10 06:19:18" 64 | }, 65 | { 66 | "name": "doctrine/inflector", 67 | "version": "dev-master", 68 | "source": { 69 | "type": "git", 70 | "url": "https://github.com/doctrine/inflector.git", 71 | "reference": "e5eaf8c7ded0877195b5d2848491e17b1c0a6c4d" 72 | }, 73 | "dist": { 74 | "type": "zip", 75 | "url": "https://api.github.com/repos/doctrine/inflector/zipball/e5eaf8c7ded0877195b5d2848491e17b1c0a6c4d", 76 | "reference": "e5eaf8c7ded0877195b5d2848491e17b1c0a6c4d", 77 | "shasum": "" 78 | }, 79 | "require": { 80 | "php": ">=5.3.2" 81 | }, 82 | "require-dev": { 83 | "phpunit/phpunit": "4.*" 84 | }, 85 | "type": "library", 86 | "extra": { 87 | "branch-alias": { 88 | "dev-master": "1.0.x-dev" 89 | } 90 | }, 91 | "autoload": { 92 | "psr-0": { 93 | "Doctrine\\Common\\Inflector\\": "lib/" 94 | } 95 | }, 96 | "notification-url": "https://packagist.org/downloads/", 97 | "license": [ 98 | "MIT" 99 | ], 100 | "authors": [ 101 | { 102 | "name": "Roman Borschel", 103 | "email": "roman@code-factory.org" 104 | }, 105 | { 106 | "name": "Benjamin Eberlei", 107 | "email": "kontakt@beberlei.de" 108 | }, 109 | { 110 | "name": "Guilherme Blanco", 111 | "email": "guilhermeblanco@gmail.com" 112 | }, 113 | { 114 | "name": "Jonathan Wage", 115 | "email": "jonwage@gmail.com" 116 | }, 117 | { 118 | "name": "Johannes Schmitt", 119 | "email": "schmittjoh@gmail.com" 120 | } 121 | ], 122 | "description": "Common String Manipulations with regard to casing and singular/plural rules.", 123 | "homepage": "http://www.doctrine-project.org", 124 | "keywords": [ 125 | "inflection", 126 | "pluralize", 127 | "singularize", 128 | "string" 129 | ], 130 | "time": "2015-01-01 18:34:57" 131 | }, 132 | { 133 | "name": "guzzlehttp/guzzle", 134 | "version": "5.3.x-dev", 135 | "source": { 136 | "type": "git", 137 | "url": "https://github.com/guzzle/guzzle.git", 138 | "reference": "28475a313d7d413a033b68d762e0db18b3aa4b02" 139 | }, 140 | "dist": { 141 | "type": "zip", 142 | "url": "https://api.github.com/repos/guzzle/guzzle/zipball/28475a313d7d413a033b68d762e0db18b3aa4b02", 143 | "reference": "28475a313d7d413a033b68d762e0db18b3aa4b02", 144 | "shasum": "" 145 | }, 146 | "require": { 147 | "guzzlehttp/ringphp": "^1.1", 148 | "php": ">=5.4.0" 149 | }, 150 | "require-dev": { 151 | "ext-curl": "*", 152 | "phpunit/phpunit": "^4.0" 153 | }, 154 | "type": "library", 155 | "autoload": { 156 | "psr-4": { 157 | "GuzzleHttp\\": "src/" 158 | } 159 | }, 160 | "notification-url": "https://packagist.org/downloads/", 161 | "license": [ 162 | "MIT" 163 | ], 164 | "authors": [ 165 | { 166 | "name": "Michael Dowling", 167 | "email": "mtdowling@gmail.com", 168 | "homepage": "https://github.com/mtdowling" 169 | } 170 | ], 171 | "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", 172 | "homepage": "http://guzzlephp.org/", 173 | "keywords": [ 174 | "client", 175 | "curl", 176 | "framework", 177 | "http", 178 | "http client", 179 | "rest", 180 | "web service" 181 | ], 182 | "time": "2015-05-26 17:54:26" 183 | }, 184 | { 185 | "name": "guzzlehttp/ringphp", 186 | "version": "dev-master", 187 | "source": { 188 | "type": "git", 189 | "url": "https://github.com/guzzle/RingPHP.git", 190 | "reference": "9465032ac5d6beaa55f10923403e6e1c36018d9c" 191 | }, 192 | "dist": { 193 | "type": "zip", 194 | "url": "https://api.github.com/repos/guzzle/RingPHP/zipball/9465032ac5d6beaa55f10923403e6e1c36018d9c", 195 | "reference": "9465032ac5d6beaa55f10923403e6e1c36018d9c", 196 | "shasum": "" 197 | }, 198 | "require": { 199 | "guzzlehttp/streams": "~3.0", 200 | "php": ">=5.4.0", 201 | "react/promise": "~2.0" 202 | }, 203 | "require-dev": { 204 | "ext-curl": "*", 205 | "phpunit/phpunit": "~4.0" 206 | }, 207 | "suggest": { 208 | "ext-curl": "Guzzle will use specific adapters if cURL is present" 209 | }, 210 | "type": "library", 211 | "extra": { 212 | "branch-alias": { 213 | "dev-master": "1.1-dev" 214 | } 215 | }, 216 | "autoload": { 217 | "psr-4": { 218 | "GuzzleHttp\\Ring\\": "src/" 219 | } 220 | }, 221 | "notification-url": "https://packagist.org/downloads/", 222 | "license": [ 223 | "MIT" 224 | ], 225 | "authors": [ 226 | { 227 | "name": "Michael Dowling", 228 | "email": "mtdowling@gmail.com", 229 | "homepage": "https://github.com/mtdowling" 230 | } 231 | ], 232 | "description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function.", 233 | "time": "2015-05-21 17:23:02" 234 | }, 235 | { 236 | "name": "guzzlehttp/streams", 237 | "version": "dev-master", 238 | "source": { 239 | "type": "git", 240 | "url": "https://github.com/guzzle/streams.git", 241 | "reference": "d1f8a6c55f0f753cfd6f6755856473eb02cedb19" 242 | }, 243 | "dist": { 244 | "type": "zip", 245 | "url": "https://api.github.com/repos/guzzle/streams/zipball/d1f8a6c55f0f753cfd6f6755856473eb02cedb19", 246 | "reference": "d1f8a6c55f0f753cfd6f6755856473eb02cedb19", 247 | "shasum": "" 248 | }, 249 | "require": { 250 | "php": ">=5.4.0" 251 | }, 252 | "require-dev": { 253 | "phpunit/phpunit": "~4.0" 254 | }, 255 | "type": "library", 256 | "extra": { 257 | "branch-alias": { 258 | "dev-master": "3.0-dev" 259 | } 260 | }, 261 | "autoload": { 262 | "psr-4": { 263 | "GuzzleHttp\\Stream\\": "src/" 264 | } 265 | }, 266 | "notification-url": "https://packagist.org/downloads/", 267 | "license": [ 268 | "MIT" 269 | ], 270 | "authors": [ 271 | { 272 | "name": "Michael Dowling", 273 | "email": "mtdowling@gmail.com", 274 | "homepage": "https://github.com/mtdowling" 275 | } 276 | ], 277 | "description": "Provides a simple abstraction over streams of data", 278 | "homepage": "http://guzzlephp.org/", 279 | "keywords": [ 280 | "Guzzle", 281 | "stream" 282 | ], 283 | "time": "2015-01-22 00:01:34" 284 | }, 285 | { 286 | "name": "illuminate/contracts", 287 | "version": "dev-master", 288 | "source": { 289 | "type": "git", 290 | "url": "https://github.com/illuminate/contracts.git", 291 | "reference": "1ece38de4ed9df03c8a9a8a4d0c76ce265d15786" 292 | }, 293 | "dist": { 294 | "type": "zip", 295 | "url": "https://api.github.com/repos/illuminate/contracts/zipball/1ece38de4ed9df03c8a9a8a4d0c76ce265d15786", 296 | "reference": "1ece38de4ed9df03c8a9a8a4d0c76ce265d15786", 297 | "shasum": "" 298 | }, 299 | "require": { 300 | "php": ">=5.5.9" 301 | }, 302 | "type": "library", 303 | "extra": { 304 | "branch-alias": { 305 | "dev-master": "5.1-dev" 306 | } 307 | }, 308 | "autoload": { 309 | "psr-4": { 310 | "Illuminate\\Contracts\\": "" 311 | } 312 | }, 313 | "notification-url": "https://packagist.org/downloads/", 314 | "license": [ 315 | "MIT" 316 | ], 317 | "authors": [ 318 | { 319 | "name": "Taylor Otwell", 320 | "email": "taylorotwell@gmail.com" 321 | } 322 | ], 323 | "description": "The Illuminate Contracts package.", 324 | "homepage": "http://laravel.com", 325 | "time": "2015-05-29 14:59:01" 326 | }, 327 | { 328 | "name": "illuminate/support", 329 | "version": "dev-master", 330 | "source": { 331 | "type": "git", 332 | "url": "https://github.com/illuminate/support.git", 333 | "reference": "83e0c1e8c51c75a6e4739941c9b4bb234a33c858" 334 | }, 335 | "dist": { 336 | "type": "zip", 337 | "url": "https://api.github.com/repos/illuminate/support/zipball/83e0c1e8c51c75a6e4739941c9b4bb234a33c858", 338 | "reference": "83e0c1e8c51c75a6e4739941c9b4bb234a33c858", 339 | "shasum": "" 340 | }, 341 | "require": { 342 | "danielstjules/stringy": "~1.8", 343 | "doctrine/inflector": "~1.0", 344 | "ext-mbstring": "*", 345 | "illuminate/contracts": "5.1.*", 346 | "php": ">=5.5.9" 347 | }, 348 | "suggest": { 349 | "jeremeamia/superclosure": "Required to be able to serialize closures (~2.0).", 350 | "symfony/var-dumper": "Required to use the dd function (2.7.*)." 351 | }, 352 | "type": "library", 353 | "extra": { 354 | "branch-alias": { 355 | "dev-master": "5.1-dev" 356 | } 357 | }, 358 | "autoload": { 359 | "psr-4": { 360 | "Illuminate\\Support\\": "" 361 | }, 362 | "files": [ 363 | "helpers.php" 364 | ] 365 | }, 366 | "notification-url": "https://packagist.org/downloads/", 367 | "license": [ 368 | "MIT" 369 | ], 370 | "authors": [ 371 | { 372 | "name": "Taylor Otwell", 373 | "email": "taylorotwell@gmail.com" 374 | } 375 | ], 376 | "description": "The Illuminate Support package.", 377 | "homepage": "http://laravel.com", 378 | "time": "2015-05-29 14:59:01" 379 | }, 380 | { 381 | "name": "react/promise", 382 | "version": "v2.2.0", 383 | "source": { 384 | "type": "git", 385 | "url": "https://github.com/reactphp/promise.git", 386 | "reference": "365fcee430dfa4ace1fbc75737ca60ceea7eeeef" 387 | }, 388 | "dist": { 389 | "type": "zip", 390 | "url": "https://api.github.com/repos/reactphp/promise/zipball/365fcee430dfa4ace1fbc75737ca60ceea7eeeef", 391 | "reference": "365fcee430dfa4ace1fbc75737ca60ceea7eeeef", 392 | "shasum": "" 393 | }, 394 | "require": { 395 | "php": ">=5.4.0" 396 | }, 397 | "type": "library", 398 | "extra": { 399 | "branch-alias": { 400 | "dev-master": "2.0-dev" 401 | } 402 | }, 403 | "autoload": { 404 | "psr-4": { 405 | "React\\Promise\\": "src/" 406 | }, 407 | "files": [ 408 | "src/functions_include.php" 409 | ] 410 | }, 411 | "notification-url": "https://packagist.org/downloads/", 412 | "license": [ 413 | "MIT" 414 | ], 415 | "authors": [ 416 | { 417 | "name": "Jan Sorgalla", 418 | "email": "jsorgalla@googlemail.com" 419 | } 420 | ], 421 | "description": "A lightweight implementation of CommonJS Promises/A for PHP", 422 | "time": "2014-12-30 13:32:42" 423 | } 424 | ], 425 | "packages-dev": [], 426 | "aliases": [], 427 | "minimum-stability": "dev", 428 | "stability-flags": [], 429 | "prefer-stable": false, 430 | "prefer-lowest": false, 431 | "platform": { 432 | "php": ">=5.3.1" 433 | }, 434 | "platform-dev": [] 435 | } 436 | -------------------------------------------------------------------------------- /scripts/install_nginx_pushstream_module.sh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cmosguy/laravel-http-pushstream-broadcaster/18982192ea5e68f9ab6a36cf23118d557621543e/scripts/install_nginx_pushstream_module.sh -------------------------------------------------------------------------------- /src/Cmosguy/Broadcasting/Broadcasters/PushStreamBroadcaster.php: -------------------------------------------------------------------------------- 1 | client = $client; 23 | } 24 | 25 | 26 | /** 27 | * Broadcast the given event. 28 | * 29 | * @param array $channels 30 | * @param string $event 31 | * @param array $payload 32 | * @return void 33 | */ 34 | public function broadcast(array $channels, $event, array $payload = array()) 35 | { 36 | 37 | foreach ($channels as $channel) { 38 | $payload = [ 39 | 'text' => array_merge(['eventtype' => $event], $payload) 40 | ]; 41 | $request = $this->client->createRequest('POST', '/pub?id=' . $channel, ['json' => $payload]); 42 | $response = $this->client->send($request); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Cmosguy/Broadcasting/PushStreamBroadcastManagerProvider.php: -------------------------------------------------------------------------------- 1 | app->make('Illuminate\Broadcasting\BroadcastManager')->extend('pushstream', function ($app, $config) { 17 | $client = new Client([ 18 | 'base_url' => $config['base_url'], 19 | 'query' => isset($config['access_key']) ? [ 20 | 'access_key' => $config['access_key'] 21 | ] : null 22 | ]); 23 | 24 | if (!empty($config['cert'])) { 25 | $client->setDefaultOption('verify', $config['cert']); 26 | } 27 | 28 | return new PushStreamBroadcaster($client); 29 | }); 30 | } 31 | 32 | /** 33 | * Register the service provider. 34 | * 35 | * @return void 36 | */ 37 | public function register() 38 | { 39 | } 40 | 41 | 42 | } 43 | --------------------------------------------------------------------------------