יום ראשון, 7 באפריל 2013

אבני הבניין של האינטרנט: HTTP בסיסי


פרוטוקול ה HTTP הוא הפרוטוקול האחראי באינטרנט על שיתוף דפי HTML.
רשת האינטרנט נבנתה בחשיבה על מספר רב של שירותים: דואר אלקטרוני, צ׳אט (IRC), שיתוף קבצים (FTP), תקשורת טקסט (Telnet) ועוד. אני לא חושב שבימים הראשונים של האינטרנט היה ברור איזה תפקיד משמעותי יהיה ל World Wide Web, לדפי ה HTML.

למען האמת, בימיו הראשונים היה ל HTTP מתחרה משמעותי בשם Gopher. לפרוטוקול זה הייתה מטרה מקבילה (שיתוף "מסמכים") והוא לא היה מבוסס על HTML, אם כי עם טקסט ללא עיצוב. היו לו דפדפנים משלו, ולא הייתה סיבה משמעותית להניח שדווקא HTTP יצא מהמאבק כשידו על העליונה.

בראשית ימיו (HTTP 0.9, שנת 1991) פרוטוקול ה HTTP היה בסיסי ביותר ונועד רק להעביר קבצי HTML (ללא קבצי CSS או js) בצורה סטנדרטית על גבי פרוטוקול TCP. מסמך ה Specification שלו היה באורך עמוד וחצי.

פחות מעשור לאחר מכן (1999), הוגדר פרוטוקול HTTP 1.1, שמסמך ה Specification שלו הוא באורך 360 עמודים - עליה תלולה במורכבות ובאחידות של הפרוטוקול.

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


פוסט זה הוא חלק מהסדרה: אבני הבניין של האינטרנט.




On the Wire

בואו נתבונן כיצד נראה פרוטוקול HTTP בפעולה.
אתייחס ל HTTP גרסה 1.1 (לא נראה לי שיצא לכם להיתקל במשהו אחר), אולם אנסה להדגיש את הפונקציה הבסיסית ביותר של הפרוטוקול שהוגדרה עוד בגרסה 0.9.
פרוטוקול HTTP כולל מחזור קבוע: בקשה ותשובה, שתיהן נשלחות כטקסט פשוט על גבי TCP Connection.
בקשה נראית כך:
POST /lyrics_server/my_song.php HTTP/1.1
Host: www.lyrics.com
User-Agent: My-Home-Made-Browser/0.1a
Content-Type: text/plain
Referer: http://softwarearchiblog.com

text="Att ahla Hamuda!"&action=post

אתם יכולים לכתוב קוד פשוט בשפת התכנות החביבה עליכם שיבצע קריאות HTTP. כל שעליכם לעשות הוא לפתוח TCP socket, לשלוח מחרוזת טקסט בפורמט הבקשה, ולפרסר את פורמט התשובה (שנראה בהמשך).
שימו לב שהבקשה בנויה בפורמט הבא:
<http method> <url> <http version>
<headers>
<empty line>
<body>

השורה הראשונה נקראת Start Line ומכילה פרטים בסיסיים לגבי הבקשה. בדוגמה זו יש קריאת POST ("שליחת מידע למשאב") שכוללת טקסט בדמות ה body. קריאות GET, למשל, הן בקשות לקריאת נתונים מהשרת, ולרוב לא יכללו body (אך עדיין יכללו את השורה הריקה).


HTTP Methods

פרוטוקול HTTP מתאר משאבים (resources, שם נרדף אחר הוא מסמכים documents), המתוארים ע"י URL ופעולות (method או verb) שניתן לבצע עליהם. המשאב יכול להיות קובץ "'קיים" / "סטטי" (למשל קובץ HTML, CSS) או קובץ שנוצר באופן דינמי עבור כל בקשה (למשל דו"ח מצב העובדים). אין מניעה שקבצי HTML או CSS ייווצרו גם הם באופן דינאמי, וזה בדיוק מה שעושים שרתי אינטרנט כמו Tomcat, IIS בעזרת טכנולוגיות כמו ASP.NET או Servlet בהתאמה.

