mirror of
https://github.com/Mikaela/Limnoria.git
synced 2024-11-15 07:04:53 +01:00
httpserver: Rewrite without the cgi module
It is removed in Python 3.13
This commit is contained in:
parent
9bcb21389a
commit
0ad61f5791
@ -1,5 +1,5 @@
|
|||||||
###
|
###
|
||||||
# Copyright (c) 2011-2021, Valentin Lorentz
|
# Copyright (c) 2011-2024, Valentin Lorentz
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without
|
# Redistribution and use in source and binary forms, with or without
|
||||||
@ -32,8 +32,8 @@ An embedded and centralized HTTP server for Supybot's plugins.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import cgi
|
|
||||||
import socket
|
import socket
|
||||||
|
import urllib.parse
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
import supybot.log as log
|
import supybot.log as log
|
||||||
@ -164,6 +164,114 @@ def get_template(filename):
|
|||||||
with open(path + '.example', 'r') as fd:
|
with open(path + '.example', 'r') as fd:
|
||||||
return fd.read()
|
return fd.read()
|
||||||
|
|
||||||
|
class HttpHeader:
|
||||||
|
__slots__ = ('name', 'value')
|
||||||
|
|
||||||
|
def __init__(self, name, value):
|
||||||
|
self.name = name
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
"""Return printable representation."""
|
||||||
|
return "HttpHeader(%r, %r)" % (self.name, self.value)
|
||||||
|
|
||||||
|
class HttpHeaders:
|
||||||
|
"""Copy of `cgi.FieldStorage
|
||||||
|
<https://github.com/python/cpython/blob/v3.12.3/Lib/cgi.py#L512-L594>`
|
||||||
|
before it was removed from the stdlib.
|
||||||
|
"""
|
||||||
|
__slots__ = ('list',)
|
||||||
|
def __init__(self, headers):
|
||||||
|
self.list = headers
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'HttpHeaders(%r)' % self.list
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self.keys())
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
if name != 'value':
|
||||||
|
raise AttributeError(name)
|
||||||
|
if self.file:
|
||||||
|
self.file.seek(0)
|
||||||
|
value = self.file.read()
|
||||||
|
self.file.seek(0)
|
||||||
|
elif self.list is not None:
|
||||||
|
value = self.list
|
||||||
|
else:
|
||||||
|
value = None
|
||||||
|
return value
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
"""Dictionary style indexing."""
|
||||||
|
if self.list is None:
|
||||||
|
raise TypeError("not indexable")
|
||||||
|
found = []
|
||||||
|
for item in self.list:
|
||||||
|
if item.name == key: found.append(item)
|
||||||
|
if not found:
|
||||||
|
raise KeyError(key)
|
||||||
|
if len(found) == 1:
|
||||||
|
return found[0]
|
||||||
|
else:
|
||||||
|
return found
|
||||||
|
|
||||||
|
def getvalue(self, key, default=None):
|
||||||
|
"""Dictionary style get() method, including 'value' lookup."""
|
||||||
|
if key in self:
|
||||||
|
value = self[key]
|
||||||
|
if isinstance(value, list):
|
||||||
|
return [x.value for x in value]
|
||||||
|
else:
|
||||||
|
return value.value
|
||||||
|
else:
|
||||||
|
return default
|
||||||
|
|
||||||
|
def getfirst(self, key, default=None):
|
||||||
|
""" Return the first value received."""
|
||||||
|
if key in self:
|
||||||
|
value = self[key]
|
||||||
|
if isinstance(value, list):
|
||||||
|
return value[0].value
|
||||||
|
else:
|
||||||
|
return value.value
|
||||||
|
else:
|
||||||
|
return default
|
||||||
|
|
||||||
|
def getlist(self, key):
|
||||||
|
""" Return list of received values."""
|
||||||
|
if key in self:
|
||||||
|
value = self[key]
|
||||||
|
if isinstance(value, list):
|
||||||
|
return [x.value for x in value]
|
||||||
|
else:
|
||||||
|
return [value.value]
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def keys(self):
|
||||||
|
"""Dictionary style keys() method."""
|
||||||
|
if self.list is None:
|
||||||
|
raise TypeError("not indexable")
|
||||||
|
return list(set(item.name for item in self.list))
|
||||||
|
|
||||||
|
def __contains__(self, key):
|
||||||
|
"""Dictionary style __contains__ method."""
|
||||||
|
if self.list is None:
|
||||||
|
raise TypeError("not indexable")
|
||||||
|
return any(item.name == key for item in self.list)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
"""Dictionary style len(x) support."""
|
||||||
|
return len(self.keys())
|
||||||
|
|
||||||
|
def __bool__(self):
|
||||||
|
if self.list is None:
|
||||||
|
raise TypeError("Cannot be converted to bool.")
|
||||||
|
return bool(self.list)
|
||||||
|
|
||||||
|
|
||||||
class SupyHTTPRequestHandler(BaseHTTPRequestHandler):
|
class SupyHTTPRequestHandler(BaseHTTPRequestHandler):
|
||||||
def do_X(self, callbackMethod, *args, **kwargs):
|
def do_X(self, callbackMethod, *args, **kwargs):
|
||||||
if self.path == '/':
|
if self.path == '/':
|
||||||
@ -199,12 +307,11 @@ class SupyHTTPRequestHandler(BaseHTTPRequestHandler):
|
|||||||
if 'Content-Type' not in self.headers:
|
if 'Content-Type' not in self.headers:
|
||||||
self.headers['Content-Type'] = 'application/x-www-form-urlencoded'
|
self.headers['Content-Type'] = 'application/x-www-form-urlencoded'
|
||||||
if self.headers['Content-Type'] == 'application/x-www-form-urlencoded':
|
if self.headers['Content-Type'] == 'application/x-www-form-urlencoded':
|
||||||
form = cgi.FieldStorage(
|
length = min(100000, int(self.headers.get('Content-Length', '100000')))
|
||||||
fp=self.rfile,
|
qs = self.rfile.read(length).decode()
|
||||||
headers=self.headers,
|
form = HttpHeaders([
|
||||||
environ={'REQUEST_METHOD':'POST',
|
HttpHeader(k, v) for (k, v) in urllib.parse.parse_qsl(qs)
|
||||||
'CONTENT_TYPE':self.headers['Content-Type'],
|
])
|
||||||
})
|
|
||||||
else:
|
else:
|
||||||
content_length = int(self.headers.get('Content-Length', '0'))
|
content_length = int(self.headers.get('Content-Length', '0'))
|
||||||
form = self.rfile.read(content_length)
|
form = self.rfile.read(content_length)
|
||||||
|
Loading…
Reference in New Issue
Block a user