Example #1
0
 def get_status(self, sock: socket.socket, address: tuple[str, int], request_parser: HTTPParser) -> HTTPEventHandler:
     try:
         with open(self.static_filename, "rb") as static_fileobj:
             status_body = static_fileobj.read()
         return HTTPEventHandler(
             self.server,
             sock,
             address,
             request_parser,
             HTTPResponse(200, b"OK", {b"Content-Type": b"application/octet-stream"}, status_body),
         )
     except IOError as exc:
         self.server.logger.exception("Error when trying to serve static status file %s:", self.static_filename)
         return HTTPEventHandler(
             self.server,
             sock,
             address,
             request_parser,
             HTTPResponse(
                 500,
                 b"Internal Server Error",
                 {b"Content-Type": b"text/plain"},
                 b"Failed to open static status file\n",
             ),
         )
Example #2
0
    def __init__(self, server, source, sock, address, request_parser,
                 content_type):
        headers = {b'Content-Length': None, b'Content-Type': content_type}
        for header in source.ICY_HEADERS:
            if header == 'metaint':
                continue

            header_value = getattr(source, 'icy_%s' % header)
            if header_value:
                headers[b'icy-%s' % header] = header_value

        # did client asked for metadata ?
        if request_parser.headers.get('Icy-Metadata') == b'1' and hasattr(
                source, 'metadata'):
            self.metadata = b''
            self.bytes_count = 0
            self.add_packet = self.add_packet_with_metadata
            headers[b'icy-metaint'] = b'%s' % self.ICY_META_INTERVAL

        StreamClient.__init__(self, server, source, sock, address,
                              request_parser, content_type,
                              HTTPResponse(
                                  200,
                                  b'OK',
                                  headers,
                              ))
Example #3
0
 def get_status(self, sock, address, request_parser):
     try:
         with open(self.static_filename) as static_fileobj:
             status_body = static_fileobj.read()
         return HTTPEventHandler(
             self.server, sock, address, request_parser,
             HTTPResponse(200, b'OK',
                          {b'Content-Type': 'application/octet-stream'},
                          status_body))
     except IOError as exc:
         self.server.logger.exception(
             'Error when trying to serve static status file %s:',
             self.static_filename)
         return HTTPEventHandler(
             self.server, sock, address, request_parser,
             HTTPResponse(500, b'Internal Server Error',
                          {b'Content-Type': 'text/plain'},
                          'Failed to open static status file\n'))
Example #4
0
 def get_status(self, sock: socket.socket, address: tuple[str, int], request_parser: HTTPParser) -> HTTPEventHandler:
     return HTTPEventHandler(
         self.server,
         sock,
         address,
         request_parser,
         HTTPResponse(
             200, b"OK", {b"Content-Type": b"text/plain"}, bytes(pprint.pformat(self.server.sources), "ascii")
         ),
     )
Example #5
0
    def __init__(
        self,
        server: "TCPServer",
        source: "ShoutcastSource",
        sock: socket.socket,
        address: tuple[str, int],
        request_parser: HTTPParser,
        content_type: str,
        http_response: Optional[HTTPResponse] = None,
    ) -> None:
        headers = {
            b"Content-Length": None,
            b"Content-Type": bytes(content_type, "ascii")
        }
        for header in source.ICY_HEADERS:
            if header == b"metaint":
                continue

            header_value = source.icy_headers.get(b"icy_%s" % header)
            if header_value:
                headers[b"icy-%s" % header] = header_value

        # did client asked for metadata ?
        if request_parser.headers.get(b"Icy-Metadata") == b"1" and hasattr(
                source, "metadata"):
            self.metadata = b""
            self.bytes_count = 0
            self.add_packet = self.add_packet_with_metadata  # type: ignore[assignment]
            headers[b"icy-metaint"] = b"%s" % self.ICY_META_INTERVAL

        super().__init__(
            server,
            source,
            sock,
            address,
            request_parser,
            content_type,
            HTTPResponse(
                200,
                b"OK",
                headers,
            ),
        )
