init
This commit is contained in:
commit
29c7308410
17 changed files with 3755 additions and 0 deletions
129
src/byteb4rb1e_utils/http/server/__init__.py
Normal file
129
src/byteb4rb1e_utils/http/server/__init__.py
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
from dataclasses import dataclass
|
||||
from http.server import SimpleHTTPRequestHandler
|
||||
|
||||
from byteb4rb1e_utils.io import ChunksIO
|
||||
|
||||
|
||||
@dataclass
|
||||
class HandlerOptions:
|
||||
"""configuration options of the HTTP POST method handler
|
||||
"""
|
||||
max_chunk_size: int = ChunksIO.max_chunk_size
|
||||
# default (in memory) buffer size in bytes (from KiB) of the sliding buffer
|
||||
# reading from the pure (unchunked) client read stream
|
||||
buffer_size: int = 512 * 1024
|
||||
|
||||
|
||||
@dataclass
|
||||
class ServerOptions:
|
||||
"""configuration options of the HTTP server
|
||||
"""
|
||||
handler: HandlerOptions
|
||||
hostname: str = ''
|
||||
port: int = 8000
|
||||
|
||||
|
||||
class MultipartUploadHandler(SimpleHTTPRequestHandler):
|
||||
"""Simple, yet compliant HTTP/1.0 MIME Multipart Upload Handler
|
||||
|
||||
Implementation of a RFC1341 & RFC7578 compliant server for handling
|
||||
multipart uploads.
|
||||
|
||||
This is meant as a utility for debugging MIME Multipart upload clients
|
||||
|
||||
Support for:
|
||||
- client 'Expect' header
|
||||
- chunked transfer-encoding
|
||||
"""
|
||||
media_subtypes = [
|
||||
'mixed',
|
||||
'alternative',
|
||||
'parallel',
|
||||
'digest',
|
||||
'form-data'
|
||||
]
|
||||
|
||||
def do_POST(self):
|
||||
h_content_type = self.headers.get('Content-Type')
|
||||
h_expect = self.headers.get('Expect')
|
||||
h_transfer_encoding = self.headers.get('Transfer-Encoding')
|
||||
|
||||
if h_content_type == None:
|
||||
self.send_error(400, 'Missing \'Content-Type\' header')
|
||||
|
||||
content_type_segments = [s.strip() for s in h_content_type.split(';')]
|
||||
try:
|
||||
media_type, media_subtype = content_type_segments[0].split('/', 1)
|
||||
except IndexError:
|
||||
self.send_error(
|
||||
400,
|
||||
'no value was supplied for \'Content-Type\' header'
|
||||
)
|
||||
except ValueError:
|
||||
self.send_error(
|
||||
400,
|
||||
'unable to parse media type and subtype from ' +
|
||||
'first (semicolon-delimited) segment of \'Content-Type\' ' +
|
||||
f'header value: {content_type_segments[0]}'
|
||||
)
|
||||
|
||||
if media_type != 'multipart':
|
||||
self.send_error(
|
||||
400,
|
||||
'unsupported media type in \'Content-Type\' header value: ' +
|
||||
f'{media_type}'
|
||||
)
|
||||
elif media_subtype not in self.media_subtypes:
|
||||
self.send_error(
|
||||
400,
|
||||
'unsupported media sub-type in \'Content-Type\' header value: ' +
|
||||
f'{media_type}. Must be one of {", ".join(self.media_subtypes)}'
|
||||
)
|
||||
|
||||
if h_transfer_encoding:
|
||||
if h_transfer_encoding != 'chunked':
|
||||
self.send_error(
|
||||
501,
|
||||
f'unable to handle transfer-encoding: {h_transfer_encoding}'
|
||||
)
|
||||
|
||||
content_type_params = {v[0].strip():v[1].strip() for v in [
|
||||
s.split('=', 1) for s in content_type_segments[1:]
|
||||
]}
|
||||
|
||||
boundary = content_type_params.get('boundary', '')
|
||||
boundary_len = len(boundary)
|
||||
if boundary == '':
|
||||
self.send_error(
|
||||
400,
|
||||
'missing \'boundary\' parameter in \'Content-Type\' header field'
|
||||
)
|
||||
elif boundary_len > 70:
|
||||
self.send_error(
|
||||
400,
|
||||
'\'boundary\' parameter value in \'Content-Type\' too long. ' +
|
||||
f'Is {boundary_len} characters long, must be less than 70.'
|
||||
)
|
||||
|
||||
del content_type_params['boundary']
|
||||
content_type_params_keys = content_type_params.keys()
|
||||
if len(content_type_params_keys) > 0:
|
||||
self.send_error(
|
||||
400,
|
||||
'None other than \'boundary\' parameter in \'Content-Type\'' +
|
||||
'header expected. Also received ' +
|
||||
'{\', \'.join(content_type_param_keys)}'
|
||||
)
|
||||
|
||||
|
||||
self.handle_expect_100()
|
||||
|
||||
# read the first 4-bytes of the body to check if it has a preamble
|
||||
# indication
|
||||
|
||||
# well great... curl is not RFC 1341 compliant. And RFC 1341 is asking
|
||||
# for tolerance towards non-compliant clients...
|
||||
|
||||
self.send_response(200, 'OK')
|
||||
self.end_headers()
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue