Detaillierte Erläuterung des Lesevorgangs für Nginx-Anforderungsheaderdaten

Detaillierte Erläuterung des Lesevorgangs für Nginx-Anforderungsheaderdaten

Im vorherigen Artikel haben wir erklärt, wie nginx die Daten der Anforderungszeile liest und die Anforderungszeile analysiert. In diesem Artikel erklären wir hauptsächlich, wie nginx die vom Client gesendeten Anforderungsheaderdaten liest und diese Daten analysiert. Im Wesentlichen ist der Datenlesevorgang der Anforderungszeile und des Anforderungsheaders grundsätzlich derselbe, da in beiden Fällen das Problem auftritt, wie Daten aus intermittierenden Datenströmen gelesen und wie die Daten verarbeitet werden.

1. Fordern Sie den Header-Lesen-Hauptprozess an

Bevor wir den Lesevorgang des Anforderungsheaders vorstellen, zeigen wir zunächst ein Beispiel einer HTTP-Anforderungsnachricht:

POST /web/buch/lesen HTTP/1.1
Host: localhost
Verbindung: Keep-Alive
Inhaltslänge: 365
Akzeptieren: application/json, text/plain, */*

Die erste Datenzeile im Beispiel ist die Anforderungszeile und die folgenden Zeilen sind Anforderungsheader. Jeder Anforderungsheader wird im Name:Wert-Format zusammengestellt und jeder Anforderungsheader nimmt eine Zeile ein. Im vorherigen Artikel, in dem der Lesevorgang der Anforderungszeile vorgestellt wurde, haben wir erwähnt, dass nginx nach dem Lesen der Anforderungszeile die Rückruffunktion des aktuellen Lese-Ereignisses in die Methode ngx_http_process_request_headers() ändert und diese Methode direkt aufruft, um zu versuchen, die Header-Daten der Anforderung zu lesen. Diese Methode ist der Hauptprozess zum Lesen der Anforderungszeilendaten. Im Folgenden finden Sie den Quellcode dieser Methode:

/**
 * Analysieren Sie die vom Client gesendeten Headerdaten*/
