Skip to content

Commit 39eda6a

Browse files
author
Ace Nassri
authored
Merge pull request GoogleCloudPlatform#882 from GoogleCloudPlatform/flex-ws
Add GAE Flex websockets sample
2 parents ef69722 + f18498b commit 39eda6a

File tree

13 files changed

+456
-0
lines changed

13 files changed

+456
-0
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
vendor/
2+
.gitignore
3+
.git/
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# PHP websockets sample for Google App Engine Flexible Environment
2+
3+
This sample demonstrates how to use websockets on [Google App Engine Flexible Environment](https://cloud.google.com/appengine).
4+
5+
## Running locally
6+
7+
Use the following commands to run locally:
8+
9+
```sh
10+
cd php-docs-samples/appengine/flexible/cloudsql
11+
php -S localhost:8080
12+
```
13+
14+
## Deploying
15+
Refer to the [top-level README](../README.md) for instructions on running and deploying.
16+
17+
Note that you will have to [create a firewall rule](https://cloud.google.com/sdk/gcloud/reference/compute/firewall-rules/create) that accepts traffic on port `8000`:
18+
19+
```sh
20+
gcloud compute firewall-rules create allow-8000 --allow=tcp:8000 --target-tags=websockets-allow-8000
21+
```
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[program:socket-server]
2+
command = php %(ENV_APP_DIR)s/socket-server.php
3+
enviroment = PORT="8000"
4+
stdout_logfile = /dev/stdout
5+
stdout_logfile_maxbytes=0
6+
stderr_logfile = /dev/stderr
7+
stderr_logfile_maxbytes=0
8+
user = root
9+
autostart = true
10+
autorestart = true
11+
priority = 10
12+
stopwaitsecs = 20
13+
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
runtime: php
2+
env: flex
3+
4+
# Use only a single instance, so that this local-memory-only chat app will work
5+
# consistently with multiple users. To work across multiple instances, an
6+
# extra-instance messaging system or data store would be needed.
7+
manual_scaling:
8+
instances: 1
9+
10+
11+
# For applications which can take advantage of session affinity
12+
# (where the load balancer will attempt to route multiple connections from
13+
# the same user to the same App Engine instance), uncomment the folowing:
14+
15+
# network:
16+
# session_affinity: true
17+
18+
runtime_config:
19+
document_root: .
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"require": {
3+
"cboden/ratchet": "^0.4.1",
4+
"guzzlehttp/psr7": "^1.5",
5+
"silex/silex": "^1.3"
6+
},
7+
"require-dev": {
8+
"phpunit/phpunit": "^5",
9+
"ratchet/pawl": "^0.3.4",
10+
"react/promise": "^2.7",
11+
"clue/block-react": "^1.3",
12+
"google/cloud-tools": "^0.9.1"
13+
}
14+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<!--
2+
Copyright 2019 Google LLC.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
[START appengine_websockets_index]
13+
-->
14+
<!doctype html>
15+
<html>
16+
<head>
17+
<title>Google App Engine Flexible Environment - PHP Websockets Chat</title>
18+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
19+
<meta charset="utf-8">
20+
<style>
21+
* { margin: 0; padding: 0; box-sizing: border-box; }
22+
body { font: 13px Helvetica, Arial; }
23+
form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
24+
form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
25+
form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
26+
#messages { list-style-type: none; margin: 0; padding: 0; }
27+
#messages li { padding: 5px 10px; }
28+
#messages li:nth-child(odd) { background: #dedede; }
29+
#messages li:last-child { background: #aea; }
30+
section {
31+
background-color: #eee;
32+
border: 3px dashed #888; border-radius: 10px;
33+
margin: 30px; margin-bottom: 80px;
34+
padding: 5px;
35+
}
36+
</style>
37+
</head>
38+
<body>
39+
40+
<!-- [START gae_flex_websockets_form] -->
41+
<h1>Websockets Chat Demo</h1>
42+
43+
<form id="chat-form">
44+
<input type="text" id="chat-text" autocomplete="off" placeholder="Enter some text...">
45+
<button type="submit">Send</button>
46+
</form>
47+
48+
<section>
49+
50+
<ul id="messages"></ul>
51+
</section>
52+
53+
<!-- [END gae_flex_websockets_form] -->
54+
55+
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
56+
<script>
57+
// [START gae_flex_websockets_js]
58+
$(function() {
59+
/* If the main page is served via https, the WebSocket must be served via
60+
"wss" (WebSocket Secure) */
61+
var scheme = window.location.protocol == "https:" ? 'wss://' : 'ws://';
62+
var webSocketUri = scheme
63+
+ window.location.hostname
64+
+ (location.port ? ':'+location.port: '')
65+
+ '/ws';
66+
/* Helper to keep an activity log on the page. */
67+
function log(text, label) {
68+
label = label || 'Status';
69+
$('#messages').append(`<li> <strong>${label}</strong>: ${text}`);
70+
}
71+
/* Establish the WebSocket connection and register event handlers. */
72+
var websocket = new WebSocket(webSocketUri);
73+
websocket.onopen = function() {
74+
log('Connected');
75+
};
76+
websocket.onclose = function() {
77+
log('Closed');
78+
};
79+
websocket.onmessage = function(e) {
80+
log(e.data, 'Message received')
81+
};
82+
websocket.onerror = function(e) {
83+
log('Error (see console)');
84+
console.log(e);
85+
};
86+
/* Handle form submission and send a message to the websocket. */
87+
$('#chat-form').submit(function(e) {
88+
e.preventDefault();
89+
var data = $('#chat-text').val();
90+
if (data) {
91+
websocket.send(data);
92+
window.scrollTo(0, document.body.scrollHeight)
93+
$('#chat-text').val('');
94+
}
95+
});
96+
});
97+
// [END gae_flex_websockets_js]
98+
</script>
99+
</body>
100+
</html>
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
/*
3+
* Copyright 2019 Google LLC.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
require_once __DIR__ . '/vendor/autoload.php';
19+
20+
use Silex\Application;
21+
use Silex\Provider\TwigServiceProvider;
22+
23+
// create the Silex application
24+
$app = new Application();
25+
$app['debug'] = true;
26+
27+
// register twig
28+
$app->register(new TwigServiceProvider(), [
29+
'twig.path' => __DIR__
30+
]);
31+
32+
$app->register(new Silex\Provider\RoutingServiceProvider());
33+
34+
$app->get('/', function () use ($app) {
35+
return file_get_contents('index.html');
36+
});
37+
38+
// @codeCoverageIgnoreStart
39+
if (PHP_SAPI != 'cli') {
40+
$app->run();
41+
}
42+
// @codeCoverageIgnoreEnd
43+
44+
return $app;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
location /ws {
2+
proxy_pass "http://localhost:8000";
3+
proxy_http_version 1.1;
4+
proxy_set_header Upgrade $http_upgrade;
5+
proxy_set_header Connection "upgrade";
6+
proxy_set_header Host $http_host;
7+
proxy_set_header X-Real-IP $remote_addr;
8+
}
9+
10+
location / {
11+
# try to serve files directly, fallback to the front controller
12+
try_files $uri /$front_controller_file$is_args$args;
13+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
Copyright 2019 Google LLC.
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
-->
17+
<phpunit bootstrap="test/bootstrap.php" colors="true">
18+
<testsuites>
19+
<testsuite name="default">
20+
<directory>test</directory>
21+
</testsuite>
22+
</testsuites>
23+
<logging>
24+
<log type="coverage-clover" target="build/logs/clover.xml"/>
25+
</logging>
26+
<filter>
27+
<whitelist>
28+
<file>socket_demo.php</file>
29+
</whitelist>
30+
</filter>
31+
</phpunit>
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
/**
3+
* Copyright 2019 Google LLC.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
# [START gae_flex_websockets_app]
19+
use Ratchet\MessageComponentInterface;
20+
use Ratchet\ConnectionInterface;
21+
22+
// Install composer dependencies with "composer install"
23+
// @see http://getcomposer.org for more information.
24+
require __DIR__ . '/vendor/autoload.php';
25+
26+
// Forwards any incoming messages to all connected clients
27+
class SocketDemo implements MessageComponentInterface
28+
{
29+
protected $clients;
30+
31+
public function __construct()
32+
{
33+
$this->clients = new \SplObjectStorage;
34+
}
35+
36+
public function onOpen(ConnectionInterface $conn)
37+
{
38+
$this->clients->attach($conn);
39+
echo "Connection opened!\n";
40+
echo "\t" . $this->clients->count() . " connection(s) active.\n";
41+
}
42+
43+
public function onMessage(ConnectionInterface $from, $msg)
44+
{
45+
$output = "Message received: " . $msg . "\n";
46+
echo $output;
47+
foreach ($this->clients as $client) {
48+
$client->send($output);
49+
}
50+
}
51+
52+
public function onClose(ConnectionInterface $conn)
53+
{
54+
$this->clients->detach($conn);
55+
echo "Connection closed gracefully!\n";
56+
echo "\t" . $this->clients->count() . " connection(s) active.\n";
57+
}
58+
59+
public function onError(ConnectionInterface $conn, \Exception $e)
60+
{
61+
$conn->close();
62+
echo "Connection closed due to error: " . $e->getMessage() . "\n";
63+
echo "\t" . $this->clients->count() . " connection(s) active.\n";
64+
}
65+
}
66+
# [END gae_flex_websockets_app]
67+
68+
return new SocketDemo;

0 commit comments

Comments
 (0)