Web Server - Issues serving pages with multiple reference files

Hi All,

I'm having some issues making a webserver that is able to host pages that reference different files hosted on the server.

i.e. my webpage (index.htm) references several .css and .js files.

I have Arduino code that interprets the incoming GET commands from the client, and responds with the correct file.
When I reference one or two small files, it works great.
It's when I start referencing more/larger files that I get the issue.

On my client,I used the inspect feature in Chrome to try and see what's happening.
I've attached a screenshot from that to this post.
It shows that index.htm, styls.css and icon32.png are all being received OK.
It also shows that menu.js and livedata.js are not being received.

If I refresh the page multiple times, occasionally one of the .js files will be received OK, so I don't believe the issue is down to how my server interprets GET requests for .js files.

I get the feeling that the client is sending a request for all the files at the same time, and the Arduino is just responding to the last one. If this is the issue, I was hoping there is a way i can specify the the client to wait for the last file to be received before sending the next request.

I guess another option would be to periodically read the incoming GET requests and line them up in a buffer. That might require some sort of interrupt that will trigger even when the server is in the process of writing the file to the client. I wanted to avoid this if possible.

My code is broken down into three files.
The first file holds the setup and loop functions. These call other functions within separate Ethernet and SD Card files.

Setup/Loop Function File:

#include <SPI.h>
#include <Ethernet2.h>
#include <SD.h>

void setup() {
  Serial.begin(9600);
  while (!Serial);
  Serial.println("Starting now!");
  setupSd();
  setupEthernet();
}

void loop() {
  runWebServer();
}

Ethernet File:

// Define ETHERNET Variables

#define BUFSIZ 100                                    //Line buffer size (Max chars on one line of SD card files)
byte mac[] = {  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //MAC Address
IPAddress dnServer(192, 168, 0, 1);                   // DNS server ip
IPAddress gateway(192, 168, 0, 1);                    // Router's gateway address:
IPAddress subnet(255, 255, 255, 0);                   // Subnet:
IPAddress ip(192,168,0,50);                           // IP address
EthernetServer server(80);                            // HTTP port (80 is standard for http)

// Function to setup Ethernet
void setupEthernet() {
  Serial.println("Starting ethernet");
  Ethernet.begin(mac,ip, dnServer, gateway, subnet);
  // start listening for clients
  server.begin();
  //print out the ip address
  Serial.print("Server IP Address = ");
  Serial.println(Ethernet.localIP());
}

//Function to run webserver
void runWebServer() {
  char clientline[BUFSIZ];
  char name[17];
  int index = 0;
  
  EthernetClient client = server.available();
  if (client) {
    // an http request ends with a blank line
    boolean current_line_is_blank = true;
    
    // reset the input buffer
    index = 0;
    
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        
        // If it isn't a new line, add the character to the buffer
        if (c != '\n' && c != '\r') {
          clientline[index] = c;
          index++;
          // are we too big for the buffer? start tossing out data
          if (index >= BUFSIZ) 
            index = BUFSIZ -1;
          
          // continue to read more data!
          continue;
        }
        
        // got a \n or \r new line, which means the string is done
        clientline[index] = 0;
        
        // Print it out for debugging
        Serial.println();
        Serial.println(clientline);
        
        // Look for substring such as a request to get the file
        if (strstr(clientline, "GET /") != 0) {
          // this time no space after the /, so a sub-file!
          char *filename;
          
          filename = clientline + 5; // look after the "GET /" (5 chars)  *******
          // a little trick, look for the " HTTP/1.1" string and 
          // turn the first character of the substring into a 0 to clear it out.
          (strstr(clientline, " HTTP"))[0] = 0;
 
          if(filename[strlen(filename)-1] == '/') {  // Trim a directory filename
            filename[strlen(filename)-1] = 0;        //  as Open throws error with trailing /
          }
          
          Serial.print(F("Web request for: ")); Serial.println(filename);  // print the file being requested
 
          File file = SD.open(filename, O_READ);
          if ( file == 0 ) {  // Opening the file with return code of 0 is an error in SDFile.open
            client.println("HTTP/1.1 404 Not Found");
            client.println("Content-Type: text/html");
            client.println();
            client.println("<h2>File Not Found!</h2>");
            client.println("
<h3>Couldn't open the File!</h3>");
            break; 
          }
          
          Serial.print("File Opened: ");
          Serial.println(filename);
                    
          client.println("HTTP/1.1 200 OK");
          if (file.isDirectory()) {
            Serial.println("is a directory");
            //file.close();
            client.println("Content-Type: text/html");
            client.println();
            client.print("<h2>Files in /");
            client.print(filename); 
            client.println(":</h2>");
            ListFiles(client,LS_SIZE,file);  
            file.close();                   
          } else { 
            // Any non-directory clicked, server will send file to client for download
            byte fileType = getFiletype(filename);
            if (fileType == 1) client.println("Content-Type: text/html");
            if (fileType == 2) client.println("Content-Type: text/css");
            if (fileType == 3) client.println("Content-Type: text/javascript");
            if (fileType == 4) client.println("Content-Type: text/x-icon");
            if (fileType == 5) client.println("Content-Type: image/png");
            client.println("Connection: close");
            client.println();
          
            char file_buffer[16];
            int avail;
            while (avail = file.available()) {
              int to_read = min(avail, 16);
              if (to_read != file.read(file_buffer, to_read)) {
                break;
              }
              client.write(file_buffer, to_read);
            }
            file.close();
            Serial.println("File was sent to client");
          }
        } else {
          // everything else is 404
          client.println("HTTP/1.1 404 Not Found");
          client.println("Content-Type: text/html");
          client.println();
          client.println("<h2>File Not Found!</h2>");
        }
        break;
      }
    }
    // give the web browser time to receive the data
    delay(1);
    client.stop();
  }
}

