Der Client sieht 200 Connection Established, beginnt mit TLS und meldet sofort Unsinn, wo ein TLS-Record stehen müsste. Suche nach einer unvollendeten HTTP-Antwort, bevor du die Cipher-Einstellungen des Ziels verantwortlich machst.

Bei einem HTTP/1-CONNECT-Austausch ist die Statuszeile nur der Anfang der Antwort. Danach können Header-Felder folgen. Die Leerzeile, die den Header-Abschnitt beendet, markiert den Wechsel in den Tunnel nach einer erfolgreichen CONNECT-Antwort.

HTTP/1.1 200 Connection Established\r\n
Proxy-Agent: example\r\n
\r\n

Diese Bytes können in einem Lesevorgang eintreffen, in mehreren, oder in einem, der zusätzlich nachfolgende Bytes enthält. TCP bewahrt die Schreibgrenzen des Servers nicht.

Mache die Übergabe zu einem expliziten Parser-Zustand

  1. Sammle und parse die Statuszeile. Nutze den numerischen Status statt eines exakten englischen Reason-Phrase.
  2. Lies weiter, bis der vollständige Abschluss des Header-Abschnitts angekommen ist. Eine erfolgreiche Statuszeile lässt die übrigen Felder nicht verschwinden.
  3. Bei einer erfolgreichen CONNECT-Antwort gilt das Byte direkt nach diesem Abschluss als Beginn der Tunnel-Daten. Bei anderen Antworten folge der passenden HTTP-Antwort- und Authentifizierungsbehandlung.
  4. Übergib die Verbindung und alle nicht verbrauchten Bytes über die vom nächsten Protokoll unterstützte gepufferte I/O-Schnittstelle.

Der letzte Schritt ist auch bei einer guten Delimiter-Suche wichtig. Ein gepufferter Reader kann mehr vom Socket gelesen haben, als der Parser angefordert hat. Wer TLS auf dem nackten Socket startet und diesen Reader aufgibt, kann Bytes im privaten Puffer des Readers stranden lassen.

Nutze eine Frist für den Aufbau und ein Limit für die Header-Größe. EOF vor dem Abschluss ist eine unvollständige Proxy-Antwort und keine Einladung, ewig zu warten oder TLS trotzdem zu versuchen.

Gehe mehrere Grenzfälle durch

Stelle dir einen Lesevorgang vor, der nach Proxy-Agent: endet. Der Parser braucht noch weitere HTTP-Header-Bytes. Stelle dir einen weiteren vor, der mitten in der letzten CRLF-Sequenz endet. Das ist noch kein vollständiger Header-Abschnitt. Und nun einen vollständigen erfolgreichen Header-Abschnitt, gefolgt von Tunnel-Bytes im selben Puffer: Diese zusätzlichen Bytes müssen die Übergabe überstehen.

Ein 407 ist ein anderes Ergebnis. Er verlangt eine Authentifizierungsbehandlung; er erlaubt kein TLS-ClientHello in einen Tunnel, der nie geöffnet wurde.

Bevorzuge einen gepflegten Connector, außer die Implementierung des Handshakes ist die Aufgabe. Diese Grenze gilt speziell für den hier gezeigten HTTP/1-Austausch. Ein HTTP/2-Proxy trägt CONNECT auf einem Stream mit anderer Rahmung.

Ähnliche Artikel