Example #6
0
    def get_status(self, sock: socket.socket, address: tuple[str, int], request_parser: HTTPParser) -> HTTPEventHandler:
        sources_dict: dict[str, dict[str, dict[int, str]]] = {}
        total_clients_number = 0

        queue_sizes = []

        for path, sources in self.server.sources.items():
            sources_dict[path] = {}
            for source, source_dict in list(sources.items()):
                source_address = "%s:%s (%s)" % (source.address[0], source.address[1], id(source))
                sources_dict[path][source_address] = {}
                for fd, client in list(source_dict["clients"].items()):
                    sources_dict[path][source_address][fd] = "%s:%s" % client.address
                    total_clients_number += 1
                    queue_sizes.append(sum(len(elt) for elt in client.output_buffer.buffer_queue))

        queue_sizes.sort()
        if not queue_sizes:
            queue_sizes = [-1]
        status_dict = {
            "total_clients_number": total_clients_number,
            "pid": os.getpid(),
            "max_buffer_queue_size": queue_sizes[-1],
            "min_buffer_queue_size": queue_sizes[0],
            "median_buffer_queue_size": queue_sizes[total_clients_number // 2],
            "average_buffer_queue_size": sum(queue_sizes) / len(queue_sizes),
            "sources": sources_dict,
        }

        return HTTPEventHandler(
            self.server,
            sock,
            address,
            request_parser,
            HTTPResponse(
                200,
                b"OK",
                {b"Content-Type": b"application/json"},
                bytes(json.dumps(status_dict, indent=4) + "\n", "utf-8"),
            ),
        )
Example #7
0
    def get_status(self, sock, address, request_parser):
        sources_dict = {}
        total_clients_number = 0

        queue_sizes = []

        for path, sources in self.server.sources.items():
            sources_dict[path] = {}
            for source, source_dict in sources.items():
                source_address = '%s:%s (%s)' % (source.address[0],
                                                 source.address[1], id(source))
                sources_dict[path][source_address] = {}
                for fd, client in source_dict['clients'].items():
                    sources_dict[path][source_address][
                        fd] = '%s:%s' % client.address
                    total_clients_number += 1
                    queue_sizes.append(
                        sum(
                            len(elt)
                            for elt in client.output_buffer.buffer_queue))

        queue_sizes.sort()
        if not queue_sizes:
            queue_sizes = [-1]
        status_dict = {
            'total_clients_number': total_clients_number,
            'pid': os.getpid(),
            'max_buffer_queue_size': queue_sizes[-1],
            'min_buffer_queue_size': queue_sizes[0],
            'median_buffer_queue_size': queue_sizes[total_clients_number / 2],
            'average_buffer_queue_size': sum(queue_sizes) / len(queue_sizes),
            'sources': sources_dict,
        }

        return HTTPEventHandler(
            self.server, sock, address, request_parser,
            HTTPResponse(200, b'OK', {b'Content-Type': 'application/json'},
                         json.dumps(status_dict, indent=4) + '\n'))
Example #8
0
    def __init__(self,
                 server,
                 source,
                 sock,
                 address,
                 request_parser,
                 content_type,
                 http_response=None):
        if http_response is None:
            http_response = HTTPResponse(
                200,
                b'OK',
                {
                    b'Content-Length': None,
                    b'Content-Type': content_type
                },
            )

        HTTPEventHandler.__init__(self, server, sock, address, request_parser,
                                  http_response)
        self.source = source
        self.timeout_state = False
        self.server.remove_inactivity_timeout(self)
Example #9
0
    def __init__(
        self,
        server: "TCPServer",
        source: StreamSource,
        sock: socket.socket,
        address: tuple[str, int],
        request_parser: HTTPParser,
        content_type: str,
        http_response: Optional[HTTPResponse] = None,
    ) -> None:
        if http_response is None:
            http_response = HTTPResponse(
                200,
                b"OK",
                {
                    b"Content-Length": None,
                    b"Content-Type": bytes(content_type, "ascii")
                },
            )

        super().__init__(server, sock, address, request_parser, http_response)
        self.source = source
        self.timeout_state = False
        self.server.remove_inactivity_timeout(self)
Example #10
0
    def transform_request(self) -> None:
        loop = self.server.loop
        # FIXME: should we shutdown() read or write depending on what
        # we do here ? (i.e. SHUT_RD for GETs, SHUT_WD for sources)

        self.server.logger.info(
            "%s:%s %s %s %s, request headers: %s",
            self.address[0],
            self.address[1],
            self.request_parser.request_method,
            self.request_parser.request_path,
            self.request_parser.http_version,
            self.request_parser.headers,
        )

        # Squash any consecutive / into one
        self.request_parser.request_path = re.sub(
            b"//+", b"/", self.request_parser.request_path)

        self.server.request_in(self.request_parser, self.sock)

        # Authorization
        for auth_handler in self.server.auth_handlers:
            auth_result = auth_handler.authorize(self.address,
                                                 self.request_parser)
            if auth_result is None:
                continue
            elif not isinstance(auth_result, HTTPResponse):
                # Wrong response from auth handler
                raise RuntimeError(
                    "Wrong response from authorization handler %s" %
                    auth_handler)
            elif auth_result.status == 200:
                # Request authorized
                break
            else:
                # Access denied
                loop.register(
                    helpers.HTTPEventHandler(self.server, self.sock,
                                             self.address, self.request_parser,
                                             auth_result),
                    looping.POLLOUT,
                )
                return

        path = self.request_parser.request_path.decode("ascii")

        response = None

        if self.request_parser.request_method in [b"PUT", b"SOURCE", b"POST"]:
            self.server.register_source(
                sources.find_source(self.server, self.sock, self.address,
                                    self.request_parser, path))
        elif self.request_parser.request_method in [b"GET", b"HEAD"]:
            # New client

            # Is our client asking for status ?
            if path in self.server.status_handlers:
                # FIXME: should we handle HEAD requests ?
                if self.request_parser.request_method not in [b"GET"]:
                    response = HTTPResponse(405, b"Method Not Allowed")
                else:
                    loop.register(
                        self.server.status_handlers[path].get_status(
                            self.sock, self.address, self.request_parser),
                        looping.POLLOUT,
                    )
            else:
                # New client for one of our sources
                if path in self.server.sources:
                    # Used by some clients to know the stream type
                    # before attempting playout
                    if self.request_parser.request_method in [b"HEAD"]:
                        source = list(self.server.sources[path].keys())[0]
                        response = HTTPResponse(
                            200,
                            b"OK",
                            {
                                b"Content-Type":
                                bytes(source.content_type, "ascii"),
                                b"Content-Length":
                                None,
                                b"Connection":
                                b"close",
                            },
                        )
                    # Check for server clients limit
                    elif self.server.clients_limit is not None and (
                            self.server.clients_limit
                            == self.server.clients_connected):
                        response = HTTPResponse(
                            503, b"Cannot handle response."
                            b" Too many clients.")
                    else:
                        # FIXME: proper source selection
                        source = random.choice(
                            list(self.server.sources[path].keys()))
                        new_client = clients.find_client(
                            self.server, source, self.sock, self.address,
                            self.request_parser)
                        # FIXME: this call may actually need to instatiate
                        # the client itself (e.g. if the source needs some
                        # dedicated code in its clients)
                        source.new_client(new_client)
                        # FIXME: see above wrt to proper source selection
                        self.server.sources[path][source]["clients"][
                            new_client.fileno()] = new_client
                        self.server.clients_connected += 1
                        loop.register(new_client, looping.POLLOUT)
                else:
                    # Stream does not exist
                    response = HTTPResponse(404, b"Stream Not Found")

        else:
            # Unknown HTTP request method
            response = HTTPResponse(405, b"Method Not Allowed")

        if response is not None:
            loop.register(
                helpers.HTTPEventHandler(self.server, self.sock, self.address,
                                         self.request_parser, response),
                looping.POLLOUT,
            )
Example #11
0
 def get_status(self, sock, address, request_parser):
     return HTTPEventHandler(
         self.server, sock, address, request_parser,
         HTTPResponse(200, b'OK', {b'Content-Type': 'text/plain'},
                      pprint.pformat(self.server.sources)))
Example #12
0
# -*- coding: utf-8 -*-

import base64
import collections
import hashlib
import time

from savate.helpers import HTTPResponse

AUTH_SUCCESS = HTTPResponse(200, b'OK')
# FIXME: make the authorization realm configurable ?
AUTH_REQUEST = HTTPResponse(401, b'Unauthorized',
                            {b'WWW-Authenticate': b'Basic realm="savate"'})
AUTH_FAILURE = HTTPResponse(403, b'Forbidden')


class AbstractAuthorization(object):
    def __init__(self, server, server_config, **config_dict):
        self.server = server
        self.server_config = server_config
        self.config = config_dict

    def authorize(self, client_address, client_request):
        # True: OK, go on serving
        # False: NOK, 403
        # None: I don't know, move on to next auth handler
        return None


class AbstractBasicAuthorization(AbstractAuthorization):
    """
Example #13
0
import collections
import hashlib
import socket
import time
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Any, Callable, Optional, TypedDict

from cyhttp11 import HTTPParser

from savate.helpers import HTTPResponse
from savate.configuration import ServerConfiguration

if TYPE_CHECKING:
    from savate.server import HTTPRequest, TCPServer

AUTH_SUCCESS = HTTPResponse(200, b"OK")
# FIXME: make the authorization realm configurable ?
AUTH_REQUEST = HTTPResponse(401, b"Unauthorized",
                            {b"WWW-Authenticate": b'Basic realm="savate"'})
AUTH_FAILURE = HTTPResponse(403, b"Forbidden")

ClientAddress = tuple[socket.socket, tuple[str, int]]


class AbstractAuthorization(ABC):
    def __init__(self, server: "TCPServer", server_config: dict[str, Any],
                 **config_dict: Any):
        self.server = server
        self.server_config = server_config
        self.config = config_dict