HTTP

2025-02-19 - תכנות

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

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

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

סקירה כללית

HTTP/1.1 הוא פרוטוקול טקסטואלי שרץ מעל TCP (ראשי תיבות ל Transmission Control Protocol). בעוד ש-HTTP מדבר על גישה לדפי אינטרנט וכדומה, TCP מגדיר איך הודעות כלליות עוברות דרך האינטרנט כך ששני הצדדים המתקשרים ביניהם יכולים להיות בטוחים שסדר ההודעות שהם שולחים נשמר.

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

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

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

בקשות (Requests)

כפי שהזכרתי מעל, בקשה מורכבת משלושה דברים עיקריים: שורת הבקשה, כותרות, וגוף הבקשה

שורת הבקשה

שורת בקשה נראית כך:


<Method> <Target> HTTP/1.1
> המתודה (Method)
מתארת את סוג הבקשה מהשרת. יש כמה סוגים של מתודות, כאשר הנפוצות ביותר הן GET - בקשת משאב מהשרת, ו-POST - שליחת תוכן לשרת שלרוב יופיע בגוף הבקשה.
> המטרה (Target)
מתארת את המטרה בתוך השרת. לדוגמא, הדף אליו אנחנו רוצים לגשת.
> הפרוטוקול
ישנם כמה גרסאות ל-HTTP. בשביל לפשט, אנחנו מדברים על גרסא HTTP/1.1.
כותרות (Headers)

יש מספר לא קטן של כותרות ומטרותיהן דיי מגוונות. בכותרות הבקשה אנחנו יכולים לציין מאיזה טיפוס המידע שאנחנו רוצים לקבל חזרה (בעזרת כותרת Accept), אנחנו יכולים לכלול את סוג הדפדפן בו אנחנו משתמשים (בעזרת כותרת User-Agent), אנחנו יכולים לצרף מידע הקשור לאימות זהותינו ואבטחה (בעזרת כותרת Cookie), ועוד.

כותרות נראות מהצורה:


<Header>: <Content>
גוף הבקשה (Body)

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

בנוסף, כאשר יש גוף לבקשה, הקליינט יוסיף את הכותרת Content-Length שיכיל את אורך גוף הבקשה.

תגובות

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

סטטוס התגובה

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

סטטוס התגובה הוא קוד בעל 3 ספרות בטווח בין 100 ל-599. שלושה סטטוסים מאוד נפוצים הם 200 OK, המציין כי הבקשה הושלמה בהצלחה, 404 Not Found, המתאר כי המשאב המבוקש לא נמצא, ו-500 Internal Server Error, המתאר שגיאה פנימית בתוכנת השרת שבגללה השרת לא יכל להשלים את הבקשה.

דוגמא

את הדוגמא שלנו אנחנו יכולים ליצור בעזרת פקודת curl -v, שיכולה לבצע בקשה כנגד שרת באינטרנט. הדגל -v (verbose) אומר לcurl שאנחנו רוצים לראות את המידע המועבר ע"י התוכנית. בואו נריץ את הפקודה הבאה שתבקש לקבל את דף האינטרנט שאתם קוראים עכשיו.

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


$ curl -v --http1.1  https://alloca.space/blog/http.html
* Host alloca.space:443 was resolved.
[...]
> GET /blog/http.html HTTP/1.1
> Host: alloca.space
> User-Agent: curl/8.9.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
< Server: nginx/1.24.0
< Date: Wed, 19 Feb 2025 09:36:45 GMT
< Content-Type: text/html; charset=utf-8
< Content-Length: 5895
< Connection: keep-alive
<
<!DOCTYPE html>

<html lang="he" dir="rtl">
  <head>
    <link rel="stylesheet" type="text/css" href="/css/style.css">
    [...]


      <footer>
        <p>☄️ alloca</p>
      </footer>

    </main>

  </body>
</html>
* Connection #0 to host alloca.space left intact

הפלט של curl מציין אילו שורות מתאימות לאיזו פעולה באמצעות הוספת תו לפני כל שורה. שורות המתחילות בתו '*' מציינות מידע כללי שcurl מספקת לנו, שורות המתחילות בתו '>' מייצגות מידע שהלקוח שולח כחלק מהבקשה, ושורות המתחילות בתו '<' מייצגות מידע שהשרת שולח כחלק מהתגובה. אנחנו יכולים לחלק את הפלט לשני חלקים:

הבקשה


> GET /blog/http.html HTTP/1.1
> Host: alloca.space
> User-Agent: curl/8.9.1
> Accept: */*
>
שורת הבקשה

GET /blog/http.html HTTP/1.1

שורת הבקשה משתמש במתודת GET על המטרה /blog/http.html כדי לבקש מהשרת שישלח את דף ה-HTML של העמוד אותו אתם עכשיו קוראים.

כותרות

 Host: alloca.space
 User-Agent: curl/8.9.1
 Accept: */*

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

גוף הבקשה

בבקשת GET זו, אין לנו מידע נוסף לשלוח לשרת, לכן גוף הבקשה ריק.

התגובה


< HTTP/1.1 200 OK
< Server: nginx/1.24.0
< Date: Wed, 19 Feb 2025 09:36:45 GMT
< Content-Type: text/html; charset=utf-8
< Content-Length: 5895
< Connection: keep-alive
<
<!DOCTYPE html>

<html lang="he" dir="rtl">
  <head>
    <link rel="stylesheet" type="text/css" href="/css/style.css">
    [...]


      <footer>
        <p>☄️ alloca</p>
      </footer>

    </main>

  </body>
</html>
סטטוס התגובה

HTTP/1.1 200 OK

השרת מחזיר לנו סטטוס 200 המעיד על כך שהמשאב המבוקש נמצא ונשלח בהצלחה.

כותרות

Server: nginx/1.24.0
Date: Wed, 19 Feb 2025 09:36:45 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 5895
Connection: keep-alive

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

גוף התגובה

<!DOCTYPE html>

<html lang="he" dir="rtl">
  <head>
    <link rel="stylesheet" type="text/css" href="/css/style.css">
    [...]


      <footer>
        <p>☄️ alloca</p>
      </footer>

    </main>

  </body>
</html>

גוף התגובה מכיל את דף ה-HTML אותו אתם קוראים עכשיו, שכמובן אין ביכולתי לכלול את כולו בגלל בעיה רקורסיבית מסויימת 😉.

סיכום

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


from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"Hello": "World"}

תוכלו לראות את הקשר לפרוטוקול מתחת ולהבין כי אנחנו מגדירים שרת HTTP וממפים בקשות ספציפיות, כמו בקשת GET /, לתגובות ספציפיות, כמו האובייקט {"Hello": "World"}.

רוצים להגיב? בדקו מהי שאלת הסינון בעמוד הראשי.