Penulisan HTTP / 1.1 & HTTP / 2 Client dan Server di Golang



Golang adalah bahasa pemrograman yang luar biasa dengan beragam fitur. Artikel ini menunjukkan bagaimana Anda dapat menulis klien dan server on Go untuk protokol HTTP / 1.1 dan HTTP / 2.

Skillbox merekomendasikan: Pengembang Python dari awal tentu saja.

Kami mengingatkan Anda: untuk semua pembaca "Habr" - diskon 10.000 rubel saat mendaftar untuk kursus Skillbox menggunakan kode promo "Habr".

HTTP / 2 adalah versi utama kedua dari protokol jaringan HTTP yang digunakan untuk mengakses World Wide Web. Protokol didasarkan pada SPDY. Pelajari lebih lanjut tentang versi ini di GitHub .

HTTP / 1.1 Server


Kode di bawah ini menunjukkan cara menulis server HTTP / 1.1 di mana pun. Fungsi utama memulai layanan HTTP (S) dengan port 9191 dan path / hello / sayHello. echoPayload bertanggung jawab atas logika gema, menganalisis lalu lintas masuk dan bereaksi sesuai itu. Jika perlu, echoPayload dapat dimodifikasi.

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)) 

Karena layanan HTTP (S) sudah berjalan, Anda perlu memberikan sertifikat dan kunci server. Kedua objek disimpan di direktori cert dengan nama server.crt dan server.key.

Contoh sertifikat dan kunci di bawah ini.

./cert/server.crt

----- MEMULAI SERTIFIKAT -----
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
----- AKHIR SERTIFIKAT -----

./cert/server.key

----- KUNCI PRIVATE AWAL -----
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 =
----- AKHIR KUNCI PRIVATE -----

Sudah waktunya untuk menguji menjalankan layanan menggunakan perintah berikut:

 $ go run http_server.go 

Jawabannya harus seperti ini:

 Go Backend: { HTTPVersion = 1 }; serving on https://localhost:9191/hello/sayHello 

Sekarang kami memulai layanan menggunakan permintaan HTTP / 1.1 POST (mode tidak aman dan aman):

 $ 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!" 

Di bawah ini adalah hasil dari program ini. Bagian pertama adalah detail jabat tangan dari server dan klien TLS. Yang kedua adalah perincian permintaan dan tanggapan HTTP / 1.1. Pada akhirnya adalah pesan teks "Hello Go!".

