Skip to content

Commit a4eee85

Browse files
author
Daniel LaBarge
committed
Initial commit.
0 parents  commit a4eee85

File tree

97 files changed

+6789
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

97 files changed

+6789
-0
lines changed

Diff for: .editorconfig

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
root = true
2+
3+
# All files
4+
[*]
5+
indent_style = space
6+
end_of_line = lf
7+
charset = utf-8
8+
trim_trailing_whitespace = true
9+
insert_final_newline = true
10+
spaces_around_operators = true
11+
12+
# 4-tabbed files
13+
[Makefile]
14+
indent_size = 4
15+
indent_style = tab
16+
17+
# 4-spaced files
18+
[**.{js,json,php,xml,md}]
19+
indent_size = 4
20+
21+
# 2-spaced files
22+
[**.{css,less,sass,scss,html,htm,xhtml,yml,blade.php}]
23+
indent_size = 2
24+
25+
[{node_modules,vendor,bower_components}/**]
26+
indent_style = ignore
27+
indent_size = ignore
28+
end_of_line = ignore
29+
charset = ignore
30+
trim_trailing_whitespace = ignore
31+
insert_final_newline = ignore
32+
spaces_around_operators = ignore

Diff for: .gitattributes

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
* text=auto
2+
*.css linguist-vendored
3+
*.scss linguist-vendored

Diff for: .gitignore

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
!.env.example
2+
*.sublime-project
3+
*.sublime-workspace
4+
.bash_history
5+
.bashrc
6+
.cache/
7+
.DS_Store
8+
.env*
9+
.gitconfig
10+
.idea
11+
.php_cs.cache
12+
.profile
13+
.viminfo
14+
/.composer/
15+
/.idea
16+
/.ssh/
17+
/.vagrant
18+
/coverage/
19+
/Vagrantfile
20+
/vendor/
21+
composer.lock
22+
composer.phar
23+
Homestead.json
24+
Homestead.yaml
25+
Thumbs.db

Diff for: .php_cs

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace PhpCsFixer;
4+
5+
$cacheDir = getenv('TRAVIS') ? getenv('HOME').'/.php-cs-fixer' : __DIR__;
6+
7+
return Config::create()
8+
->setUsingCache(true)
9+
->setCacheFile($cacheDir.'/.php_cs.cache')
10+
->setRules([
11+
'@PSR2' => true,
12+
'@Symfony' => true,
13+
'no_multiline_whitespace_before_semicolons' => true,
14+
'not_operator_with_space' => true,
15+
'ordered_imports' => true,
16+
'phpdoc_order' => true,
17+
'phpdoc_type_to_var' => true,
18+
'psr0' => false,
19+
'short_array_syntax' => true,
20+
'unalign_double_arrow' => false,
21+
'unalign_equals' => false,
22+
])
23+
->finder(
24+
Finder::create()
25+
->in(__DIR__.'/src')
26+
->in(__DIR__.'/config')
27+
->in(__DIR__.'/tests')
28+
);

Diff for: LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2017 Artisans Collaborative (http://artisanscollaborative.com)
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Diff for: composer.json

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"name": "artisansdk/server",
3+
"description": "A service-based, Laravel PHP implementation of an async, realtime, WebSocket server.",
4+
"keywords": ["server", "laravel", "reactphp", "async", "realtime", "websocket", "socket", "process"],
5+
"license": "MIT",
6+
"type": "project",
7+
"require": {
8+
"php": ">=5.6.4",
9+
"cboden/ratchet": "~0.3.6",
10+
"illuminate/bus": "~5.4",
11+
"illuminate/contracts": "~5.4",
12+
"illuminate/support": "~5.4",
13+
"ramsey/uuid": "~3.5",
14+
"react/child-process": "~0.4.3",
15+
"wyrihaximus/react-guzzle-psr7": "~2.0"
16+
},
17+
"require-dev": {
18+
"friendsofphp/php-cs-fixer": "2.0.0-alpha",
19+
"fzaninotto/faker": "~1.4",
20+
"mockery/mockery": "0.9.*",
21+
"phpunit/phpunit": "~5.7"
22+
},
23+
"autoload": {
24+
"psr-4": {
25+
"ArtisanSDK\\Server\\": "src/"
26+
}
27+
},
28+
"autoload-dev": {
29+
"psr-4": {
30+
"ArtisanSDK\\Server\\Tests\\": "tests/"
31+
}
32+
},
33+
"scripts": {
34+
"fix": [
35+
"vendor/bin/php-cs-fixer fix -v"
36+
],
37+
"test": [
38+
"vendor/bin/phpunit --colors=always",
39+
"vendor/bin/php-cs-fixer fix -v --diff --dry-run;"
40+
]
41+
},
42+
"config": {
43+
"preferred-install": "dist",
44+
"sort-packages": true
45+
}
46+
}

Diff for: config/server.php

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
return [
4+
/*
5+
|--------------------------------------------------------------------------
6+
| Server Address Bindings
7+
|--------------------------------------------------------------------------
8+
|
9+
| The address and port that the server will bind to for client connections.
10+
|
11+
*/
12+
13+
'address' => env('SERVER_ADDRESS', '0.0.0.0'),
14+
'port' => 8080,
15+
16+
/*
17+
|--------------------------------------------------------------------------
18+
| Message Queue Connector
19+
|--------------------------------------------------------------------------
20+
|
21+
| The message queue that the server will be responsible for processing.
22+
| Defaults to the default queue on the default connection for the
23+
| default queue driver. Connection must exist in queue.php.
24+
|
25+
*/
26+
27+
'connector' => env('SERVER_QUEUE_DRIVER', env('QUEUE_DRIVER', 'beanstalkd')),
28+
'queue' => env('SERVER_QUEUE', 'default'),
29+
30+
/*
31+
|--------------------------------------------------------------------------
32+
| Admin Command Password
33+
|--------------------------------------------------------------------------
34+
|
35+
| The secret key that the server will use to authenticate connections that
36+
| can execute remote admin commands like restarting the server.
37+
|
38+
*/
39+
40+
'password' => env('SERVER_KEY', 'password'),
41+
42+
/*
43+
|--------------------------------------------------------------------------
44+
| Max Connections Allowed
45+
|--------------------------------------------------------------------------
46+
|
47+
| The maximum number of connections the server will accept. Do not specify
48+
| a value or set to zero to allow for unlimited connections.
49+
|
50+
*/
51+
52+
'max_connections' => env('SERVER_MAX_CONNECTIONS'),
53+
54+
/*
55+
|--------------------------------------------------------------------------
56+
| Message Namespaces
57+
|--------------------------------------------------------------------------
58+
|
59+
| The class namespaces that a message can resolve to. Remember to keep the
60+
| first order in the order you want the paths to be resolved as the first
61+
| namespace that has a corresponding class will be resolved.
62+
|
63+
*/
64+
65+
'namespaces' => [
66+
'ArtianSDK\\Server\\Messages\\',
67+
],
68+
69+
];

