Use microWebSrv instead for www

This commit is contained in:
Maff 2018-05-31 21:18:49 +01:00
parent ec476cc54b
commit fd117de9a6
2 changed files with 931 additions and 58 deletions

109
main.py
View File

@ -6,65 +6,58 @@ hw = uPyConfig.esp8266(variant='d1-r2')
init_sample.init_sample(hw)
# Main app
from uPySensor import LM75A, SHT21
thsen=SHT21(hw.i2c.bus)
tsen=LM75A(hw.i2c.bus)
json={
'sht21_t': '{"temperature":"%0.3f"}',
'sht21_h': '{"humidity":"%0.3f"}',
'sht21': '{"temperature":"%0.3f","humidity":"%0.3f"}',
'lm75a_t': '{"temperature":"%0.1f"}',
'lm75a': '{"temperature":"%0.1f"}',
from uPySensor import BME280, LM75A, SHT21
sensors={
'bme280': BME280(hw.i2c.bus),
'lm75a': LM75A(hw.i2c.bus),
'sht21': SHT21(hw.i2c.bus),
}
json_all='{"sht21":{"temperature":"%0.3f","humidity":"%0.3f"},"lm75a":{"temperature":"%0.1f"}}'
def header():
return "HTTP/1.1 200 OK\r\n" \
"Content-Type: application/json\r\n" \
"Server: horny\r\n" \
"\r\n"
from microWebSrv import MicroWebSrv
ws = MicroWebSrv(webPath='www/')
ws.WebSocketThreaded = False
def get():
return json_all % (thsen.read_tempC(), thsen.read_hum(), tsen.read_tempC())
wshead={
'Server':'horny',
}
wsctype='application/json'
wscharset='UTF-8'
@MicroWebSrv.route('/')
def get_root(wscl, wsres):
wsres.WriteResponseOk(
headers=wshead,
contentType=wsctype,
contentCharset=wscharset,
content='{"result":"error","message":"use /bme280, /lm75a or /sht21 for sensor readings"}'
)
@MicroWebSrv.route('/bme280')
def get_bme280(wscl, wsres):
sensors['bme280'].update_sensor()
json = '{"temperature":"%0.2f","humidity":"%0.2f","pressure":"%0.2f"}' % (
sensors['bme280'].temperature, sensors['bme280'].humidity, sensors['bme280'].pressure)
wsres.WriteResponseOk(
headers=wshead,
contentType=wsctype,
contentCharset=wscharset,
content=None
)
@MicroWebSrv.route('/lm75a')
def get_lm75a(wscl, wsres):
json = '{"temperature":"%0.1f"}' % sensors['lm75a'].read_tempC()
wsres.WriteResponseOk(
headers=wshead,
contentType=wsctype,
contentCharset=wscharset,
content=json
)
@MicroWebSrv.route('/sht21')
def sht21(wscl, wsres):
json = '{"temperature":"%0.3f","humidity":"%0.3f"}' % (sensors['sht21'].read_tempC(), sensors['sht21'].read_hum())
wsres.WriteResponseOk(
headers=wshead,
contentType=wsctype,
contentCharset=wscharset,
content=json
)
def get_sht21_t():
return json['sht21_t'] % (thsen.read_tempC())
def get_sht21_h():
return json['sht21_h'] % (thsen.read_hum())
def get_sht21():
return json['sht21'] % (thsen.read_tempC(), thsen.read_hum())
def get_lm75a_t():
return json['lm75a_t'] % (tsen.read_tempC())
def get_lm75a():
return json['lm75a'] % (tsen.read_tempC())
#begin web
import socket
wssock=socket.getaddrinfo("0.0.0.0",80)[0][-1]
wss=socket.socket()
wss.bind(wssock)
wss.listen(1)
print("Server started on 0.0.0.0:80")
#main loop
while True:
wcl, wcsock = wss.accept()
print("Client connected")
wclfh = wcl.makefile('rwb', 0)
jsn=''
while True:
wcline = wclfh.readline()
if not wcline or wcline == b'\r\n': break
wcline=wcline.split(b' ')
if len(wcline) == 3 and wcline[2].startswith('HTTP'):
if wcline[0] != b'GET': wcl.close()
elif wcline[1] == b'/': jsn=get()
elif wcline[1] == b'/sht21': jsn=get_sht21()
elif wcline[1] == b'/lm75a': jsn=get_lm75a()
wcl.send(header()+jsn)
wcl.close()
ws.Start(threaded=False, stackSize=8192)

880
microWebSrv.py Normal file
View File

@ -0,0 +1,880 @@
"""
The MIT License (MIT)
Copyright © 2018 Jean-Christophe Bos & HC² (www.hc2.fr)
Copyright © 2018 LoBo (https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo)
"""
from json import loads, dumps
from os import stat
import _thread
import network
import time
import socket
import websocket
import gc
import re
try:
from microWebTemplate import MicroWebTemplate
except:
pass
try:
from microWebSocket import MicroWebSocket
except:
pass
class MicroWebSrvRoute:
def __init__(self, route, method, func, routeArgNames, routeRegex):
self.route = route
self.method = method
self.func = func
self.routeArgNames = routeArgNames
self.routeRegex = routeRegex
class MicroWebSrv:
# ============================================================================
# ===( Constants )============================================================
# ============================================================================
_indexPages = [
"index.pyhtml",
"index.html",
"index.htm",
"default.pyhtml",
"default.html",
"default.htm"
]
_mimeTypes = {
".txt": "text/plain",
".htm": "text/html",
".html": "text/html",
".css": "text/css",
".csv": "text/csv",
".js": "application/javascript",
".xml": "application/xml",
".xhtml": "application/xhtml+xml",
".json": "application/json",
".zip": "application/zip",
".pdf": "application/pdf",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".png": "image/png",
".gif": "image/gif",
".svg": "image/svg+xml",
".ico": "image/x-icon"
}
_html_escape_chars = {
"&": "&",
'"': """,
"'": "'",
">": ">",
"<": "&lt;"
}
_pyhtmlPagesExt = '.pyhtml'
# ============================================================================
# ===( Class globals )=======================================================
# ============================================================================
_docoratedRouteHandlers = []
# ============================================================================
# ===( Utils )===============================================================
# ============================================================================
@classmethod
def route(cls, url, method='GET'):
""" Adds a route handler function to the routing list """
def route_decorator(func):
item = (url, method, func)
cls._docoratedRouteHandlers.append(item)
return func
return route_decorator
# ----------------------------------------------------------------------------
@staticmethod
def HTMLEscape(s):
return ''.join(MicroWebSrv._html_escape_chars.get(c, c) for c in s)
# ----------------------------------------------------------------------------
@staticmethod
def _tryAllocByteArray(size):
for x in range(10):
try:
gc.collect()
return bytearray(size)
except:
pass
return None
# ----------------------------------------------------------------------------
@staticmethod
def _tryStartThread(func, args=(), stacksize=8192):
_ = _thread.stack_size(stacksize)
for x in range(10):
try:
gc.collect()
th = _thread.start_new_thread("MicroWebServer", func, args)
return th
except:
time.sleep_ms(100)
return False
# ----------------------------------------------------------------------------
@staticmethod
def _unquote(s):
r = s.split('%')
for i in range(1, len(r)):
s = r[i]
try:
r[i] = chr(int(s[:2], 16)) + s[2:]
except:
r[i] = '%' + s
return ''.join(r)
# ----------------------------------------------------------------------------
@staticmethod
def _unquote_plus(s):
return MicroWebSrv._unquote(s.replace('+', ' '))
# ----------------------------------------------------------------------------
@staticmethod
def _fileExists(path):
try:
stat(path)
return True
except:
return False
# ----------------------------------------------------------------------------
@staticmethod
def _isPyHTMLFile(filename):
return filename.lower().endswith(MicroWebSrv._pyhtmlPagesExt)
# ============================================================================
# ===( Constructor )==========================================================
# ============================================================================
def __init__(self,
routeHandlers=[],
port=80,
bindIP='0.0.0.0',
webPath="/flash/www"):
self._srvAddr = (bindIP, port)
self._webPath = webPath
self._notFoundUrl = None
self._started = False
self.thID = None
self.isThreaded = False
self._state = "Stoped"
self.MaxWebSocketRecvLen = 1024
self.WebSocketThreaded = True
self.WebSocketStackSize = 4096
self.AcceptWebSocketCallback = None
self._routeHandlers = []
routeHandlers += self._docoratedRouteHandlers
for route, method, func in routeHandlers:
routeParts = route.split('/')
# -> ['', 'users', '<uID>', 'addresses', '<addrID>', 'test', '<anotherID>']
routeArgNames = []
routeRegex = ''
for s in routeParts:
if s.startswith('<') and s.endswith('>'):
routeArgNames.append(s[1:-1])
routeRegex += '/(\\w*)'
elif s:
routeRegex += '/' + s
routeRegex += '$'
# -> '/users/(\w*)/addresses/(\w*)/test/(\w*)$'
routeRegex = re.compile(routeRegex)
self._routeHandlers.append(MicroWebSrvRoute(route, method, func, routeArgNames, routeRegex))
# ============================================================================
# ===( Server Process )=======================================================
# ============================================================================
def _serverProcess(self):
self._started = True
self._state = "Running"
while True:
try:
client, cliAddr = self._server.accepted()
if client == None:
if self.isThreaded:
notify = _thread.getnotification()
if notify == _thread.EXIT:
break
elif notify == _thread.SUSPEND:
self._state = "Suspended"
while _thread.wait() != _thread.RESUME:
pass
self._state = "Running"
# gc.collect()
time.sleep_ms(2)
continue
except Exception as e:
if not self.isThreaded:
print(e)
break
self._client(self, client, cliAddr)
self._started = False
self._state = "Stoped"
self.thID = None
# ============================================================================
# ===( Functions )============================================================
# ============================================================================
def Start(self, threaded=True, stackSize=8192):
if not self._started:
if not network.WLAN().wifiactive():
print("WLAN not connected!")
return
gc.collect()
self._server = socket.socket(socket.AF_INET,
socket.SOCK_STREAM,
socket.IPPROTO_TCP)
self._server.setsockopt(socket.SOL_SOCKET,
socket.SO_REUSEADDR,
1)
self._server.bind(self._srvAddr)
self._server.listen(1)
self.isThreaded = threaded
# using non-blocking socket
self._server.settimeout(0.5)
if threaded:
th = MicroWebSrv._tryStartThread(self._serverProcess, stacksize=stackSize)
if th:
self.thID = th
else:
self._serverProcess()
# ----------------------------------------------------------------------------
def Stop(self):
if self._started:
self._server.close()
if self.isThreaded:
_ = _thread.notify(self.thID, _thread.EXIT)
# ----------------------------------------------------------------------------
def IsStarted(self):
return self._started
# ----------------------------------------------------------------------------
def threadID(self):
return self.thID
# ----------------------------------------------------------------------------
def State(self):
return self._state
# ----------------------------------------------------------------------------
def SetNotFoundPageUrl(self, url=None):
self._notFoundUrl = url
# ----------------------------------------------------------------------------
def GetMimeTypeFromFilename(self, filename):
filename = filename.lower()
for ext in self._mimeTypes:
if filename.endswith(ext):
return self._mimeTypes[ext]
return None
# ----------------------------------------------------------------------------
def GetRouteHandler(self, resUrl, method):
if self._routeHandlers:
# resUrl = resUrl.upper()
if resUrl.endswith('/'):
resUrl = resUrl[:-1]
method = method.upper()
for rh in self._routeHandlers:
if rh.method == method:
m = rh.routeRegex.match(resUrl)
if m: # found matching route?
if rh.routeArgNames:
routeArgs = {}
for i, name in enumerate(rh.routeArgNames):
value = m.group(i + 1)
try:
value = int(value)
except:
pass
routeArgs[name] = value
return (rh.func, routeArgs)
else:
return (rh.func, None)
return (None, None)
# ----------------------------------------------------------------------------
def _physPathFromURLPath(self, urlPath):
if urlPath == '/':
for idxPage in self._indexPages:
physPath = self._webPath + '/' + idxPage
if MicroWebSrv._fileExists(physPath):
return physPath
else:
physPath = self._webPath + urlPath
if MicroWebSrv._fileExists(physPath):
return physPath
return None
# ============================================================================
# ===( Class Client )========================================================
# ============================================================================
class _client:
# ------------------------------------------------------------------------
def __init__(self, microWebSrv, socket, addr):
socket.settimeout(2)
self._microWebSrv = microWebSrv
self._socket = socket
self._addr = addr
self._method = None
self._path = None
self._httpVer = None
self._resPath = "/"
self._queryString = ""
self._queryParams = {}
self._headers = {}
self._contentType = None
self._contentLength = 0
self._processRequest()
# ------------------------------------------------------------------------
def _processRequest(self):
try:
response = MicroWebSrv._response(self)
if self._parseFirstLine(response):
if self._parseHeader(response):
upg = self._getConnUpgrade()
if not upg:
routeHandler, routeArgs = self._microWebSrv.GetRouteHandler(self._resPath, self._method)
if routeHandler:
if routeArgs is not None:
routeHandler(self, response, routeArgs)
else:
routeHandler(self, response)
elif self._method.upper() == "GET":
filepath = self._microWebSrv._physPathFromURLPath(self._resPath)
if filepath:
if MicroWebSrv._isPyHTMLFile(filepath):
response.WriteResponsePyHTMLFile(filepath)
else:
contentType = self._microWebSrv.GetMimeTypeFromFilename(filepath)
if contentType:
response.WriteResponseFile(filepath, contentType)
else:
response.WriteResponseForbidden()
else:
response.WriteResponseNotFound()
else:
response.WriteResponseMethodNotAllowed()
elif upg == 'websocket' and 'MicroWebSocket' in globals() \
and self._microWebSrv.AcceptWebSocketCallback:
MicroWebSocket(socket=self._socket,
httpClient=self,
httpResponse=response,
maxRecvLen=self._microWebSrv.MaxWebSocketRecvLen,
threaded=self._microWebSrv.WebSocketThreaded,
acceptCallback=self._microWebSrv.AcceptWebSocketCallback,
stackSize=self._microWebSrv.WebSocketStackSize)
return
else:
response.WriteResponseNotImplemented()
else:
response.WriteResponseBadRequest()
except:
response.WriteResponseInternalServerError()
try:
self._socket.close()
except:
pass
# ------------------------------------------------------------------------
def _parseFirstLine(self, response):
try:
elements = self._socket.readline().decode().strip().split()
if len(elements) == 3:
self._method = elements[0].upper()
self._path = elements[1]
self._httpVer = elements[2].upper()
elements = self._path.split('?', 1)
if len(elements) > 0:
self._resPath = MicroWebSrv._unquote_plus(elements[0])
if len(elements) > 1:
self._queryString = elements[1]
elements = self._queryString.split('&')
for s in elements:
param = s.split('=', 1)
if len(param) > 0:
value = MicroWebSrv._unquote(param[1]) if len(param) > 1 else ''
self._queryParams[MicroWebSrv._unquote(param[0])] = value
return True
except:
pass
return False
# ------------------------------------------------------------------------
def _parseHeader(self, response):
while True:
elements = self._socket.readline().decode().strip().split(':', 1)
if len(elements) == 2:
self._headers[elements[0].strip()] = elements[1].strip()
elif len(elements) == 1 and len(elements[0]) == 0:
if self._method == 'POST':
self._contentType = self._headers.get("Content-Type", None)
self._contentLength = int(self._headers.get("Content-Length", 0))
return True
else:
return False
# ------------------------------------------------------------------------
def _getConnUpgrade(self):
if 'upgrade' in self._headers.get('Connection', '').lower():
return self._headers.get('Upgrade', '').lower()
return None
# ------------------------------------------------------------------------
def GetServer(self):
return self._microWebSrv
# ------------------------------------------------------------------------
def GetAddr(self):
return self._addr
# ------------------------------------------------------------------------
def GetIPAddr(self):
return self._addr[0]
# ------------------------------------------------------------------------
def GetPort(self):
return self._addr[1]
# ------------------------------------------------------------------------
def GetRequestMethod(self):
return self._method
# ------------------------------------------------------------------------
def GetRequestTotalPath(self):
return self._path
# ------------------------------------------------------------------------
def GetRequestPath(self):
return self._resPath
# ------------------------------------------------------------------------
def GetRequestQueryString(self):
return self._queryString
# ------------------------------------------------------------------------
def GetRequestQueryParams(self):
return self._queryParams
# ------------------------------------------------------------------------
def GetRequestHeaders(self):
return self._headers
# ------------------------------------------------------------------------
def GetRequestContentType(self):
return self._contentType
# ------------------------------------------------------------------------
def GetRequestContentLength(self):
return self._contentLength
# ------------------------------------------------------------------------
def ReadRequestContent(self, size=None):
self._socket.setblocking(False)
b = None
try:
if not size:
b = self._socket.read(self._contentLength)
elif size > 0:
b = self._socket.read(size)
except:
pass
self._socket.setblocking(True)
return b if b else b''
# ------------------------------------------------------------------------
def ReadRequestPostedFormData(self):
res = {}
data = self.ReadRequestContent()
if len(data) > 0:
elements = data.decode().split('&')
for s in elements:
param = s.split('=', 1)
if len(param) > 0:
value = MicroWebSrv._unquote(param[1]) if len(param) > 1 else ''
res[MicroWebSrv._unquote(param[0])] = value
return res
# ------------------------------------------------------------------------
def ReadRequestContentAsJSON(self):
try:
return loads(self.ReadRequestContent())
except:
return None
# ============================================================================
# ===( Class Response )======================================================
# ============================================================================
class _response:
# ------------------------------------------------------------------------
def __init__(self, client):
self._client = client
# ------------------------------------------------------------------------
def _write(self, data):
if type(data) == str:
data = data.encode()
return self._client._socket.write(data)
# ------------------------------------------------------------------------
def _writeFirstLine(self, code):
reason = self._responseCodes.get(code, ('Unknown reason',))[0]
self._write("HTTP/1.1 %s %s\r\n" % (code, reason))
# ------------------------------------------------------------------------
def _writeHeader(self, name, value):
self._write("%s: %s\r\n" % (name, value))
# ------------------------------------------------------------------------
def _writeContentTypeHeader(self, contentType, charset=None):
if contentType:
ct = contentType \
+ (("; charset=%s" % charset) if charset else "")
else:
ct = "application/octet-stream"
self._writeHeader("Content-Type", ct)
# ------------------------------------------------------------------------
def _writeServerHeader(self):
self._writeHeader("Server", "MicroWebSrv by JC`zic")
# ------------------------------------------------------------------------
def _writeEndHeader(self):
self._write("\r\n")
# ------------------------------------------------------------------------
def _writeBeforeContent(self, code, headers, contentType, contentCharset, contentLength):
self._writeFirstLine(code)
if isinstance(headers, dict):
for header in headers:
self._writeHeader(header, headers[header])
if contentLength > 0:
self._writeContentTypeHeader(contentType, contentCharset)
self._writeHeader("Content-Length", contentLength)
self._writeServerHeader()
self._writeHeader("Connection", "close")
self._writeEndHeader()
# ------------------------------------------------------------------------
def WriteSwitchProto(self, upgrade, headers=None):
self._writeFirstLine(101)
self._writeHeader("Connection", "Upgrade")
self._writeHeader("Upgrade", upgrade)
if isinstance(headers, dict):
for header in headers:
self._writeHeader(header, headers[header])
self._writeServerHeader()
self._writeEndHeader()
# ------------------------------------------------------------------------
def WriteResponse(self, code, headers, contentType, contentCharset, content):
try:
contentLength = len(content) if content else 0
self._writeBeforeContent(code, headers, contentType, contentCharset, contentLength)
if contentLength > 0:
self._write(content)
return True
except:
return False
# ------------------------------------------------------------------------
def WriteResponsePyHTMLFile(self, filepath, headers=None):
if 'MicroWebTemplate' in globals():
with open(filepath, 'r') as file:
code = file.read()
gc.collect()
mWebTmpl = MicroWebTemplate(code, escapeStrFunc=MicroWebSrv.HTMLEscape, filepath=filepath)
try:
tmplResult = mWebTmpl.Execute()
return self.WriteResponse(200, headers, "text/html", "UTF-8", tmplResult)
except Exception as ex:
return self.WriteResponse(500,
None,
"text/html",
"UTF-8",
self._execErrCtnTmpl % {
'module': 'PyHTML',
'message': str(ex)
})
return self.WriteResponseNotImplemented()
# ------------------------------------------------------------------------
def WriteResponseFile(self, filepath, contentType=None, headers=None):
try:
size = stat(filepath)[6]
if size > 0:
with open(filepath, 'rb') as file:
self._writeBeforeContent(200, headers, contentType, None, size)
buf = MicroWebSrv._tryAllocByteArray(1024)
if buf:
while size > 0:
x = file.readinto(buf)
if x < len(buf):
buf = memoryview(buf)[:x]
self._write(buf)
size -= x
return True
self.WriteResponseInternalServerError()
return False
except:
pass
self.WriteResponseNotFound()
return False
# ------------------------------------------------------------------------
def WriteResponseFileAttachment(self, filepath, attachmentName, headers=None):
if not isinstance(headers, dict):
headers = {}
headers["Content-Disposition"] = "attachment; filename=\"%s\"" % attachmentName
return self.WriteResponseFile(filepath, None, headers)
# ------------------------------------------------------------------------
def WriteResponseOk(self, headers=None, contentType=None, contentCharset=None, content=None):
return self.WriteResponse(200, headers, contentType, contentCharset, content)
# ------------------------------------------------------------------------
def WriteResponseJSONOk(self, obj=None, headers=None):
return self.WriteResponse(200, headers, "application/json", "UTF-8", dumps(obj))
# ------------------------------------------------------------------------
def WriteResponseRedirect(self, location):
headers = {"Location": location}
return self.WriteResponse(302, headers, None, None, None)
# ------------------------------------------------------------------------
def WriteResponseError(self, code):
responseCode = self._responseCodes.get(code, ('Unknown reason', ''))
return self.WriteResponse(code,
None,
"text/html",
"UTF-8",
self._errCtnTmpl % {
'code': code,
'reason': responseCode[0],
'message': responseCode[1]
})
# ------------------------------------------------------------------------
def WriteResponseJSONError(self, code, obj=None):
return self.WriteResponse(code,
None,
"application/json",
"UTF-8",
dumps(obj if obj else {}))
# ------------------------------------------------------------------------
def WriteResponseBadRequest(self):
return self.WriteResponseError(400)
# ------------------------------------------------------------------------
def WriteResponseForbidden(self):
return self.WriteResponseError(403)
# ------------------------------------------------------------------------
def WriteResponseNotFound(self):
if self._client._microWebSrv._notFoundUrl:
self.WriteResponseRedirect(self._client._microWebSrv._notFoundUrl)
else:
return self.WriteResponseError(404)
# ------------------------------------------------------------------------
def WriteResponseMethodNotAllowed(self):
return self.WriteResponseError(405)
# ------------------------------------------------------------------------
def WriteResponseInternalServerError(self):
return self.WriteResponseError(500)
# ------------------------------------------------------------------------
def WriteResponseNotImplemented(self):
return self.WriteResponseError(501)
# ------------------------------------------------------------------------
_errCtnTmpl = """\
<html>
<head>
<title>Error</title>
</head>
<body>
<h1>%(code)d %(reason)s</h1>
%(message)s
</body>
</html>
"""
# ------------------------------------------------------------------------
_execErrCtnTmpl = """\
<html>
<head>
<title>Page execution error</title>
</head>
<body>
<h1>%(module)s page execution error</h1>
%(message)s
</body>
</html>
"""
# ------------------------------------------------------------------------
_responseCodes = {
100: ('Continue', 'Request received, please continue'),
101: ('Switching Protocols',
'Switching to new protocol; obey Upgrade header'),
200: ('OK', 'Request fulfilled, document follows'),
201: ('Created', 'Document created, URL follows'),
202: ('Accepted',
'Request accepted, processing continues off-line'),
203: ('Non-Authoritative Information', 'Request fulfilled from cache'),
204: ('No Content', 'Request fulfilled, nothing follows'),
205: ('Reset Content', 'Clear input form for further input.'),
206: ('Partial Content', 'Partial content follows.'),
300: ('Multiple Choices',
'Object has several resources -- see URI list'),
301: ('Moved Permanently', 'Object moved permanently -- see URI list'),
302: ('Found', 'Object moved temporarily -- see URI list'),
303: ('See Other', 'Object moved -- see Method and URL list'),
304: ('Not Modified',
'Document has not changed since given time'),
305: ('Use Proxy',
'You must use proxy specified in Location to access this '
'resource.'),
307: ('Temporary Redirect',
'Object moved temporarily -- see URI list'),
400: ('Bad Request',
'Bad request syntax or unsupported method'),
401: ('Unauthorized',
'No permission -- see authorization schemes'),
402: ('Payment Required',
'No payment -- see charging schemes'),
403: ('Forbidden',
'Request forbidden -- authorization will not help'),
404: ('Not Found', 'Nothing matches the given URI'),
405: ('Method Not Allowed',
'Specified method is invalid for this resource.'),
406: ('Not Acceptable', 'URI not available in preferred format.'),
407: ('Proxy Authentication Required', 'You must authenticate with '
'this proxy before proceeding.'),
408: ('Request Timeout', 'Request timed out; try again later.'),
409: ('Conflict', 'Request conflict.'),
410: ('Gone',
'URI no longer exists and has been permanently removed.'),
411: ('Length Required', 'Client must specify Content-Length.'),
412: ('Precondition Failed', 'Precondition in headers is false.'),
413: ('Request Entity Too Large', 'Entity is too large.'),
414: ('Request-URI Too Long', 'URI is too long.'),
415: ('Unsupported Media Type', 'Entity body in unsupported format.'),
416: ('Requested Range Not Satisfiable',
'Cannot satisfy request range.'),
417: ('Expectation Failed',
'Expect condition could not be satisfied.'),
500: ('Internal Server Error', 'Server got itself in trouble'),
501: ('Not Implemented',
'Server does not support this operation'),
502: ('Bad Gateway', 'Invalid responses from another server/proxy.'),
503: ('Service Unavailable',
'The server cannot process the request due to a high load'),
504: ('Gateway Timeout',
'The gateway server did not receive a timely response'),
505: ('HTTP Version Not Supported', 'Cannot fulfill request.'),
}
# ============================================================================
# ============================================================================
# ============================================================================