* Mencoba 127.0.0.1 ...
* Terhubung ke localhost (127.0.0.1) port 9191 (# 0)
* ALPN, menawarkan http / 1.1
* Pilihan sandi: SEMUA:! EKSPOR:! EKSPOR40:! EKSPOR56:! ANULL :! RENDAH:! RC4: @STRENGTH
* Berhasil menetapkan lokasi verifikasi sertifikat:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: tidak ada
* TLSv1.2 (OUT), header TLS, Status Sertifikat (22):
* TLSv1.2 (OUT), jabat tangan TLS, Klien halo (1):
* TLSv1.2 (IN), jabat tangan TLS, Server halo (2):
* TLSv1.2 (IN), jabat tangan TLS, Sertifikat (11):
* TLSv1.2 (IN), jabat tangan TLS, pertukaran kunci Server (12):
* TLSv1.2 (IN), jabat tangan TLS, Server selesai (14):
* TLSv1.2 (OUT), jabat tangan TLS, pertukaran kunci Klien (16):
* TLSv1.2 (OUT), TLS ubah sandi, Klien halo (1):
* TLSv1.2 (OUT), jabat tangan TLS, Selesai (20):
* TLSv1.2 (IN), TLS ubah sandi, Klien halo (1):
* TLSv1.2 (IN), jabat tangan TLS, Selesai (20):
* Koneksi SSL menggunakan TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server diterima untuk menggunakan http / 1.1
* Sertifikat server:
* subjek: C = LK; ST = Barat; L = Colombo; O = LDCLAKMAL; OU = Teknik; CN = Chanaka Lakmal; emailAddress=ldclakmal@gmail.com
* tanggal mulai: 20 April 03:03:58 2019 GMT
* tanggal kedaluwarsa: 19 April 03:03:58 2020 GMT
* penerbit: C = LK; ST = Barat; L = Colombo; O = LDCLAKMAL; OU = Teknik; CN = Chanaka Lakmal; emailAddress=ldclakmal@gmail.com
* Hasil verifikasi sertifikat SSL: sertifikat yang ditandatangani sendiri (18), tetap berlanjut.
> POST / halo / sayaikan Halo HTTP / 1.1
> Host: localhost: 9191
> User-Agent: curl / 7.46.0
> Terima: * / *
> Panjang Konten: 9
> Tipe-Konten: application / x-www-form-urlencoded
>
* unggahan sepenuhnya dikirim: 9 dari 9 byte
<HTTP / 1.1 200 OK
<Tanggal: Sab, 20 April 2019 06:56:19 GMT
<Panjang Konten: 10
<Tipe-Konten: teks / polos; charset = utf-8
<
Halo pergi!
* Koneksi # 0 untuk meng-host localhost dibiarkan utuh

Klien HTTP / 1.1


Kode baru adalah contoh penulisan klien HTTP / 1.1 sederhana di Go. Klien ini mengirimkan permintaan POST HTTP (S) ke localhost : 9191 / hello / sayHello dengan pesan "Hello Go!".

 package main import ( "bytes" "crypto/tls" "crypto/x509" "fmt" "io/ioutil" "log" "net/http" ) func main() { client := &http.Client{} // Create a pool with the server certificate since it is not signed // by a known CA caCert, err := ioutil.ReadFile("./cert/server.crt") if err != nil { log.Fatalf("Reading server certificate: %s", err) } caCertPool := x509.NewCertPool() caCertPool.AppendCertsFromPEM(caCert) // Create TLS configuration with the certificate of the server tlsConfig := &tls.Config{ RootCAs: caCertPool, } // Use the proper transport in the client client.Transport = &http.Transport{ TLSClientConfig: tlsConfig, } // Perform the request resp, err := client.Post("https://localhost:9191/hello/sayHello", "text/plain", bytes.NewBufferString("Hello Go!")) if err != nil { log.Fatalf("Failed get: %s", err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { log.Fatalf("Failed reading response body: %s", err) } fmt.Printf("Got response %d: %s %s", resp.StatusCode, resp.Proto, string(body)) 

Karena layanan HTTP (S) juga dimulai di sini, Anda perlu memberikan sertifikat dan kunci. Data yang diperlukan ditempatkan di direktori cert dengan nama server.crt dan server.key.

Untuk memulai klien, Anda perlu menginisialisasi server. Untuk melakukan ini, kami melakukan tindakan yang dijelaskan di bagian pertama, atau kami memulai layanan HTTP (S) apa pun dengan port 9191 dan path / hello / sayHello.

 $ go run http_client.go 

Outputnya harus seperti ini:

 Got response 200: HTTP/1.1 Hello Go! 

Klien menunjukkan operasi perintah curl:

 $ curl -v --cacert ./cert/server.crt https://localhost:9191/hello/sayHello -d "Hello Go!" 

Setelah Anda selesai bekerja dengan HTTP / 1.1, Anda harus mencoba mengulangi hal yang sama untuk HTTP / 2.

HTTP / 2 Server


Seperti pada bagian sebelumnya, Anda harus terlebih dahulu membuat server gema.

 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)) 

Untuk memeriksa server, Anda perlu mengirim perintah:

 $ go run http2_server.go 

Jawabannya harus seperti ini:

 Go Backend: { HTTPVersion = 2 }; serving on https://localhost:9191/hello/sayHello 

Sekarang server sedang berjalan, Anda dapat menginisialisasi dengan menggunakan permintaan HTTP / 1.1 atau HTTP / 2. Perintah HTTP / 1.1 POST untuk menguji operasi server diberikan di bawah ini:

 $ 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!" 

Dan hal yang sama, tetapi dengan 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!" 

Di bawah ini adalah contoh dari output operasi server. Bagian pertama adalah jabat tangan TLS antara server dan klien, yang kedua adalah rincian permintaan dan tanggapan HTTP / 2.