Diff for: phpunit.xml

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit backupGlobals="false"
3+
backupStaticAttributes="false"
4+
bootstrap="vendor/autoload.php"
5+
colors="true"
6+
verbose="true"
7+
convertErrorsToExceptions="true"
8+
convertNoticesToExceptions="true"
9+
convertWarningsToExceptions="true"
10+
processIsolation="false"
11+
stopOnFailure="false">
12+
<testsuites>
13+
<testsuite name="Feature Tests">
14+
<directory suffix="Test.php">./tests/Feature</directory>
15+
</testsuite>
16+
<testsuite name="Unit Tests">
17+
<directory suffix="Test.php">./tests/Unit</directory>
18+
</testsuite>
19+
</testsuites>
20+
<filter>
21+
<whitelist processUncoveredFilesFromWhitelist="true">
22+
<directory suffix=".php">./src</directory>
23+
</whitelist>
24+
</filter>
25+
<logging>
26+
<log type="coverage-clover" target="./coverage/logs/clover.xml"/>
27+
<log type="coverage-html" target="./coverage/coverage" charset="UTF-8" yui="true" highlight="true"/>
28+
<log type="coverage-text" target="./coverage/coverage.txt"/>
29+
<log type="json" target="./coverage/report.json"/>
30+
<log type="junit" target="./coverage/report.junit.xml"/>
31+
<log type="tap" target="./coverage/report.tap"/>
32+
</logging>
33+
</phpunit>

