Skip to content

Commit 055bd01

Browse files
authored
Merge branch 'master' into master
2 parents c6604ff + da2c559 commit 055bd01

Some content is hidden

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

50 files changed

+814
-679
lines changed

appengine/flexible/multiple_services/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,5 +63,5 @@ and enter `Y` when prompted. Or to skip the check add `-q`.
6363
To deploy multiple services simultaneously just add the path to each `app.yaml`
6464
file as an argument to `gcloud app deploy `:
6565
```Bash
66-
$ gcloud app deploy gateway/app.yaml static/app.yaml
66+
$ gcloud app deploy gateway-service/app.yaml static-service/app.yaml
6767
```
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Python 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+
Refer to the [top-level README](../README.md) for instructions on running and deploying.
8+
9+
To run locally, you need to use gunicorn with the ``flask_socket`` worker:
10+
11+
$ gunicorn -b 127.0.0.1:8080 -k flask_sockets.worker main:app
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
runtime: python
2+
env: flex
3+
4+
# Use a special gunicorn worker class to support websockets.
5+
entrypoint: gunicorn -b :$PORT -k flask_sockets.worker main:app
6+
7+
runtime_config:
8+
python_version: 3
9+
10+
# Use only a single instance, so that this local-memory-only chat app will work
11+
# consistently with multiple users. To work across multiple instances, an
12+
# extra-instance messaging system or data store would be needed.
13+
manual_scaling:
14+
instances: 1
15+
16+
17+
# For applications which can take advantage of session affinity
18+
# (where the load balancer will attempt to route multiple connections from
19+
# the same user to the same App Engine instance), uncomment the folowing:
20+
21+
# network:
22+
# session_affinity: true
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Copyright 2018 Google LLC
2+
#
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+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from __future__ import print_function
16+
17+
# [START gae_flex_websockets_app]
18+
from flask import Flask, render_template
19+
from flask_sockets import Sockets
20+
21+
22+
app = Flask(__name__)
23+
sockets = Sockets(app)
24+
25+
26+
@sockets.route('/chat')
27+
def chat_socket(ws):
28+
while not ws.closed:
29+
message = ws.receive()
30+
if message is None: # message is "None" if the client has closed.
31+
continue
32+
# Send the message to all clients connected to this webserver
33+
# process. (To support multiple processes or instances, an
34+
# extra-instance storage or messaging system would be required.)
35+
clients = ws.handler.server.clients.values()
36+
for client in clients:
37+
client.ws.send(message)
38+
# [END gae_flex_websockets_app]
39+
40+
41+
@app.route('/')
42+
def index():
43+
return render_template('index.html')
44+
45+
46+
if __name__ == '__main__':
47+
print("""
48+
This can not be run directly because the Flask development server does not
49+
support web sockets. Instead, use gunicorn:
50+
51+
gunicorn -b 127.0.0.1:8080 -k flask_sockets.worker main:app
52+
53+
""")
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Copyright 2018 Google LLC
2+
#
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+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import socket
16+
import subprocess
17+
18+
import pytest
19+
import requests
20+
from retrying import retry
21+
import websocket
22+
23+
24+
@pytest.fixture(scope='module')
25+
def server():
26+
"""Provides the address of a test HTTP/websocket server.
27+
The test server is automatically created before
28+
a test and destroyed at the end.
29+
"""
30+
# Ask the OS to allocate a port.
31+
sock = socket.socket()
32+
sock.bind(('127.0.0.1', 0))
33+
port = sock.getsockname()[1]
34+
35+
# Free the port and pass it to a subprocess.
36+
sock.close()
37+
38+
bind_to = '127.0.0.1:{}'.format(port)
39+
server = subprocess.Popen(
40+
['gunicorn', '-b', bind_to, '-k' 'flask_sockets.worker', 'main:app'])
41+
42+
# Wait until the server responds before proceeding.
43+
@retry(wait_fixed=50, stop_max_delay=5000)
44+
def check_server(url):
45+
requests.get(url)
46+
47+
check_server('http://{}/'.format(bind_to))
48+
49+
yield bind_to
50+
51+
server.kill()
52+
53+
54+
def test_http(server):
55+
result = requests.get('http://{}/'.format(server))
56+
assert 'Python Websockets Chat' in result.text
57+
58+
59+
def test_websocket(server):
60+
url = 'ws://{}/chat'.format(server)
61+
ws_one = websocket.WebSocket()
62+
ws_one.connect(url)
63+
64+
ws_two = websocket.WebSocket()
65+
ws_two.connect(url)
66+
67+
message = 'Hello, World'
68+
ws_one.send(message)
69+
70+
assert ws_one.recv() == message
71+
assert ws_two.recv() == message
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Flask==0.12.2
2+
Flask-Sockets==0.2.1
3+
gunicorn==19.7.1
4+
requests==2.14.2
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
{#
2+
# Copyright 2018 Google LLC
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#}
16+
<!doctype html>
17+
<html>
18+
<head>
19+
<title>Google App Engine Flexible Environment - Python Websockets Chat</title>
20+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
21+
</head>
22+
<body>
23+
24+
<!-- [START gae_flex_websockets_form] -->
25+
<p>Chat demo</p>
26+
<form id="chat-form">
27+
<textarea id="chat-text" placeholder="Enter some text..."></textarea>
28+
<button type="submit">Send</button>
29+
</form>
30+
31+
<div>
32+
<p>Messages:</p>
33+
<ul id="chat-response"></ul>
34+
</div>
35+
36+
<div>
37+
<p>Status:</p>
38+
<ul id="chat-status"></ul>
39+
</div>
40+
<!-- [END gae_flex_websockets_form] -->
41+
42+
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
43+
<script>
44+
// [START gae_flex_websockets_js]
45+
$(function() {
46+
/* If the main page is served via https, the WebSocket must be served via
47+
"wss" (WebSocket Secure) */
48+
var scheme = window.location.protocol == "https:" ? 'wss://' : 'ws://';
49+
var webSocketUri = scheme
50+
+ window.location.hostname
51+
+ (location.port ? ':'+location.port: '')
52+
+ '/chat';
53+
54+
/* Get elements from the page */
55+
var form = $('#chat-form');
56+
var textarea = $('#chat-text');
57+
var output = $('#chat-response');
58+
var status = $('#chat-status');
59+
60+
/* Helper to keep an activity log on the page. */
61+
function log(text){
62+
status.append($('<li>').text(text))
63+
}
64+
65+
/* Establish the WebSocket connection and register event handlers. */
66+
var websocket = new WebSocket(webSocketUri);
67+
68+
websocket.onopen = function() {
69+
log('Connected');
70+
};
71+
72+
websocket.onclose = function() {
73+
log('Closed');
74+
};
75+
76+
websocket.onmessage = function(e) {
77+
log('Message received');
78+
output.append($('<li>').text(e.data));
79+
};
80+
81+
websocket.onerror = function(e) {
82+
log('Error (see console)');
83+
console.log(e);
84+
};
85+
86+
/* Handle form submission and send a message to the websocket. */
87+
form.submit(function(e) {
88+
e.preventDefault();
89+
var data = textarea.val();
90+
websocket.send(data);
91+
});
92+
});
93+
// [END gae_flex_websockets_js]
94+
</script>
95+
</body>
96+
</html>

appengine/standard/channel/README.md

Lines changed: 0 additions & 9 deletions
This file was deleted.

appengine/standard/channel/app.yaml

Lines changed: 0 additions & 15 deletions
This file was deleted.

0 commit comments

Comments
 (0)