Webserver ohne String Objekt.

Ich habe mich entschlossen mein Projekt von den String Objekten auf char array umzustellen.

Ich fange in der loop Browserrequest an, dort schaut es bisher so aus.
Habe viele Dinge enfernt ist nur das Grundgerüst.

string HTTP_req;
  
 EthernetClient client = server.available();

  if (client) {
   boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
         HTTP_req += c;



        // if you've gotten to the end of the line (received a newline
        // character) and the line is blank, the http request has ended,
        // so you can send a reply
if (c == '\n' && currentLineIsBlank) {

                                 html_to_browser(client);  

       
                                  break;
                              }
if (c == '\n') {
          // you're starting a new line
          
          currentLineIsBlank = true;
                 }
                 
              else if (c != '\r') {
                               // you've gotten a character on the current line
                                  currentLineIsBlank = false;
                             }     
                             
            }
    }
    // give the web browser time to receive the data
    delay(1);
          // close the connection:
    client.stop();
    
    HTTP_req="";
    
  }

Als char array Version, hatte ich gedacht es könnte in etwa so funktionieren:

char* HTTP_req;  
unsigned int index=0;  
  
EthernetClient client = server.available();

  if (client) {
   boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
         HTTP_req[index++] = c;



        // if you've gotten to the end of the line (received a newline
        // character) and the line is blank, the http request has ended,
        // so you can send a reply
if (c == '\n' && currentLineIsBlank) {

                                 html_to_browser(client)    

                       HTTP_req[index] ='\0' ;
                                  break;
                              }
if (c == '\n') {
          // you're starting a new line
          
          currentLineIsBlank = true;
                 }
                 
              else if (c != '\r') {
                               // you've gotten a character on the current line
                                  currentLineIsBlank = false;
                             }     
                             
            }
    }
    // give the web browser time to receive the data
    delay(1);
          // close the connection:
    client.stop();
  index=0;

Abgesehen von möglichen Fehlern in meinem Pseudocode wird dies zu einem Crash führen da ich für HTTP_req keinen Speicher reserviere.
Und genau das wäre die Frage:
Ich weiß vorher nicht genau wieviele Bytes eintrudeln.
Ich könnte vorher einfach sowas machen

HTTP_req=(unsigned char*) malloc(1000*sizeof(bytes));

1000 Bytes als Schätzwert pi mal Daumen finde ich unprofessionell, und führt im worst case zu einem Absturz.
Meine Idee wäre mit malloc ein Byte zu reservieren und bei jedem neuen Zeichen mit realloc den Bereich zu vergrößern.
An Ende der Übertragung dann wieder auf 1 Byte Größe zurücksetzen. '

Das wäre dann dynamisch. Was ist in der Praxis die optimale Methode ?

Jedesmal den Bereich um ein Byte zu vergrößern ist Unsinn. Bei sowas fängt mal normalerweise mit x Byte an. Was man halt ungefähr erwartet. Dann vergrößert man jedesmal um z.B 20 oder 50 Byte wenn der Speicher voll ist. Das hängt von der Anwendung ab. Man muss halt einen Kompromiss finden zwischen Gesamt-Verbrauch und der Anzahl der Speicher-Resevierungen.

Wobei das bei realloc() auch nicht so gravierend ist. Idealerweise wird wirklich einfach der Speicherbereich erweitert und nichts umkopiert. Es schadet aber trotzdem nichts die Aufrufe etwas zu minimieren.

In der Praxis ist der Speicher knapp, und die Aufgabenvielfalt für deinen Webserver eher klein.
Ein Request vom Client sollte in z.B. 80 Zeichen / Zeile passen. Die hast du im einfachsten Fall statisch (global) zur Verfügung.
Alles was größer ist, ignoriert dein Webserver und liefert eine Standard-Antwort.

Wenn es noch knapper ist, wird gleich beim Einlesen alles vergessen, was den webserver nicht interessiert.
Nick Gammon hat ein schönes Beipiel, wie man seriell Zahlen einliest ohne sie als Text zwischenzuspeichern.

