HTTP2: Unterschied zwischen den Versionen
Root (Diskussion | Beiträge) |
Root (Diskussion | Beiträge) |
||
Zeile 313: | Zeile 313: | ||
2018/11/06 15:20:43 [debug] 1039#1039: *1 http2 frame sent: 01380900 sid:0 bl:0 len:4 | 2018/11/06 15:20:43 [debug] 1039#1039: *1 http2 frame sent: 01380900 sid:0 bl:0 len:4 | ||
2018/11/06 15:20:43 [debug] 1039#1039: *1 http2 frame sent: 01380990 sid:0 bl:0 len:0 | 2018/11/06 15:20:43 [debug] 1039#1039: *1 http2 frame sent: 01380990 sid:0 bl:0 len:0 | ||
HEADER and DATA here | |||
2018/11/09 10:48:05 [debug] 19882#19882: *2 Outgoing bytes(hex_dump) : "000012040000000000000300000080000400010000000500ffffff0000040800000000007fff0000" | |||
FRAME_SETTINGS, Flags=[00] received | |||
MAX_CONCURRENT_STREAMS=128 | |||
INITIAL_WINDOW_SIZE=65536 | |||
MAX_FRAME_SIZE=16777215 | |||
FRAME_WINDOW_UPDATE, Flags=[00] received | |||
Stream 0 Window_Size_Increment=2147418112 | |||
=== 2. Stufe (Header) === | === 2. Stufe (Header) === |
Version vom 9. November 2018, 11:40 Uhr
- Im Moment entsteht der OrgaMon-HTTP2 Server in FreePascal auf GitHub.
- Eine erste Alpha Version ist auf github erschienen
https://github.com/Andreas-Filsinger/OrgaMon-HTTP2
Inbetriebnahme
openSSL
Windows
- http://slproweb.com/products/Win32OpenSSL.html
- Downloade und Installiere die 64bit Light Version (im Moment "Win64 OpenSSL v1.1.1 Light" .exe)
- Standard-Ziel akzeptieren, "in das Systemverzeichnis" angekreuzt lassen
Linux
- selbst aus den Quellen compilieren, da zumeist 1.0 Teil der Distributionen ist
Zertifikat
- mit openssl erst mal key.pem und cert.pem erstellen (hier im Beispiel für die Server-Identität "localhost")
openssl req -x509 -nodes -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -subj '/CN=localhost'
- ein Hauptverzeichnis ausdenken und dort ein Verzeichnis pro CN erstellen
- jeweils die 2 Dateien dort rein
- Beispiel: Dein Server ist als "localhost" auf der Maschine selbst, und als "rom" im eigenen Netz, und als "orgamon-2.dyndns.org" von aussen ansprechbar, dann must Du 3x openssl rifen wir oben angegeben mit wechselnder CN
\srv\hosts\ .\localhost\ key.pem cert.pem .\rom\ key.pem cert.pem .\orgamon-2.dyndns.org\ key.pem cert.pem
Port 443 auf dem eigenen System öffnen
Wenn Du einen von aussen sichtbaren Webserver (HTTPS) auf dem eigenen Rechner betreiben willst musst Du Port :443 öffnen.
- Auf deinem Router brauchst Du eine feste IP oder ein Dyndns
- Auf deinem Router musst Du eingehenden Netzwerkverkehr auf Port :443 auf deinen lokalen Rechner leiten (AVM nennt das "Freigabe")
Informationsquellen
RFC
HTTP/2
TLS
- https://wiki.openssl.org/index.php/Simple_TLS_Server
- http://openssl.6102.n7.nabble.com/TLS-1-3-client-hello-issue-td72449.html#a72460
- http://slproweb.com/products/Win32OpenSSL.html
- https://github.com/openssl/openssl/blob/master/include/openssl/tls1.h
- https://www.openssl.org/blog/blog/2017/05/04/tlsv1.3/
- http://gnutls.org/manual/gnutls.html#Server-name-indication
- https://github.com/jay/http2_blacklisted_ciphers
Konzept
- cOrgaMon ist ein https:// Server nach HTTP/2 Standard ohne UI
- eine Instanz von cOrgaMon kümmert sich um eine langbestehende TCP-Clientverbindung (KEEP_ALIVE)
- HTTPS:// TLS 1.2 (nichts anderes!) TLS 1.3 wenn von openSSL angeboten wird
- Clients, die NICHT die "Server Name Indication (SNI) extension" liefern werden abgelehnt
- Target ist win64 und linux
- openSSL Lib der Version 1.1 "e" oder besser wird verwendet
- die Verbindung bleibt ständig bestehen
- Test https://tools.keycdn.com/http2-test
Implementierung TCP
- unit HPACK.pas erstellen (Quelle github)
- testen mit https://github.com/http2jp/hpack-test-case (uses json)
- unit HTTP2Server.pas erstellen (https://github.com/nghttp2/nghttp2)
- ev. auch mit https://python-hyper.org/h2/en/stable/index.html und Kopplung über das WSGI Interface
Test
Sollte dein cOrgaMon-Host ROM heissen:
- openssl s_client -debug -servername rom -connect rom:443
Sollte interessant sein, was der Server so liefert, kann man das in eine Datei umleiten:
# # openssl s_client -servername rom -nextprotoneg "h2,h3" -sess_out out.http2 -connect rom:443 >o.http2 # # # hexdump -C o.http2 # (leider ist ein openssl interner Prefix nicht vermeidbar, aber ab einer gewissen Position gehts los!) # 00000df0 28 73 65 6c 66 20 73 69 67 6e 65 64 20 63 65 72 |(self signed cer| 00000e00 74 69 66 69 63 61 74 65 29 0a 2d 2d 2d 0a 50 52 |tificate).---.PR| 00000e10 49 20 2a 20 48 54 54 50 2f 32 2e 30 0d 0a 0d 0a |I * HTTP/2.0....| 00000e20 53 4d 0d 0a 0d 0a 00 00 18 04 00 00 00 00 00 00 |SM..............| 00000e30 01 00 00 10 00 00 03 00 00 00 65 00 04 00 00 ff |..........e.....| 00000e40 ff 00 05 00 10 00 00 00 00 04 08 00 00 00 00 00 |................| 00000e50 00 0a 00 00 |....| 00000e54
Aufbau
HTTP2
- Zentrale Steuerunit für den HTTP2 Server
- Öffnet und Schliesst die Verbindung, dabei wird die openSSL entsprechend Konfiguriert
- Beobachtet die Verbindungsaushandlung
- Lehnt ungeeignetes ab
- Verhindert Fallbacks in unsichere Technik
- Verarbeitet den Server-Domain Namen Callback
- Stellt .pem und .cert zur Verfügung
- implementiert den Datenmultiplexer laut RFC, fügt also den fragmentierten Netzwerkverkehr wieder in geordnete Bahnen
- Feuert Events wenn Daten "fertig" vorliegen an HTTP2
typischer HTTP/2 Content am Anfang der Verbindung.
Client -> Server 50 52 49 20 2A 20 48 54 54 50 2F 32 2E 30 0D 0A PRI.*.HTTP/2.0.. 0D 0A 53 4D 0D 0A 0D 0A 00 00 12 (18 Bytes Content) 04 00 00 00 00 00 SETTINGS, also 3 Settings!) 00 01 00 01 00 00 00 04 00 02 00 00 00 05 00 00 40 00 00 00 04 (4 Bytes content) 08 00 00 00 00 00 WINDOW_UPDATE 00 BF 00 01 00 00 05 (5 Bytes Content) 02 00 00 00 00 03 PRIO 00 00 00 00 C8 00 00 05 (5 Bytes Content) 02 00 00 00 00 05 PRIO 00 00 00 00 64 00 00 05 (5 Bytes Content) 02 00 00 00 00 07 PRIO 00 00 00 00 00 00 00 05 (5 Bytes Content) 02 00 00 00 00 09 PRIO 00 00 00 07 00 00 00 05 (5 Bytes Content) 02 00 00 00 00 0B PRIO 00 00 00 03 00 FRAME_SETTINGS HEADER_TABLE_SIZE 65536 INITIAL_WINDOW_SIZE 131072 MAX_FRAME_SIZE 16384 FRAME_WINDOW_UPDATE 0 has Window_Size_Increment 12517377 FRAME_PRIORITY Stream 3.0 has 200 FRAME_PRIORITY Stream 5.0 has 100 FRAME_PRIORITY Stream 7.0 has 0 FRAME_PRIORITY Stream 9.7 has 0 FRAME_PRIORITY Stream 11.3 has 0
openSSL - Installation
- der HTTP/2 server benötigt OpenSSL für den Verbindungsaufbau zum Client
wget https://www.openssl.org/source/openssl-1.1.0d.tar.gz tar -xzf openssl-1.1.0d.tar.gz cd openssl-1.1.0d/ ./config make cp -p libcrypto.so.1.1 /usr/lib64 cp -p libssl.so.1.1 /usr/lib64
- Prüfen, welche API zur Verfügung gestellt wird
nm --defined-only -g /usr/lib64/libssl.so.1.1 >libssl.def
HMUX
Entfällt, wurde in HTTP2 integriert
HPACK
- implementiert die Header Compression laut RFC
cryptossl
- spricht mit der openSSL Bibliothek und implementiert dabei nur was gebraucht wird.
Informationsquellen
- https://stackoverflow.com/questions/19029647/ssl-ctx-use-privatekey-file-failed
- https://letsencrypt.org
- https://github.com/certbot/certbot
- Der Client MUSS den Servername mitteilen damit cOrgaMon erkennen kann welcher Mandant geladen werden soll
- auf diesen Server-Namen MUSS Certifikat Pinnig angewendet werden, er darf also KEIN anderes Zertifikat verwendet werden
Schritt für Schritt mit Lets Encrypt
Wir sind als in Besitz einer Domain und haben es geschafft Port 80 auf die eigene WIndows 10 Kiste zu lenken
- Sorry, aber wir brauchen als erstes einen HTTP Webserver
- https://www.windowspro.de/wolfgang-sommergut/web-server-iis-windows-10-installieren-konfigurieren
- letsencrypt-win-simple.V1.9.3.zip
- https://github.com/Lone-Coder/letsencrypt-win-simple/releases
HTTP2 Kommunikation, Step by Step
- nginx 15.5 <-> firefox 63.0.1
- Ich benutzte einen modifizierten HTTP2-Server um die Kommunikation zwischen nginx (server) und firefox (browser) zu beobachten
der Client stellt sich vor
CLIENT_PREFIX received FRAME_SETTINGS, Flags=[00] received HEADER_TABLE_SIZE=65536 INITIAL_WINDOW_SIZE=131072 MAX_FRAME_SIZE=16384 FRAME_WINDOW_UPDATE, Flags=[00] received Stream 0 Window_Size_Increment=12517377 FRAME_PRIORITY, Flags=[00] received Stream 3.0 Weight=200 FRAME_PRIORITY, Flags=[00] received Stream 5.0 Weight=100 FRAME_PRIORITY, Flags=[00] received Stream 7.0 Weight=0 FRAME_PRIORITY, Flags=[00] received Stream 9.7 Weight=0 FRAME_PRIORITY, Flags=[00] received Stream 11.3 Weight=0 FRAME_PRIORITY, Flags=[00] received Stream 13.0 Weight=240 FRAME_HEADERS, Flags=[25] received Stream 15 ACK|END_STREAM END_HEADERS PRIORITY Stream Dependency 13 NOT EXCLUSIVE Weight 41 HEADER_SIZE 193 HEADER :method=GET :path=/ :authority=localhost :scheme=https user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0 accept=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 accept-language=de,en-US;q=0.7,en;q=0.3 accept-encoding=gzip, deflate, br upgrade-insecure-requests=1 te=trailers FRAME_WINDOW_UPDATE, Flags=[00] received Stream 15 Window_Size_Increment=12451840
der Server sagt hallo
2018/11/06 15:20:43 [debug] 1039#1039: *1 http2 frame out: 01380990 sid:0 bl:0 len:0 2018/11/06 15:20:43 [debug] 1039#1039: *1 http2 frame out: 01380900 sid:0 bl:0 len:4 2018/11/06 15:20:43 [debug] 1039#1039: *1 http2 frame out: 01380870 sid:0 bl:0 len:18 2018/11/06 15:20:43 [debug] 1039#1039: *1 malloc: 013B61B8:16384 2018/11/06 15:20:43 [debug] 1039#1039: *1 SSL buf copy: 27 2018/11/06 15:20:43 [debug] 1039#1039: *1 SSL buf copy: 13 2018/11/06 15:20:43 [debug] 1039#1039: *1 SSL buf copy: 9 2018/11/06 15:20:43 [debug] 1039#1039: *1 SSL to write: 49 # # Frage: Was sind das für 49 Bytes # 2018/11/08 13:27:48 [debug] 15793#15793: *1 Outgoing bytes(hex_dump) : "000012040000000000000300000080000400010000000500ffffff0000040800000000007fff0000000000040100000000" # # # FRAME_SETTINGS, Flags=[00] received # MAX_CONCURRENT_STREAMS=128 # INITIAL_WINDOW_SIZE=65536 # MAX_FRAME_SIZE=16777215 # FRAME_WINDOW_UPDATE, Flags=[00] received # Stream 0 Window_Size_Increment=2147418112 # FRAME_SETTINGS, Flags=[01] received # ACK|END_STREAM # 2018/11/06 15:20:43 [debug] 1039#1039: *1 SSL_write: 49 2018/11/06 15:20:43 [debug] 1039#1039: *1 http2 frame sent: 01380870 sid:0 bl:0 len:18 2018/11/06 15:20:43 [debug] 1039#1039: *1 http2 frame sent: 01380900 sid:0 bl:0 len:4 2018/11/06 15:20:43 [debug] 1039#1039: *1 http2 frame sent: 01380990 sid:0 bl:0 len:0
HEADER and DATA here
2018/11/09 10:48:05 [debug] 19882#19882: *2 Outgoing bytes(hex_dump) : "000012040000000000000300000080000400010000000500ffffff0000040800000000007fff0000" FRAME_SETTINGS, Flags=[00] received MAX_CONCURRENT_STREAMS=128 INITIAL_WINDOW_SIZE=65536 MAX_FRAME_SIZE=16777215 FRAME_WINDOW_UPDATE, Flags=[00] received Stream 0 Window_Size_Increment=2147418112
2. Stufe (Header)
2018/11/06 15:20:46 [debug] 1039#1039: *1 http2 output header: ":status: 200" 2018/11/06 15:20:46 [debug] 1039#1039: *1 http2 output header: "server: nginx/1.15.5" 2018/11/06 15:20:46 [debug] 1039#1039: *1 http2 output header: "date: Tue, 06 Nov 2018 14:20:46 GMT" 2018/11/06 15:20:46 [debug] 1039#1039: *1 http2 output header: "content-type: text/html; charset=UTF-8" 2018/11/06 15:20:46 [debug] 1039#1039: *1 http2:15 create HEADERS frame 013B7A80: len:57 fin:0 2018/11/06 15:20:46 [debug] 1039#1039: *1 http cleanup add: 013B7B40 2018/11/06 15:20:46 [debug] 1039#1039: *1 http2 frame out: 013B7A80 sid:15 bl:1 len:57 2018/11/06 15:20:46 [debug] 1039#1039: *1 malloc: 013C1B78:16384 2018/11/06 15:20:46 [debug] 1039#1039: *1 SSL buf copy: 9 2018/11/06 15:20:46 [debug] 1039#1039: *1 SSL buf copy: 57 2018/11/06 15:20:46 [debug] 1039#1039: *1 http2:15 HEADERS frame 013B7A80 was sent 2018/11/06 15:20:46 [debug] 1039#1039: *1 http2 frame sent: 013B7A80 sid:15 bl:1 len:57
3. Stufe (Data)
2018/11/06 15:20:46 [debug] 1039#1039: *1 http2:15 create DATA frame 013B7A80: len:8128 flags:0 2018/11/06 15:20:46 [debug] 1039#1039: *1 http2 frame out: 013B7A80 sid:15 bl:0 len:8128 2018/11/06 15:20:46 [debug] 1039#1039: *1 SSL buf copy: 9 2018/11/06 15:20:46 [debug] 1039#1039: *1 SSL buf copy: 4032 2018/11/06 15:20:46 [debug] 1039#1039: *1 SSL buf copy: 4096 2018/11/06 15:20:46 [debug] 1039#1039: *1 SSL to write: 8203 # # Gemeinsames Senden von "HEADER" + "DATA" # 2018/11/06 15:20:46 [debug] 1039#1039: *1 SSL_write: 8203 2018/11/06 15:20:46 [debug] 1039#1039: *1 http2:15 DATA frame 013B7A80 was sent 2018/11/06 15:20:46 [debug] 1039#1039: *1 http2 frame sent: 013B7A80 sid:15 bl:0 len:8128
Paralleles Arbeiten
- Ein einzelner OrgaMon-Prozess ist für eine dauerhafte TCP/Connection zuständig
Thread: SSL_read()
- Ein Thread sollte SSL_read ausführen, wenn da ein Datenblock oder FRAME fertig ist, sollte dieser als "EventProc" an den Main-Thread übergeben werden
Thread: JOBs
- Möglicherweise startet OrgaMon wiederum SubProgramme mit langen Laufzeiten, diese SubProgramme schreiben Status Infos in memcached
- Rückmeldungen dieser lang laufenden Prozesse ev. über https://developer.mozilla.org/de/docs/Web/API/Push_API
Pascal -> JavaScript
Durch den Wegfall der GUI muss Code auf der Client GUI autark laufen können. Dieser soll im OrgaMon Programm als FreePascal-Code aufnotiert werden, die compilierung teilt den Code auf (Client Code / Server Code) und verteilt das Java-Script über den HTTP2 Server. Freepascal hat schon eine Implementierung die Pascal nach JS ermöglicht.
Details:
- Die Tatsache "JavaScript" auf der Clientside wird der Lazarus-IDE scheinbar verheimlicht
- Es muss also der Pascalcode in JavaScript umgewandelt werden
- Für den Client muss eine Art Code-Lib zur Verfügung stehen, die Pascal rufen kann und umgekehrt
- Die IDE soll denken/vorleben das Client-Seitig auch "Pascal" gesprochen wird
- Debugging: Hier muss der WebClient in den remote-Debugging Modus gebracht werden
- Es könnte die Infrastruktur der "Source maps" verwendet werden, also "komprimierte" Zeilen des JavaScript Quellcodes zeigen auf die entsprechenden Zeilen, die in FreePascal formuliert sind. Es werden also "fake"-Source-Maps zur Verfügung gestellt, so das eine korrekte 1:1 Beziehung zwischen Pascal und Javascript bestehen kann.
- FreePascal und die Lazarus IDE müssen lernen, dass Teile vom Quelltext "remote" executables sind
- FreePascal Lazarus muss Möglichkeiten bieten von einem eintretendem JAvaScript Breakpoint gerufen zu werden
- Es soll alles auch in der Lazarus IDE mit Break-Points programmierbar sein, die IDE soll ein Verständnis haben welche Zeile welchen Code verbirgt
- Die IDE sollte den Client-Side Part ev. farblich hinterlegen?!
- Es muss so sein, dass eine Unit weiterhin ServerSide- / ClientSide- Code mischen kann
program SimpleCalculator; var Panel : TPanel; Button1 : TButton; Button2 : TButton; procedure Panel.OnClick; begin beep; // This goes to JavaScript Client Code end; procedure Button1.OnClick; begin Button2.Disable; // This goes to JavaScript Client Code end; procedure Button1.OnClick; begin Button2.Disable; // This goes to JavaScript Client Code end; begin App.add(Panel); App.add(Button1); App.add(Button2); end.
- die IDE soll ein Verständnis haben dass auf Client-Seite nicht "alles" Programmierbar ist
- aus den JavaScript Parts soll über einen Dispatcher der nonGUI Code aufbar gemacht werden
- In einer Datenbank soll das Know-How für die Web Objekte gespeichert sein
Retry-, Reconnect- Fähig
- immer eine lang stehende Keep-Alive TCP Verbindung, mit "Full Reconnect"- und "Client-IP-Change"- Möglichkeit
- Ein kleines Symbol oder eine Uhr, oder ich weis noch nicht, soll symbolisieren wenn es sich um eine frisch aufgebaute Verbindung handelt
- Es. soll eine Reconnect auch visualisiert werden, oder auch ein Verbindungsende / Abbruch
- In der Anfangszeit brauche ich aber ein Feedback das mir beweist dass NICHT immer neue TCP verbindungen aufgebaut werden
- Der HTTP/2 Server soll eine Modus haben, wo er nur EINE Verbindung EINER IP akzetiert
Meilensteine
08.11.2018 Firefox 63 macht leider keine TLS 1.3 Verbindung, aber immerhin TLS 1.2 26.06.2018 Firefox 61 mit nativ aktiviertem TLS 1.3 ist erschienen 24.11.2017 Erster Client Hello via Read-Thread 23.08.2017 Erster Client Hello von Chrome 14.08.2017 Erster Client Hello von Firefox
todo
SETTINGS ACK! nach HEADER_TABLE_SIZE dann so eine TABLE erstellen HEADERS und DATA an den richtigen STREAM senden! Content!!! HPACK encoder STREAM Objekt HTTP2 Objekt