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", ), )
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, ))
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'))
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") ), )
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, ), )
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"), ), )
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'))
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)
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)
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, )
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)))
# -*- 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): """
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