statischer void ngx_http_process_request_headers(ngx_event_t *rev) {
 u_char *p;
 Größe_t Länge;
 ssize_t n;
 ngx_int_t rc, rv;
 ngx_table_elt_t *h;
 ngx_connection_t *c;
 ngx_http_header_t *hh;
 ngx_http_request_t *r;
 ngx_http_core_srv_conf_t *cscf;
 ngx_http_core_main_conf_t *cmcf;

 c = rev->Daten;
 r = c->Daten;

 wenn (rev->timedout) {
  ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "Zeitüberschreitung beim Client");
  c->Zeitüberschreitung = 1;
  ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
  zurückkehren;
 }

 cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
 rc = NGX_AGAIN;

 für (;;) {
  wenn (rc == NGX_AGAIN) {
   // Wenn im aktuellen Header-Puffer kein freier Platz mehr vorhanden ist, neuen Platz beantragen, if (r->header_in->pos == r->header_in->end) {
    // Neuen Speicherplatz beantragen rv = ngx_http_alloc_large_header_buffer(r, 0);
    wenn (rv == NGX_ERROR) {
     ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
     zurückkehren;
    }

    // Der vom Client gesendete Header ist zu lang und überschreitet die durch large_client_header_buffers angegebene Maximalgröße if (rv == NGX_DECLINED) {
     p = r->Headername_Start;
     r->lingering_close = 1;
     wenn (p == NULL) {
      ngx_log_error(NGX_LOG_INFO, c->log, 0, „Client hat zu große Anfrage gesendet“);
      ngx_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_ZU_GROSS);
      zurückkehren;
     }

     Länge = r->header_in->end - p;
     wenn (Länge > NGX_MAX_ERROR_STR - 300) {
      Länge = NGX_MAX_ERROR_STR – 300;
     }

     ngx_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_ZU_GROSS);
     zurückkehren;
    }
   }

   // Versuchen Sie, die neu gesendeten Daten vom verbundenen Client zu lesen n = ngx_http_read_request_header(r);
   wenn (n == NGX_AGAIN || n == NGX_ERROR) {
    zurückkehren;
   }
  }

  cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
  // Hier konvertieren wir hauptsächlich die gelesenen Daten rc = ngx_http_parse_header_line(r, r->header_in, cscf->underscores_in_headers);

  // NGX_OK zeigt an, dass die Analyse und der Abruf von Header-Daten erfolgreich waren, wenn (rc == NGX_OK) {
   r->Anforderungslänge += r->Header_in->Pos - r->Header_Name_Start;
   // Ungültige Header filtern
   wenn (r->ungültiger_Header und cscf->ungültige_Header_ignorieren) {
    weitermachen;
   }

   // Erstellen Sie eine Struktur zum Speichern von Headern h = ngx_list_push(&r->headers_in.headers);
   wenn (h == NULL) {
    ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
    zurückkehren;
   }

   h->Hash = r->Header_Hash;
   //Verwende den Header-Namen als Schlüssel der Hash-Tabelle
   h->Schlüssel.Länge = r->Headername_Ende - r->Headername_Start;
   h->Schlüssel.Daten = r->Headername_Start;
   h->Schlüssel.Daten[h->Schlüssel.Länge] = '\0';

   //Verwende den Header-Wert als Hash-Tabellenwert
   h->Wert.Länge = r->Header_Ende - r->Header_Start;
   h->Wert.Daten = r->Header_Start;
   h->Wert.Daten[h->Wert.Länge] = '\0';

   h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);
   wenn (h->lowcase_key == NULL) {
    ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
    zurückkehren;
   }

   wenn (h->key.len == r->lowcase_index) {
    ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
   } anders {
    ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
   }

   // headers_in_hash speichert alle Header. Hier prüfen wir, ob der vom aktuellen Client gesendete Header ein gültiger Header ist.
   hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, h->lowcase_key, h->key.len);
   // Der Handler ist hier die für jeden Header in ngx_http_headers_in definierte Verarbeitungsmethode. Nach der Verarbeitung durch die // handler()-Methode jedes Headers wird der vom Client gesendete Header in die verschiedenen Attribute in der Struktur r->headers_in konvertiert, wenn (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
    zurückkehren;
   }

   weitermachen;
  }

  // NGX_HTTP_PARSE_HEADER_DONE bedeutet, dass alle Header verarbeitet wurden, wenn (rc == NGX_HTTP_PARSE_HEADER_DONE) {
   r->Anforderungslänge += r->Header_in->Pos - r->Header_Name_Start;
   r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;
   // Überprüfen Sie die Legitimität der vom Client gesendeten Header-Daten rc = ngx_http_process_request_header(r);
   wenn (rc != NGX_OK) {
    zurückkehren;
   }

   ngx_http_process_request(r);
   zurückkehren;
  }

  // NGX_AGAIN zeigt an, dass die gelesenen Kopfzeilendaten unvollständig sind und weiter gelesen werden müssen, wenn (rc == NGX_AGAIN) {
   weitermachen;
  }
  
  ngx_log_error(NGX_LOG_INFO, c->log, 0, "Client hat ungültige Headerzeile gesendet");
  ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
  zurückkehren;
 }
}

Das Lesen des Anforderungsheaders gliedert sich hierbei im Wesentlichen in folgende Schritte:

  • Überprüfen Sie zunächst, ob das aktuelle Leseereignis abgelaufen ist. Wenn ja, schließen Sie die aktuelle Verbindung direkt.
  • Bestimmen Sie, ob r->header_in->pos == r->header_in->end wahr ist. Dies dient hauptsächlich dazu, zu prüfen, ob im aktuellen Lesepuffer Speicherplatz vorhanden ist, der die neu gelesenen Daten speichern kann. Wenn nicht, beantragen Sie einen neuen Speicherplatz aus dem Speicherpool.
  • Rufen Sie die Methode ngx_http_read_request_header() auf, um die Daten im aktuellen Verbindungshandle zu lesen. Wenn der Rückgabewert größer als 0 ist, gibt er die Länge der gelesenen Daten an. Wenn er gleich 0 ist, bedeutet dies, dass der Client die Verbindung getrennt hat. Wenn er NGX_ERROR ist, bedeutet dies, dass beim Lesen eine Ausnahme aufgetreten ist. Wenn er NGX_AGAIN ist, wurden diesmal keine Daten gelesen und es müssen neue Daten gelesen werden. Wie Sie sehen, ermitteln wir hier zunächst, ob der Rückgabewert NGX_AGAIN ist. Wenn dies der Fall ist, wird er direkt zurückgegeben, ohne dass eine weitere Verarbeitung durchgeführt wird. Dies liegt hauptsächlich daran, dass die Rückruffunktion des aktuellen Leseereignisses immer noch ngx_http_process_request_headers() ist. Wenn ein neues Leseereignis ausgelöst wird, wird immer noch ngx_http_read_request_header() aufgerufen, um die Daten erneut zu lesen. Wenn andererseits in der Methode ngx_http_read_request_header() der Rückgabewert NGX_AGAIN ist, wird das aktuelle Lese-Ereignis erneut zur Ereigniswarteschlange hinzugefügt und das Lese-Ereignis im Epoll-Handle für die aktuelle Verbindung registriert.
  • Rufen Sie die Methode ngx_http_parse_header_line() auf, um die Header-Daten der Leseanforderung zu analysieren. Es ist zu beachten, dass jeder Aufruf dieser Methode nur einen Anforderungsheader analysiert, aber nach einer unendlichen For-Schleife und einem kontinuierlichen Ereignisauslösemechanismus werden schließlich alle Anforderungsheader-Daten gelesen.
  • Gemäß dem Rückgabewert der Methode ngx_http_parse_header_line() wird der neu gelesene Header in der Liste r->headers_in.headers gespeichert, wenn dieser NGX_OK ist.
  • Wenn der Rückgabewert der Methode ngx_http_parse_header_line() NGX_HTTP_PARSE_HEADER_DONE ist, bedeutet dies, dass alle Header erfolgreich gelesen wurden. Zu diesem Zeitpunkt wird zuerst die Methode ngx_http_process_request_header() aufgerufen, um die Legitimität des gelesenen Headers zu überprüfen, und dann wird die Methode ngx_http_process_request() aufgerufen, um die 11 Phasen des http-Moduls in nginx zu starten. Das Implementierungsprinzip dieser Methode wird im folgenden Artikel erläutert.