void ListFiles(EthernetClient client, uint8_t flags, File dir) {
  client.println("<ul>");
  while (true) {
    File entry = dir.openNextFile();
   
    // done if past last used entry
     if (! entry) {
       // no more files
       break;
     }
 
    // print any indent spaces
    client.print("<li><a href=\"");
    client.print(entry.name());
    if (entry.isDirectory()) {
       client.println("/");
    }
    client.print("\">");
    
    // print file name
    client.print(entry.name());
    if (entry.isDirectory()) {
       client.println("/");
    }
        
    client.print("</a>");
    client.println("</li>");
    entry.close();
  }
  client.println("</ul>");
}

byte getFiletype(char* filename) {
  int8_t len = strlen(filename);
  byte result;
  if (strstr(strlwr(filename + (len - 4)), ".htm"))
  {
    return 1;
  } 
  else if (strstr(strlwr(filename + (len - 4)), ".css"))
  {
    return 2;
  }
  else if (strstr(strlwr(filename + (len - 3)), ".js"))
  {
    return 3;
  } 
  else if (strstr(strlwr(filename + (len - 4)), ".ico"))
  {
    return 4;
  } 
  else if (strstr(strlwr(filename + (len - 4)), ".png"))
  {
    return 5;
  } 
  else 
  {
    return 0;
  }
  return result;
}

A lot of code came from other posts on the Arduino Forums. Thanks to those I've taken it from!

Am I barking up the wrong tree? Does anyone have any advice on how I might be able to fix this issue?
This is my first post, so please go easy on me!

I'm using an ATmega32U4 based microcontroller together with Ethernet and SD card modules.
The ethernet module is a W5500 that looks like this:

I get the feeling that the client is sending a request for all the files at the same time, and the Arduino is just responding to the last one.

The client first has to receive a file, to see that it references other files. Then, it makes requests for those files one at a time. Each request is an independent request, but the are made one after another very quickly.

The Arduino can only handle so many pending requests before there are no more sockets available.

Since the Arduino is very slow, compared to a PC or mainframe, it is likely that more requests are made than the Arduino can handle, so some get discarded.

It would be better to store the static css and js files on another server, and reference them from that location. That way, the Arduino only serves the page(s) that actually change.