HTTP/1.0 הגדיר את המתודות הבאות:
  • GET - קריאת ה resource.
  • POST - עדכון ה resource.
  • HEAD - כמו GET רק ללא קבלת ה body, שזו סוג של "סימולציה" מה היה קורה לו היינו קוראים ל GET. טוב לבדיקה אם URL הוא תקין, למשל.

תקן HTTP/1.1 הגדיר מתודות נוספות:
  • OPTIONS - בקשת מידע על הפעולות האפשריות על המשאב.
  • PUT - שמירה של מידע כמשאב ב URL המסופק.
  • DELETE - מחיקה של המשאב. כנראה שאתם צריכים הרשאות מתאימות בכדי לעשות זאת.
  • TRACE - תאורטית משמשת ל debug, אולם בפועל לא כ"כ בשימוש בגלל סכנות אבטחה שבשימוש בה.
  • CONNECT - מתודה טכנית שנועדה לתמוך ב HTTP Tunneling.
בפועל, השימוש העיקרי באתרים או באפליקציות ווב הוא במתודות GET ו POST. אני זוכר ש Flex גרסה 3 בכלל לא אפשרה לשלוח קריאה לשרת עם מתודה אחרת, והיא כנראה לא הייתה הטכנולוגיה היחידה.
מתודת HEAD יכולה להיות שימושית לפעמים, ומתודות PUT ו DELETE הן שימושיות למדי כאשר מדובר בארכיטקטורת REST. בדרך כלל יהיה מדובר ב web service שהאפליקציה שלנו ניגשת אליו.

אינני רוצה לגלוש ל REST, לכך הקדשתי 2 פוסטים אחרים.

אלמנט חשוב מאוד במתודות ה HTTP הוא הקטלוג שלהם ל2 משפחות:
  • Safe Methods: שהן GET, HEAD ו OPTIONS.
  • Unsafe Methods: שהן: POST, DELETE ו PUT.
כחלק מהחוזה ש HTTP מספק נקבע שהפעלת ״מתודות בטוחות" לא אמורה לשנות את ה state בצד-השרת, בעוד הפעלה של מתודות "לא-בטוחות" עשויות לעשות זאת (אבל לא חייבות).

למשל, קרוב לוודאי שיצא לכם להיתקל במצב בו לחצתם על "Refresh", אך הדפדפן פתח לכם popup שהודיע שייתכן וישלח מידע פעם נוספת לשרת ושאל האם אתם רוצים להמשיך. קרוב לוודאי שלחצתם "yes", אלא אם בדיוק בשלב של אישור עסקה כספית גדולה.

מה שקרה הוא שדף אינטרנט שהוצג לכם, בעת שלחצתם על refresh, הוא תוצאה של פעולת HTTP "לא בטוחה" (כנראה POST). מכיוון ש Refresh שולח את המידע לשרת שוב, הדפדפן מתריע על הסכנה לשינוי שלא בכוונה של ה state של המשאב בשרת.