2. Lesen der Anforderungsheaderdaten

Wie Sie sehen, gibt es zwei Hauptmethoden zum Lesen des Anforderungsheaders: ngx_http_read_request_header() und ngx_http_parse_header_line(). Die zweite Methode hier ist relativ lang, aber ihre Logik ist sehr einfach. Sie analysiert hauptsächlich die gelesenen Daten, um zu sehen, ob sie einen vollständigen Anforderungsheader bilden können (in der Form Name: Wert und nimmt eine Zeile ein). Wenn ja, gibt sie NGX_OK zurück, andernfalls gibt sie NGX_AGAIN zurück, in der Hoffnung, mit dem Lesen der Daten fortzufahren. Wir werden diese Methode hier nicht erklären. Leser können den Quellcode selbst lesen. Wir werden hauptsächlich erklären, wie die Methode ngx_http_read_request_header() die vom Client gesendeten Anforderungsheaderdaten liest:

statische ssize_t ngx_http_read_request_header(ngx_http_request_t *r) {
 ssize_t n;
 ngx_event_t *rev;
 ngx_connection_t *c;
 ngx_http_core_srv_conf_t *cscf;

 c = r->Verbindung;
 rev = c->lesen;

 // Berechnen, wie viele Daten noch nicht verarbeitet wurden n = r->header_in->last - r->header_in->pos;

 // Wenn n größer als 0 ist, bedeutet dies, dass noch unverarbeitete Daten vorhanden sind. Dann gib n direkt zurück
 wenn (n > 0) {
  Rückkehr n;
 }

 // Wenn Sie hierher gehen, bedeutet dies, dass die aktuell gelesenen Daten verarbeitet wurden. Daher wird hier eine Beurteilung vorgenommen. Wenn der Bereitschaftsparameter des aktuellen Ereignisses 1 ist,
 // Dies bedeutet, dass der Handle der aktuellen Verbindung ungelesene Daten speichert, sodass die Methode c->recv() aufgerufen wird, um die Daten zu lesen. Andernfalls wird das aktuelle Ereignis zur // Ereigniswarteschlange hinzugefügt und das Leseereignis des aktuellen Verbindungs-Handles wird weiterhin überwacht, wenn (rev->ready) {
  // Daten im Verbindungsdateideskriptor lesen n = c->recv(c, r->header_in->last, r->header_in->end - r->header_in->last);
 } anders {
  n = NGX_AGAIN;
 }

 // Wenn n NGX_AGAIN ist, füge das aktuelle Ereignis dem Ereignislistener hinzu und höre weiterhin auf das Leseereignis des aktuellen Epoll-Handles, wenn (n == NGX_AGAIN) {
  wenn (!rev->timer_set) {
   cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
   ngx_add_timer(rev, cscf->client_header_timeout);
  }

  wenn (ngx_handle_read_event(rev, 0) != NGX_OK) {
   ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
   gib NGX_ERROR zurück;
  }

  gib NGX_AGAIN zurück;
 }

 // Wenn n 0 ist, bedeutet dies, dass der Client die Verbindung geschlossen hat, if (n == 0) {
  ngx_log_error(NGX_LOG_INFO, c->log, 0, „Client hat Verbindung vorzeitig geschlossen“);
 }

 // Wenn der Client die Verbindung schließt oder abnormal liest, recyceln Sie die aktuelle Anforderungsstruktur, wenn (n == 0 || n == NGX_ERROR) {
  c->Fehler = 1;
  c->log->action = "Client-Anforderungsheader lesen";
  ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
  gib NGX_ERROR zurück;
 }

 //Aktualisiere den aktuell gelesenen Datenzeiger r->header_in->last += n;
 Rückkehr n;
}



