Socket handling with Chrome

All ESP32 boards running MicroPython.
Target audience: MicroPython users with an ESP32 board.
Post Reply
gian7
Posts: 1
Joined: Mon Dec 07, 2020 2:12 pm

Socket handling with Chrome

Post by gian7 » Mon Dec 07, 2020 2:34 pm

Hi everyone,
I'm trying to make a simple http server, that should also be not blocking, allowing to handle other stuff.
Using IExploer i works quite well.
My problem is that Chrome, opens multiple tcp conenctions (at least 2):
- on one connection it sends the actual http request
- on the other one, it simply opens a tcp connection, without sending anything: that make python script stucked in socket.readline (same for socket.recv)

Here's the main code for socket handling

Code: Select all

try:
  import usocket as socket
except:
  import socket
import network
import time
from machine import Timer,Pin

def init_network():
    gc.mem_free()
    ssid="YYYYYYY"
    password = 'XXXXXX'
    station = network.WLAN(network.STA_IF)
    station.active(True)
    station.connect(ssid, password)
    c=0
    while station.isconnected() == False:
        print ("init_network.. %d"%c, end="\r")
        time.sleep_ms(1000)
        c+=1
    print('\nConnection successful: ',end="")
    print(station.ifconfig())
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(('', 80))
    s.listen(5)

    #gc.mem_free()
    return s

def web_page():
  if led.value() == 1:
    gpio_state="ON"
  else:
    gpio_state="OFF"
  
  html = """<html><head> <title>ESP Web Server</title> <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" href="data:,"> <style>html{font-family: Helvetica; display:inline-block; margin: 0px auto; text-align: center;}
  h1{color: #0F3376; padding: 2vh;}p{font-size: 1.5rem;}.button{display: inline-block; background-color: #e7bd3b; border: none; 
  border-radius: 4px; color: white; padding: 16px 40px; text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}
  .button2{background-color: #4286f4;}</style></head><body> <h1>ESP Web Server</h1> 
  <p>GPIO state: <strong>""" + gpio_state + """</strong></p><p><a href="/?led=on"><button class="button">ON</button></a></p>
  <p><a href="/?led=off"><button class="button button2">OFF</button></a></p></body></html>"""
  return html

led = Pin(2, Pin.OUT)
s = init_network()

#s.settimeout(0.5)
import uselect as select
p = select.poll()
p.register(s,select.POLLOUT | select.POLLIN)
while True:
  print ("Waiting connections..")
  res = p.poll()
  for sock, ev in res:
    if ev & select.POLLIN:
      try:
          client_sock, addr = s.accept()
      except OSError as e:
          print (e)
          continue

      print('Got a connection from %s' % str(addr))
      client_stream = client_sock.makefile("rwb")
     
      request = str(client_stream.readline())
      print(request)
      while True:
          h = client_stream.readline()
          print ("  %s"%str(h))
          if h == b"" or h == b"\r\n":
             break
      response = web_page()
      try:
        print ("Sending")
        r='HTTP/1.1 200 OK\n'
        r+='Content-Type: text/html\n'
        r+='Connection: close\n\n'
        r+=response
        sent=client_stream.write(r)
            
        print ("Sent %d bytes"%sent)
        client_stream.close()
        
      except Exception as e:
        print(str(e))
And here's a test with Chrome:
connection on 52049 is correctly handled, but the other one (52050), make script blocked (until Chrome ask a new page), but that make the esp32 blocked (e.g. timer don't work and neither socket connections from other clients are not handled)

Code: Select all

init_network.. 
Connection successful: ('192.168.1.10', '255.255.255.0', '192.168.1.1', '192.168.1.1')
Waiting connections..
Got a connection from ('192.168.1.7', 52049)
b'GET / HTTP/1.1\r\n'
  b'Host: 192.168.1.10\r\n'
  b'Connection: keep-alive\r\n'
  b'Cache-Control: max-age=0\r\n'
  b'Upgrade-Insecure-Requests: 1\r\n'
  b'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36\r\n'
  b'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\n'
  b'Accept-Encoding: gzip, deflate\r\n'
  b'Accept-Language: it-IT,it;q=0.9,en-US;q=0.8,en;q=0.7\r\n'
  b'\r\n'
Sending
Sent 839 bytes
Waiting connections..
Got a connection from ('192.168.1.7', 52050)

kr-g
Posts: 48
Joined: Sun Dec 01, 2019 7:52 pm
Contact:

Re: Socket handling with Chrome

Post by kr-g » Wed Dec 09, 2020 9:29 am

i had the same problem

after accept() use the client (or client_socket in your example)
to poll with a timeout if some data is available, otherwise close the socket

probably this code snipplet helps...?

try with:
import uselect
timeout = 200 # 200 ms, or any value what fits into your reaction time ...

Code: Select all

       
        poll = uselect.poll()
        poll.register(client, uselect.POLLIN)
        res = poll.poll(timeout)
        poll.unregister(client)
        if res == None or len(res) == 0:
            client.close()
            raise Exception("socket timeout")
link to docu:
https://docs.micropython.org/en/latest/ ... elect.html

kr-g
Posts: 48
Joined: Sun Dec 01, 2019 7:52 pm
Contact:

Re: Socket handling with Chrome

Post by kr-g » Thu Dec 10, 2020 4:43 pm

one small remark:

in case a connection breaks (intentionally or not) chrome will try to fetch the content of the url again.
only in case of this "staying-open" connection. here it will not open a new one again.
firefox will do (by the way) also a retry in case the connection breaks.
both try 3 times before raising an error in the browsers javascript.
- at least it's that what i have observed -

Post Reply