jczic / MicroWebSrv

A micro HTTP Web server that supports WebSockets, html/python language templating and routing handlers, for MicroPython (used on Pycom modules & ESP32)
https://github.com/jczic/MicroWebSrv
MIT License
652 stars 116 forks source link
captive-portal esp32 hc2 http iot language-templating micropython microwebsrv pyboard pycom pyhtml routing support-websockets template-engine templating webserver websocket websocket-server websockets wipy

Also check out the new ESP32 MPY-Jama free IDE and the latest MicroWebSrv2 below:

New microWebSrv2                   New microWebSrv2



MicroWebSrv is a micro HTTP Web server that supports WebSockets, html/python language templating and routing handlers, for MicroPython (principally used on ESP32 and Pycom modules. Now supports all variants of Pyboard D-series from the makers of Micropython)

HC²

Very easy to integrate and very light with 3 files only :

Mini tuto in video by rdagger68 :

Simple but effective :

Using microWebSrv main class :

Name Function
Constructor mws = MicroWebSrv(routeHandlers=None, port=80, bindIP='0.0.0.0', webPath="/flash/www")
Start Web server mws.Start(threaded=True)
Stop Web server mws.Stop()
Check if Web server is running mws.IsStarted()
Set URL location for not found page mws.SetNotFoundPageUrl(url=None)
Get mime type from file extention mws.GetMimeTypeFromFilename(filename)
Get handler function from route (routeHandler, routeArgs) = mws.GetRouteHandler(resUrl, method)
Callback function to enable and accept WebSockets mws.AcceptWebSocketCallback = _acptWS _acptWS(webSocket, httpClient) { }
Maximum length of memory allocated to receive WebSockets data (1024 by default) mws.MaxWebSocketRecvLen
New thread used for each WebSocket connection (True by default) mws.WebSocketThreaded
Escape string to HTML usage MicroWebSrv.HTMLEscape(s)

Basic example :

from microWebSrv import MicroWebSrv
mws = MicroWebSrv()      # TCP port 80 and files in /flash/www
mws.Start(threaded=True) # Starts server in a new thread

Using as captive portal :

# To intercept all not found queries and redirect it,
mws.SetNotFoundPageUrl("http://my-device.wifi")

Using route handlers example :


routeHandlers = [
  ( "relative_url_route_1", "METHOD", handlerFunc_1 ),
  ( "relative_url_route_2", "METHOD", handlerFunc_2 ),
  ( ... )
]
def handlerFunc_1(httpClient, httpResponse, routeArgs=None) :
  print("In HTTP handler 1")

def handlerFunc_2(httpClient, httpResponse, routeArgs=None) :
  print("In HTTP handler 2")

Using directly route handlers decorators example :

@MicroWebSrv.route('/get-test')
def handlerFuncGet(httpClient, httpResponse) :
  print("In GET-TEST HTTP")

@MicroWebSrv.route('/post-test', 'POST')
def handlerFuncPost(httpClient, httpResponse) :
  print("In POST-TEST HTTP")

Using variable routes example :

routeHandlers = [
  ( "/edit/<testid>/<testpath>", "GET", handlerFuncEdit ),
  ( ... )
]
def handlerFuncEdit(httpClient, httpResponse, routeArgs) :
  print("In EDIT HTTP variable route :")
  print(" - testid   = %s" % routeArgs['testid'])
  print(" - testpath = %s" % routeArgs['testpath'])

Or direclty with route handler decorator :

@MicroWebSrv.route('/edit/<testid>/<testpath>')
def handlerFuncEdit(httpClient, httpResponse, routeArgs) :
  print("In EDIT HTTP variable route :")
  print(" - testid   = %s" % routeArgs['testid'])
  print(" - testpath = %s" % routeArgs['testpath'])

Using httpClient class in a route handler function :

Name Function
Get MicroWebSrv class httpClient.GetServer()
Get client address as tuple httpClient.GetAddr()
Get client IP address httpClient.GetIPAddr()
Get client TCP port httpClient.GetPort()
Get client request method httpClient.GetRequestMethod()
Get client request total path httpClient.GetRequestTotalPath()
Get client request ressource path httpClient.GetRequestPath()
Get client request query string httpClient.GetRequestQueryString()
Get client request query parameters as list httpClient.GetRequestQueryParams()
Get client request headers as list httpClient.GetRequestHeaders()
Get client request content type httpClient.GetRequestContentType()
Get client request content length httpClient.GetRequestContentLength()
Get client request content httpClient.ReadRequestContent(size=None)
Get client request form data as list httpClient.ReadRequestPostedFormData()
Get client request as JSON object httpClient.ReadRequestContentAsJSON()

Using httpResponse class in a route handler function :

Name Function
Write switching protocols response httpResponse.WriteSwitchProto(upgrade, headers=None)
Write generic response httpResponse.WriteResponse(code, headers, contentType, contentCharset, content)
Write PyHTML rendered response page httpResponse.WriteResponsePyHTMLFile(filepath, headers=None, vars=None)
Write file directly as response httpResponse.WriteResponseFile(filepath, contentType=None, headers=None)
Write attached file as response httpResponse.WriteResponseFileAttachment(filepath, attachmentName, headers=None)
Write OK response httpResponse.WriteResponseOk(headers=None, contentType=None, contentCharset=None, content=None)
Write JSON object as OK response httpResponse.WriteResponseJSONOk(obj=None, headers=None)
Write redirect response httpResponse.WriteResponseRedirect(location)
Write error response httpResponse.WriteResponseError(code)
Write JSON object as error response httpResponse.WriteResponseJSONError(code, obj=None)
Write bad request response httpResponse.WriteResponseBadRequest()
Write forbidden response httpResponse.WriteResponseForbidden()
Write not found response httpResponse.WriteResponseNotFound()
Write method not allowed response httpResponse.WriteResponseMethodNotAllowed()
Write internal server error response httpResponse.WriteResponseInternalServerError()
Write not implemented response httpResponse.WriteResponseNotImplemented()

