Chrome based browsers lock up webserver

I'm running a simple HTTP webserver on the Portenta Control (based on the Ethernet library) but requests from Chromium based browsers (Edge, Chrome, Vivaldi) lock up the webserver. This issue does not occur with Firefox or curl requests. In Wireshark I see the following when I enter the ip address of the Arduino http://192.168.5.5

For Firefox everything looks normal. In the second image I selected follow HTTP conversation.

For Edge there are 2 HTTP conversations visible. The first one is normal. But the second one ends in a tcp RST

After a few such requests in a Chrome based browser, the Portenta simply stops working.

Is this an issue with the Portenta ethernet library ? Any idea how to fix this

This is the simplified code I use

void WebListenForClients() {
  EthernetClient client = server.available();
  // client detected
  if (client) {
    // read request header
	while (client.connected() || client.available())  {
      if (client.available()) {
        String HTTP_header = client.readStringUntil('\n');  // read the header line of HTTP request
        if (HTTP_header.equals("\r"))  // the end of HTTP request
          break;
      }
    }
	// write response
    client.println("HTTP/1.1 200 OK"); 
    client.println("Connection: close"); 
    client.println("Content-Type: text/html; charset=utf-8");
    client.println("");
    client.println("<html><head><title>test</title></head><body>test page</body></html>");
    client.flush();
    delay(1);
    client.stop();
  }
}

I don't really follow your post, but I do know it is extremely common for programmers to determine which browser they are communicating with as there are a great number of cases where they don't all respond the same way. This sounds like that situation. Are you saying the arduino library code is not accommodating every kind of browser?

Specific browser issues are more with the actual HTML/javascript. What I seem to have is an issue on the lower TCP layer. You can see in the wireshark screenshots that Edge is opening a second connection to the Arduino which doesn't seem to be handled properly by the Arduino Ethernet library and ends in a RESET

I don't use this. Try it without these.

    client.flush();
    delay(1);

Sorry, I don't have anywhere near that depth of knowledge in that area, that's all I got.

It doesn't make a difference unfortunately

Clients are happier when they know they've got the entire response payload. This can be done by setting the Content-Length header for the response (when you know it in advance), or using Transfer-Encoding: chunked, which sends a final "all done" chunk. Try

  String payload = "<html><head><title>test</title></head><body>test page</body></html>";
  client.println("HTTP/1.1 200 OK"); 
  client.println("Connection: close"); 
  client.println("Content-Type: text/html; charset=utf-8");
  client.print("Content-Length: ");
  client.println(payload.length());
  client.println();
  client.print(payload);  // not println -- don't need those extra bytes

Doing that should also allow you to stop lying that you're HTTP 1.1, and be honest that you're going to close the connection, even though the client asked for Connection: keep-alive. You'll also save about twenty bytes

  client.println("HTTP/1.0 200 OK"); 
  client.println("Content-Type: text/html; charset=utf-8");
  client.print("Content-Length: ");
  client.println(payload.length());
  client.println();
  client.print(payload);  // not println -- don't need those extra bytes

ETA: chunked only available with 1.1 (and not with HTTP/2 either)

Some browsers will make 2 requests back-to-back, a request for the page, then request favicon. Are you returning "HTTP/1.1 200 OK" to both? If no favicon, return 404 to that request.

Just a thought...

I need to further testing but it seems the issues gets fixed by adding a timeout and closing that second TCP connection without sending any data

    connection_timeout=false;
    unsigned long connection_time= millis();
    
    while (client.connected())  {
        
       if ((millis()-connection_time) > (2000)) {
          debug_print("break: connection timeout");
          connection_timeout=true;
          break;
        }

        if (client.available()) 
        {
          String HTTP_header = client.readStringUntil('\n');  // read the header line of HTTP request
          if (HTTP_header.equals("\r")) {
            debug_print("break: end of header");
            break;
          }         
      }
    }
   
  if (connection_timeout) {
     client.stop()
  } else {
     [ write response ]
  }


You decided to ignore the second connection rather than reading the GET header line?

My approach is different. I return "HTTP/1.1 404 File Not Found" unless the GET line has an existing file or in your case "GET / HTTP/1.1" (default file)
Then it will return the 404 message to the client browser when it asks for favicon.ico. That will also prevent the problem in the correct way.
Just my opinion.

The thing is there is no GET header. That second TCP connection is never used and makes the Arduino hang. It returns client.connected()=true but client.available()=false forever. Or maybe it times out or closes on certain conditions because in the past these browsers locked up my arduino after a few requests. But now that I implemented a watchdog to reset on 30sec timeout, it resets my Arduino basically on every request (unless I use the connection_timeout check in the code above)

I installed the opensource PC program SimpleWebServer and checked Wireshark. In Edge I perform a request to http://127.0.0.1:8080/test.html

Also here we see Edge opening 2 tcp connections but never use the second one. There is no GET /favicon. After a while SimpleWebServer responds with "400 bad request"

I don't get why this is happening and how is not locking up everyones Arduino when using these browsers since all the Arduino code examples I found don't have this connection_timeout check. I checked this now on different networks and different PCs with different Windows version.

If it's Chromium or Firefox, they send a GET header. "GET /filename HTTP/1.1"
They will also send several more lines of header.
I have a web server on a Mega2560 with an ethernet shield, and mine does not freeze. It will send favicon.ico to the client if it is on my SD card.
It serves all kinds of files. Text, html, ico, css, jpg, and more. Whatever the client requires to build the webpage.
If the request to my server does not include a GET line, my server returns "HTTP/1.1 400 Bad Request"