How to render a file HTML and to handle GET and POST requests


#1

I am working on a web server to serve up a index.html file.  I have it working fine using Python 3.6.3.

Here is that code that works just fine.

from http.server import BaseHTTPRequestHandler

class GetHandler(BaseHTTPRequestHandler):

def do_GET(self):
    try:
        f = open(‘index.html’,‘rb’)

        self.send_response(200)
        self.send_header(‘Content-type’, ‘text/html’)
        self.end_headers()
        self.wfile.write(f.read())

        f.close()
        return
    except IOError:
        self.send_error(404, ‘File Not Found: %s’ % self.path)

if name == ‘main’:
    from http.server import HTTPServer
    server = HTTPServer((‘localhost’, 8080), GetHandler)
    print(‘Starting server, use to stop’)
    server.serve_forever()


Since Zerynth does not have wfile.write I don’t know what to use in its place.

I have tried, 

f = open(“resource://index.html”,‘rb’)
data = f.read()
print(data, stream=client)

But when I open the browser, I only get the first character in the index.html file.
What can I use to send the whole file back to the browser ?

Here is the heart of the code which was taken from the Mini Web Server example

while True:
    try:
        # Type in your browser the board ip!
        print("Waiting for connection...")
        # here we wait for a connection
        clientsock,addr = sock.accept()
        print("Incoming connection from",addr)
        
        # yes! a connection is ready to use
        # first let's create a SocketStream
        # it's like a serial stream, but with a socket underneath.
        # This way we can read and print to the socket
        client = streams.SocketStream(clientsock)
        
        # let's read all the HTTP headers from the browser
        # stop when a blank line is received
        line = client.readline()
        while line!="\n" and line!="\r\n":
            line = client.readline()
        print("HTTP request received!")
        
        # let's now send our headers (very minimal)
        # hint: \n is added by print
        print("HTTP/1.1 200 OK\r",stream=client)
        print("Content-Type: text/html\r",stream=client)
        print("Connection: close\r\n\r",stream=client)
        # see? as easy as print!
        print("Hello Zerynth!",random(0,100),"",stream=client)
        # close connection and go waiting for another one
        client.close()
    except Exception as e:
        print("ooops, something wrong:",e)

#2

Hi seulater,

To render an HTML page from your access point, you have to create your index.html page in your project folder and use the ResourceStream to include it in your script.

When you receive the HTTP request, you have to generate the response from your access point in HTML standard format:

html_response = "HTTP/1.1 200 OK\r\n"
html_response += "Content-Type: text/html\r\n"
html_response += "Content-Length: "+str(len(your_index.html_page))+"\r\n"
html_response += "Connection: close\r\n\r\n"
clientsock.send(html_response)


Then you can read block by block (for example 32 bytes block) and render them through the clientsock.send() method.

At the end, remember to close the socket with the clientsock.close() method.

Hope this can help you!


#3

Thanks for the help, Just to be clear do you mean this way ?

###############################################################################
# Mini Web Server
#
# Created by Zerynth Team 2015 CC
# Authors: G. Baldi, D. Mazzei
###############################################################################

# import streams & socket
import streams
import socket

# import the wifi interface
from wireless import wifi

# the wifi module needs a networking driver to be loaded
# in order to control the board hardware.
# FOR THIS EXAMPLE TO WORK, A NETWORK DRIVER MUST BE SELECTED BELOW

# uncomment the following line to use the CC3000 driver (Particle Core or CC3000 Wifi shields)
# from texas.cc3000 import cc3000 as wifi_driver

# uncomment the following line to use the BCM43362 driver (Particle Photon)
# from broadcom.bcm43362 import bcm43362 as wifi_driver

streams.serial()

# init the wifi driver!
# The driver automatically registers itself to the wifi interface
# with the correct configuration for the selected board
wifi_driver.auto_init()
# use the wifi interface to link to the Access Point
# change network name, security and password as needed
print("Establishing Link...")
try:
    # FOR THIS EXAMPLE TO WORK, "Network-Name" AND "Wifi-Password" MUST BE SET
    # TO MATCH YOUR ACTUAL NETWORK CONFIGURATION
    wifi.link("Network-Name",wifi.WIFI_WPA2,"Wifi-Password")
except Exception as e:
    print("ooops, something wrong while linking :(", e)
    while True:
        sleep(1000)

# Yes! we are connected
print("Linked!")

# Let's print our ip, it will be needed soon
info = wifi.link_info()
print("My IP is:",info[0])

# Now let's create a socket and listen for incoming connections on port 80
sock = socket.socket()
sock.bind(80)
sock.listen()


while True:
    try:
        # Type in your browser the board ip!
        print("Waiting for connection...")
        # here we wait for a connection
        clientsock,addr = sock.accept()
        print("Incoming connection from",addr)
        
        # yes! a connection is ready to use
        # first let's create a SocketStream
        # it's like a serial stream, but with a socket underneath.
        # This way we can read and print to the socket
        client = streams.SocketStream(clientsock)
        
        # let's read all the HTTP headers from the browser
        # stop when a blank line is received
        line = client.readline()
        while line!="\n" and line!="\r\n":
            line = client.readline()
        print("HTTP request received!")
        
        # let's now send our headers (very minimal)
        # hint: \n is added by print
        print("HTTP/1.1 200 OK\r",stream=client)
        print("Content-Type: text/html\r",stream=client)
        print("Connection: close\r\n\r",stream=client)
        # see? as easy as print!
        print("Hello Zerynth!",random(0,100),"",stream=client)
        # close connection and go waiting for another one
        client.close()
    except Exception as e:
        print("ooops, something wrong:",e)

#4

Hi @seulater,

sorry for the late answer; I analyze, change a little, and test your code. now it works properly.
Here the while True cycle modified:

while True:
    try:
        # Type in your browser the board ip!
        print("Waiting for connection...")
        # here we wait for a connection
        clientsock,addr = sock.accept()
        print("Incoming connection from",addr)
        
        # yes! a connection is ready to use
        # first let's create a SocketStream
        # it's like a serial stream, but with a socket underneath.
        # This way we can read and print to the socket
        client = streams.SocketStream(clientsock)
        
        # let's read all the HTTP headers from the browser
        # stop when a blank line is received
        line = client.readline()
        while line!="\n" and line!="\r\n":
            line = client.readline()
            # print(line)
        print("HTTP request received!")

        f = open("resource://index.html",'rb')
        
        html_response = "HTTP/1.1 200 OK\r\n"
        html_response += "Content-Type: text/html\r\n"
        html_response += "Content-Length: "+str(f.size)+"\r\n"
        html_response += "Connection: close\r\n\r\n"

        clientsock.send(html_response)
        
        offset = 0
        chunk = 32
        while True:
            data = f.read(chunk)
            if data:
                clientsock.send(data)
                offset += chunk
                f.seek(offset)
            else:
                print("end")
                break

        clientsock.close()

    except Exception as e:
        print("ooops, something wrong:",e)

Keep me posted :slight_smile:


#5

Thank you very much for taking the time to show me. That worked.
So let me ask this to you and others. Am i trying to reinvent the wheel here. I would have assumed that one of the first things people whould have wanted to do it create a webserver that severed up their own site files, like site.html, site.css, site.js and even maybe a graphic file site.pg or .bmp.  But I am having a tough time finding anything that would help in this endeavor by parsing the and looking  for the GET and POST commands.

Ideally the whole goal is to serve up a webpage when the ESP32 is in AP mode, so that one can enter in their credentials for it to then connect to their own AP.


#6

Hi seulater,

The problem of connecting a device to a specific network without uplink a new firmware with new credentials according to available nets is known and there are very few solutions to solve it.

One of them can certainly be to access to your device in AP mode, see the list of available nets discovered by the device, and enter the right credentials to connect the device to the selected net.

 To handle the GET and POST requests coming to your device in AP mode, according to HTTP standard request, you can know the request method by reading the first line of the request and, according to the method received (and related payload data for example in post requests), you can develop your application.

Here few lines of code to edit your script:

    client = streams.SocketStream(clientsock)
    
    # let's read all the HTTP headers from the browser
    # stop when a blank line is received
    line = client.readline()
    print(line) # here you can see the request method

    if "GET /" in line:
        # handle get request
        while line!="\n" and line!="\r\n":
            line = client.readline()
            print(line) # here you can see remaining body of the request
    elif "POST /" in line:
        # handle post request
        data_len = 0
        data_recv = ""
        while line!="\n" and line!="\r\n":
            line = client.readline()
            print(line) # here you can see remaining body of the request
            # check lenght of payload data to be received
            if "Content-Length:" in line:
                data_len = int(line[16:-1])
                print("lenght of data to be received:", data_len)
            sleep(10)
        while data_len>0:
            tmp = clientsock.recv(min(32,data_len))
            data_len-=len(tmp)
            if tmp:
                data_recv += tmp
            else:
                break
        print("payload data:", data_recv)

    print("HTTP request received!")&nbsp;<br>

Regarding the question related to hosting a complete complex website including file *.css and *.js, remember that you are using a microcontroller that has limited resources, so import many files as internal resources to be rendered by the device is not recommended.

Zerynth offers its Advanced Device Manager to do that!!! (here the link of our documentation).

Thanks to the ZADM, you can render any template (including file *.html, *.css, *.js, images, etc.), you can link your device to the ZADM (you can follow the step-by-step instructions in the doc or here in our blog you can find an example of usage for controlling remotely a servo motor), and you can interact with the device through web page in a browser (by clicking the eye icon in the Zerynth Studio when the device is connected) or the ZerynthApp.

Keep me posted :wink:


Guidance in creating an AP WebServer for ESP32
#7

I rename the title of this discussion and move it to the right section to help other members of our community that can have same problems


#8

Thanks for saving my time