Das Lesen der Anforderungsheaderdaten gliedert sich hierbei im Wesentlichen in die folgenden Schritte:

  • Bestimmen Sie, ob sich im aktuellen Puffer unverarbeitete Daten befinden. Wenn ja, kehren Sie direkt zurück. Der Grund, warum Daten nicht gelesen wurden, liegt hauptsächlich darin, dass beim vorherigen Lesen der Anforderungszeilendaten möglicherweise ein Teil oder alle Anforderungsheaderdaten gelesen wurden. Daher wird hier eine Überprüfung durchgeführt.
  • Bestimmen Sie, ob das aktuelle Leseereignis bereit ist. Wenn es bereit ist, rufen Sie die Methode c->recv() auf, um die Daten im aktuellen Verbindungs-Handle zu lesen.
  • Wenn das aktuelle Lese-Ereignis nicht bereit ist, fügen Sie das aktuelle Lese-Ereignis erneut zur Ereigniswarteschlange hinzu und registrieren Sie das Lese-Ereignis im Epoll-Handle für die aktuelle Verbindung.
  • Der Rückgabewert des zweiten Schritts wird beurteilt. Wenn er 0 ist, bedeutet dies, dass der Client die Verbindung getrennt hat. Wenn er NGX_ERROR ist, bedeutet dies, dass das Lesen der Daten abnormal ist. In beiden Fällen wird die aktuelle Verbindung geschlossen und ein 400-Statuscode an den Client zurückgegeben. Wenn der Rückgabewert NGX_AGAIN lautet, fahren Sie mit den Schritten in Schritt 3 fort, um weiterhin auf Lese-Ereignisse zu warten. Wenn der Rückgabewert größer als 0 ist, bedeutet dies, dass der Lesevorgang erfolgreich war, und dieser Wert größer als 0 gibt die Länge der gelesenen Daten an.
  • Aktualisiert die Zeigerdaten des Puffers, in dem die gelesenen Daten gespeichert sind.

3. Zusammenfassung

In diesem Artikel wird hauptsächlich erläutert, wie nginx den Anforderungsheader liest und analysiert, und der Schwerpunkt liegt auf dem Hauptprozesscode zum Lesen von Daten und den detaillierten Schritten des Lesens.

Dies ist das Ende dieses Artikels mit der detaillierten Erklärung des Nginx-Anforderungsheaderdaten-Lesevorgangs. Weitere relevante Inhalte zum Nginx-Anforderungsheaderdaten-Lesevorgang finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder durchsuchen Sie die folgenden verwandten Artikel weiter. Ich hoffe, dass jeder 123WORDPRESS.COM in Zukunft unterstützen wird!

Das könnte Sie auch interessieren:
  • So lesen Sie die regionalen Informationen einer IP mithilfe des Nginx- und GeoIP-Moduls
  • So lesen und analysieren Sie Nginx-Fehlerprotokolle mit NodeJS
  • Detaillierte Erläuterung der Nginx-Konfigurationsfunktion zum Lesen des Anforderungstexts

<<:  Lebenszyklus und Ausführungsreihenfolge von React-Class-Komponenten

>>:  Ausführliches Tutorial zur Installation von WinX64 unter MySQL8.0.18 (mit Bildern und Text)

Artikel empfehlen

Optimierte Implementierung von count() für große MySQL-Tabellen

Das Folgende ist mein Urteil basierend auf der Da...

Detaillierte Erklärung der Javascript-Grundlagen

Inhaltsverzeichnis Variable Datentypen Erweiterun...

Vorschläge zur Optimierung der Webseiten-Kopfzeile

Logo-Optimierung: 1. Das Logobild sollte so klein...

Vorteile von MySQL-Abdeckungsindizes

Ein allgemeiner Vorschlag besteht darin, Indizes ...

Vor- und Nachteile des Tabellenlayouts und warum es nicht empfohlen wird

Nachteile von Tabellen 1. Tabellen nehmen mehr Byt...

Grundlegende Verwendung und Beispiele von yum (empfohlen)

yum-Befehl Yum (vollständiger Name Yellow Dog Upd...

JavaScript Canvas implementiert Tic-Tac-Toe-Spiel

In diesem Artikel wird der spezifische Code von J...

Nodejs implementiert Intranet-Penetrationsdienst

Inhaltsverzeichnis 1. Proxy im LAN 2. Intranet-Pe...