Using route handler function example :

def _httpHandlerTestPost(httpClient, httpResponse) :
  formData  = httpClient.ReadRequestPostedFormData()
  firstname = formData["firstname"]
  lastname  = formData["lastname"]
  content   = """\
  <!DOCTYPE html>
  <html>
    <head>
      <meta charset="UTF-8" />
      <title>TEST POST</title>
    </head>
    <body>
      <h1>TEST POST</h1>
      Firstname = %s<br />
      Lastname = %s<br />
    </body>
  </html>
  """ % ( MicroWebSrv.HTMLEscape(firstname),
          MicroWebSrv.HTMLEscape(lastname) )
  httpResponse.WriteResponseOk( headers         = None,
                                contentType     = "text/html",
                                contentCharset  = "UTF-8",
                                content         = content )

Known mime types (content types) :

File extension Mime type
.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

Default index pages order (for http://hostname/) :

Filename
index.pyhtml
index.html
index.htm
default.pyhtml
default.html
default.htm

Using optional module microWebSocket to connect WebSockets :

Enable and accept WebSockets :

from microWebSrv import MicroWebSrv
mws = MicroWebSrv()                                    # TCP port 80 and files in /flash/www
mws.MaxWebSocketRecvLen     = 256                      # Default is set to 1024
mws.WebSocketThreaded       = False                    # WebSockets without new threads
mws.AcceptWebSocketCallback = _acceptWebSocketCallback # Function to receive WebSockets
mws.Start(threaded=True)                               # Starts server in a new thread
Name Function
Callback function to receive text message ws.RecvTextCallback = func(webSocket, msg)
Callback function to receive binary data ws.RecvBinaryCallback = func(webSocket, data)
Callback function when connection was closed ws.ClosedCallback = func(webSocket)
Send a text message ws.SendText(msg)
Send a binary message ws.SendBinary(data)
Check connection state ws.IsClosed()
Close the connection ws.Close()

Basic example of callback functions :

def _acceptWebSocketCallback(webSocket, httpClient) :
  print("WS ACCEPT")
  webSocket.RecvTextCallback   = _recvTextCallback
  webSocket.RecvBinaryCallback = _recvBinaryCallback
  webSocket.ClosedCallback     = _closedCallback

def _recvTextCallback(webSocket, msg) :
  print("WS RECV TEXT : %s" % msg)
  webSocket.SendText("Reply for %s" % msg)

def _recvBinaryCallback(webSocket, data) :
  print("WS RECV DATA : %s" % data)

def _closedCallback(webSocket) :
  print("WS CLOSED")

Using optional module microWebTemplate for .pyhtml rendered pages :

Instruction Schema
PY {{ py }} MicroPython code {{ end }}
IF {{ if MicroPython condition }} html bloc {{ end }}
ELIF {{ elif MicroPython condition }} html bloc {{ end }}
ELSE {{ else }} html bloc {{ end }}
FOR {{ for identifier in MicroPython iterator }} html bloc {{ end }}
INCLUDE {{ include pyhtml_filename }}
?   {{ MicroPython expression }}

Using {{ py }} :

{{ py }}
  import machine
  from utime import sleep
  test = 123
  def testFunc(x) :
    return 2 * x
{{ end }}

Using {{ if ... }} :

{{ if testFunc(5) <= 3 }}
  <span>titi</span>
{{ elif testFunc(10) >= 15 }}
  <span>tata</span>
{{ else }}
  <span>I like the number {{ test }} !</span>
{{ end }}

Using {{ for ... }} :

{{ for toto in range(testFunc(3)) }}
  <div>toto x 10 equal {{ toto * 10 }}</div>
  <hr />
{{ end }}

Using {{ include ... }} :

{{ include myTemplate.pyhtml }}

Example of a .pyhtml file :

<html>
  <head>
    <title>TEST PYHTML</title>
  </head>
  <body>
    <h1>BEGIN</h1>
    {{ py }}
      def _testFunction(x) :
        return "IN TEST FUNCTION %s" % x
    {{ end }}
    <div style="background-color: black; color: white;">
      {{ for toto in range(3) }}
        This is an HTML test...<br />
        TOTO = {{ toto + 1 }} !<br />
        {{ for toto2 in range(3) }}
          TOTO2 = {{ _testFunction(toto2) }}
        {{ end }}
        Ok good.<br />
      {{ end }}
    </div>
    {{ _testFunction(100) }}<br />
    <br />
    {{ if 2+5 < 3 }}
      IN IF (1)
    {{ elif 10+15 != 25 }}
      IN ELIF (2)
    {{ elif 10+15 == 25 }}
      IN ELIF (3)
    {{ else }}
      IN ELSE (4)
    {{ end }}
  </body>
</html>

:wink:  Author

Jean-Christophe Bos (:fr:)

By JC`zic for HC² ;')

Keep it simple, stupid :+1: