Completed
Push — main ( b36a86...681ed3 )
by Jochen
01:59
created

weitersager.httpreceiver.ReceiveServer.__init__()   A

Complexity

Conditions 1

Size

Total Lines 4
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 4
nop 2
dl 0
loc 4
ccs 4
cts 4
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
"""
2
weitersager.httpreceiver
3
~~~~~~~~~~~~~~~~~~~~~~~~
4
5
HTTP server to receive messages
6
7
:Copyright: 2007-2020 Jochen Kupperschmidt
8
:License: MIT, see LICENSE for details.
9
"""
10
11 1
from dataclasses import dataclass
12 1
from http import HTTPStatus
13 1
from http.server import BaseHTTPRequestHandler, HTTPServer
14 1
import json
15 1
import sys
16
17 1
from .signals import message_received
18 1
from .util import log, start_thread
19
20
21 1
@dataclass(frozen=True)
22
class Config:
23
    """An HTTP receiver configuration."""
24 1
    host: str
25 1
    port: int
26
27
28 1
@dataclass(frozen=True)
29
class Message:
30 1
    channel: str
31 1
    text: str
32
33
34 1
def parse_json_message(json_data):
35
    """Extract message from JSON."""
36 1
    data = json.loads(json_data)
37
38 1
    channel = data['channel']
39 1
    text = data['text']
40
41 1
    return Message(channel=channel, text=text)
42
43
44 1
class RequestHandler(BaseHTTPRequestHandler):
45
    """Handler for messages submitted via HTTP."""
46
47 1
    def do_POST(self):
48 1
        try:
49 1
            content_length = int(self.headers.get('Content-Length', 0))
50 1
            data = self.rfile.read(content_length).decode('utf-8')
51 1
            message = parse_json_message(data)
52 1
        except (KeyError, ValueError):
53 1
            log('Invalid message received from {}:{:d}.', *self.client_address)
54 1
            self.send_error(HTTPStatus.BAD_REQUEST)
55 1
            return
56
57 1
        self.send_response(HTTPStatus.ACCEPTED)
58 1
        self.end_headers()
59
60 1
        message_received.send(
61
            channel_name=message.channel,
62
            text=message.text,
63
            source_address=self.client_address,
64
        )
65
66
67 1
class ReceiveServer(HTTPServer):
68
    """HTTP server that waits for messages."""
69
70 1
    def __init__(self, config):
71 1
        address = (config.host, config.port)
72 1
        HTTPServer.__init__(self, address, RequestHandler)
73 1
        log('Listening for HTTP requests on {}:{:d}.', *address)
74
75
76 1
def start_receive_server(config):
77
    """Start in a separate thread."""
78
    try:
79
        receiver = ReceiveServer(config)
80
    except Exception as e:
81
        sys.stderr.write(f'Error {e.errno:d}: {e.strerror}\n')
82
        sys.stderr.write(
83
            f'Probably no permission to open port {config.port}. '
84
            'Try to specify a port number above 1,024 (or even '
85
            '4,096) and up to 65,535.\n'
86
        )
87
        sys.exit(1)
88
89
    thread_name = receiver.__class__.__name__
90
    start_thread(receiver.serve_forever, thread_name)
91