Wofür willst du malloc verwenden ? Dynamischen Speicher bräuchtest du doch nur wieder für die nächste client.read() Sequenz.

rudirabbit:
Das wäre dann dynamisch. Was ist in der Praxis die optimale Methode ?

Dieses "dynamische" Einlesen von Bytes bis der Arzt kommt (wie Du es auch mit String-Objekten wohl bisher gemacht hast) ist die absolut sicherste Methode, dass Dein Webserver in höchstem Maße durch "Denial-of-Service" Angriffe gefährdet ist. Jeder, der eine HTTP-Anfrage an den Webserver senden kann, kann nämlich Deinen Server zum Absturz bringen. Der Angreifer braucht nur eine überlange HTTP-Anfrage an Deinen Server zu senden, mit mehr Bytes als Dein Server RAM-Speicher hat, und der verarbeitet und verarbeitet die eintreffenden Bytes, bis er keinen RAM-Speicher mehr frei hat und abstürzt.

Die optimale Methode in der Praxis ist, sich die maximale Länge der zu verarbeitenden Anfragen vorher zu überlegen.
char HTTP_req[81];
oder
char HTTP_req[130];
Oder wie Du es brauchst. Und die Einleseroutine dann so zu gestalten, dass niemals mehr Zeichen in das char-Array eingelesen werden als es aufnehmen kann.

Und dann kann ein böswilliger Angreifer bei einer Denial-of-Service Attacke so lange Requests und überlange Headerzeilen senden wie er möchte, und Dein Server bricht das Einlesen bei Erreichen der Maximallänge ab statt abzustürzen und "sich aufzuhängen".

Ein "optimal" programmierter Webserver hat nämlich die Eigenschaft, dass er nicht nur die Anfragen einwandfrei verarbeiten kann, die er verarbeiten soll, sondern dass er auch auf böswillige Anfragen von unliebsamen "Angreifern" angemessen reagieren kann, z.B. sich durch "falsche" überlange Anfragen nicht zum Absturz wegen RAM-Speichermangel bringen zu lassen.

Danke für die vielen Antworten:

Wofür willst du malloc verwenden ?

Im Zusammenspiel mit realloc. Also erst mit malloc ein Byte allokieren und dann mit realloc auf die tatsächlich gebrauchte Größe erweitern.

Aber diese Idee ist dank der Erklärungen von jurs und Serenifly eh vom Tisch.
Ich werde wohl jurs Vorschlag umsetzen.

Ram Probleme habe ich im Prinzip keine, da ich am Mega2560 eine Ram Ext. dran habe, und mit Code::blocks steht mir der Ram als Heap zur Verfügung. Wurde hier mal besprochen wie das funktioniert - Funktioniert perfekt.

Also obwohl ich nicht am Ram sparen muss, wollte ich natürlich eine möglichst ressourcenschonende Umsetzung
Da mir die Erfahrung unter C mit den char Array und Strings ein bisschen fehlt habe ich nachgefragt.

Dann sollte statische Allokation kein Problem sein.

Wobei man auch bei dynamischem Speicher problemlos eine Obergrenze setzten kann, so dass nicht Speicher angelegt ist bis keiner mehr da ist.

Wobei man auch bei dynamischem Speicher problemlos eine Obergrenze setzten kann, so dass nicht Speicher angelegt ist bis keiner mehr da ist.

Sehe ich zwar auch so, nur bei dieser Methode muss der Code natürlich sehr sauber sein.
Ich glaube ich muss nochmals darüber nachdenken.
Was passiert wenn ich via realloc() den Speicher ständig in den Größe verändere ?

Wobei das bei realloc() auch nicht so gravierend ist. Idealerweise wird wirklich einfach der Speicherbereich erweitert und nichts umkopiert.

Und wenn nicht Ideal ?

Wenn es geht wird nur der Speicherbereich vergrößert. Wenn der Heap allerdings fragmentiert ist (was hier aber eher nicht der Fall sein wird), können die Daten in einen größeren freien Bereich kopiert werden.