פתרון מקובל לבעיה היא "תבנית עיצוב" שנקראת (Post/Redirect/Get (PRG בה לאחר פעולת POST השרת מבצע פעולת redirect (קוד 302 שמתואר בהמשך) לעמוד GET שאין שום מניעה לעשות Refresh.


הבדל חשוב לא פחות הוא שתוצאות של פעולות "בטוחות" ניתן לשמור כ Cache. רכיבי רשת שונים (gateway, proxy, cdn) עשויים לשמור עותק מקומי של תוצאות של פעולות בטוחות ולהחזיר את התשובה עבור בקשות עתידיות. כמובן שחשוב שיהיה תהליך invalidation בו לאחר זמן, התוצאות שנשמרו ב cache יזרקו לטובת תוצאות מעודכנות יותר.

אם אתם כותבים קוד צד-לקוח, שמתשאל את השרת בעזרת מתודת POST (בגלל שאתם צריכים לשלוח פרמטרים) - התוצאה של השאילתה לא תישמר "ברשת", עובדה שיכולה להיות משמעותית מאוד עבור ביצועים (במיוחד ברשתות WAN) ועבור ה Scalability של השרת שצריך לחשב ולנפק את אותה התשובה פעם אחר פעם.

הדרך הנכונה היא לשלוח פקודות GET ולהשתמש ב Query String על מנת לשלוח פרמטרים. לא אמורה להיות לכם כיום מגבלה מעשית בצורת עבודה זו.


שורה ריקה ושורות חדשות

תקן HTTP 0.9 קבע שכל שורה צריכה להסתיים ב LF (בשפות תכנות רבות: "n\") או ב CRLF (בשפות תכנות רבות: "r\n\"). תקן 1.1 קבע שגם CR (בשפות רבות: "r\") הוא סוף שורה תקני. כיצד יודעים אם CRLF הוא סיום שורה רגיל או "השורה הריקה" המיוחלת? אין לכך תשובה חד משמעית.

בנוסף יש את החוק המבלבל הבא: שורה שהתחילה ב white space (טאב או רווח) נחשבת כהמשך לשורה הקודמת, למרות שהיה line break (אחד מהשלושה) קודם לכן.

מעניין לציין ששני שרתי הווב הנפוצים בעולם (Apache ו IIS) לא מכבדים את התקן ולא מאפשרים לשלוח CR בלבד, בכדי להגן מהבעיות שיכולות להיווצר בגישה הזו. ישנה משפחה של התקפות אבטחה שנקראות header injection שמנסות לגרום לדחיפה של סימני LF, CR ו CRLF מקומות שונים בתוך ה headers ולגרום לקוד שמפרסר אותם להתבלבל ולשגות.



Request Headers

כפי שציינו, הבקשה כוללת רשימה של Headers, כל אחד הוא שורה. ה Header היחידי שהוא חובה הוא header ה Host - כל השאר הם אופציונליים.

הסיבה ששולחים את Host על ה URL היא מצב שנקרא Shared Hosting או Virtual Hosting: דמיינו מצב בו שרת אחד מארח 2 או יותר אתרי אינטרנט (מצב מקובל מאוד בימי הענן), ששני ה hostnames שלהם מקונפגים לאותו ה IP Address על שרתי ה DNS. מכיוון שהשרת המארח מקבל רק את הכתובת היחסית של האתר, שלרוב תהייה פשוט "/" או "index.html" - הוא לא היה יודע לאיזה אתר מתייחסים. Header ה Host מאפשר לו לדעת לאיזה אתר התכוונו ולהפנות את הבקשה לאתר הנכון.
ה Host Header לא היה קיים ב HTTP 1.0 וגרם לבעיות, החל מ HTTP 1.1 הוא חובה.

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

בקריאה לפרוקסי תכלול שורת הפתיחה URL מלא הכולל את ה Host. עבור הדוגמה למעלה, שורת הפתיחה תראה כך:

POST http://www.lyrics.com/lyrics_server/queen_song.php HTTP/1.1

באופן זה ידע שרת הפרוקסי לנתב נכון את הקריאה.
אמנם יש כאן כפילות עם ה "Host Header" שנוסף ב HTTP 1.1. מצופה משרתי Proxy לוודא שה host בשורת הפתיחה וב Host Header הם זהים.


תהליך חשוב שמתרחש בעת החלפת ה headers ב request וה response הוא תהליך שנקרא "Content Negotiation". בתהליך זה ה client (כנראה דפדפן, אבל לא בהכרח) מצהיר באילו סוגי תוכן ובאיזה פורמטים הוא תומך. השרת ינסה להיענות לדרישה כמיטב יכולתו. הנה קבוצת headers שכזו לדוגמה:



Accept: text/html, application/xhtml+xml, application/xml; q=0.9, *.*;q=0.8
Accept-Language: he, en-us;q=0.8, en;q=0.6
Accept-Charset: ISO-112, utf-8;q=0.7,*;q=0.3
Accept-Encoding: gzip, deflate

שימו לב ש "Accept-Charset" הוא ה header שמצביע על סוג הקידוד (encoding) בעוד "Accept-Encoding" מתאר דווקא את סוג הדחיסה (compression). סוג של חוסר-דיוק מינוחי שנעשה בתקן.

לפני שאנו נגשים ל headers עצמם בואו נבין את התחביר:
לכל header יש מספר ערכים מופרדים בפסיקים.
לערכים יש ערכי משנה, מופרדים בנקודה-פסיק.

Accept-Language: he, en-us;q=0.8, en;q=0.6


q הוא מדד בין 0 ל 1 שמבטא את חוזק ההעדפה (quantity?) עבור ה content negotiation.
כלומר ב header זה הדפדפן אומר: "אחי, אני הכי רוצה עברית (q לא צויין = 1) אבל אני מוכן לקבל אנגלית אמריקאית כהעדפה קצת יותר נמוכה או אנגלית-סתם כהעדפה שלישית.


Headers שכדאי לציין הם:
  • Accept (נקרא גם Accept-Media) - מציין את רשימת ה media types (נקראים גם MIME types) שה client תומך בהם. * משמעה: "תן לי הכל, אני אסתדר כמיטב יכולתי".רשימת ה MIME types נראית קצת מסורבלת, וכנראה שאם תשלחו MIME type שגוי הדפדפן יידע להסתדר (הוא מפענח את התחילה של הקובץ כדי לדעת איזה סוג קובץ זה באמת, בלי קשר ל MIME type שנשלח) - אבל זהו התקן ורוב השרתים מצייתים לחלק זה.
  • Accept-Encoding - סוגי דחיסה שונים שהדפדפן יודע לפתוח. קבצים נדחסים על מנת להעביר פחות מידע על גבי הרשת. הדחיסה המועדפת היא gzip (שזה וריאציה קלה-לפענוח של אלגוריתם הדחיסה ZIP). אין טעם לציין כאן q מכיוון שלדפדפן באמת אין העדפה.
  • User-Agent - זהו header קצת חריג בנוף של ה content negotiation: במקום לתאר יכולת של ה Agent כגון encoding או supported media types, הוא מתאר פרטים מזהים על ה Agent כמו מנוע הרינדור שה agent משתמש בו או מערכת ההפעלה שהוא רץ עליה. בעזרת פרטים אלו יכול השרת לנחש ולנסות להתאים את התוכן טוב יותר ל agent.
אומר זאת בבירור: User Agent הוא סוג של Anti-Pattern בו עושים שימוש נפוץ בכדי להתאים תוכן של אתרים / אפליקציות לדפדפן ספציפי / מערכת הפעלה, על בסיס הידיעה כיצד מתנהג כל דפדפן (באיזו גרסה?).
זיהוי דפדפן (למשל IE7) הוא לא משימה קלה מכיוון שבעוד שבמחשב שלכם IE7 ייצר User Agent String (בקיצור UA) אחד, למשל:

Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.2; Win64; x64; Trident/6.0; .NET4.0E)


במחשב אחר (לא עוד disk image מאותו IT) הוא עלול לייצר UA שנראה כך:

Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 6.0)



"אין בעיה!", תאמרו לעצמכם. "MSIE 7" תמיד יופיע ועליו נסתמך. מה שכן, הנה UA שהגיע מדפדפן IE8:

Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 5.0; Trident/4.0; FBSMTWB; msn OptimizedIE8; ENUS)


