
Golang est un excellent langage de programmation avec un large éventail de fonctionnalités. Cet article montre comment vous pouvez écrire un client et un serveur sur Go pour les protocoles HTTP / 1.1 et HTTP / 2.
Skillbox recommande: Le cours pratique de Python Developer from scratch .
Nous vous rappelons: pour tous les lecteurs de «Habr» - une remise de 10 000 roubles lors de l'inscription à un cours Skillbox en utilisant le code promo «Habr».
HTTP / 2 est la deuxième version majeure du protocole réseau HTTP utilisé pour accéder au World Wide Web. Le protocole est basé sur SPDY. En savoir plus sur cette version
sur GitHub .
Serveur HTTP / 1.1
Le code ci-dessous montre comment écrire un serveur HTTP / 1.1 sur Go. La fonction principale démarre le service HTTP (S) avec le port 9191 et le chemin / hello / sayHello. echoPayload est responsable de la logique d'écho, analyse le trafic entrant et réagit en conséquence. Si nécessaire, echoPayload peut être modifié.
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))
Étant donné que le service HTTP (S) est déjà en cours d'exécution, vous devez fournir un certificat et une clé de serveur. Les deux objets sont stockés dans le répertoire cert sous les noms server.crt et server.key.
Un exemple de certificat et de clé est présenté ci-dessous.
./cert/server.crt
----- COMMENCER LE CERTIFICAT -----
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
----- CERTIFICAT FINAL -----
./cert/server.key
----- COMMENCER LA CLÉ PRIVÉE -----
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 =
----- FIN CLÉ PRIVÉE -----
Il est temps de tester l'exécution du service à l'aide de la commande suivante:
$ go run http_server.go
La réponse devrait être comme ceci:
Go Backend: { HTTPVersion = 1 }; serving on https://localhost:9191/hello/sayHello
Maintenant, nous lançons le service à l'aide de la requête HTTP / 1.1 POST (modes dangereux et sans échec):
$ 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!"
Voici le résultat du programme. La première partie concerne les détails de l'établissement de liaison du serveur et du client TLS. Le second concerne les détails de la demande et de la réponse HTTP / 1.1. À la fin se trouve le message texte «Hello Go!».
* Essayer 127.0.0.1 ...
* Connecté au port localhost (127.0.0.1) 9191 (# 0)
* ALPN, offrant http / 1.1
* Sélection du chiffre: TOUS:! EXPORT:! EXPORT40:! EXPORT56:! ANULL:! LOW:! RC4: @STRENGTH
* définir avec succès les emplacements de vérification des certificats:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: aucun
* TLSv1.2 (OUT), en-tête TLS, état du certificat (22):
* TLSv1.2 (OUT), prise de contact TLS, bonjour client (1):
* TLSv1.2 (IN), prise de contact TLS, bonjour du serveur (2):
* TLSv1.2 (IN), prise de contact TLS, certificat (11):
* TLSv1.2 (IN), négociation TLS, échange de clés de serveur (12):
* TLSv1.2 (IN), prise de contact TLS, serveur terminé (14):
* TLSv1.2 (OUT), négociation TLS, échange de clés client (16):
* TLSv1.2 (OUT), chiffrement de changement TLS, bonjour client (1):
* TLSv1.2 (OUT), prise de contact TLS, terminé (20):
* TLSv1.2 (IN), changement de chiffrement TLS, bonjour client (1):
* TLSv1.2 (IN), poignée de main TLS, terminé (20):
* Connexion SSL utilisant TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, serveur accepté pour utiliser http / 1.1
* Certificat de serveur:
* sujet: C = LK; ST = Western; L = Colombo; O = LDCLAKMAL; OU = Ingénierie; CN = Chanaka Lakmal; emailAddress=ldclakmal@gmail.com
* date de début: 20 avril 03:03:58 2019 GMT
* date d'expiration: 19 avril 03:03:58 2020 GMT
* émetteur: C = LK; ST = Western; L = Colombo; O = LDCLAKMAL; OU = Ingénierie; CN = Chanaka Lakmal; emailAddress=ldclakmal@gmail.com
* Résultat de vérification du certificat SSL: certificat auto-signé (18), continuant quand même.
> POST / bonjour / sayHello HTTP / 1.1
> Hôte: localhost: 9191
> User-Agent: curl / 7.46.0
> Accepter: * / *
> Longueur du contenu: 9
> Type de contenu: application / x-www-form-urlencoded
>
* téléchargement complètement envoyé: 9 octets sur 9
<HTTP / 1.1 200 OK
<Date: sam.20 avril 2019 06:56:19 GMT
<Longueur du contenu: 10
<Content-Type: text / plain; charset = utf-8
<
Bonjour allez!
* Connexion # 0 pour héberger localhost laissée intacte
Client HTTP / 1.1
Le nouveau code est un exemple d'écriture d'un simple client HTTP / 1.1 sur Go. Ce client envoie une requête HTTP (S) POST à
localhost : 9191 / hello / sayHello avec le message «Hello Go!».
package main import ( "bytes" "crypto/tls" "crypto/x509" "fmt" "io/ioutil" "log" "net/http" ) func main() { client := &http.Client{}
Étant donné que le service HTTP (S) démarre également ici, vous devez fournir un certificat et une clé. Les données nécessaires sont placées dans le répertoire cert sous les noms server.crt et server.key.
Pour démarrer le client, vous devez initialiser le serveur. Pour ce faire, nous effectuons les actions décrites dans la première section, ou nous démarrons tout service HTTP (S) avec le port 9191 et le chemin / hello / sayHello.
$ go run http_client.go
La sortie devrait être comme ceci:
Got response 200: HTTP/1.1 Hello Go!
Le client montre le fonctionnement de la commande curl:
$ curl -v --cacert ./cert/server.crt https://localhost:9191/hello/sayHello -d "Hello Go!"
Après avoir fini de travailler avec HTTP / 1.1, vous devriez essayer de répéter la même chose pour HTTP / 2.
Serveur HTTP / 2
Comme dans la section précédente, vous devez d'abord créer un serveur d'écho.
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))
Pour vérifier le serveur, vous devez envoyer la commande:
$ go run http2_server.go
La réponse devrait être comme ceci:
Go Backend: { HTTPVersion = 2 }; serving on https://localhost:9191/hello/sayHello
Maintenant que le serveur fonctionne, vous pouvez l'initialiser à l'aide de requêtes HTTP / 1.1 ou HTTP / 2. Les commandes HTTP / 1.1 POST pour tester le fonctionnement du serveur sont données ci-dessous:
$ 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!"
Et la même chose, mais avec 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!"
Voici un exemple de sortie d'opération de serveur. La première partie est une négociation TLS entre le serveur et le client, la seconde est les détails de la demande et de la réponse HTTP / 2.
* Essayer 127.0.0.1 ...
* Connecté au port localhost (127.0.0.1) 9191 (# 0)
* ALPN, offrant h2
* ALPN, offrant http / 1.1
* Sélection du chiffre: TOUS:! EXPORT:! EXPORT40:! EXPORT56:! ANULL:! LOW:! RC4: @STRENGTH
* définir avec succès les emplacements de vérification des certificats:
* CAfile: src / hello / cert / server.crt
CApath: aucun
* TLSv1.2 (OUT), en-tête TLS, état du certificat (22):
* TLSv1.2 (OUT), prise de contact TLS, bonjour client (1):
* TLSv1.2 (IN), prise de contact TLS, bonjour du serveur (2):
* TLSv1.2 (IN), prise de contact TLS, certificat (11):
* TLSv1.2 (IN), négociation TLS, échange de clés de serveur (12):
* TLSv1.2 (IN), prise de contact TLS, serveur terminé (14):
* TLSv1.2 (OUT), négociation TLS, échange de clés client (16):
* TLSv1.2 (OUT), chiffrement de changement TLS, bonjour client (1):
* TLSv1.2 (OUT), prise de contact TLS, terminé (20):
* TLSv1.2 (IN), changement de chiffrement TLS, bonjour client (1):
* TLSv1.2 (IN), poignée de main TLS, terminé (20):
* Connexion SSL utilisant TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, serveur accepté pour utiliser h2
* Certificat de serveur:
* sujet: C = LK; ST = Western; L = Colombo; O = LDCLAKMAL; OU = Ingénierie; CN = localhost; emailAddress=ldclakmal@gmail.com
* date de début: 20 avril 05:27:33 2019 GMT
* date d'expiration: 19 avril 05:27:33 2020 GMT
* nom commun: localhost (correspondant)
* émetteur: C = LK; ST = Western; L = Colombo; O = LDCLAKMAL; OU = Ingénierie; CN = localhost; emailAddress=ldclakmal@gmail.com
* Le certificat SSL vérifie bien.
* En utilisant HTTP2, le serveur prend en charge la multi-utilisation
* L'état de connexion a changé (HTTP / 2 confirmé)
* TCP_NODELAY défini
* Copie des données HTTP / 2 dans le tampon de flux vers le tampon de connexion après la mise à niveau: len = 0
* Utilisation de Stream ID: 1 (prise en main facile 0x10ddf20)
> POST / bonjour / sayHello HTTP / 1.1
> Hôte: localhost: 9191
> User-Agent: curl / 7.46.0
> Accepter: * / *
> Longueur du contenu: 9
> Type de contenu: application / x-www-form-urlencoded
>
* Nous sommes complètement téléchargés et bien
<HTTP / 2.0 200
<type de contenu: texte / simple; charset = utf-8
<longueur du contenu: 10
<date: sam.20 avril 2019 06:54:50 GMT
<
Bonjour allez!
* Connexion # 0 pour héberger localhost laissée intacte
Client HTTP / 2
La dernière partie consiste à créer un client HTTP / 2. Voici l'implémentation de l'envoi d'une requête HTTP (S) POST pour
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{}
Comme auparavant, vous avez besoin d'une clé et d'un certificat pour fonctionner. Ils sont stockés dans cert avec les noms server.crt et server.key.
Lancement client:
$ go run http2_client.go
Et la réponse attendue:
Got response 200: HTTP/2 Hello Go!
Détaillant le travail du client:
$ curl -v --http2 --cacert ./cert/server.crt https://localhost:9191/hello/sayHello -d "Hello Go!"
C’est tout. Le travail est relativement simple, mais il permet de comprendre comment implémenter les services réseau de base dans Go.
Skillbox recommande: