Skip to content

Commit

Permalink
feat(router): implement (method, path) routing
Browse files Browse the repository at this point in the history
BREAKING CHANGE: http file renamed to server, route to router
  • Loading branch information
ssube committed Aug 17, 2019
1 parent db3fd98 commit ce967b7
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 14 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -1 +1,2 @@
*.swp
**/__pycache__/
6 changes: 4 additions & 2 deletions README.md
Expand Up @@ -4,8 +4,10 @@ A [Prometheus](https://prometheus.io/) SDK for CircuitPython/MicroPython boards,
into existing Prometheus/Grafana monitoring infrastructure.

- only depends on `socket`
- API-compatible with [prometheus/client_python](https://github.com/prometheus/client_python)
- basic HTTP server with path routing
- runs on CircuitPython 4.x for embedded devices
- runs on CPython 3.x for local testing
- API compatible with the official [prometheus/client_python](https://github.com/prometheus/client_python)
- basic HTTP server with path/method routing
- not terribly slow (`wrk` reports upwards of 100rps with 2 metrics)

## Contents
Expand Down
26 changes: 22 additions & 4 deletions examples/feather-m4-express.py
@@ -1,7 +1,8 @@
# custom
from prometheus_express.http import start_http_server, await_http_request
from prometheus_express.metric import Counter, Gauge
from prometheus_express.registry import CollectorRegistry
from prometheus_express.router import Router
from prometheus_express.server import start_http_server, await_http_request

# system
import board
Expand Down Expand Up @@ -62,14 +63,31 @@ def main():
ready = False
bound = False

server = False

registry = CollectorRegistry(namespace='prom_express')
metric_c = Counter('test_counter',
'a test counter', registry=registry)
metric_g = Gauge('test_gauge',
'a test gauge', registry=registry)

def icon_handler(headers, body):
return {
'status': '200 OK',
'type': 'image/png;base64',
'content': 'iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAFklEQVQI12N8HmPBxMPHxMTDx8TNBwAUNwHSqFS0zAAAAABJRU5ErkJggg==',
}

def prom_handler(headers, body):
return {
'status': '200 OK',
'content': '\r\n'.join(registry.print()),
}

router = Router([
('GET', '/favicon.ico', icon_handler),
('GET', '/metrics', prom_handler),
])
server = False

rgb[0] = RED # starting
while ready == False:
ready = check_network()
Expand All @@ -83,7 +101,7 @@ def main():
metric_c.inc(random.randint(0, 50))
metric_g.set(random.randint(0, 5000))
try:
await_http_request(server, registry)
await_http_request(server, router)
except OSError as err:
print('Error accepting request: {}'.format(err))
server, bound = bind()
Expand Down
2 changes: 1 addition & 1 deletion examples/pc-python-3.py
@@ -1,9 +1,9 @@
#! /usr/bin/env python3

# custom
from prometheus_express.http import start_http_server, await_http_request
from prometheus_express.metric import Counter, Gauge
from prometheus_express.registry import CollectorRegistry
from prometheus_express.server import start_http_server, await_http_request

# system
import socket
Expand Down
Empty file removed prometheus_express/route.py
Empty file.
22 changes: 22 additions & 0 deletions prometheus_express/router.py
@@ -0,0 +1,22 @@
def errorHandler(headers, body):
return {
'status': 404,
'content': 'Not Found',
}


class Router():
routes = []

def __init__(self, routes=[]):
self.routes = routes

def register(self, method, path, handler):
self.routes.add((method, path, handler))

def select(self, method, path):
for r in self.routes:
if r[0] == method and r[1] == path:
return r[2]

return errorHandler
16 changes: 9 additions & 7 deletions prometheus_express/http.py → prometheus_express/server.py
Expand Up @@ -14,27 +14,29 @@ def start_http_server(port, address='0.0.0.0', extraRoutes={}, metricsRoute='/me
return http_socket


def await_http_request(server_socket, registry):
def await_http_request(server_socket, router):
conn, addr = server_socket.accept()
print('Connection: {}'.format(addr))

req = conn.recv(1024).decode(http_encoding)
req_headers = parse_headers(req)
print('Headers: {}'.format(req_headers))

if req_headers['path'] == registry.path:
metrics = registry.print()
send_http_response(conn, http_break.join(metrics))
handler = router.select(req_headers['method'], req_headers['path'])
resp = handler(req_headers, '')

if 'type' in resp:
send_http_response(conn, resp['status'], resp['content'], type=resp['type'])
else:
send_http_response(conn, 'Hello World!')
send_http_response(conn, resp['status'], resp['content'])


def send_http_response(conn, body):
def send_http_response(conn, status, body, type='text/plain'):
content_data = body.encode(http_encoding)
content_length = len(content_data)

line_break = http_break.encode(http_encoding)
headers = print_http_headers(length=content_length)
headers = print_http_headers(status=status, type=type, length=content_length)

try:
for line in headers:
Expand Down

0 comments on commit ce967b7

Please sign in to comment.