גם הוא מכיל משום מה "תאימות ל MSIE 7".

לפני שאתם משחררים קוד המתבסס על UA, כדאי שתבדקו את ה regular expressions שלכם מול בנק של UAs כגון לינק1 או לינק2. הדרך הנכונה לעבוד היא להשתמש בספריות כגון modernizr, שבודקות בזמן ריצה האם יכולות ספציפיות נתמכות בדפדפן כגון SVG או Touch ולא לנסות לנחש זאת דרך ה UA.

עדיין שאלות חשובות כגון ״האם מדובר ב Smartphone או ב Tablet?״ לא יכולות להיענות בצורה טובה ללא גישה ל UA - אבל כדאי לעשות זאת בזהירות.

ניתן לגשת ל UA גם מתוך ג'אווהסקריפט בעזרת פקודת navigation.useragent.

גם ג'ון רזיג, היוצר של jQuery, לא "מת" על השימוש ב UA. מקור: twitter

Headers חשובים אחרים ב HTTP Request הם:
  • If-Modified-Since - בעזרת header זה הדפדפן מצהיר: "יש לי את הקובץ הזה (זכרו שקריאת HTTP היא גם לתמונות או מסמכים, לא רק קבצי HTML) מתאריך abc, שלח לי אותו חזרה רק אם הוא השתנה מאז". אם הקובץ לא השתנה, ה response יחזור עם body ריק אבל עם קוד 304 (not-modified) שיאמר לדפדפן להשתמש במשאב שכבר ברשותו, מכיוון שהוא מעודכן.
  • Referer - זיהוי האתר שהפנה את הבקשה (בהנחה שבקשת ה HTTP היא תוצאה של לחיצה על לינק). מידע זה הוא שימושי למדי לכלי Web Analytics. בעלי אתרים רוצים לדעת אילו מפרסמים יעילים יותר עבור האתר שלהם. שימו לב ששגיאת הכתיב (באנגלית יש לכתוב Referrer) היא שגיאה שנעשתה במקור בתקן - ונשתמרה.
  • Cookie ו Caches - שני נושאים בעלי נפח שראוי להקדיש להם דיון עצמאי.
  • (X-forward-for (XFF - פעמים רבות, לפני השרת שלנו יישב Reverse Proxy או Load Balancer המנטרים את התעבורה. מכיוון שכלפי-חוץ (האינטרנט) אותו רכיב רשת הוא היעד הסופי של התעבורה, ה packet של פרוטוקול ה IP יציין את רכיב הרשת בתור היעד ולא את השרת שלנו. רכיב הרשת ישלח שוב את אותה הבקשה אלינו ושם הוא יציין את המקור (ברמת פרוטוקול ה IP) ככתובת שלו, ולא כתובת השולח המקורי. XFF נועד "to patch" מגבלה זו של פרוטוקול IP ולתאר לנו את המקור האמיתי של התעבורה.

אם אתם רוצים לבחון את ה Headers של המערכת שלכם, ניתן לעשות זאת בקלות:


הקישו ב F12 בדפדפן בכדי לפתוח את כלי הפיתוח. בכרום, למשל, לכו לטאב ה Network ולחצו F5 כדי לטעון שוב את הדף. עכשיו תראו את רשימת כל קריאות ה HTTP, כאשר בכל אחת ניתן לראות את ה Headers שנשלחו. בדפדפנים אחרים הדרך למצוא את ה Haders אינה שונה בהרבה.



HTTP Response

חפרנו דיי הרבה בבקשת ה HTTP, בואו נעבור על כמה נקודות מעניינות בתשובה. פורמט התשובה הוא דומה למדי:

<http version> <status code> <reason = status text>
<headers>
<empty line>
<body>

ההבדל הניכר הוא בשורת הפתיחה, ובסט של Headers אפשריים שהוא שונה מאלו שנשלחים ב Request.

HTTP/1.1 200 OK
Server: Some-Server/0.9.2
Content-Type: text/plain
Connection: close

<DOCTYPE html><html><head></head><body><p>
This is currently an <b>html</b> document, but http response body can include images, css files, binary files etc.<p>
</body><html>


מקור: http://www.gog.com/error/404


שורת הפתיחה
שורת הפתיחה כוללת את גרסת הפרוטוקול, קוד סטטוס (מספרי) וטקסט שמתאר את הסטטוס.
אמנם כמעט תמיד נראה את התיאור "OK" עם קוד 200 ("הכל בסדר"), אבל אין מניעה לשלוח טקסט אחר. שדה זה נועד בכדי לשלוח הודעות שגיאה טקסטואליות כאשר עובדים, למשל, עם REST API. הודעת שגיאה כמו ״Bad Request״ היא לא מועילה וכדאי להחליף אותה.

מבין כ-50 הקודים שמוגדרים בפרוטוקול ה HTTP יש אולי 15 שבאמת שימושיים בפועל, בואו נעבור עליהם בקצרה:
  • 200 - "הכל בסדר": התוכן שנתבקש אמור להגיע ב body
  • 204 - No Content: כאשר אין ערך החזרה. הדפדפן לא ינווט לכתובת החדשה אלא יישאר בכתובת האחרונה שהיה.
  • 206 - Partial Content: כמו 200, רק משמש עבור בקשות של range בתוך המסמך, המבוצעות על בסיס ה header שנקרא Content-Range.
  • 301, 302 או 303 - הדפדפן מציין שה URL למשאב זה הוחלף, וה URL החדש מצויים ב header בשם Location. דפדפנים יפנו אוטומטית לכתובת החדשה, מבלי שהמשתמש מודע לכך, מה שנקרא "Client-Side ReDirect". אם הפעולה הייתה פעולת POST, הדפדפן יגש ל URL החדש בפעולת GET (כדי למנוע שליחה כפולה של הנתונים). דפים שמציגים למשתמש הודעה "...You are being redirected" מחזירים בעצם קוד 200, ופעולת ה Redirect היא בעצם אפליקטיבית (בעזרת קוד ג'אווהסקריפט, לרוב).
  • 304 - not modified: ראו את ההסבר אודות סטטוס זה תחת ההסבר על ה header בשם If-Modified-Since למעלה.
  • 307 - Temporary Redirect: כמו 301-303 רק ללא ביטול פעולת ה POST.
  • 400 - bad request: השרת מציין שמשהו לא בסדר בבקשה שנשלחה, אולי הפורמט ואולי תוכן הבקשה. הפירוט אמור להגיע בהודעת השגיאה.
  • 401 - Unauthorized: השרת לא מזהה את המשתמש ולכן אינו יכול לאפשר לו לגשת למשאב שהגישה אליו מוגבלת. קוד זה יזניק חלון "שם/ססמה" או יהיה נקודת הפתיחה לסט קריאות מורכב כחלק מהפעלת פרוטוקולים לאימות המשתמש כגון Kerberos או SAML.
  • 403 - Forbidden: השרת מזהה את המשתמש, אך למשתמש אין הרשאות לגשת למשאב.
  • 404 - לא קיים משאב בשם שתואר ב URL. תשאלו כל ילד בן 12 ומטה, והוא יסביר לכם :)
  • 405 - Method not allowed: פעולת ה HTTP שביקשנו (למשל Delete) לא זמינה למשאב הספציפי. ייתכן והיא תהיה זמינה עבור משאב (URL) אחר.
  • 500 - Internal Error: תקלה פנימית בשרת, שיכולה להיות בעצם כל דבר. התקלה לרוב תכתב לקובצי הלוג של השרת ולא תשלח (מטעמי בטיחות) למשתמש. אל תצפו להודעת שגיאה מפורטת או מועילה על קודים אלו.
  • 503 - Service Unavailable: כמו 500, אך בא להדגיש שמדובר בתקלה זמנית בגלל שאיזה service בדיוק נמצא ב maintainable או restart, למשל.
כמובן שקודים אלו הם קודים טכניים, שלא רבים מהמשתמשים מכירים או מבינים. אתרי אינטרנט יעדיפו להחזיר תמיד קוד 200 ולהחזיר דף HTML המתאר בצורה קלה ונוחה (חחחח, לפעמים זו הודעה מעוצבת בה כתוב רק "404"), מהי התקלה. קודי HTTP, אם כן, רלוונטיים יותר לשירותי REST.
בעצם, גם שירותים שקוראים לעצמם "REST" לא תמיד משתמשים בקודים בצורה נכונה. אפשר לומר שיש כאן כוונה טובה והרבה מקום לשיפור. בלינק זה תוכלו למצוא את ההגדרה התקנית לכל הקודים. שימו לב ששרתים או פרוטוקולים מסוימים אמצו לעצמם קודים נוספים, את הנפוצים שבהם תוכלו למצוא כאן.



Headers מעניינים בתשובה

הנה כמה מה Headers המעניינים שיכולים לחזור בתשובה:
  • Content-Encoding, Content-Language, Content-Type ועוד - המקבילים ל Headers של ה Content Negotiation בבקשה, אולם הפעם מצוינת ההחלטה. לא יהיו יותר q לבטא את מידת הרצון כי אם החלטה: "התוכן הוא HTML בצרפתית והוא דחוס ב gzip", לדוגמה.
    ה Content Type יתואר בעזרת אחד מה MIME types, כמו שהזכרנו קודם.
  • Server - ממש כמו user agent - רק בצד השרת. מתאר איזה שרת זה ואיזו גרסה.
  • מעולם לא ראיתי קוד שעשה שימוש במידע זה, אך למי שמדבג פלטפורמה מרוחקת מידע זה עשוי להיות שימושי.
  • Content-Length - אורך ה body. יכול לסייע ל client להיערך בהקצאת זיכרון.
  • Cache-Control, Pragma, ETag - אלו headers המתארים את היכולת לשמור את התוכן ב cache בצד הלקוח, ופרטים אודות התקפות שלו. נושא בפני עצמו.
סתם נקודה מעניינת לציין היא שאין חוק ברור כיצד להתנהג כאשר אותו ה Header מופיע פעמיים. דפדפנים מסוימים מתייחסים רק למופע הראשון, ואחרים - רק למופע האחרון.


סיכום

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

שיהיה בהצלחה!


----

קישורים רלוונטיים

מדריך מוצלח לבחירת Status Code ל API שלכם:
http://racksburg.com/choosing-an-http-status-code/

Status Codes בהם משתמשים APIs מפורסמים:
https://gist.github.com/vkostyukov/32c84c0c01789425c29a


9 תגובות:

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

    השבמחק
  2. יפה מאד!

    מספר הערות:
    1. CRLF = \r\n ולא \r כפי שמצויין בטעות.
    2. Accept-Encoding - סוגי קידוד, ולא סוגי דחיסה.
    3. "בכרום, הקישו Ctrl+Shift+I כדי לפתוח את כלי הפיתוח.". F12 יעשה את העבודה בעזרת אצבע אחת בלבד. כך גם עבור התוסף FireBug הידוע, בשועל האש.

    פינת הלשון:
    "מסמך ה-Specification שלו" = "המפרט שלו"
    Header - כותר. אני רגיל לומר כותר/כותרים, אך לא בהכרח שזה נכון יותר מאשר כותרת/כותרות.
    Method - שיטה
    Web Server - שרת אינטרנט (למרות ש-Web אינו "אינטרנט").
    ישנן מילים נוספות, אך לא אתפס לקטנות :)

    השבמחק
    תשובות
    1. תודה רבה, ישראל!

      1-2 צודק בהחלט - טעויות כתיבה שאתקן.
      3 אני רגיל להגיע לשם עם העכבר, ולכן חיפשתי מה כתוב בתיעוד :) צודק בהחלט. F12 עובד גם ב "סייר האינטרנט", אפרופו.

      לגבי העברית: תודה, אם כי אני לא בטוח שאאמץ - וזאת בכדי להקל על הקוראים (ולא רק על עצמי).

      ע"פ הידוע לי יש לומר, למשל, מנשק (מקור: נושק, נשיקה) ולא ממשק בכדי לתאר interface - אך מי יבין זאת?
      גם אם יבינו, יחידת-העיבוד-המרכזי במוחנו תאלץ לעבוד עוד כמה מחזורי-עיבוד בכדי לתרגם זאת.

      או בשפה שקרובה יותר ל"מכונה" (מוח):

      "גם אם יבינו, ה CPU במוח שלנו יאלץ לעבוד עוד כמה cycles בשביל התרגום"

      כלומר: אני אוהב ללמוד עברית תקנית ולדון בה :), אך פחות להשתמש בה בכתיבה כי אני מאמין שזו אינה השפה "הטבעית" לעיסוק בתחום התוכנה. אני מנסה לכתוב קרוב יותר לשפה הטבעית. כשאני חושב "מפרט" צריך אצלי לעבור תרגום ל specification ולא להיפך, למרות שעברית היא שפת האם שלי. כנ"ל "מחזורים" ל "cycles". אז למה לכתוב בשפה "זרה"?

      המשך להעיר על העברית, אני שמח לשמוע ואקח מה שאוכל מבלי להקריב את קלות הקריאה של הקוראים (להבנתי) או של הכותב (יתר על המידה:) )

      ליאור

      מחק
    2. להבהיר:

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

      בלי קשר, אני מוצא עניין בעברית-אקדמית (תחום אקדמי/תיאורטי מבחינתי) - ועל כן אני שמח על ההערות.

      ליאור

      מחק
    3. הערה אחרונה:

      עשיתי double-check בתקן, ודווקא Accept-Encoding הוא אכן הכותר לתיאור דחיסה. חוסר דיוק של התקן. Encoding מתואר בכותר Accept-Charset. מקור:
      http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

      הדיון הזכיר לי עובדה מעניינת ש Referer היא שגיאת כתיב בתקן שהושרשה (יש לכתוב Referrer). אנקדוטה מעניינת שהוספתי לפוסט :)

      ליאור

      מחק
  3. Content-Length בתשובה משמש לא רק להקצאת זכרון, אלא גם למתן חיווי טוב יותר למשתמש איזה חלק מכל התשובה כבר הגיע מהשרת (ע"י הצגת פס התקדמות) - שימושי בעיקר בתשובות שמחזירות קבצים גדולים מהשרת.

    השבמחק
  4. היי ליאור,

    פוסט נהדר כרגיל..

    כתבת: Server - ממש כמו user agent - רק בצד השרת. מתאר איזה שרת זה ואיזו גרסה. מעולם לא ראיתי קוד שעשה שימוש במידע זה.

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

    השבמחק
    תשובות
    1. אני אסביר קצת, לגוגל יש מערכת הפעלה שלהם עם מערכת קבצים (FileSystem למי שלא הבין) משלהם.
      השרת היחיד שיכול להחזיר בשם השרת GFE הוא גוגל. ולכן אם יש פרוקסי, שם השרת יהיה שונה.

      אגב, בכרום גם יש את הבעיה הזאת אבל הם מתמודדים איתה בדרך אחרת.
      מה הבעיה? בכרום לראשונה החיפוש אוחד יחד עם שורת הכתובת. הדבר יוצר בעיה מסוימת, נניח שאני כותב בשורת הכתובת "Trial" האם אני מתכוון לפנות לפורט 80 של השרת המקומי "Trial" או לחפש בגוגל את המילה "Trial"? הפתרון של גוגל היה פשוט. אם חוזר 404 מפעילים חיפוש על המילה, אם לא הולכים לדף.
      הבעיה חמורה הרבה יותר במקרה שאני מחפש "vb.net" האם אני מתכוון לאתר vb.net או לחיפוש של "vb.net". לFirefox יש פתרון פשוט, אם יש נקודה, זה אתר אם לא זה חיפוש.
      הפתרון של כרום אומנם יותר אלגנטי, אבל מה אם יש פרוקסי שמחזיר תמיד דף אמיתי "הדפדוף נחסם"? הפתרון של גוגל הוא דומה לפתרון הזה אבל הרבה יותר יצירתי.
      כשפותחים את הכרום, הדפדפן פונה לשלש כתובות שמורכבות מטקסט רנדומלי שמשתנה בכל פעם עם נקודה באמצע. הוא משווה את התוצאה החוזרת משלושתם, ואם לפחות שתיים מהם זהות הוא שומר את זה בתור הדף של הפרוקסי ואליו הוא משווה את התוצאות של הדפדוף כדי לדעת האם זה דפדוף שנחסם או חיפוש.

      הפתרון של השרת של גוגל יותר אלגנטי, אבל טיפה פחות אמין. (למקרה שהפרוקסי חוסם אתר אחר ומתיר את גוגל)

      מחק