Completed
Push — main ( 8ad40f...5f8723 )
by Jochen
01:34
created

weitersager.httpreceiver.RequestHandler.do_POST()   A

Complexity

Conditions 2

Size

Total Lines 17
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 5.0813

Importance

Changes 0
Metric Value
eloc 15
dl 0
loc 17
ccs 1
cts 12
cp 0.0833
rs 9.65
c 0
b 0
f 0
cc 2
nop 1
crap 5.0813
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.server import BaseHTTPRequestHandler, HTTPServer
13 1
import json
14 1
import sys
15
16 1
from .signals import message_received
17 1
from .util import log, start_thread
18
19
20 1
@dataclass(frozen=True)
21
class Message:
22 1
    channel: str
23 1
    text: str
24
25 1
    @classmethod
26
    def from_json(cls, json_data):
27 1
        data = json.loads(json_data)
28
29 1
        channel = data['channel']
30 1
        text = data['text']
31
32 1
        return cls(channel=channel, text=text)
33
34
35 1
class RequestHandler(BaseHTTPRequestHandler):
36
    """Handler for messages submitted via HTTP."""
37
38 1
    def do_POST(self):
39
        try:
40
            content_length = int(self.headers.get('Content-Length', 0))
41
            data = self.rfile.read(content_length).decode('utf-8')
42
            message = Message.from_json(data)
43
        except (KeyError, ValueError):
44
            log('Invalid message received from {}:{:d}.', *self.client_address)
45
            self.send_error(400)
46
            return
47
48
        self.send_response(200)
49
        self.end_headers()
50
51
        message_received.send(
52
            channel_name=message.channel,
53
            text=message.text,
54
            source_address=self.client_address,
55
        )
56
57
58 1
class ReceiveServer(HTTPServer):
59
    """HTTP server that waits for messages."""
60
61 1
    def __init__(self, ip_address, port):
62
        address = (ip_address, port)
63
        HTTPServer.__init__(self, address, RequestHandler)
64
        log('Listening for HTTP requests on {}:{:d}.', *address)
65
66 1
    @classmethod
67
    def start(cls, ip_address, port):
68
        """Start in a separate thread."""
69
        try:
70
            receiver = cls(ip_address, port)
71
        except Exception as e:
72
            sys.stderr.write(f'Error {e.errno:d}: {e.strerror}\n')
73
            sys.stderr.write(
74
                f'Probably no permission to open port {port}. '
75
                'Try to specify a port number above 1,024 (or even '
76
                '4,096) and up to 65,535.\n'
77
            )
78
            sys.exit(1)
79
80
        thread_name = cls.__name__
81
        start_thread(receiver.serve_forever, thread_name)
82