* Mencoba 127.0.0.1 ...
* Terhubung ke localhost (127.0.0.1) port 9191 (# 0)
* ALPN, menawarkan h2
* ALPN, menawarkan http / 1.1
* Pilihan sandi: SEMUA:! EKSPOR:! EKSPOR40:! EKSPOR56:! ANULL :! RENDAH:! RC4: @STRENGTH
* Berhasil menetapkan lokasi verifikasi sertifikat:
* CAfile: src / hello / cert / server.crt
CApath: tidak ada
* TLSv1.2 (OUT), header TLS, Status Sertifikat (22):
* TLSv1.2 (OUT), jabat tangan TLS, Klien halo (1):
* TLSv1.2 (IN), jabat tangan TLS, Server halo (2):
* TLSv1.2 (IN), jabat tangan TLS, Sertifikat (11):
* TLSv1.2 (IN), jabat tangan TLS, pertukaran kunci Server (12):
* TLSv1.2 (IN), jabat tangan TLS, Server selesai (14):
* TLSv1.2 (OUT), jabat tangan TLS, pertukaran kunci Klien (16):
* TLSv1.2 (OUT), TLS ubah sandi, Klien halo (1):
* TLSv1.2 (OUT), jabat tangan TLS, Selesai (20):
* TLSv1.2 (IN), TLS ubah sandi, Klien halo (1):
* TLSv1.2 (IN), jabat tangan TLS, Selesai (20):
* Koneksi SSL menggunakan TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server diterima untuk menggunakan h2
* Sertifikat server:
* subjek: C = LK; ST = Barat; L = Colombo; O = LDCLAKMAL; OU = Teknik; CN = localhost; emailAddress=ldclakmal@gmail.com
* tanggal mulai: 20 April 05:27:33 2019 GMT
* tanggal kedaluwarsa: 19 April 05:27:33 2020 GMT
* nama umum: localhost (cocok)
* penerbit: C = LK; ST = Barat; L = Colombo; O = LDCLAKMAL; OU = Teknik; CN = localhost; emailAddress=ldclakmal@gmail.com
* Sertifikat SSL memverifikasi ok.
* Menggunakan HTTP2, server mendukung multi guna
* Status koneksi berubah (HTTP / 2 dikonfirmasi)
* Set TCP_NODELAY
* Menyalin data HTTP / 2 dalam buffer aliran ke buffer koneksi setelah peningkatan: len = 0
* Menggunakan Stream ID: 1 (pegangan mudah 0x10ddf20)
> POST / halo / sayaikan Halo HTTP / 1.1
> Host: localhost: 9191
> User-Agent: curl / 7.46.0
> Terima: * / *
> Panjang Konten: 9
> Tipe-Konten: application / x-www-form-urlencoded
>
* Kami sepenuhnya diunggah dan baik-baik saja
<HTTP / 2.0 200
<tipe konten: teks / polos; charset = utf-8
<panjang konten: 10
<tanggal: Sab, 20 April 2019 06:54:50 GMT
<
Halo pergi!
* Koneksi # 0 untuk meng-host localhost kiri utuh

HTTP / 2 Klien


Bagian terakhir adalah membuat klien HTTP / 2. Berikut adalah implementasi pengiriman permintaan POST HTTP (S) untuk 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{} // Create a pool with the server certificate since it is not signed // by a known CA caCert, err := ioutil.ReadFile("./cert/server.crt") if err != nil { log.Fatalf("Reading server certificate: %s", err) } caCertPool := x509.NewCertPool() caCertPool.AppendCertsFromPEM(caCert) // Create TLS configuration with the certificate of the server tlsConfig := &tls.Config{ RootCAs: caCertPool, } // Use the proper transport in the client client.Transport = &http2.Transport{ TLSClientConfig: tlsConfig, } // Perform the request resp, err := client.Post("https://localhost:9191/hello/sayHello", "text/plain", bytes.NewBufferString("Hello Go!")) if err != nil { log.Fatalf("Failed get: %s", err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { log.Fatalf("Failed reading response body: %s", err) } fmt.Printf("Got response %d: %s %s", resp.StatusCode, resp.Proto, string(body)) 

Seperti sebelumnya, Anda memerlukan kunci dan sertifikat untuk berfungsi. Mereka disimpan dalam sertifikat dengan nama server.crt dan server.key.

Peluncuran klien:

 $ go run http2_client.go 

Dan jawaban yang diharapkan:

 Got response 200: HTTP/2 Hello Go! 

Merinci pekerjaan klien:

 $ curl -v --http2 --cacert ./cert/server.crt https://localhost:9191/hello/sayHello -d "Hello Go!" 

Itu saja. Pekerjaan ini relatif sederhana, tetapi memberikan pemahaman tentang bagaimana menerapkan layanan jaringan dasar di Go.

Skillbox merekomendasikan:

Source: https://habr.com/ru/post/id451032/


All Articles