Diff for: readme.md

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# Server
2+
3+
A service-based, Laravel PHP implementation of an async, realtime, WebSocket server.
4+
5+
## Installation
6+
7+
The package installs into a Laravel application like any other Laravel package:
8+
9+
```
10+
composer require artisansdk/server ~1.0
11+
```
12+
13+
> **Show Me:** You can see how to integrate this package by browsing the source
14+
code of [`larandomizer/app`](http://github.com/larandomizer/app) which powers
15+
[Larandomizer.com](http://larandomizer.com).
16+
17+
### Configure the Environment
18+
19+
You will still want to edit the `.env` file to customize environment settings.
20+
Note that no database is used as all data is stored in memory on the server.
21+
Restarting the server will cause all data to be lost. Below are available options
22+
for server customization:
23+
24+
- `SERVER_ADDRESS` (`127.0.0.1`): sets the address the server should bind to (`0.0.0.0` would be for allowing all external connections)
25+
- `SERVER_PORT` (`8080`): sets the port the server will listen on for websocket connections
26+
- `SERVER_MAX_CONNECTIONS` (`100`): the server rejects new connections after this limit (set to `0` to allow unlimited)
27+
- `SERVER_QUEUE` (`default`): the name of the queue that realtime messages will be sent to
28+
- `SERVER_QUEUE_DRIVER` (`beanstalkd`): the driver to use for the realtime message queue
29+
- `SERVER_KEY` (`password`): the admin password to authenticate connections against admin protected connections
30+
31+
There is a basic auth scheme in place which allows the server to `PromptForAuthentication`
32+
against a connection and then remember that the connection is authenticated. This
33+
simplifies further message processing and relies on any `ClientMessage` that must
34+
be authenticated to implement the `authorize()` method. There are three basic
35+
traits that can be used on any message to achieve a couple of common strategies:
36+
37+
- `ArtisanSDK\Server\Traits\NoProtection`: always returns true so allows any client to send the message
38+
- `ArtisanSDK\Server\Traits\ClientProtection`: allows admin connections and a specific related connection to be authorized
39+
- `ArtisanSDK\Server\Traits\AdminProtection`: allows only admins to be authorized to send the message
40+
41+
### Nginx Websocket Proxy Configuration
42+
43+
Nginx makes the perfect lightweight frontend server for the Laravel backend
44+
application. Additionally it can be used to proxy websockets connecting on port
45+
`80` to the `8080` default server socket. Doing so helps get around some firewall
46+
settings. The following should be placed just before your default `location`
47+
directive for the Laravel application itself (e.g.: Forge's default). Using these
48+
settings you can host websockets securely with the `wss://` protocol allowing
49+
Nginx to handle the SSL connection and your websocket server handling basic HTTP.
50+
51+
```
52+
location /socket/ {
53+
proxy_pass http://127.0.0.1:8080;
54+
proxy_set_header X-Real-IP $remote_addr;
55+
proxy_set_header Host $host;
56+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
57+
proxy_read_timeout 5m;
58+
proxy_http_version 1.1;
59+
proxy_set_header Upgrade $http_upgrade;
60+
proxy_set_header Connection "upgrade";
61+
}
62+
```
63+
64+
A quick note on the settings used:
65+
66+
- `location /socket/` directs all traffic going to `/socket/` to the proxy
67+
- `proxy_pass` passes the traffic to the localhost webserver on port `8080`
68+
- `proxy_read_timeout` customizes the connection drop to hang up idle connections
69+
- `proxy_http_version` is the version of the websocket protocol in HTTP
70+
- `X-Real-IP` header gives your websocket server the real IP of the client connection
71+
- `Upgrade` and `Connection` headers instruct the browser to upgrade to websocket connection
72+
73+
## Usage Guide
74+
75+
### Starting the Server
76+
77+
The websocket server can be ran as an console command using `php artisan server:start`
78+
and if you pass `--help` to the command you can see additional options. You can
79+
stop the running server by killing the process with `CMD + C` (or `CTRL + C`).
80+
81+
In production you would want to have Supervisor monitor the server and restart
82+
it if ever it crashes. The demo application has a "Restart Server" command which
83+
actually just stops the server and expects Supervisor to start it again automatically.
84+
If you are using Laravel Forge this is pretty easy to do by adding a New Deamon
85+
on the server with a configuration of:
86+
87+
- Command: `/usr/bin/php /home/forge/default/artisan server:start`
88+
- User: `forge`
89+
90+
The resulting Supervisor config might be:
91+
92+
```
93+
[program:server]
94+
command=/usr/bin/php /home/forge/default/artisan server:start
95+
autostart=true
96+
autorestart=true
97+
user=forge
98+
redirect_stderr=true
99+
startsecs=1
100+
stdout_logfile=/home/forge/.forge/server.log
101+
```
102+
103+
Forge does not add the `startsecs` by default but in practice this may be needed
104+
to give the server ample time to start without hard exiting and forcing Supervisor
105+
to give up on starting the process.
106+
107+
### Pushing Messages to the Realtime Queue
108+
109+
By default the `ArtisanSDK\Server\Manager@start()` method adds a queue worker to the
110+
async event loop so that "offline" messages can be sent to the "realtime" connected
111+
websocket clients. You can use any async driver (basically don't use `sync` as
112+
the queue driver) but if you are using Laravel Forge it is pretty easy to use
113+
`beanstalkd` driver. Set `SERVER_QUEUE_DRIVER` and `SERVER_QUEUE` in your `.env`
114+
to configure the driver and queue name for your realtime messages.
115+
116+
To send messages from your "offline" code (e.g.: controllers, repositories, etc.)
117+
to your "realtime" code you can `use ArtisanSDK\Server\Traits\WebsocketQueue` trait in
118+
your caller class and then call `$this->queue(new Command)` to push server
119+
commands into the event loop of the websocket server. Commands should run nearly
120+
instantly though there can be some lag depending on remaining commands within the
121+
event loop. You can tweak the timing of the worker in `ArtisanSDK\Server\Manager@start()`
122+
method's configuration of the worker.
123+
124+
## Licensing
125+
126+
Copyright (c) 2017 [Artisans Collaborative](http://artisanscollaborative.com)
127+
128+
This package is released under the MIT license. Please see the LICENSE file
129+
distributed with every copy of the code for commercial licensing terms.

0 commit comments

Comments
 (0)