| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880 |
- """
- The MIT License (MIT)
- Copyright © 2018 Jean-Christophe Bos & HC² (www.hc2.fr)
- """
- from json import loads, dumps
- from os import stat
- from _thread import start_new_thread
- import socket
- 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",
- ".ts" : "application/typescript",
- ".woff" : "font/woff",
- ".woff2" : "font/woff2",
- ".ttf" : "font/ttf",
- ".otf" : "font/otf",
- ".jpg" : "image/jpeg",
- ".jpeg" : "image/jpeg",
- ".png" : "image/png",
- ".gif" : "image/gif",
- ".svg" : "image/svg+xml",
- ".ico" : "image/x-icon"
- }
- _html_escape_chars = {
- "&" : "&",
- '"' : """,
- "'" : "'",
- ">" : ">",
- "<" : "<"
- }
- _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 _startThread(func, args=()) :
- try :
- start_new_thread(func, args)
- except :
- global _mwsrv_thread_id
- try :
- _mwsrv_thread_id += 1
- except :
- _mwsrv_thread_id = 0
- try :
- start_new_thread('MWSRV_THREAD_%s' % _mwsrv_thread_id, func, args)
- except :
- return False
- return True
- # ----------------------------------------------------------------------------
- @staticmethod
- def _unquote(s) :
- r = str(s).split('%')
- try :
- b = r[0].encode()
- for i in range(1, len(r)) :
- try :
- b += bytes([int(r[i][:2], 16)]) + r[i][2:].encode()
- except :
- b += b'%' + r[i].encode()
- return b.decode('UTF-8')
- except :
- return str(s)
- # ------------------------------------------------------------------------------
- @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.MaxWebSocketRecvLen = 1024
- self.WebSocketThreaded = True
- self.AcceptWebSocketCallback = None
- self.LetCacheStaticContentLevel = 2
- 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
- while True :
- try :
- client, cliAddr = self._server.accept()
- except Exception as ex :
- if ex.args and ex.args[0] == 113 :
- break
- continue
- self._client(self, client, cliAddr)
- self._started = False
- # ============================================================================
- # ===( Functions )============================================================
- # ============================================================================
- def Start(self, threaded=False) :
- if not self._started :
- self._server = socket.socket()
- self._server.setsockopt( socket.SOL_SOCKET,
- socket.SO_REUSEADDR,
- 1 )
- self._server.bind(self._srvAddr)
- self._server.listen(16)
- if threaded :
- MicroWebSrv._startThread(self._serverProcess)
- else :
- self._serverProcess()
- # ----------------------------------------------------------------------------
- def Stop(self) :
- if self._started :
- self._server.close()
- # ----------------------------------------------------------------------------
- def IsStarted(self) :
- return self._started
- # ----------------------------------------------------------------------------
- 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.replace('../', '/')
- 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
-
- if hasattr(socket, 'readline'): # MicroPython
- self._socketfile = self._socket
- else: # CPython
- self._socketfile = self._socket.makefile('rwb')
-
- 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 :
- try :
- if routeArgs is not None:
- routeHandler(self, response, routeArgs)
- else :
- routeHandler(self, response)
- except Exception as ex :
- print('MicroWebSrv handler exception:\r\n - In route %s %s\r\n - %s' % (self._method, self._resPath, ex))
- raise ex
- 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 :
- if self._microWebSrv.LetCacheStaticContentLevel > 0 :
- if self._microWebSrv.LetCacheStaticContentLevel > 1 and \
- 'if-modified-since' in self._headers :
- response.WriteResponseNotModified()
- else:
- headers = { 'Last-Modified' : 'Fri, 1 Jan 2018 23:42:00 GMT', \
- 'Cache-Control' : 'max-age=315360000' }
- response.WriteResponseFile(filepath, contentType, headers)
- else :
- 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 )
- return
- else :
- response.WriteResponseNotImplemented()
- else :
- response.WriteResponseBadRequest()
- except :
- response.WriteResponseInternalServerError()
- try :
- if self._socketfile is not self._socket:
- self._socketfile.close()
- self._socket.close()
- except :
- pass
- # ------------------------------------------------------------------------
- def _parseFirstLine(self, response) :
- try :
- elements = self._socketfile.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._socketfile.readline().decode().strip().split(':', 1)
- if len(elements) == 2 :
- self._headers[elements[0].strip().lower()] = elements[1].strip()
- elif len(elements) == 1 and len(elements[0]) == 0 :
- if self._method == 'POST' or self._method == 'PUT' :
- 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) :
- if size is None :
- size = self._contentLength
- if size > 0 :
- try :
- return self._socketfile.read(size)
- except :
- pass
- return b''
- # ------------------------------------------------------------------------
- def ReadRequestPostedFormData(self) :
- res = { }
- data = self.ReadRequestContent()
- if data :
- elements = data.decode().split('&')
- for s in elements :
- param = s.split('=', 1)
- if len(param) > 0 :
- value = MicroWebSrv._unquote_plus(param[1]) if len(param) > 1 else ''
- res[MicroWebSrv._unquote_plus(param[0])] = value
- return res
- # ------------------------------------------------------------------------
- def ReadRequestContentAsJSON(self) :
- data = self.ReadRequestContent()
- if data :
- try :
- return loads(data.decode())
- except :
- pass
- return None
-
- # ============================================================================
- # ===( Class Response )======================================================
- # ============================================================================
- class _response :
- # ------------------------------------------------------------------------
- def __init__(self, client) :
- self._client = client
- # ------------------------------------------------------------------------
- def _write(self, data, strEncoding='ISO-8859-1') :
- if data :
- if type(data) == str :
- data = data.encode(strEncoding)
- data = memoryview(data)
- while data :
- n = self._client._socketfile.write(data)
- if n is None :
- return False
- data = data[n:]
- return True
- return False
- # ------------------------------------------------------------------------
- def _writeFirstLine(self, code) :
- reason = self._responseCodes.get(code, ('Unknown reason', ))[0]
- return self._write("HTTP/1.1 %s %s\r\n" % (code, reason))
- # ------------------------------------------------------------------------
- def _writeHeader(self, name, value) :
- return 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) :
- return 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()
- if self._client._socketfile is not self._client._socket :
- self._client._socketfile.flush() # CPython needs flush to continue protocol
- # ------------------------------------------------------------------------
- def WriteResponse(self, code, headers, contentType, contentCharset, content) :
- try :
- if content :
- if type(content) == str :
- content = content.encode(contentCharset)
- contentLength = len(content)
- else :
- contentLength = 0
- self._writeBeforeContent(code, headers, contentType, contentCharset, contentLength)
- if content :
- return self._write(content)
- return True
- except :
- return False
- # ------------------------------------------------------------------------
- def WriteResponsePyHTMLFile(self, filepath, headers=None, vars=None) :
- if 'MicroWebTemplate' in globals() :
- with open(filepath, 'r') as file :
- code = file.read()
- mWebTmpl = MicroWebTemplate(code, escapeStrFunc=MicroWebSrv.HTMLEscape, filepath=filepath)
- try :
- tmplResult = mWebTmpl.Execute(None, vars)
- 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)
- try :
- buf = bytearray(1024)
- while size > 0 :
- x = file.readinto(buf)
- if x < len(buf) :
- buf = memoryview(buf)[:x]
- if not self._write(buf) :
- return False
- size -= x
- return True
- except :
- 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 WriteResponseNotModified(self) :
- return self.WriteResponseError(304)
- # ------------------------------------------------------------------------
- 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)
- # ------------------------------------------------------------------------
- def FlashMessage(self, messageText, messageStyle='') :
- if 'MicroWebTemplate' in globals() :
- MicroWebTemplate.MESSAGE_TEXT = messageText
- MicroWebTemplate.MESSAGE_STYLE = messageStyle
- # ------------------------------------------------------------------------
- _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.'),
- }
- # ============================================================================
- # ============================================================================
- # ============================================================================
|