microWebSrv.py 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880
  1. """
  2. The MIT License (MIT)
  3. Copyright © 2018 Jean-Christophe Bos & HC² (www.hc2.fr)
  4. """
  5. from json import loads, dumps
  6. from os import stat
  7. from _thread import start_new_thread
  8. import socket
  9. import gc
  10. import re
  11. try :
  12. from microWebTemplate import MicroWebTemplate
  13. except :
  14. pass
  15. try :
  16. from microWebSocket import MicroWebSocket
  17. except :
  18. pass
  19. class MicroWebSrvRoute :
  20. def __init__(self, route, method, func, routeArgNames, routeRegex) :
  21. self.route = route
  22. self.method = method
  23. self.func = func
  24. self.routeArgNames = routeArgNames
  25. self.routeRegex = routeRegex
  26. class MicroWebSrv :
  27. # ============================================================================
  28. # ===( Constants )============================================================
  29. # ============================================================================
  30. _indexPages = [
  31. "index.pyhtml",
  32. "index.html",
  33. "index.htm",
  34. "default.pyhtml",
  35. "default.html",
  36. "default.htm"
  37. ]
  38. _mimeTypes = {
  39. ".txt" : "text/plain",
  40. ".htm" : "text/html",
  41. ".html" : "text/html",
  42. ".css" : "text/css",
  43. ".csv" : "text/csv",
  44. ".js" : "application/javascript",
  45. ".xml" : "application/xml",
  46. ".xhtml" : "application/xhtml+xml",
  47. ".json" : "application/json",
  48. ".zip" : "application/zip",
  49. ".pdf" : "application/pdf",
  50. ".ts" : "application/typescript",
  51. ".woff" : "font/woff",
  52. ".woff2" : "font/woff2",
  53. ".ttf" : "font/ttf",
  54. ".otf" : "font/otf",
  55. ".jpg" : "image/jpeg",
  56. ".jpeg" : "image/jpeg",
  57. ".png" : "image/png",
  58. ".gif" : "image/gif",
  59. ".svg" : "image/svg+xml",
  60. ".ico" : "image/x-icon"
  61. }
  62. _html_escape_chars = {
  63. "&" : "&",
  64. '"' : """,
  65. "'" : "'",
  66. ">" : ">",
  67. "<" : "&lt;"
  68. }
  69. _pyhtmlPagesExt = '.pyhtml'
  70. # ============================================================================
  71. # ===( Class globals )=======================================================
  72. # ============================================================================
  73. _docoratedRouteHandlers = []
  74. # ============================================================================
  75. # ===( Utils )===============================================================
  76. # ============================================================================
  77. @classmethod
  78. def route(cls, url, method='GET'):
  79. """ Adds a route handler function to the routing list """
  80. def route_decorator(func):
  81. item = (url, method, func)
  82. cls._docoratedRouteHandlers.append(item)
  83. return func
  84. return route_decorator
  85. # ----------------------------------------------------------------------------
  86. @staticmethod
  87. def HTMLEscape(s) :
  88. return ''.join(MicroWebSrv._html_escape_chars.get(c, c) for c in s)
  89. # ----------------------------------------------------------------------------
  90. @staticmethod
  91. def _startThread(func, args=()) :
  92. try :
  93. start_new_thread(func, args)
  94. except :
  95. global _mwsrv_thread_id
  96. try :
  97. _mwsrv_thread_id += 1
  98. except :
  99. _mwsrv_thread_id = 0
  100. try :
  101. start_new_thread('MWSRV_THREAD_%s' % _mwsrv_thread_id, func, args)
  102. except :
  103. return False
  104. return True
  105. # ----------------------------------------------------------------------------
  106. @staticmethod
  107. def _unquote(s) :
  108. r = str(s).split('%')
  109. try :
  110. b = r[0].encode()
  111. for i in range(1, len(r)) :
  112. try :
  113. b += bytes([int(r[i][:2], 16)]) + r[i][2:].encode()
  114. except :
  115. b += b'%' + r[i].encode()
  116. return b.decode('UTF-8')
  117. except :
  118. return str(s)
  119. # ------------------------------------------------------------------------------
  120. @staticmethod
  121. def _unquote_plus(s) :
  122. return MicroWebSrv._unquote(s.replace('+', ' '))
  123. # ------------------------------------------------------------------------------
  124. @staticmethod
  125. def _fileExists(path) :
  126. try :
  127. stat(path)
  128. return True
  129. except :
  130. return False
  131. # ----------------------------------------------------------------------------
  132. @staticmethod
  133. def _isPyHTMLFile(filename) :
  134. return filename.lower().endswith(MicroWebSrv._pyhtmlPagesExt)
  135. # ============================================================================
  136. # ===( Constructor )==========================================================
  137. # ============================================================================
  138. def __init__( self,
  139. routeHandlers = [],
  140. port = 80,
  141. bindIP = '0.0.0.0',
  142. webPath = "/flash/www" ) :
  143. self._srvAddr = (bindIP, port)
  144. self._webPath = webPath
  145. self._notFoundUrl = None
  146. self._started = False
  147. self.MaxWebSocketRecvLen = 1024
  148. self.WebSocketThreaded = True
  149. self.AcceptWebSocketCallback = None
  150. self.LetCacheStaticContentLevel = 2
  151. self._routeHandlers = []
  152. routeHandlers += self._docoratedRouteHandlers
  153. for route, method, func in routeHandlers :
  154. routeParts = route.split('/')
  155. # -> ['', 'users', '<uID>', 'addresses', '<addrID>', 'test', '<anotherID>']
  156. routeArgNames = []
  157. routeRegex = ''
  158. for s in routeParts :
  159. if s.startswith('<') and s.endswith('>') :
  160. routeArgNames.append(s[1:-1])
  161. routeRegex += '/(\\w*)'
  162. elif s :
  163. routeRegex += '/' + s
  164. routeRegex += '$'
  165. # -> '/users/(\w*)/addresses/(\w*)/test/(\w*)$'
  166. routeRegex = re.compile(routeRegex)
  167. self._routeHandlers.append(MicroWebSrvRoute(route, method, func, routeArgNames, routeRegex))
  168. # ============================================================================
  169. # ===( Server Process )=======================================================
  170. # ============================================================================
  171. def _serverProcess(self) :
  172. self._started = True
  173. while True :
  174. try :
  175. client, cliAddr = self._server.accept()
  176. except Exception as ex :
  177. if ex.args and ex.args[0] == 113 :
  178. break
  179. continue
  180. self._client(self, client, cliAddr)
  181. self._started = False
  182. # ============================================================================
  183. # ===( Functions )============================================================
  184. # ============================================================================
  185. def Start(self, threaded=False) :
  186. if not self._started :
  187. self._server = socket.socket()
  188. self._server.setsockopt( socket.SOL_SOCKET,
  189. socket.SO_REUSEADDR,
  190. 1 )
  191. self._server.bind(self._srvAddr)
  192. self._server.listen(16)
  193. if threaded :
  194. MicroWebSrv._startThread(self._serverProcess)
  195. else :
  196. self._serverProcess()
  197. # ----------------------------------------------------------------------------
  198. def Stop(self) :
  199. if self._started :
  200. self._server.close()
  201. # ----------------------------------------------------------------------------
  202. def IsStarted(self) :
  203. return self._started
  204. # ----------------------------------------------------------------------------
  205. def SetNotFoundPageUrl(self, url=None) :
  206. self._notFoundUrl = url
  207. # ----------------------------------------------------------------------------
  208. def GetMimeTypeFromFilename(self, filename) :
  209. filename = filename.lower()
  210. for ext in self._mimeTypes :
  211. if filename.endswith(ext) :
  212. return self._mimeTypes[ext]
  213. return None
  214. # ----------------------------------------------------------------------------
  215. def GetRouteHandler(self, resUrl, method) :
  216. if self._routeHandlers :
  217. #resUrl = resUrl.upper()
  218. if resUrl.endswith('/') :
  219. resUrl = resUrl[:-1]
  220. method = method.upper()
  221. for rh in self._routeHandlers :
  222. if rh.method == method :
  223. m = rh.routeRegex.match(resUrl)
  224. if m : # found matching route?
  225. if rh.routeArgNames :
  226. routeArgs = {}
  227. for i, name in enumerate(rh.routeArgNames) :
  228. value = m.group(i+1)
  229. try :
  230. value = int(value)
  231. except :
  232. pass
  233. routeArgs[name] = value
  234. return (rh.func, routeArgs)
  235. else :
  236. return (rh.func, None)
  237. return (None, None)
  238. # ----------------------------------------------------------------------------
  239. def _physPathFromURLPath(self, urlPath) :
  240. if urlPath == '/' :
  241. for idxPage in self._indexPages :
  242. physPath = self._webPath + '/' + idxPage
  243. if MicroWebSrv._fileExists(physPath) :
  244. return physPath
  245. else :
  246. physPath = self._webPath + urlPath.replace('../', '/')
  247. if MicroWebSrv._fileExists(physPath) :
  248. return physPath
  249. return None
  250. # ============================================================================
  251. # ===( Class Client )========================================================
  252. # ============================================================================
  253. class _client :
  254. # ------------------------------------------------------------------------
  255. def __init__(self, microWebSrv, socket, addr) :
  256. socket.settimeout(2)
  257. self._microWebSrv = microWebSrv
  258. self._socket = socket
  259. self._addr = addr
  260. self._method = None
  261. self._path = None
  262. self._httpVer = None
  263. self._resPath = "/"
  264. self._queryString = ""
  265. self._queryParams = { }
  266. self._headers = { }
  267. self._contentType = None
  268. self._contentLength = 0
  269. if hasattr(socket, 'readline'): # MicroPython
  270. self._socketfile = self._socket
  271. else: # CPython
  272. self._socketfile = self._socket.makefile('rwb')
  273. self._processRequest()
  274. # ------------------------------------------------------------------------
  275. def _processRequest(self) :
  276. try :
  277. response = MicroWebSrv._response(self)
  278. if self._parseFirstLine(response) :
  279. if self._parseHeader(response) :
  280. upg = self._getConnUpgrade()
  281. if not upg :
  282. routeHandler, routeArgs = self._microWebSrv.GetRouteHandler(self._resPath, self._method)
  283. if routeHandler :
  284. try :
  285. if routeArgs is not None:
  286. routeHandler(self, response, routeArgs)
  287. else :
  288. routeHandler(self, response)
  289. except Exception as ex :
  290. print('MicroWebSrv handler exception:\r\n - In route %s %s\r\n - %s' % (self._method, self._resPath, ex))
  291. raise ex
  292. elif self._method.upper() == "GET" :
  293. filepath = self._microWebSrv._physPathFromURLPath(self._resPath)
  294. if filepath :
  295. if MicroWebSrv._isPyHTMLFile(filepath) :
  296. response.WriteResponsePyHTMLFile(filepath)
  297. else :
  298. contentType = self._microWebSrv.GetMimeTypeFromFilename(filepath)
  299. if contentType :
  300. if self._microWebSrv.LetCacheStaticContentLevel > 0 :
  301. if self._microWebSrv.LetCacheStaticContentLevel > 1 and \
  302. 'if-modified-since' in self._headers :
  303. response.WriteResponseNotModified()
  304. else:
  305. headers = { 'Last-Modified' : 'Fri, 1 Jan 2018 23:42:00 GMT', \
  306. 'Cache-Control' : 'max-age=315360000' }
  307. response.WriteResponseFile(filepath, contentType, headers)
  308. else :
  309. response.WriteResponseFile(filepath, contentType)
  310. else :
  311. response.WriteResponseForbidden()
  312. else :
  313. response.WriteResponseNotFound()
  314. else :
  315. response.WriteResponseMethodNotAllowed()
  316. elif upg == 'websocket' and 'MicroWebSocket' in globals() \
  317. and self._microWebSrv.AcceptWebSocketCallback :
  318. MicroWebSocket( socket = self._socket,
  319. httpClient = self,
  320. httpResponse = response,
  321. maxRecvLen = self._microWebSrv.MaxWebSocketRecvLen,
  322. threaded = self._microWebSrv.WebSocketThreaded,
  323. acceptCallback = self._microWebSrv.AcceptWebSocketCallback )
  324. return
  325. else :
  326. response.WriteResponseNotImplemented()
  327. else :
  328. response.WriteResponseBadRequest()
  329. except :
  330. response.WriteResponseInternalServerError()
  331. try :
  332. if self._socketfile is not self._socket:
  333. self._socketfile.close()
  334. self._socket.close()
  335. except :
  336. pass
  337. # ------------------------------------------------------------------------
  338. def _parseFirstLine(self, response) :
  339. try :
  340. elements = self._socketfile.readline().decode().strip().split()
  341. if len(elements) == 3 :
  342. self._method = elements[0].upper()
  343. self._path = elements[1]
  344. self._httpVer = elements[2].upper()
  345. elements = self._path.split('?', 1)
  346. if len(elements) > 0 :
  347. self._resPath = MicroWebSrv._unquote_plus(elements[0])
  348. if len(elements) > 1 :
  349. self._queryString = elements[1]
  350. elements = self._queryString.split('&')
  351. for s in elements :
  352. param = s.split('=', 1)
  353. if len(param) > 0 :
  354. value = MicroWebSrv._unquote(param[1]) if len(param) > 1 else ''
  355. self._queryParams[MicroWebSrv._unquote(param[0])] = value
  356. return True
  357. except :
  358. pass
  359. return False
  360. # ------------------------------------------------------------------------
  361. def _parseHeader(self, response) :
  362. while True :
  363. elements = self._socketfile.readline().decode().strip().split(':', 1)
  364. if len(elements) == 2 :
  365. self._headers[elements[0].strip().lower()] = elements[1].strip()
  366. elif len(elements) == 1 and len(elements[0]) == 0 :
  367. if self._method == 'POST' or self._method == 'PUT' :
  368. self._contentType = self._headers.get("content-type", None)
  369. self._contentLength = int(self._headers.get("content-length", 0))
  370. return True
  371. else :
  372. return False
  373. # ------------------------------------------------------------------------
  374. def _getConnUpgrade(self) :
  375. if 'upgrade' in self._headers.get('connection', '').lower() :
  376. return self._headers.get('upgrade', '').lower()
  377. return None
  378. # ------------------------------------------------------------------------
  379. def GetServer(self) :
  380. return self._microWebSrv
  381. # ------------------------------------------------------------------------
  382. def GetAddr(self) :
  383. return self._addr
  384. # ------------------------------------------------------------------------
  385. def GetIPAddr(self) :
  386. return self._addr[0]
  387. # ------------------------------------------------------------------------
  388. def GetPort(self) :
  389. return self._addr[1]
  390. # ------------------------------------------------------------------------
  391. def GetRequestMethod(self) :
  392. return self._method
  393. # ------------------------------------------------------------------------
  394. def GetRequestTotalPath(self) :
  395. return self._path
  396. # ------------------------------------------------------------------------
  397. def GetRequestPath(self) :
  398. return self._resPath
  399. # ------------------------------------------------------------------------
  400. def GetRequestQueryString(self) :
  401. return self._queryString
  402. # ------------------------------------------------------------------------
  403. def GetRequestQueryParams(self) :
  404. return self._queryParams
  405. # ------------------------------------------------------------------------
  406. def GetRequestHeaders(self) :
  407. return self._headers
  408. # ------------------------------------------------------------------------
  409. def GetRequestContentType(self) :
  410. return self._contentType
  411. # ------------------------------------------------------------------------
  412. def GetRequestContentLength(self) :
  413. return self._contentLength
  414. # ------------------------------------------------------------------------
  415. def ReadRequestContent(self, size=None) :
  416. if size is None :
  417. size = self._contentLength
  418. if size > 0 :
  419. try :
  420. return self._socketfile.read(size)
  421. except :
  422. pass
  423. return b''
  424. # ------------------------------------------------------------------------
  425. def ReadRequestPostedFormData(self) :
  426. res = { }
  427. data = self.ReadRequestContent()
  428. if data :
  429. elements = data.decode().split('&')
  430. for s in elements :
  431. param = s.split('=', 1)
  432. if len(param) > 0 :
  433. value = MicroWebSrv._unquote_plus(param[1]) if len(param) > 1 else ''
  434. res[MicroWebSrv._unquote_plus(param[0])] = value
  435. return res
  436. # ------------------------------------------------------------------------
  437. def ReadRequestContentAsJSON(self) :
  438. data = self.ReadRequestContent()
  439. if data :
  440. try :
  441. return loads(data.decode())
  442. except :
  443. pass
  444. return None
  445. # ============================================================================
  446. # ===( Class Response )======================================================
  447. # ============================================================================
  448. class _response :
  449. # ------------------------------------------------------------------------
  450. def __init__(self, client) :
  451. self._client = client
  452. # ------------------------------------------------------------------------
  453. def _write(self, data, strEncoding='ISO-8859-1') :
  454. if data :
  455. if type(data) == str :
  456. data = data.encode(strEncoding)
  457. data = memoryview(data)
  458. while data :
  459. n = self._client._socketfile.write(data)
  460. if n is None :
  461. return False
  462. data = data[n:]
  463. return True
  464. return False
  465. # ------------------------------------------------------------------------
  466. def _writeFirstLine(self, code) :
  467. reason = self._responseCodes.get(code, ('Unknown reason', ))[0]
  468. return self._write("HTTP/1.1 %s %s\r\n" % (code, reason))
  469. # ------------------------------------------------------------------------
  470. def _writeHeader(self, name, value) :
  471. return self._write("%s: %s\r\n" % (name, value))
  472. # ------------------------------------------------------------------------
  473. def _writeContentTypeHeader(self, contentType, charset=None) :
  474. if contentType :
  475. ct = contentType \
  476. + (("; charset=%s" % charset) if charset else "")
  477. else :
  478. ct = "application/octet-stream"
  479. self._writeHeader("Content-Type", ct)
  480. # ------------------------------------------------------------------------
  481. def _writeServerHeader(self) :
  482. self._writeHeader("Server", "MicroWebSrv by JC`zic")
  483. # ------------------------------------------------------------------------
  484. def _writeEndHeader(self) :
  485. return self._write("\r\n")
  486. # ------------------------------------------------------------------------
  487. def _writeBeforeContent(self, code, headers, contentType, contentCharset, contentLength) :
  488. self._writeFirstLine(code)
  489. if isinstance(headers, dict) :
  490. for header in headers :
  491. self._writeHeader(header, headers[header])
  492. if contentLength > 0 :
  493. self._writeContentTypeHeader(contentType, contentCharset)
  494. self._writeHeader("Content-Length", contentLength)
  495. self._writeServerHeader()
  496. self._writeHeader("Connection", "close")
  497. self._writeEndHeader()
  498. # ------------------------------------------------------------------------
  499. def WriteSwitchProto(self, upgrade, headers=None) :
  500. self._writeFirstLine(101)
  501. self._writeHeader("Connection", "Upgrade")
  502. self._writeHeader("Upgrade", upgrade)
  503. if isinstance(headers, dict) :
  504. for header in headers :
  505. self._writeHeader(header, headers[header])
  506. self._writeServerHeader()
  507. self._writeEndHeader()
  508. if self._client._socketfile is not self._client._socket :
  509. self._client._socketfile.flush() # CPython needs flush to continue protocol
  510. # ------------------------------------------------------------------------
  511. def WriteResponse(self, code, headers, contentType, contentCharset, content) :
  512. try :
  513. if content :
  514. if type(content) == str :
  515. content = content.encode(contentCharset)
  516. contentLength = len(content)
  517. else :
  518. contentLength = 0
  519. self._writeBeforeContent(code, headers, contentType, contentCharset, contentLength)
  520. if content :
  521. return self._write(content)
  522. return True
  523. except :
  524. return False
  525. # ------------------------------------------------------------------------
  526. def WriteResponsePyHTMLFile(self, filepath, headers=None, vars=None) :
  527. if 'MicroWebTemplate' in globals() :
  528. with open(filepath, 'r') as file :
  529. code = file.read()
  530. mWebTmpl = MicroWebTemplate(code, escapeStrFunc=MicroWebSrv.HTMLEscape, filepath=filepath)
  531. try :
  532. tmplResult = mWebTmpl.Execute(None, vars)
  533. return self.WriteResponse(200, headers, "text/html", "UTF-8", tmplResult)
  534. except Exception as ex :
  535. return self.WriteResponse( 500,
  536. None,
  537. "text/html",
  538. "UTF-8",
  539. self._execErrCtnTmpl % {
  540. 'module' : 'PyHTML',
  541. 'message' : str(ex)
  542. } )
  543. return self.WriteResponseNotImplemented()
  544. # ------------------------------------------------------------------------
  545. def WriteResponseFile(self, filepath, contentType=None, headers=None) :
  546. try :
  547. size = stat(filepath)[6]
  548. if size > 0 :
  549. with open(filepath, 'rb') as file :
  550. self._writeBeforeContent(200, headers, contentType, None, size)
  551. try :
  552. buf = bytearray(1024)
  553. while size > 0 :
  554. x = file.readinto(buf)
  555. if x < len(buf) :
  556. buf = memoryview(buf)[:x]
  557. if not self._write(buf) :
  558. return False
  559. size -= x
  560. return True
  561. except :
  562. self.WriteResponseInternalServerError()
  563. return False
  564. except :
  565. pass
  566. self.WriteResponseNotFound()
  567. return False
  568. # ------------------------------------------------------------------------
  569. def WriteResponseFileAttachment(self, filepath, attachmentName, headers=None) :
  570. if not isinstance(headers, dict) :
  571. headers = { }
  572. headers["Content-Disposition"] = "attachment; filename=\"%s\"" % attachmentName
  573. return self.WriteResponseFile(filepath, None, headers)
  574. # ------------------------------------------------------------------------
  575. def WriteResponseOk(self, headers=None, contentType=None, contentCharset=None, content=None) :
  576. return self.WriteResponse(200, headers, contentType, contentCharset, content)
  577. # ------------------------------------------------------------------------
  578. def WriteResponseJSONOk(self, obj=None, headers=None) :
  579. return self.WriteResponse(200, headers, "application/json", "UTF-8", dumps(obj))
  580. # ------------------------------------------------------------------------
  581. def WriteResponseRedirect(self, location) :
  582. headers = { "Location" : location }
  583. return self.WriteResponse(302, headers, None, None, None)
  584. # ------------------------------------------------------------------------
  585. def WriteResponseError(self, code) :
  586. responseCode = self._responseCodes.get(code, ('Unknown reason', ''))
  587. return self.WriteResponse( code,
  588. None,
  589. "text/html",
  590. "UTF-8",
  591. self._errCtnTmpl % {
  592. 'code' : code,
  593. 'reason' : responseCode[0],
  594. 'message' : responseCode[1]
  595. } )
  596. # ------------------------------------------------------------------------
  597. def WriteResponseJSONError(self, code, obj=None) :
  598. return self.WriteResponse( code,
  599. None,
  600. "application/json",
  601. "UTF-8",
  602. dumps(obj if obj else { }) )
  603. # ------------------------------------------------------------------------
  604. def WriteResponseNotModified(self) :
  605. return self.WriteResponseError(304)
  606. # ------------------------------------------------------------------------
  607. def WriteResponseBadRequest(self) :
  608. return self.WriteResponseError(400)
  609. # ------------------------------------------------------------------------
  610. def WriteResponseForbidden(self) :
  611. return self.WriteResponseError(403)
  612. # ------------------------------------------------------------------------
  613. def WriteResponseNotFound(self) :
  614. if self._client._microWebSrv._notFoundUrl :
  615. self.WriteResponseRedirect(self._client._microWebSrv._notFoundUrl)
  616. else :
  617. return self.WriteResponseError(404)
  618. # ------------------------------------------------------------------------
  619. def WriteResponseMethodNotAllowed(self) :
  620. return self.WriteResponseError(405)
  621. # ------------------------------------------------------------------------
  622. def WriteResponseInternalServerError(self) :
  623. return self.WriteResponseError(500)
  624. # ------------------------------------------------------------------------
  625. def WriteResponseNotImplemented(self) :
  626. return self.WriteResponseError(501)
  627. # ------------------------------------------------------------------------
  628. def FlashMessage(self, messageText, messageStyle='') :
  629. if 'MicroWebTemplate' in globals() :
  630. MicroWebTemplate.MESSAGE_TEXT = messageText
  631. MicroWebTemplate.MESSAGE_STYLE = messageStyle
  632. # ------------------------------------------------------------------------
  633. _errCtnTmpl = """\
  634. <html>
  635. <head>
  636. <title>Error</title>
  637. </head>
  638. <body>
  639. <h1>%(code)d %(reason)s</h1>
  640. %(message)s
  641. </body>
  642. </html>
  643. """
  644. # ------------------------------------------------------------------------
  645. _execErrCtnTmpl = """\
  646. <html>
  647. <head>
  648. <title>Page execution error</title>
  649. </head>
  650. <body>
  651. <h1>%(module)s page execution error</h1>
  652. %(message)s
  653. </body>
  654. </html>
  655. """
  656. # ------------------------------------------------------------------------
  657. _responseCodes = {
  658. 100: ('Continue', 'Request received, please continue'),
  659. 101: ('Switching Protocols',
  660. 'Switching to new protocol; obey Upgrade header'),
  661. 200: ('OK', 'Request fulfilled, document follows'),
  662. 201: ('Created', 'Document created, URL follows'),
  663. 202: ('Accepted',
  664. 'Request accepted, processing continues off-line'),
  665. 203: ('Non-Authoritative Information', 'Request fulfilled from cache'),
  666. 204: ('No Content', 'Request fulfilled, nothing follows'),
  667. 205: ('Reset Content', 'Clear input form for further input.'),
  668. 206: ('Partial Content', 'Partial content follows.'),
  669. 300: ('Multiple Choices',
  670. 'Object has several resources -- see URI list'),
  671. 301: ('Moved Permanently', 'Object moved permanently -- see URI list'),
  672. 302: ('Found', 'Object moved temporarily -- see URI list'),
  673. 303: ('See Other', 'Object moved -- see Method and URL list'),
  674. 304: ('Not Modified',
  675. 'Document has not changed since given time'),
  676. 305: ('Use Proxy',
  677. 'You must use proxy specified in Location to access this '
  678. 'resource.'),
  679. 307: ('Temporary Redirect',
  680. 'Object moved temporarily -- see URI list'),
  681. 400: ('Bad Request',
  682. 'Bad request syntax or unsupported method'),
  683. 401: ('Unauthorized',
  684. 'No permission -- see authorization schemes'),
  685. 402: ('Payment Required',
  686. 'No payment -- see charging schemes'),
  687. 403: ('Forbidden',
  688. 'Request forbidden -- authorization will not help'),
  689. 404: ('Not Found', 'Nothing matches the given URI'),
  690. 405: ('Method Not Allowed',
  691. 'Specified method is invalid for this resource.'),
  692. 406: ('Not Acceptable', 'URI not available in preferred format.'),
  693. 407: ('Proxy Authentication Required', 'You must authenticate with '
  694. 'this proxy before proceeding.'),
  695. 408: ('Request Timeout', 'Request timed out; try again later.'),
  696. 409: ('Conflict', 'Request conflict.'),
  697. 410: ('Gone',
  698. 'URI no longer exists and has been permanently removed.'),
  699. 411: ('Length Required', 'Client must specify Content-Length.'),
  700. 412: ('Precondition Failed', 'Precondition in headers is false.'),
  701. 413: ('Request Entity Too Large', 'Entity is too large.'),
  702. 414: ('Request-URI Too Long', 'URI is too long.'),
  703. 415: ('Unsupported Media Type', 'Entity body in unsupported format.'),
  704. 416: ('Requested Range Not Satisfiable',
  705. 'Cannot satisfy request range.'),
  706. 417: ('Expectation Failed',
  707. 'Expect condition could not be satisfied.'),
  708. 500: ('Internal Server Error', 'Server got itself in trouble'),
  709. 501: ('Not Implemented',
  710. 'Server does not support this operation'),
  711. 502: ('Bad Gateway', 'Invalid responses from another server/proxy.'),
  712. 503: ('Service Unavailable',
  713. 'The server cannot process the request due to a high load'),
  714. 504: ('Gateway Timeout',
  715. 'The gateway server did not receive a timely response'),
  716. 505: ('HTTP Version Not Supported', 'Cannot fulfill request.'),
  717. }
  718. # ============================================================================
  719. # ============================================================================
  720. # ============================================================================