Passed
Push — main ( 5f8723...49d37f )
by Jochen
01:39
created

weitersager.httpreceiver   A

Complexity

Total Complexity 6

Size/Duplication

Total Lines 84
Duplicated Lines 0 %

Test Coverage

Coverage 80.95%

Importance

Changes 0
Metric Value
wmc 6
eloc 48
dl 0
loc 84
rs 10
c 0
b 0
f 0
ccs 34
cts 42
cp 0.8095

2 Functions

Rating   Name   Duplication   Size   Complexity  
A start_receive_server() 0 15 2
A parse_json_message() 0 8 1

2 Methods

Rating   Name   Duplication   Size   Complexity  
A ReceiveServer.__init__() 0 4 1
A RequestHandler.do_POST() 0 17 2
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 Message:
23 1
    channel: str
24 1
    text: str
25
26
27 1
def parse_json_message(json_data):
28
    """Extract message from JSON."""
29 1
    data = json.loads(json_data)
30
31 1
    channel = data['channel']
32 1
    text = data['text']
33
34 1
    return Message(channel=channel, text=text)
35
36
37 1
class RequestHandler(BaseHTTPRequestHandler):
38
    """Handler for messages submitted via HTTP."""
39
40 1
    def do_POST(self):
41 1
        try:
42 1
            content_length = int(self.headers.get('Content-Length', 0))
43 1
            data = self.rfile.read(content_length).decode('utf-8')
44 1
            message = parse_json_message(data)
45 1
        except (KeyError, ValueError):
46 1
            log('Invalid message received from {}:{:d}.', *self.client_address)
47 1
            self.send_error(HTTPStatus.BAD_REQUEST)
48 1
            return
49
50 1
        self.send_response(HTTPStatus.ACCEPTED)
51 1
        self.end_headers()
52
53 1
        message_received.send(
54
            channel_name=message.channel,
55
            text=message.text,
56
            source_address=self.client_address,
57
        )
58
59
60 1
class ReceiveServer(HTTPServer):
61
    """HTTP server that waits for messages."""
62
63 1
    def __init__(self, ip_address, port):
64 1
        address = (ip_address, port)
65 1
        HTTPServer.__init__(self, address, RequestHandler)
66 1
        log('Listening for HTTP requests on {}:{:d}.', *address)
67
68
69 1
def start_receive_server(ip_address, port):
70
    """Start in a separate thread."""
71
    try:
72
        receiver = ReceiveServer(ip_address, port)
73
    except Exception as e:
74
        sys.stderr.write(f'Error {e.errno:d}: {e.strerror}\n')
75
        sys.stderr.write(
76
            f'Probably no permission to open port {port}. '
77
            'Try to specify a port number above 1,024 (or even '
78
            '4,096) and up to 65,535.\n'
79
        )
80
        sys.exit(1)
81
82
    thread_name = receiver.__class__.__name__
83
    start_thread(receiver.serve_forever, thread_name)
84