Skip to content

Commit

Permalink
Merge pull request #29 from olegz-circle/add_sample_server
Browse files Browse the repository at this point in the history
Sample code to handle notifications message
  • Loading branch information
olegz-circle committed Apr 7, 2020
2 parents 84d4b15 + 902592d commit 6cd6ecc
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 0 deletions.
38 changes: 38 additions & 0 deletions devtools/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#### Dev Tools

#### HTTP server for local debug

In order to confirm SNS subscriptions / receive notifications a local server could be useful. The sample server allows to:
- confirm a subscription
- log notification messages

To start the server
```sh
cd python
# starts the server at specified interface and port 8080
python httpserver.py --bind 127.0.0.1

# starts the server at custom interface and port 8000
python httpserver.py 8000 --bind 127.0.0.1
```
or for node server
```sh
cd node
npm install

# starts the server at 127.0.0.1:8080
node httpserver.js

# starts the server at custom interface and port
node httpserver.js 127.0.0.1 8000
```

To expose the server IP to the internet some tools such as [ngrok](https://ngrok.com/download) can be used:

```sh
ngrok http 8080
...
Forwarding http://<ID>.ngrok.io -> http://localhost:8080
Forwarding https://<ID>.ngrok.io -> http://localhost:8080
...
```
19 changes: 19 additions & 0 deletions devtools/node/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"env": {
"browser": true,
"commonjs": true,
"es6": true
},
"extends": [
"standard"
],
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
}
}
80 changes: 80 additions & 0 deletions devtools/node/httpserver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
Copyright (c) 2020, Circle Internet Financial Trading Company Limited.
All rights reserved.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL CIRCLE INTERNET FINANCIAL TRADING COMPANY
LIMITED BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/

/* jshint esversion: 6 */

const http = require('http')
const r = require('request')

const server = http.createServer((request, response) => {
if (request.method === 'POST') {
let body = ''
request.on('data', (data) => {
body += data
})
request.on('end', () => {
console.log(`POST request, \nPath: ${request.url}`)
console.log('Headers: ')
console.dir(request.headers)
console.log(`Body: ${body}`)

response.writeHead(200, {
'Content-Type': 'text/html'
})
response.end(`POST request for ${request.url}`)
handleBody(body)
})
} else {
console.log('GET methods not supported')
}

const handleBody = (body) => {
const envelope = JSON.parse(body)
switch (envelope.Type) {
case 'SubscriptionConfirmation': {
r(envelope.SubscribeURL, (err) => {
if (err) {
console.err('Subscription NOT confirmed.')
} else {
console.log('Subscription confirmed.')
}
})
break
}
case 'Notification': {
console.log(`Received message ${envelope.Message}`)
break
}
default: {
console.error(`Message of type ${body.Type} not supported`)
}
}
}
})

const args = process.argv.slice(2)
let host = '127.0.0.1'
let port = 8080
if (args.length === 2) {
host = args[0]
port = args[1]
}
if (args.length === 1) {
port = args[0]
}
server.listen(port, host)
console.log(`Starting httpd on port ${port}`)
22 changes: 22 additions & 0 deletions devtools/node/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "devtools",
"version": "1.0.0",
"description": "#### HTTP server for local debug",
"main": "httpserver.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Circle",
"license": "MIT",
"devDependencies": {
"eslint": "^6.8.0",
"eslint-config-standard": "^14.1.1",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1"
},
"dependencies": {
"request": "^2.88.2"
}
}
71 changes: 71 additions & 0 deletions devtools/python/httpserver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Copyright (c) 2020, Circle Internet Financial Trading Company Limited.
# All rights reserved.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL CIRCLE INTERNET FINANCIAL TRADING COMPANY
# LIMITED BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

from http.server import BaseHTTPRequestHandler, HTTPServer
import logging
import json
import urllib.request


class Server(BaseHTTPRequestHandler):
def _set_response(self, code=200):
self.send_response(code)
self.send_header('Content-type', 'application/json')
self.end_headers()

def do_POST(self):
content_length = int(self.headers['Content-Length'])
post_data = self.rfile.read(content_length)
logging.info("POST request,\nPath: %s\nHeaders:\n%s\n\nBody:\n%s\n",
str(self.path), str(self.headers), post_data.decode('utf-8'))

url = json.loads(post_data.decode('utf-8')).get("SubscribeURL")

if url is not None and url.lower().startswith('http'):
req = urllib.request.Request(url)
# nosec as url is from AWS
with urllib.request.urlopen(req) as response:
if response.status == 200:
logging.info("Subscription confirmed for url %s", url)
else:
logging.error("GET request to %s failed.", url)
self._set_response(code=response.status)
else:
logging.info("Received message %s.", post_data.decode('utf-8'))
self._set_response()

self.wfile.write("POST request for {}".format(self.path).encode('utf-8'))


def run(server_class=HTTPServer, handler_class=Server, port=8080):
logging.basicConfig(level=logging.INFO)
server_address = ('', port)
httpd = server_class(server_address, handler_class)
logging.info('Starting httpd on port %s\n', str(port))
try:
httpd.serve_forever()
except KeyboardInterrupt:
pass
httpd.server_close()
logging.info('Stopping httpd...\n')


if __name__ == '__main__':
from sys import argv

if len(argv) > 1:
run(port=int(argv[1]))
else:
run()
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"version": "1.0.0",
"description": "Sample App for Payments API",
"author": "Circle",
"license": "MIT",
"private": true,
"scripts": {
"dev": "cross-env NODE_ENV=development nuxt-ts",
Expand Down

0 comments on commit 6cd6ecc

Please sign in to comment.