
Golang é uma ótima linguagem de programação com uma ampla variedade de recursos. Este artigo mostra como você pode escrever um cliente e um servidor no Go para os protocolos HTTP / 1.1 e HTTP / 2.
A Skillbox recomenda: O desenvolvedor Python desde o início do curso prático .
Lembramos que: para todos os leitores de "Habr" - um desconto de 10.000 rublos ao se inscrever em qualquer curso Skillbox usando o código promocional "Habr".
HTTP / 2 é a segunda versão principal do protocolo de rede HTTP usado para acessar a World Wide Web. O protocolo é baseado no SPDY. Saiba mais sobre esta versão
no GitHub .
Servidor HTTP / 1.1
O código abaixo demonstra como gravar um servidor HTTP / 1.1 em movimento. A função principal inicia o serviço HTTP (S) com a porta 9191 e o caminho / hello / sayHello. O echoPayload é responsável pela lógica do eco, analisando o tráfego recebido e reagindo de acordo. Se necessário, o echoPayload pode ser modificado.
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))
Como o serviço HTTP (S) já está em execução, você precisa fornecer um certificado e uma chave do servidor. Ambos os objetos são armazenados no diretório cert com os nomes server.crt e server.key.
Um exemplo de certificado e chave está abaixo.
./cert/server.crt
----- COMEÇAR CERTIFICADO -----
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
----- TERMINAR CERTIFICADO -----
./cert/server.key
----- COMECE A CHAVE PRIVADA -----
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 =
----- TERMINAR CHAVE PRIVADA -----
É hora de testar a execução do serviço usando o seguinte comando:
$ go run http_server.go
A resposta deve ser assim:
Go Backend: { HTTPVersion = 1 }; serving on https://localhost:9191/hello/sayHello
Agora, iniciamos o serviço usando a solicitação HTTP / 1.1 POST (modos inseguro e seguro):
$ 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!"
Abaixo está o resultado do programa. A primeira parte são os detalhes do handshake do servidor e cliente TLS. O segundo são os detalhes da solicitação e resposta HTTP / 1.1. No final, está a mensagem de texto "Hello Go!".
* Tentando 127.0.0.1 ...
* Conectado à porta 9191 do host local (127.0.0.1) (# 0)
* ALPN, oferecendo http / 1.1
* Seleção de cifra: TODOS :! EXPORT :! EXPORT40 :! EXPORT56 :! ANULL :! LOW :! RC4: @STRENGTH
* definir com êxito o certificado verificar locais:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: nenhum
* TLSv1.2 (OUT), cabeçalho TLS, Status do certificado (22):
* TLSv1.2 (OUT), handshake TLS, olá cliente (1):
* TLSv1.2 (IN), handshake TLS, Olá ao servidor (2):
* TLSv1.2 (IN), handshake TLS, certificado (11):
* TLSv1.2 (IN), handshake TLS, troca de chaves do servidor (12):
* TLSv1.2 (IN), handshake TLS, servidor concluído (14):
* TLSv1.2 (OUT), handshake TLS, troca de chaves do cliente (16):
* TLSv1.2 (OUT), cifra de mudança TLS, Olá do cliente (1):
* TLSv1.2 (OUT), handshake TLS, Finalizado (20):
* TLSv1.2 (IN), cifra de mudança TLS, Olá do cliente (1):
* TLSv1.2 (IN), handshake TLS, Finalizado (20):
* Conexão SSL usando TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, servidor aceito para usar http / 1.1
* Certificado do servidor:
* sujeito: C = LK; ST = ocidental; L = Colombo; O = LDCLAKMAL; OU = Engenharia; CN = Chanaka Lakmal; emailAddress=ldclakmal@gmail.com
* data de início: 20 de abril 03:03:58 2019 GMT
* data de validade: 19 de abril 03:03:58 2020 GMT
* emissor: C = LK; ST = ocidental; L = Colombo; O = LDCLAKMAL; OU = Engenharia; CN = Chanaka Lakmal; emailAddress=ldclakmal@gmail.com
* Resultado da verificação do certificado SSL: certificado autoassinado (18), continuando assim mesmo.
> POST / hello / sayOlá HTTP / 1.1
> Anfitrião: localhost: 9191
> Agente do Usuário: curl / 7.46.0
> Aceitar: * / *
> Comprimento do conteúdo: 9
> Tipo de conteúdo: application / x-www-form-urlencoded
>
* upload completamente enviado: 9 de 9 bytes
<HTTP / 1.1 200 OK
<Data: sábado, 20 de abril de 2019 06:56:19 GMT
<Comprimento do conteúdo: 10
<Tipo de conteúdo: texto / sem formatação; charset = utf-8
<
Olá, vá!
* Conexão # 0 para hospedar o host local deixado intacto
Cliente HTTP / 1.1
O novo código é um exemplo de gravação de um cliente HTTP / 1.1 simples no Go. Este cliente envia uma solicitação HTTP (S) POST para o
host local : 9191 / hello / sayHello com a mensagem "Hello Go!".
package main import ( "bytes" "crypto/tls" "crypto/x509" "fmt" "io/ioutil" "log" "net/http" ) func main() { client := &http.Client{}
Como o serviço HTTP (S) também começa aqui, você precisa fornecer um certificado e uma chave. Os dados necessários são colocados no diretório cert com os nomes server.crt e server.key.
Para iniciar o cliente, você precisa inicializar o servidor. Para fazer isso, executamos as ações descritas na primeira seção ou iniciamos qualquer serviço HTTP (S) com a porta 9191 e o caminho / hello / sayHello.
$ go run http_client.go
A saída deve ser assim:
Got response 200: HTTP/1.1 Hello Go!
O cliente mostra a operação do comando curl:
$ curl -v --cacert ./cert/server.crt https://localhost:9191/hello/sayHello -d "Hello Go!"
Depois de concluir o trabalho com o HTTP / 1.1, tente repetir o mesmo para o HTTP / 2.
Servidor HTTP / 2
Como na seção anterior, você deve primeiro criar um servidor de eco.
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))
Para verificar o servidor, você precisa enviar o comando:
$ go run http2_server.go
A resposta deve ser assim:
Go Backend: { HTTPVersion = 2 }; serving on https://localhost:9191/hello/sayHello
Agora o servidor está em execução, você pode inicializá-lo usando solicitações HTTP / 1.1 ou HTTP / 2. Os comandos HTTP / 1.1 POST para testar a operação do servidor são fornecidos abaixo:
$ 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!"
E a mesma coisa, mas com 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!"
Abaixo está um exemplo de saída da operação do servidor. A primeira parte é um handshake TLS entre o servidor e o cliente, a segunda são os detalhes da solicitação e resposta HTTP / 2.
* Tentando 127.0.0.1 ...
* Conectado à porta 9191 do host local (127.0.0.1) (# 0)
* ALPN, oferecendo h2
* ALPN, oferecendo http / 1.1
* Seleção de cifra: TODOS :! EXPORT :! EXPORT40 :! EXPORT56 :! ANULL :! LOW :! RC4: @STRENGTH
* definir com êxito o certificado verificar locais:
* CAfile: src / hello / cert / server.crt
CApath: nenhum
* TLSv1.2 (OUT), cabeçalho TLS, Status do certificado (22):
* TLSv1.2 (OUT), handshake TLS, olá cliente (1):
* TLSv1.2 (IN), handshake TLS, Olá ao servidor (2):
* TLSv1.2 (IN), handshake TLS, certificado (11):
* TLSv1.2 (IN), handshake TLS, troca de chaves do servidor (12):
* TLSv1.2 (IN), handshake TLS, servidor concluído (14):
* TLSv1.2 (OUT), handshake TLS, troca de chaves do cliente (16):
* TLSv1.2 (OUT), cifra de mudança TLS, Olá do cliente (1):
* TLSv1.2 (OUT), handshake TLS, Finalizado (20):
* TLSv1.2 (IN), cifra de mudança TLS, Olá do cliente (1):
* TLSv1.2 (IN), handshake TLS, Finalizado (20):
* Conexão SSL usando TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, servidor aceito para usar h2
* Certificado do servidor:
* sujeito: C = LK; ST = ocidental; L = Colombo; O = LDCLAKMAL; OU = Engenharia; CN = host local; emailAddress=ldclakmal@gmail.com
* data de início: 20 de abril às 05:27:33 2019 GMT
* data de validade: 19 de abril 05:27:33 2020 GMT
* nome comum: localhost (correspondente)
* emissor: C = LK; ST = ocidental; L = Colombo; O = LDCLAKMAL; OU = Engenharia; CN = host local; emailAddress=ldclakmal@gmail.com
* Certificado SSL verifique ok.
* Usando HTTP2, o servidor suporta multiuso
* Estado da conexão alterado (HTTP / 2 confirmado)
* TCP_NODELAY definido
* Copiando dados HTTP / 2 no buffer de fluxo para o buffer de conexão após a atualização: len = 0
* Usando o Stream ID: 1 (fácil manuseio 0x10ddf20)
> POST / hello / sayOlá HTTP / 1.1
> Anfitrião: localhost: 9191
> Agente do Usuário: curl / 7.46.0
> Aceitar: * / *
> Comprimento do conteúdo: 9
> Tipo de conteúdo: application / x-www-form-urlencoded
>
* Estamos completamente carregados e bem
<HTTP / 2.0 200
<tipo de conteúdo: texto / sem formatação; charset = utf-8
<comprimento do conteúdo: 10
<data: sábado, 20 abr 2019 06:54:50 GMT
<
Olá, vá!
* Conexão # 0 para hospedar o host local deixado intacto
Cliente HTTP / 2
A última parte está criando um cliente HTTP / 2. Aqui está a implementação do envio de uma solicitação POST HTTP (S) para
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{}
Como antes, você precisa de uma chave e um certificado para funcionar. Eles são armazenados em cert com os nomes server.crt e server.key.
Lançamento do cliente:
$ go run http2_client.go
E a resposta esperada:
Got response 200: HTTP/2 Hello Go!
Detalhando o cliente:
$ curl -v --http2 --cacert ./cert/server.crt https://localhost:9191/hello/sayHello -d "Hello Go!"
Isso é tudo. O trabalho é relativamente simples, mas fornece uma compreensão de como implementar serviços básicos de rede no Go.
A Skillbox recomenda: