הנחיות לגבי HTTP

חשוב להבין איך Google APIs פועלים עם גרסאות שונות של HTTP ועם הטמעות שונות, אם אתם כותבים קוד בהתאמה אישית שמאפשר גישה לממשק REST של API באמצעות צד הלקוח ב-HTTP של צד שלישי. ברוב עומסי העבודה, מומלץ להשתמש בספריות הלקוח המומלצות של Google, שמטפלות אוטומטית בתקשורת ברמת השרת שמתוארת במסמך הזה.

עבודה עם פרוטוקולים של רשת (HTTP/*)

בקטע הזה מתוארים פרוטוקולי התקשורת הנתמכים (בדרך כלל גרסה של HTTP) שבהם ממשקי Google API יכולים להשתמש כדי לתקשר בין לקוחות לשרתים, ואיך אנחנו ממליצים להשתמש בהם.

סמנטיקה של HTTP

כשמפתחים קוד של לקוח API, צריך לפעול לפי הסמנטיקה הסטנדרטית של פרוטוקול HTTP. יכול להיות ששרתי proxy בצד השרת או סטאקים של API יתמכו רק בחלק מהתכונות הרגילות של HTTP, ויכול להיות שהם יתמכו גם בגרסאות שלהם שתואמות לדורות קודמים.

מחסנית השרת שולטת בסמנטיקה של פרוטוקול HTTP, שצריך לטפל בה בהטמעות של ממשקי API בצד השרת. אפשר להסתמך על סמנטיקה כזו רק אם התכונות האלה מתועדות באופן מפורש כחלק ממפרט ה-API, כמו תמיכה במטמון.

גרסאות HTTP

הלקוחות יכולים להשתמש בכל פרוטוקול HTTP/* ‎, בהתאם למה שמותר בפלטפורמת הלקוח או ברשת בצד הלקוח, או בהתאם למה שסוכם עם שרת ה-proxy בצד השרת. הפרוטוקולים הנתמכים כוללים את HTTP/1.1,‏ HTTP/2 ו-HTTP/3 ‏ (QUIC). לא מומלץ להשתמש בתמיכה מדור קודם ב-HTTP/1.0.

יכול להיות שחלק מהתכונות של ה-API נתמכות רק בגרסאות חדשות יותר של פרוטוקולי HTTP. חלק מהתכונות מוגדרות באופן מלא רק ב-HTTP/2 וב-HTTP/3, כמו סטרימינג ב-multiplexing ב-full-duplex. חשוב להיות מודעים למגבלות של גרסאות HTTP שונות אם אתם צריכים אחת מהתכונות האלה כחלק ממפרט ה-API. שימו לב שתכונות ישנות יותר, כמו דחיפת שרת ב-HTTP/2, הוצאו משימוש ולא נתמכות על ידי לקוחות מודרניים בדפדפן.

באופן כללי, אנחנו ממליצים להשתמש ב-HTTP/3 או ב-HTTP/2 כדי לשפר את הביצועים, לצמצם את חסימת הראש של התור ולשפר את העמידות בפני כשלים ברשת.

ערוצים

המונח 'ערוצים' מתייחס לחיבורי רשת בשכבה 4, שהם בדרך כלל שקעי TCP עבור HTTP/1.1 ו-HTTP/2, ושקעי UDP עבור HTTP/3 ‏ (QUIC). אפליקציות לקוח לא צריכות להניח הנחות לגבי האופן שבו הערוצים מנוהלים מקצה לקצה, כי החיבורים כמעט תמיד מסתיימים על ידי שרתי proxy של ממשק הקצה של Google‏ (GFE) בשם תהליך השרת.

לקוחות HTTP/1.1: אם משתמשים ב-HTTP/1.1, צריך תמיד להשתמש מחדש בחיבורי TCP (חיבור: Keep-Alive). ספריות לקוח של HTTP בדרך כלל מנהלות מאגר חיבורים כדי לאפשר שימוש חוזר. הימנעו מ-HTTP pipelining בחיבורי HTTP/1.1. התמיכה בו מוגבלת והוא עלול לגרום לבעיות. מידע נוסף זמין במאמר HTTP ו-TCP.

לקוחות HTTP/2 ו-HTTP/3: לקוחות ודפדפנים מודרניים משתמשים בעיקר ב-HTTP/2 או ב-HTTP/3. שני הפרוטוקולים תומכים בריבוב (multiplexing), שמאפשר לשלוח כמה בקשות ותגובות בו-זמנית דרך חיבור אחד.

  • ‫HTTP/2 – משתמש בחיבור TCP אחד לכל מקור.
  • ‫HTTP/3 – משתמש בחיבור QUIC יחיד דרך UDP לכל מקור. פרוטוקול QUIC משלב הצפנת TLS, בקרת עומס וניהול חיבורים, ולרוב מספק יתרונות כמו הגדרת חיבור מהירה יותר (0-RTT או 1-RTT) וחסינות לחסימת ראש התור של TCP בסטרימינג.

ב-HTTP/2 וב-HTTP/3, מגבלות הדפדפן על מספר חיבורי ה-TCP המקבילים למארח יחיד (לדוגמה, 2-10) כבר לא מהוות בעיית ביצועים עיקרית. עם זאת, חשוב לדעת ששרתים (או שרתי proxy כמו GFE) עדיין יכולים לאכוף מגבלות על המספר המקסימלי של סטרימינג בו-זמני בחיבור יחיד של HTTP/2 או HTTP/3. כך אפשר למנוע עומס יתר ולהבטיח שימוש הוגן במשאבים (לדוגמה, על ידי הגבלת מספר הבקשות המקבילות או הסטרימינג בו-זמנית ל-100 לכל חיבור).

HTTPS

לקוחות יכולים לגשת ל-API באמצעות HTTPS או HTTP, בהתאם לתמיכה של מפרט ה-API. משא ומתן על TLS וגרסאות TLS הם שקופים לאפליקציות הלקוח. כברירת מחדל, ממשקי Google API מקבלים רק תעבורת נתונים ב-HTTPS.

פורמטים של בקשות ותשובות

בקטע הזה מתוארים המבנה של האינטראקציות עם ה-API, כולל השימוש בנתונים בקידוד URL, שיטות HTTP ספציפיות לפעולות RESTful ופורמטים של מטען ייעודי (payload) שמבוססים על JSON.

כתובות URL של בקשות

מיפוי JSON-REST תומך בנתוני בקשות בקידוד URL, וגוף בקשת HTTP והתגובה משתמש ב-application/json בתור Content-Type.

גוף ה-HTTP משתמש במערך JSON כדי לתמוך בשיטות RPC בסטרימינג, ומערך ה-JSON יכול להכיל מספר כלשהו של הודעות JSON או הודעת JSON של סטטוס שגיאה.

כתובות URL ארוכות של בקשות

לכתובת ה-URL יש מגבלת אורך מעשית, שבדרך כלל מוגדרת כברירת מחדל ל-16KB, אבל היא עשויה להשתנות בהתאם לשרת. אם ה-API שלכם משתמש בבקשות GET עם כתובות URL שאורכן חורג מהאורך הזה, יכול להיות שהבקשות לא יגיעו לשרת ה-API של היעד ויידחו על ידי ממשק הקצה של Google‏ (GFE) עם הודעת השגיאה Your client has issued a malformed or illegal request.

כדי לעקוף את המגבלה, קוד הלקוח צריך להשתמש בבקשת POST עם Content-Type של application/x-www-form-urlencoded, יחד עם כותרת ה-HTTP‏ X-HTTP-Method-Override: GET. הגישה הזו מתאימה גם לבקשות DELETE.

שיטות HTTP (פעלים)

אם כתובות ה-URL של הבקשות פועלות לפי מודל REST, שיטות ה-HTTP שלהן מצוינות כחלק ממפרט ה-API. בפרט, כל שיטת API חייבת לעמוד בדרישות של פרוטוקול ה-HTTP, בהתאם לפועל ה-HTTP הספציפי שאליו ממופה שיטת ה-API. מידע נוסף זמין במפרט של Hypertext Transfer Protocol וב-RFC של PATCH Method.

שיטות בטוחות, כמו HTTP GET ו-HEAD, לא אמורות לייצג פעולה אחרת מלבד אחזור. במילים אחרות, בקשות HTTP GET צריכות להיחשב בטוחות ולא אמורות להיות להן תופעות לוואי שגלויות ללקוח.

אידמפוטנטיות ב-HTTP פירושה שההשפעות של מספר בקשות זהות זהות לאלה של בקשה אחת. ‫GET,‏ PUT ו-DELETE הן שיטות HTTP אידמפוטנטיות שרלוונטיות למדריך הסגנון. חשוב לציין שהאידמפוטנטיות מתבטאת רק במונחים של תופעות לוואי בצד השרת, ולא מציינת דבר לגבי התגובה. באופן ספציפי, DELETE למשאבים שלא קיימים צריך להחזיר 404 (Not Found).

‫HTTP POST ו-PATCH הם לא בטוחים ולא אידמפוטנטיים. (PATCH הוצג ב-RFC 5789)

פועל HTTP בטוח אידמפוטנטי
GET כן כן
PUT   כן
DELETE   כן
POST  
PATCH  

פורמטים של מטען ייעודי (payload)

  • הבקשה והתגובה צריכות להיות מאותו סוג תוכן, אלא אם הבקשה היא מסוג GET או POST עם גוף מסוג application/x-www-form-urlencoded.

  • יש תמיכה ב-JSON בסוג MIME‏ application/json. המיפוי מ-proto3 ל-JSON מפורט באופן רשמי במיפוי JSON.

  • אפשר להשתמש בפרמטרים של טופס (POST) במקום בפרמטרים של שאילתה בכתובת URL (GET), לפי אותו כלל מיפוי בסגנון REST למיפוי שדות של בקשות לפרמטרים של שאילתות. הערך הנתמך של Content-Type הוא application/x-www-form-urlencoded.

סטרימינג

בקטע הזה מוסבר איך Google APIs מטפלים בהזרמת נתונים (streaming) של לקוחות ושרתים, ומתייחסים באופן ספציפי למגבלות של תקשורת half-duplex לעומת תקשורת full-duplex, ולדרישות הקידוד של הודעות JSON שמועברות בסטרימינג.

‫Half-duplex לעומת full-duplex

‫HTTP הוא פרוטוקול בקשה-תגובה שמאפשר להעביר את גוף הבקשה או התגובה דרך אמצעי תעבורה שונים שמכוונים לסטרימינג, כמו TCP ‏ (HTTP/1.x) או הווריאציות המרובות שלו (SPDY,‏ HTTP/2,‏ QUIC).

כמפתחי לקוח, האפליקציה שלכם יכולה ליצור את גוף הבקשה במצב סטרימינג, כלומר סטרימינג בצד הלקוח. באופן דומה, האפליקציה יכולה גם לצרוך את גוף התגובה במצב סטרימינג, כלומר סטרימינג מהשרת.

עם זאת, במפרט HTTP לא מצוין אם לשרת מותר להזרים בחזרה את גוף התגובה (למעט תגובות שגיאה) כשגוף הבקשה עדיין בהמתנה. הסמנטיקה הזו נקראת סטרימינג ב-full-duplex. למרות שתוכנות רבות של לקוחות/שרתים/פרוקסי HTTP מאפשרות full-duplex streaming, גם עבור HTTP/1.1, כדי למנוע בעיות תאימות, Cloud APIs מבוססי-HTTP בענן מוגבלים ל-half-duplex streaming בלבד.

כברירת מחדל, שיטות סטרימינג דו-כיווניות ב-Cloud APIs מניחות את הסמנטיקה של full-duplex. כלומר, לא בטוח להשתמש ב-HTTP כדי להפעיל שיטה כזו. אם שיטת סטרימינג היא רק half-duplex (כפי שנאכף על ידי השרת), במסמך ה-API צריך לציין בבירור את התנהגות ה-half-duplex.

בלקוחות דפדפן, הסמנטיקה הרגילה של HTTP מוגבלת עוד יותר על ידי ממשקי ה-API של הרשת בדפדפן. דפדפנים תומכים בהזרמת נתונים מהשרת (שבדרך כלל מכבדת מסגור ברמת התעבורה) באמצעות XHR או Fetch. ‫Fetch API משתמש ב-whatwg streams.

בגלל מגבלות של הדפדפן, ממשקי Cloud API שדורשים תמיכה בדפדפן צריכים להימנע מסטרימינג בצד הלקוח ומסטרימינג ב-full-duplex, או לספק API נפרד במיוחד ללקוחות בדפדפן.

באופן כללי, סטרימינג בצד הלקוח דרך האינטרנט פחות שימושי מסטרימינג בצד השרת. הסיבה לכך היא ששימוש בסטרימינג מהלקוח מוביל לעיתים קרובות לשירות עם מצב, מה שמשפיע לרעה על איזון העומסים והופך את המערכת לפגיעה יותר לכשלים או להתקפות. לעומת זאת, סטרימינג בצד השרת יכול להיות שימושי כי הוא יכול להפחית באופן משמעותי את זמן האחזור ברשתות עם עיכובים ארוכים ב-RTT.

קידוד ההודעה

הודעות JSON בסטרימינג מקודדות כמערך של הודעות JSON. גוף הבקשה או התגובה יישאר כסוג MIME תקין של JSON.

דוגמה לקידוד של סטרימינג בלקוח:

1 <length> <message-bytes> 1 <length> <message-bytes>  … EOF

דוגמה לקידוד של סטרימינג בשרת:

1 <length> <message-bytes>  … 2 <length> <status-bytes> EOF

קידוד ברמת החיבור: ההגדרה של StreamBody משמעותית רק בהקצאה של מזהי תגים לשדות "הודעות" ו "סטטוס" <length>, שיקודדו כ-varint עם 1-2 בייטים להודעות רגילות, כך שהתקורה הכוללת של הקידוד היא 2-3 בייטים לכל הודעה.

כדי לתמוך בסטרימינג בקידוד base64, צריך שדה אופציונלי של אפסים:

message StreamBody {
  repeated bytes message = 1;
  google.rpc.Status status = 2;
  repeated bytes padding = 15;   // max one-byte tag-id: xxx01111
}

הודעות השגיאה צריכות להתווסף כרכיב האחרון במערך ה-JSON או ה-protobuf, באותו פורמט כמו הודעות רגילות.

ניהול מצב

התנהגות של סגירה חלקית מוגדרת היטב בכל גרסה של HTTP עבור לקוח או שרת, כדי לסמן לצד השני שהגוף הושלם.

באופן ספציפי, קוד הלקוח יכול להשלים את הבקשה בזמן שהוא עדיין ממתין לתגובה. באופן דומה, יכול להיות שהלקוח יראה תגובה מלאה בזמן שגוף הבקשה עדיין נכתב בשרת. לפי תקן ה-HTTP, הלקוח אמור לבטל או להשלים את הבקשה כשהתגובה מסתיימת באופן לא צפוי, בדרך כלל עם סטטוס שגיאה. כלומר, בתנאים רגילים השרת לא אמור להשלים את התגובה כשהלקוח עדיין שולח את הבקשה.

ביטול

תמיכה בביטול מאפשרת ללקוח לבטל בקשה כשהבקשה או התגובה עדיין בהמתנה.

אין תמיכה אמינה בביטול ללקוחות HTTP/1.* ‎, כי הלקוח יכול לסגור חיבור TCP אחרי שהבקשה הושלמה בלי לבטל את טרנזקציית הבקשה או התגובה. ב-HTTP/1.1, אין לפרש TCP FIN כביטול, גם כשהחיבור מסומן כחיבור פעיל (Connection: Keep-Alive).

עם זאת, אחרי שהלקוח סוגר את חיבור ה-TCP, אם השרת מנסה לכתוב נתונים ללקוח, נוצר RST שיכול להפעיל ביטול.

חשוב לציין שביטול הוא בעיה גם בממשקי API שאינם סטרימינג. זה נכון במיוחד כשהתגובה כוללת דגימה ארוכה, ולכן החיבור עשוי להישאר במצב של חוסר פעילות למשך זמן רב.

ביטול מפורש נתמך ב-SPDY, ב-HTTP/2 וב-QUIC, במיוחד עם הודעת go-away.

Keep-alive

תמיכה ב-keep-alive מאפשרת ללקוח או לשרת לזהות עמית שנכשל, גם במקרה של אובדן מנות או כשלים ברשת.

אין תמיכה ב-keep-alive ב-HTTP/1.1 כי TCP keep-alive היא לא גישה מעשית.

פרוטוקול QUIC או HTTP/2 מציע הודעות בקרה מיוחדות לצורך הטמעת תמיכה ב-keep-alive על ידי אפליקציות, כולל דפדפנים.

עם זאת, כדי לזהות כשלים ולשמור על החיבור, כנראה תצטרכו ספריית לקוח עם תמיכה בצד השרת: סטרימינג לטווח ארוך באינטרנט עלול לגרום לשגיאות אם מסתמכים על HTTP בסיסי כפרוטוקול התקשורת.

בקרה על זרימת נתונים

כדי לתמוך בבקרה על זרימת נתונים, הלקוח צריך להפיץ אירועים של בקרה על זרימת נתונים ברמת התעבורה לאפליקציית הלקוח. המנגנון בפועל תלוי בסגנון של ה-API של צד הלקוח ב-HTTP שבו משתמשת אפליקציית הלקוח. לדוגמה, אתם צריכים קריאות וכתיבות חסימה, או קריאות וכתיבות ללא חסימה עם תמיכה מפורשת בבקרת זרימה, כדי שהאפליקציות יטפלו באירועים של בקרת זרימה ויכבדו אותם, וכדי למנוע עומס יתר על הלקוח או על השרת.

פרוטוקול HTTP/1.1 מסתמך על בקרת זרימה של TCP.

ל-SPDY ול-HTTP/2 יש בקרה על זרימת נתונים משלהם ברמת הסטרימינג, שכפופה גם לבקרה על זרימת נתונים של TCP ברמת החיבור, כי הבקשות מרובבות בחיבור TCP יחיד.

פרוטוקול QUIC פועל על UDP ולכן הוא מנהל את בקרת הזרימה באופן עצמאי לחלוטין.