
Golang هي لغة برمجة رائعة مع مجموعة واسعة من الميزات. يوضح هذا المقال كيف يمكنك كتابة عميل وخادم على Go لبروتوكولي HTTP / 1.1 و HTTP / 2.
توصي Skillbox بما يلي: The Python Developer من البداية إلى نقطة الصفر .
نذكرك: لجميع قراء "Habr" - خصم بقيمة 10،000 روبل عند التسجيل في أي دورة تدريبية في Skillbox باستخدام الرمز الترويجي "Habr".
HTTP / 2 هو الإصدار الرئيسي الثاني من بروتوكول شبكة HTTP المستخدم للوصول إلى شبكة الويب العالمية. يعتمد البروتوكول على SPDY. تعرف على المزيد حول هذا الإصدار
على جيثب .
خادم HTTP / 1.1
يوضح الرمز أدناه كيفية كتابة خادم HTTP / 1.1 على Go. الوظيفة الرئيسية تبدأ خدمة HTTP (S) مع المنفذ 9191 والمسار / hello / sayHello. echoPayload هو المسؤول عن منطق الصدى ، وتحليل حركة المرور الواردة والرد وفقا لذلك. إذا لزم الأمر ، يمكن تعديل echoPayload.
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))
نظرًا لأن خدمة HTTP (S) قيد التشغيل بالفعل ، فأنت بحاجة إلى تقديم شهادة ومفتاح خادم. يتم تخزين كل من الكائنات في الدليل سيرت مع أسماء server.crt و server.key.
مثال على الشهادة والمفتاح أدناه.
./cert/server.crt
----- شهادة بدء -----
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
KZHbfq7eYDE5EIt3cZp5ySSscdTEay9ReH2 + 8k32gpH46CMwPV3XvtQuBVVAC4u
szrq1eWKhYI2zf4iUVpwvq89OynVGIp0atng + q3A2cBhi3NGo6Ho1s2rywQyqiq8
up4PUSVQ6WBoJFx5PEEDxD84VMS7Pan6dT34b9n56tq5R06retZTUZ8jMM88CGX4
88pSPU + XImp6DdNVBmW6Lz76jiSNHLkZGm4jumjeyUGzBjBEBOgSegeWlinMtWE9
gaVxeUHrqHk8xzwJ4oIu
----- شهادة النهاية -----
./cert/server.key
----- بداية مفتاح خاص -----
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 =
----- نهاية مفتاح خاص -----
حان الوقت لاختبار تشغيل الخدمة باستخدام الأمر التالي:
$ go run http_server.go
يجب أن يكون الجواب مثل هذا:
Go Backend: { HTTPVersion = 1 }; serving on https://localhost:9191/hello/sayHello
نبدأ الآن الخدمة باستخدام طلب HTTP / 1.1 POST (أوضاع غير آمنة وآمنة):
$ 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!"
أدناه هو نتيجة البرنامج. الجزء الأول هو تفاصيل مصافحة خادم TLS والعميل. والثاني هو تفاصيل طلب HTTP / 1.1 والاستجابة له. في النهاية توجد رسالة نصية "Hello Go!".
* جرب 127.0.0.1 ...
* متصلاً المضيف المحلي (127.0.0.1) المنفذ 9191 (# 0)
* ALPN ، تقدم http / 1.1
* اختيار التشفير: ALL:! EXPORT:! EXPORT40:! EXPORT56:! ANULL:! LOW:! RC4: @ STRENGTH
* بنجاح تعيين شهادة التحقق من المواقع:
* CAfile: /etc/ssl/certs/ca-certificates.crt
كاباث: لا شيء
* TLSv1.2 (OUT) ، رأس TLS ، حالة الشهادة (22):
* TLSv1.2 (OUT) ، مصافحة TLS ، عميل الترحيب (1):
* TLSv1.2 (IN) ، مصافحة TLS ، خادم الترحيب (2):
* TLSv1.2 (IN) ، مصافحة TLS ، الشهادة (11):
* TLSv1.2 (IN) ، مصافحة TLS ، تبادل مفتاح الخادم (12):
* TLSv1.2 (IN) ، مصافحة TLS ، انتهى الخادم (14):
* TLSv1.2 (OUT) ، مصافحة TLS ، تبادل مفتاح العميل (16):
* TLSv1.2 (OUT) ، تغيير شفرة TLS ، مرحباً بالعميل (1):
* TLSv1.2 (OUT) ، مصافحة TLS ، انتهى (20):
* TLSv1.2 (IN) ، تشفير TLS ، عميل مرحبا (1):
* TLSv1.2 (IN) ، مصافحة TLS ، انتهى (20):
* اتصال SSL باستخدام TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN ، تم قبول الخادم لاستخدام http / 1.1
* شهادة الخادم:
* الموضوع: C = LK ؛ ST = الغربية ؛ لام = كولومبو O = LDCLAKMAL ؛ OU = الهندسة ؛ CN = Chanaka Lakmal؛ emailAddress=ldclakmal@gmail.com
* تاريخ البدء: 20 أبريل 03:03:58 2019 بتوقيت جرينتش
* تاريخ انتهاء الصلاحية: 19 أبريل 03:03:58 2020 بتوقيت جرينتش
* المصدر: C = LK ؛ ST = الغربية ؛ لام = كولومبو O = LDCLAKMAL ؛ OU = الهندسة ؛ CN = Chanaka Lakmal؛ emailAddress=ldclakmal@gmail.com
* شهادة SSL تحقق من النتيجة: شهادة موقعة ذاتيا (18) ، تستمر على أي حال.
> POST / hello / sayHello HTTP / 1.1
> المضيف: المضيف المحلي: 9191
> وكيل المستخدم: curl / 7.46.0
> قبول: * / *
> طول المحتوى: 9
> نوع المحتوى: application / x-www-form-urlencoded
>
* تحميل تم إرساله بالكامل: 9 من أصل 9 بايت
<HTTP / 1.1 200 موافق
<التاريخ: السبت ، 20 أبريل 2019 06:56:19 بتوقيت جرينتش
<طول المحتوى: 10
<نوع المحتوى: نص / عادي ؛ charset = utf-8
<
مرحبا اذهب!
* اتصال # 0 لاستضافة مضيف محلي اليسار
عميل HTTP / 1.1
الرمز الجديد هو مثال لكتابة عميل HTTP / 1.1 بسيط على Go. يرسل هذا العميل طلب POST HTTP (S) إلى
المضيف المحلي: 9191 / hello / sayHello مع الرسالة "Hello Go!".
package main import ( "bytes" "crypto/tls" "crypto/x509" "fmt" "io/ioutil" "log" "net/http" ) func main() { client := &http.Client{}
نظرًا لأن خدمة HTTP (S) تبدأ أيضًا من هنا ، فأنت بحاجة إلى تقديم شهادة ومفتاح. يتم وضع البيانات اللازمة في الدليل سيرت مع أسماء server.crt و server.key.
من أجل بدء العميل ، تحتاج إلى تهيئة الخادم. للقيام بذلك ، نقوم بتنفيذ الإجراءات الموضحة في القسم الأول ، أو نبدأ تشغيل أي خدمة HTTP (S) مع المنفذ 9191 والمسار / hello / sayHello.
$ go run http_client.go
يجب أن يكون الإخراج مثل هذا:
Got response 200: HTTP/1.1 Hello Go!
يعرض العميل تشغيل الأمر curl:
$ curl -v --cacert ./cert/server.crt https://localhost:9191/hello/sayHello -d "Hello Go!"
بعد الانتهاء من العمل مع HTTP / 1.1 ، يجب أن تحاول تكرار نفس الشيء بالنسبة HTTP / 2.
خادم HTTP / 2
كما في القسم السابق ، يجب عليك أولاً إنشاء خادم صدى.
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))
من أجل التحقق من الخادم ، تحتاج إلى إرسال الأمر:
$ go run http2_server.go
يجب أن يكون الجواب مثل هذا:
Go Backend: { HTTPVersion = 2 }; serving on https://localhost:9191/hello/sayHello
الآن الخادم قيد التشغيل ، يمكنك تهيئته باستخدام طلبات HTTP / 1.1 أو HTTP / 2. فيما يلي أوامر HTTP / 1.1 POST لاختبار تشغيل الخادم:
$ 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!"
ونفس الشيء ، ولكن مع 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!"
يوجد أدناه مثال على إخراج عملية الخادم. الجزء الأول هو مصافحة TLS بين الخادم والعميل ، والثاني هو تفاصيل طلب HTTP / 2 والاستجابة له.
* جرب 127.0.0.1 ...
* متصلاً المضيف المحلي (127.0.0.1) المنفذ 9191 (# 0)
* ALPN ، تقدم h2
* ALPN ، تقدم http / 1.1
* اختيار التشفير: ALL:! EXPORT:! EXPORT40:! EXPORT56:! ANULL:! LOW:! RC4: @ STRENGTH
* بنجاح تعيين شهادة التحقق من المواقع:
* CAfile: src / hello / cert / server.crt
كاباث: لا شيء
* TLSv1.2 (OUT) ، رأس TLS ، حالة الشهادة (22):
* TLSv1.2 (OUT) ، مصافحة TLS ، عميل الترحيب (1):
* TLSv1.2 (IN) ، مصافحة TLS ، خادم الترحيب (2):
* TLSv1.2 (IN) ، مصافحة TLS ، الشهادة (11):
* TLSv1.2 (IN) ، مصافحة TLS ، تبادل مفتاح الخادم (12):
* TLSv1.2 (IN) ، مصافحة TLS ، انتهى الخادم (14):
* TLSv1.2 (OUT) ، مصافحة TLS ، تبادل مفتاح العميل (16):
* TLSv1.2 (OUT) ، تغيير شفرة TLS ، مرحباً بالعميل (1):
* TLSv1.2 (OUT) ، مصافحة TLS ، انتهى (20):
* TLSv1.2 (IN) ، تشفير TLS ، عميل مرحبا (1):
* TLSv1.2 (IN) ، مصافحة TLS ، انتهى (20):
* اتصال SSL باستخدام TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN ، تم قبول الخادم لاستخدام h2
* شهادة الخادم:
* الموضوع: C = LK ؛ ST = الغربية ؛ لام = كولومبو O = LDCLAKMAL ؛ OU = الهندسة ؛ CN = مضيف محلي ؛ emailAddress=ldclakmal@gmail.com
* تاريخ البدء: 20 أبريل 05:27:33 2019 بتوقيت جرينتش
* تاريخ انتهاء الصلاحية: 19 أبريل 05:27:33 2020 بتوقيت جرينتش
* الاسم الشائع: مضيف محلي (متطابق)
* المصدر: C = LK ؛ ST = الغربية ؛ لام = كولومبو O = LDCLAKMAL ؛ OU = الهندسة ؛ CN = مضيف محلي ؛ emailAddress=ldclakmal@gmail.com
* شهادة SSL تحقق موافق.
* باستخدام HTTP2 ، يدعم الخادم الاستخدام المتعدد
* تم تغيير حالة الاتصال (تم تأكيد HTTP / 2)
* مجموعة TCP_NODELAY
* نسخ بيانات HTTP / 2 في المخزن المؤقت للتيار إلى المخزن المؤقت للاتصال بعد الترقية: len = 0
* باستخدام Stream ID: 1 (مقبض سهل 0x10ddf20)
> POST / hello / sayHello HTTP / 1.1
> المضيف: المضيف المحلي: 9191
> وكيل المستخدم: curl / 7.46.0
> قبول: * / *
> طول المحتوى: 9
> نوع المحتوى: application / x-www-form-urlencoded
>
* نحن يتم تحميلها بالكامل وغرامة
<HTTP / 2.0 200
<نوع المحتوى: نص / عادي ؛ charset = utf-8
<طول المحتوى: 10
<التاريخ: السبت 20 أبريل 2019 06:54:50 بتوقيت جرينتش
<
مرحبا اذهب!
* اتصال # 0 لاستضافة مضيف محلي اليسار
عميل HTTP / 2
الجزء الأخير هو إنشاء عميل HTTP / 2. في ما يلي تنفيذ إرسال طلب HTTP (S) POST لـ
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{}
كما كان من قبل ، فأنت بحاجة إلى مفتاح وشهادة للعمل. يتم تخزينها في سيرت مع أسماء server.crt و server.key.
إطلاق العميل:
$ go run http2_client.go
والإجابة المتوقعة:
Got response 200: HTTP/2 Hello Go!
تفصيل عمل العميل:
$ curl -v --http2 --cacert ./cert/server.crt https://localhost:9191/hello/sayHello -d "Hello Go!"
هذا كل شيء. العمل بسيط نسبيًا ، لكنه يعطي فهمًا لكيفية تنفيذ خدمات الشبكة الأساسية في Go.
توصي Skillbox بما يلي: