
Golang ist eine großartige Programmiersprache mit einer Vielzahl von Funktionen. Dieser Artikel zeigt, wie Sie einen Client und einen Server für die Protokolle HTTP / 1.1 und HTTP / 2 auf Go schreiben können.
Skillbox empfiehlt: Der Python-Entwickler von Grund auf zum Anfassen .
Wir erinnern Sie daran: Für alle Leser von „Habr“ - ein Rabatt von 10.000 Rubel bei der Anmeldung für einen Skillbox-Kurs mit dem Promo-Code „Habr“.
HTTP / 2 ist die zweite Hauptversion des HTTP-Netzwerkprotokolls für den Zugriff auf das World Wide Web. Das Protokoll basiert auf SPDY. Erfahren Sie mehr über diese Version
auf GitHub .
HTTP / 1.1-Server
Der folgende Code zeigt, wie ein HTTP / 1.1-Server auf Go geschrieben wird. Die Hauptfunktion startet den HTTP (S) -Dienst mit Port 9191 und dem Pfad / hello / sayHello. echoPayload ist für die Echologik verantwortlich, analysiert den eingehenden Verkehr und reagiert entsprechend. Bei Bedarf kann echoPayload geändert werden.
package main import ( "fmt" "io/ioutil" "log" "net/http" ) func main() { http.HandleFunc("/hello/sayHello", echoPayload) log.Printf("Go Backend: { HTTPVersion = 1 }; serving on https://localhost:9191/hello/sayHello") log.Fatal(http.ListenAndServeTLS(":9191", "./cert/server.crt", "./cert/server.key", nil)) } func echoPayload(w http.ResponseWriter, req *http.Request) { log.Printf("Request connection: %s, path: %s", req.Proto, req.URL.Path[1:]) defer req.Body.Close() contents, err := ioutil.ReadAll(req.Body) if err != nil { log.Fatalf("Oops! Failed reading body of the request.\n %s", err) http.Error(w, err.Error(), 500) } fmt.Fprintf(w, "%s\n", string(contents))
Da der HTTP (S) -Dienst bereits ausgeführt wird, müssen Sie ein Zertifikat und einen Serverschlüssel bereitstellen. Beide Objekte werden im Zertifikatverzeichnis mit den Namen server.crt und server.key gespeichert.
Ein Beispiel für ein Zertifikat und einen Schlüssel finden Sie unten.
./cert/server.crt
----- BEGIN CERTIFICATE -----
MIID + zCCAuOgAwIBAgIJAPsvGCCAC2i + MA0GCSqGSIb3DQEBCwUAMIGTMQswCQYD
VQQGEwJMSzEQMA4GA1UECAwHV2VzdGVybjEQMA4GA1UEBwwHQ29sb21ibzESMBAG
A1UECgwJTERDTEFLTUFMMRQwEgYDVQQLDAtFbmdpbmVlcmluZzESMBAGA1UEAwwJ
bG9jYWxob3N0MSIwIAYJKoZIhvcNAQkBFhNsZGNsYWttYWxAZ21haWwuY29tMB4X
DTE5MDQyMDA1MjczM1oXDTIwMDQxOTA1MjczM1owgZMxCzAJBgNVBAYTAkxLMRAw
DgYDVQQIDAdXZXN0ZXJuMRAwDgYDVQQHDAdDb2xvbWJvMRIwEAYDVQQKDAlMRENM
QUtNQUwxFDASBgNVBAsMC0VuZ2luZWVyaW5nMRIwEAYDVQQDDAlsb2NhbGhvc3Qx
IjAgBgkqhkiG9w0BCQEWE2xkY2xha21hbEBnbWFpbC5jb20wggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQC9PKAOlJcOBUI9CGnVjMjQHNRqYv01CaUdC4 / e
YFyegxLpoMpYvEC + nYlHT2j7BOhQBV + TkH1D4YOK2WP3V0FLv5hM7Nxsgf25WNHa
zi2DTBvcBgB9sDJA / avIvF + 63 + Btnyggp3xq6MaHy5DNH0kPnSiPiy7PRKToEUn6
oqPnB10rRBFZqs3ePmEDxVL3T / TUZSXR3P95fV1vDCqrJbr3YwWOzFCq8kEJFslK
B7GSEKpPgmK0g5krmAQqUOuCJ3 / xFlCP4trKg / lvSJZ5S / LZD5teDDg6Ax3Mvthj
kMh9 / OM5GGTTjRwhct9dHjFI8POj + TMbLZvoPVXjsmATEgtLAgMBAAGjUDBOMB0G
A1UdDgQWBBQ1CmWXmrHOv6b8f763 / bk80EpbajAfBgNVHSMEGDAWgBQ1CmWXmrHO
v6b8f763 / bk80EpbajAMBgNVHRMEBTADAQH / MA0GCSqGSIb3DQEBCwUAA4IBAQAH
D51Uoe2K4N9 / GxRgww5mMW2dUJ7Hc / tGsr / J1fNqHY8SXNAn5i + GwI + xBvwxFHL3
KZHbfq7eYDE5EItt3cZp5ySSscdTEay9ReH2 + 8k32gpH46CMwPV3XvtQuBVVAC4u
szrq1eWKhYI2zf4iUVpwvq89OynVGIp0atng + q3A2cBhi3NGo6Ho1s2rywQyqiq8
up4PUSVQ6WBoJFx5PEEDxD84VMS7Pan6dT34b9n56tq5R06retZTUZ8jMM88CGX4
88pSPU + XImp6DdNVBmW6Lz76jiSNHLkZGm4jumjeyUGzBjBEBOgSegeWlinMtWE9
gaVxeUHrqHk8xzwJ4oIu
----- ENDZERTIFIKAT -----
./cert/server.key
----- PRIVATSCHLÜSSEL BEGINNEN -----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC9PKAOlJcOBUI9
CGnVjMjQHNRqYv01CaUdC4 / eYFyegxLpoMpYvEC + nYlHT2j7BOhQBV + TkH1D4YOK
2WP3V0FLv5hM7Nxsgf25WNHazi2DTBvcBgB9sDJA / avIvF + 63 + Btnyggp3xq6MaH
y5DNH0kPnSiPiy7PRKToEUn6oqPnB10rRBFZqs3ePmEDxVL3T / TUZSXR3P95fV1v
DCqrJbr3YwWOzFCq8kEJFslKB7GSEKpPgmK0g5krmAQqUOuCJ3 / xFlCP4trKg / lv
SJZ5S / LZD5teDDg6Ax3MvthjkMh9 / OM5GGTTjRwhct9dHjFI8POj + TMbLZvoPVXj
smATEgtLAgMBAAECggEAbaS2yDvn2cPKQTqit4y + vXY2zP1V4GkaNd4BGcOTZnRj
fOIg25EXoln8tEiadva888BpREKvkakUYlraxPDVcGIuiEOk42nd7Io97R0Q2cY7
ThxcJHb2ZxmTctdSUCBvFJTm1ySzve3pOb0ExRSfbGCOo7zs / kKzmZKK3qFlffGS
Ga9O7hyLOuXPU22CM + 5Lq0JPTER73z0DpAweZc0L14j6dzhcG3qUwk0K6K47VZgE
NhEORul7xDj91bh2iEoSbaQe8HxLaMQoMXOC / 9oey2UKKTe9WZE3 + XCvg + vkw / sS
biQ + b4EZ9LuhAhCZ0UE6 + y7PZY + 8G / YsbGg0Zo8cAQKBgQDyTuG47rWBgbdHsEB /
MSKGU6w + a1SdLk6jG + Enji5Q624 / h0xt5nF9ah2eRf3Rlhn9WEKM / uE9ouEODBKE
8rnIDsjufEMI8moPEloRBSsxPNw + fNMSSCZjL + qPtTJUbRio7WA23sfdnE57ygBa
wlPQ9UBBWSm2se4veEZtHjtngQKBgQDH7gnH5Att6ZYazRTgD72g0C5v1l4LYVEQ
jxdBcs6TJA8wHfifZ45F67W95QunmM813UxfS + ybdjytlb8 / lmi2BnK6lDx5HWIL
31jnbg2CxCrNv9oZLjKVDmkp4WUcEp5W33R1 / MGDTRfyARP + 6QYQO / ATMdqtm5Uu
cD6clrL4ywKBgQCQ0niy0WmGaAMlQ8CoxLM / 2c6 + 1 + OQtlalwkoGHEKudqhELBeQ
MAVw0fW13Vtg4vfRpejQ4J26 + xjMDocbEv / bBIsvjvF57XlaXLucJJy2Jwv0BSMa
cCkRa1gkYEYek74DaSzyXqDSYVO / RPKFTFRQNeUbqbD20s3rbVWablFPAQKBgB5y
zUCJJYh2w6qPQzegjhO4wOm9bxMyngL0l + ka0AUuv7VnSx8TyWIytLoX8P90UVJ1
wpTc3ksK5dDV9ot7n7ThJIXv34nehLkkKckNRLd + oro1FsUw + PkkebWsIxb0avL2
EymI9fvGOPhdW6s91 / OO / VAfDpvUDxNEevSkKtujAoGAcMOsXtn / UyT3Lssxgla3
K + DCaFhAQPSUXOmpZwEbQ0yQlksDe4flsam8bEDI5D5iHx1ziSfh583qJl3BEZ5u
VZTEO2YLvT9QRz7pv2qspqj7nzSyBU2BFAajq43 / G1b8FHfVgN + YdVtzVrigfql5
2a + JxOxFfpjnGQ7RfSxSb + Q =
----- END PRIVATE KEY -----
Es ist Zeit, den Dienst mit dem folgenden Befehl zu testen:
$ go run http_server.go
Die Antwort sollte so lauten:
Go Backend: { HTTPVersion = 1 }; serving on https://localhost:9191/hello/sayHello
Jetzt initiieren wir den Dienst mithilfe der HTTP / 1.1-POST-Anforderung (unsicherer und sicherer Modus):
$ curl -k -v https://localhost:9191/hello/sayHello -d "Hello Go!" $ curl -v --cacert ./cert/server.crt https://localhost:9191/hello/sayHello -d "Hello Go!"
Unten ist das Ergebnis des Programms. Der erste Teil sind die Handshake-Details des TLS-Servers und -Clients. Das zweite sind die Details der HTTP / 1.1-Anforderung und -Antwort. Am Ende steht die SMS „Hello Go!“.
* Versuch 127.0.0.1 ...
* Verbunden mit localhost (127.0.0.1) Port 9191 (# 0)
* ALPN mit http / 1.1
* Verschlüsselungsauswahl: ALL:! EXPORT:! EXPORT40:! EXPORT56:! ANULL:! LOW:! RC4: @STRENGTH
* Zertifikatsüberprüfungsorte erfolgreich festgelegt:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: keine
* TLSv1.2 (OUT), TLS-Header, Zertifikatstatus (22):
* TLSv1.2 (OUT), TLS-Handshake, Client-Hallo (1):
* TLSv1.2 (IN), TLS-Handshake, Server Hallo (2):
* TLSv1.2 (IN), TLS-Handshake, Zertifikat (11):
* TLSv1.2 (IN), TLS-Handshake, Serverschlüsselaustausch (12):
* TLSv1.2 (IN), TLS-Handshake, Server beendet (14):
* TLSv1.2 (OUT), TLS-Handshake, Client-Schlüsselaustausch (16):
* TLSv1.2 (OUT), TLS-Änderungsverschlüsselung, Client-Hallo (1):
* TLSv1.2 (OUT), TLS-Handshake, Fertig (20):
* TLSv1.2 (IN), TLS-Änderungsverschlüsselung, Client-Hallo (1):
* TLSv1.2 (IN), TLS-Handshake, Fertig (20):
* SSL-Verbindung mit TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, Server akzeptiert http / 1.1
* Serverzertifikat:
* Betreff: C = LK; ST = Western; L = Colombo; O = LDCLAKMAL; OU = Engineering; CN = Chanaka Lakmal; emailAddress=ldclakmal@gmail.com
* Startdatum: 20. April 03:03:58 2019 GMT
* Ablaufdatum: 19. April 03:03:58 2020 GMT
* Emittent: C = LK; ST = Western; L = Colombo; O = LDCLAKMAL; OU = Engineering; CN = Chanaka Lakmal; emailAddress=ldclakmal@gmail.com
* Ergebnis der Überprüfung des SSL-Zertifikats: Selbstsigniertes Zertifikat (18), das trotzdem fortgesetzt wird.
> POST / hello / sayHello HTTP / 1.1
> Host: localhost: 9191
> User-Agent: curl / 7.46.0
> Akzeptieren: * / *
> Inhaltslänge: 9
> Inhaltstyp: application / x-www-form-urlencoded
>
* Upload komplett verschickt: 9 von 9 Bytes
<HTTP / 1.1 200 OK
<Datum: Sa, 20. April 2019, 06:56:19 GMT
<Inhaltslänge: 10
<Inhaltstyp: text / plain; Zeichensatz = utf-8
<
Hallo geh!
* Verbindung Nr. 0 zum Host localhost bleibt intakt
HTTP / 1.1-Client
Der neue Code ist ein Beispiel für das Schreiben eines einfachen HTTP / 1.1-Clients auf Go. Dieser Client sendet eine HTTP (S) POST-Anfrage an
localhost : 9191 / hello / sayHello mit der Meldung "Hello Go!".
package main import ( "bytes" "crypto/tls" "crypto/x509" "fmt" "io/ioutil" "log" "net/http" ) func main() { client := &http.Client{}
Da der HTTP (S) -Dienst auch hier startet, müssen Sie ein Zertifikat und einen Schlüssel bereitstellen. Die erforderlichen Daten werden mit den Namen server.crt und server.key im Zertifikatverzeichnis abgelegt.
Um den Client zu starten, müssen Sie den Server initialisieren. Dazu führen wir die im ersten Abschnitt beschriebenen Aktionen aus oder starten einen HTTP (S) -Dienst mit Port 9191 und dem Pfad / hello / sayHello.
$ go run http_client.go
Die Ausgabe sollte folgendermaßen aussehen:
Got response 200: HTTP/1.1 Hello Go!
Der Client zeigt die Funktionsweise des Befehls curl an:
$ curl -v --cacert ./cert/server.crt https://localhost:9191/hello/sayHello -d "Hello Go!"
Nachdem Sie mit HTTP / 1.1 fertig sind, sollten Sie versuchen, dasselbe für HTTP / 2 zu wiederholen.
HTTP / 2-Server
Wie im vorherigen Abschnitt müssen Sie zuerst einen Echoserver erstellen.
package main import ( "fmt" "golang.org/x/net/http2" "io/ioutil" "log" "net/http" ) func main() { var httpServer = http.Server{ Addr: ":9191", } var http2Server = http2.Server{} _ = http2.ConfigureServer(&httpServer, &http2Server) http.HandleFunc("/hello/sayHello", echoPayload) log.Printf("Go Backend: { HTTPVersion = 2 }; serving on https://localhost:9191/hello/sayHello") log.Fatal(httpServer.ListenAndServeTLS("./cert/server.crt", "./cert/server.key")) } func echoPayload(w http.ResponseWriter, req *http.Request) { log.Printf("Request connection: %s, path: %s", req.Proto, req.URL.Path[1:]) defer req.Body.Close() contents, err := ioutil.ReadAll(req.Body) if err != nil { log.Fatalf("Oops! Failed reading body of the request.\n %s", err) http.Error(w, err.Error(), 500) } fmt.Fprintf(w, "%s\n", string(contents))
Um den Server zu überprüfen, müssen Sie den folgenden Befehl senden:
$ go run http2_server.go
Die Antwort sollte so lauten:
Go Backend: { HTTPVersion = 2 }; serving on https://localhost:9191/hello/sayHello
Jetzt, da der Server ausgeführt wird, können Sie ihn mithilfe von HTTP / 1.1- oder HTTP / 2-Anforderungen initialisieren. Die HTTP / 1.1-POST-Befehle zum Testen des Serverbetriebs sind nachstehend aufgeführt:
$ curl -k -v https://localhost:9191/hello/sayHello -d "Hello Go!" $ curl -v --cacert ./cert/server.crt https://localhost:9191/hello/sayHello -d "Hello Go!"
Und das Gleiche, aber mit HTTP / 2 POST:
$ curl -k -v --http2 https://localhost:9191/hello/sayHello -d "Hello Go!" $ curl -v --http2 --cacert ./cert/server.crt https://localhost:9191/hello/sayHello -d "Hello Go!"
Unten finden Sie ein Beispiel für die Ausgabe von Serveroperationen. Der erste Teil ist ein TLS-Handshake zwischen dem Server und dem Client, der zweite Teil enthält die Details der HTTP / 2-Anforderung und -Antwort.
* Versuch 127.0.0.1 ...
* Verbunden mit localhost (127.0.0.1) Port 9191 (# 0)
* ALPN mit h2
* ALPN mit http / 1.1
* Verschlüsselungsauswahl: ALL:! EXPORT:! EXPORT40:! EXPORT56:! ANULL:! LOW:! RC4: @STRENGTH
* Zertifikatsüberprüfungsorte erfolgreich festgelegt:
* CAfile: src / hello / cert / server.crt
CApath: keine
* TLSv1.2 (OUT), TLS-Header, Zertifikatstatus (22):
* TLSv1.2 (OUT), TLS-Handshake, Client-Hallo (1):
* TLSv1.2 (IN), TLS-Handshake, Server Hallo (2):
* TLSv1.2 (IN), TLS-Handshake, Zertifikat (11):
* TLSv1.2 (IN), TLS-Handshake, Serverschlüsselaustausch (12):
* TLSv1.2 (IN), TLS-Handshake, Server beendet (14):
* TLSv1.2 (OUT), TLS-Handshake, Client-Schlüsselaustausch (16):
* TLSv1.2 (OUT), TLS-Änderungsverschlüsselung, Client-Hallo (1):
* TLSv1.2 (OUT), TLS-Handshake, Fertig (20):
* TLSv1.2 (IN), TLS-Änderungsverschlüsselung, Client-Hallo (1):
* TLSv1.2 (IN), TLS-Handshake, Fertig (20):
* SSL-Verbindung mit TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, Server akzeptiert h2
* Serverzertifikat:
* Betreff: C = LK; ST = Western; L = Colombo; O = LDCLAKMAL; OU = Engineering; CN = localhost; emailAddress=ldclakmal@gmail.com
* Startdatum: 20. April 05:27:33 2019 GMT
* Ablaufdatum: 19. April 05:27:33 2020 GMT
* gebräuchlicher Name: localhost (übereinstimmend)
* Emittent: C = LK; ST = Western; L = Colombo; O = LDCLAKMAL; OU = Engineering; CN = localhost; emailAddress=ldclakmal@gmail.com
* Überprüfung des SSL-Zertifikats in Ordnung.
* Mit HTTP2 unterstützt der Server die Mehrfachnutzung
* Verbindungsstatus geändert (HTTP / 2 bestätigt)
* TCP_NODELAY gesetzt
* Kopieren von HTTP / 2-Daten im Stream-Puffer in den Verbindungspuffer nach dem Upgrade: len = 0
* Verwenden der Stream-ID: 1 (einfache Handhabung 0x10ddf20)
> POST / hello / sayHello HTTP / 1.1
> Host: localhost: 9191
> User-Agent: curl / 7.46.0
> Akzeptieren: * / *
> Inhaltslänge: 9
> Inhaltstyp: application / x-www-form-urlencoded
>
* Wir sind komplett hochgeladen und in Ordnung
<HTTP / 2.0 200
<Inhaltstyp: Text / Plain; Zeichensatz = utf-8
<Inhaltslänge: 10
<Datum: Sa, 20. April 2019, 06:54:50 GMT
<
Hallo geh!
* Verbindung Nr. 0 zum Host localhost bleibt intakt
HTTP / 2-Client
Der letzte Teil erstellt einen HTTP / 2-Client. Hier ist die Implementierung des Sendens einer HTTP (S) POST-Anforderung für
localhost : 9191 / hello / sayHello:
package main import ( "bytes" "crypto/tls" "crypto/x509" "fmt" "golang.org/x/net/http2" "io/ioutil" "log" "net/http" ) func main() { client := &http.Client{}
Nach wie vor benötigen Sie einen Schlüssel und ein Zertifikat, um zu funktionieren. Sie werden in cert mit den Namen server.crt und server.key gespeichert.
Client-Start:
$ go run http2_client.go
Und die erwartete Antwort:
Got response 200: HTTP/2 Hello Go!
Detaillierung der Arbeit des Kunden:
$ curl -v --http2 --cacert ./cert/server.crt https://localhost:9191/hello/sayHello -d "Hello Go!"
Das ist alles. Die Arbeit ist relativ einfach, vermittelt jedoch ein Verständnis für die Implementierung grundlegender Netzwerkdienste in Go.
Skillbox empfiehlt: