رفتن به محتوا
مبانی کامپیوتر و اینترنتاینترنت چطور کار می‌کنددرس ۱۱ از ۱۶

وب و HTTP

زبان وب را با چشم خودت ببین: اجزای URL، درخواست و پاسخ HTTP خط به خط با curl -v، متدهای GET و POST و PUT و DELETE، کدهای وضعیت ‎2xx تا ‎5xx، هدرهای مهم (Host، Content-Type، Content-Length، Location)، ریدایرکت، فرستادن فرم و JSON، بی‌حالت بودن HTTP و کوکی، و تب Network مرورگر.

  • حدود ۲۱ دقیقه مطالعه
  • ۷ مثال اجراشده
  • ۳ تمرین با جواب
  • مقدماتی (از صفر مطلق)
  • به‌روزرسانی: ۲۹ مهر ۱۴۰۵

توی این درس یاد می‌گیری مرورگر و سرور وب دقیقاً چه چیزی به هم می‌گویند. HTTP پروتکل وب است و خبر خوب این است که متن ساده است: می‌توانی خط به خطش را بخوانی. اجزای یک URL را می‌شناسی، با curl -v درخواست و پاسخ کامل را می‌بینی، متدها (GET، POST، …) و کدهای وضعیت (۲۰۰، ۳۰۱، ۴۰۴، ۵۰۰ و خانواده‌هایشان) را یاد می‌گیری، هدرهای مهم را می‌خوانی، یک فرم و یک JSON به سرور می‌فرستی و می‌فهمی سایت‌ها با کوکی چطور «یادشان می‌ماند» که تو کی هستی.

مسئله: «سایت خطا می‌دهد»

Section titled “مسئله: «سایت خطا می‌دهد»”

وقتی کاربری می‌گوید «سایت کار نمی‌کند»، ده‌ها مشکل متفاوت ممکن است پشتش باشد: صفحه پیدا نشده، دسترسی ندارد، سرور خراب شده، یا آدرس عوض شده. هر کدام از این‌ها در HTTP یک کد مشخص دارد و هر کدام سراغ یک نفر متفاوت را می‌گیرد. اگر HTTP را بخوانی، به‌جای «کار نمی‌کند» می‌گویی «/api/orders با ۵۰۲ جواب می‌دهد، یعنی nginx سالم است ولی برنامه‌ی پشتش جواب نمی‌دهد». این جمله نصف راه حل است.

یک درخواست HTTP مثل فرم اداری است. بالای فرم نوشته‌ای چه کاری می‌خواهی و از کدام بخش («درخواست دریافت پرونده‌ی شماره‌ی ۴۲»): این خط درخواست است. بعد چند خانه‌ی مشخصات پر می‌کنی (اسم، تاریخ، زبان مورد نظر): این‌ها هدرند. و اگر لازم باشد، پیوست (یک فرم پرشده یا مدرک): این بدنه (body) است. جوابی که می‌گیری هم همین ساختار را دارد: یک مهر بالایش («انجام شد»، «پیدا نشد»، «به بخش دیگر مراجعه کنید»): کد وضعیت؛ چند خانه مشخصات؛ و خود پرونده.

URL: آدرس کامل یک چیز در وب

Section titled “URL: آدرس کامل یک چیز در وب”
هر بخش URL را یک لایه‌ی متفاوت استفاده می‌کند: scheme پروتکل، host برای DNS، port برای پورت، path و query برای خود سرور، و fragment فقط برای مرورگر.

حرف‌های غیرانگلیسی و نمادهای خاص در URL کدگذاری درصدی می‌شوند: «علی» در URL می‌شود %D8%B9%D9%84%DB%8C؛ یعنی همان شش بایت UTF-8 درس encoding، هر کدام با % و دو رقم هگز. برای همین وقتی لینک فارسی را کپی می‌کنی، گاهی شکل عجیبی دارد.

همه‌ی مثال‌ها روی سرورهای خود کامپیوترت اجرا می‌شوند؛ اینترنت لازم نیست. در یک ترمینال سرور درس قبل را روشن کن و در ترمینال دیگر کار کن:

Terminal window
cd ~/site && python3 -m http.server 8000

مثال ۱: یک درخواست و پاسخ کامل

Section titled “مثال ۱: یک درخواست و پاسخ کامل”
Terminal window
curl -sv http://localhost:8000/notes.txt
خروجی
* Host localhost:8000 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* Trying 127.0.0.1:8000...
* Connected to localhost (127.0.0.1) port 8000
> GET /notes.txt HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/8.5.0
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: SimpleHTTP/0.6 Python/3.12.3
< Date: Wed, 21 Oct 2026 20:57:16 GMT
< Content-type: text/plain
< Content-Length: 9
< Last-Modified: Wed, 21 Oct 2026 20:54:07 GMT
<
* Closing connection
سلام

-v (verbose) همه‌چیز را نشان می‌دهد. سه نوع خط داری:

  • * کارهایی که خود curl انجام داد: اسم را پیدا کرد (درس DNS؛ این‌جا از /etc/hosts)، به 127.0.0.1:8000 وصل شد (درس پورت؛ دست‌دادن TCP).
  • > چیزی که curl فرستاد: درخواست.
  • < چیزی که سرور برگرداند: پاسخ. بعد از آن، خود محتوا (سلام).

درخواست را خط به خط بخوان:

خط معنی
GET /notes.txt HTTP/1.1 متد (GET = بده)، مسیر، نسخه‌ی پروتکل
Host: localhost:8000 اسم سایتی که می‌خواهم؛ یک سرور با یک IP می‌تواند صدها سایت داشته باشد و با همین هدر می‌فهمد کدام
User-Agent: curl/8.5.0 چه برنامه‌ای درخواست داده (مرورگرها اسم و نسخه‌ی خودشان را می‌گذارند)
Accept: */* چه نوع پاسخی قبول دارم (*/* یعنی هر چیزی)
خط خالی پایان هدرها

و پاسخ:

خط معنی
HTTP/1.0 200 OK خط وضعیت: نسخه، کد و توضیح کوتاه
Server: SimpleHTTP/0.6 Python/3.12.3 نرم‌افزار سرور
Date: ... GMT زمان سرور (همیشه به وقت گرینویچ)
Content-type: text/plain نوع محتوا؛ مرورگر بر اساس این تصمیم می‌گیرد نشانش دهد، دانلودش کند یا اجرایش کند
Content-Length: 9 اندازه‌ی محتوا به بایت: «سلام» ۸ بایت (درس encoding) + یک خط جدید
خط خالی پایان هدرها، بعد بدنه

همه‌اش متن است. مرورگر هم برای هر صفحه دقیقاً همین را می‌فرستد (با هدرهای بیشتر). (HTTP/1.0 در پاسخ به این دلیل است که سرور ساده‌ی پایتون نسخه‌ی قدیمی را حرف می‌زند؛ سرورهای واقعی HTTP/1.1 یا HTTP/2 جواب می‌دهند.)

مثال ۲: فقط هدرها، و ریدایرکت

Section titled “مثال ۲: فقط هدرها، و ریدایرکت”
Terminal window
curl -sI http://localhost:8000/images
خروجی
HTTP/1.0 301 Moved Permanently
Server: SimpleHTTP/0.6 Python/3.12.3
Date: Wed, 21 Oct 2026 20:57:16 GMT
Location: /images/
Content-Length: 0

-I فقط هدرها را می‌گیرد (با متد HEAD). سرور گفت 301: «این آدرس جابه‌جا شده، برو به آدرسی که در Location نوشته‌ام» (/images/ با اسلش آخر، چون پوشه است). مرورگر این را خودکار دنبال می‌کند و تو اصلاً متوجه نمی‌شوی. curl نه، مگر اینکه -L بدهی:

Terminal window
curl -sL -o /dev/null -w "%{http_code} %{url_effective} redirects=%{num_redirects}\n" http://localhost:8000/images
خروجی
200 http://localhost:8000/images/ redirects=1

با -L، curl یک ریدایرکت را دنبال کرد و به ۲۰۰ رسید. ریدایرکت همه‌جا هست: از http:// به https://، از example.com به www.example.com، از آدرس قدیمی یک مقاله به آدرس جدید، و بعد از ورود موفق به صفحه‌ی اصلی.

کد سه‌رقمی است و رقم اول خانواده‌اش را می‌گوید. فقط با رقم اول نصف تشخیص را انجام داده‌ای:

خانواده معنی کدهای رایج تقصیر کیست؟
2xx موفق 200 OK، 201 Created، 204 No Content هیچ‌کس
3xx برو جای دیگر 301 (دائمی)، 302 (موقت)، 304 Not Modified (از کش خودت استفاده کن) هیچ‌کس، معمولاً
4xx درخواست تو مشکل دارد 400 Bad Request، 401 (وارد نشده‌ای)، 403 Forbidden (اجازه نداری)، 404 Not Found، 405، 429 Too Many Requests کلاینت
5xx سرور مشکل دارد 500 Internal Server Error، 501 Not Implemented، 502 Bad Gateway، 503 Service Unavailable، 504 Gateway Timeout سرور

سرور ساده‌ی پایتون فقط خواندن را بلد است. ببین به متدهای دیگر چه می‌گوید:

Terminal window
curl -s -o /dev/null -w "%{http_code}\n" -X POST -d "name=ali" http://localhost:8000/
curl -s -o /dev/null -w "%{http_code}\n" -X DELETE http://localhost:8000/notes.txt
خروجی
501
501

-X متد را تعیین می‌کند. 501 Not Implemented: «این متد را پیاده نکرده‌ام» (سرورهای دیگر برای همین حالت 405 Method Not Allowed می‌دهند). لاگ سرور هم همین را ثبت کرد:

خروجی (ترمینال سرور)
127.0.0.1 - - [22/Oct/2026 00:27:16] code 501, message Unsupported method ('POST')
127.0.0.1 - - [22/Oct/2026 00:27:16] "POST / HTTP/1.1" 501 -
127.0.0.1 - - [22/Oct/2026 00:27:16] code 501, message Unsupported method ('DELETE')
127.0.0.1 - - [22/Oct/2026 00:27:16] "DELETE /notes.txt HTTP/1.1" 501 -
⚡ بررسی سریع

کاربری می‌گوید سایت «خطای 403» می‌دهد. مشکل احتمالاً کجاست؟

مثال ۴: متدها، فعل‌های HTTP

Section titled “مثال ۴: متدها، فعل‌های HTTP”
متد معنی مثال
GET بده (فقط خواندن؛ نباید چیزی را عوض کند) باز کردن صفحه، جست‌وجو
POST این را بگیر و کاری کن (ساختن چیز جدید) ثبت سفارش، ارسال فرم
PUT / PATCH این را جایگزین / بخشی‌اش را عوض کن ویرایش پروفایل
DELETE حذف کن حذف یک کالا از سبد
HEAD مثل GET ولی فقط هدرها curl -I

قانون مهم: GET نباید چیزی را تغییر دهد. مرورگرها، موتورهای جست‌وجو و کش‌ها آزادانه درخواست‌های GET را تکرار یا از قبل اجرا می‌کنند. لینکی مثل /delete?id=5 که با GET کار می‌کند، یک روز توسط رباتی که فقط لینک‌ها را دنبال می‌کند «کلیک» می‌شود.

مثال ۵: فرستادن داده، فرم و JSON

Section titled “مثال ۵: فرستادن داده، فرم و JSON”

برای دیدن دقیقاً چیزی که فرستاده می‌شود، از nc درس پورت به‌جای سرور استفاده کن: هر چه برسد را چاپ می‌کند. در ترمینال سرور (اول http.server را با Ctrl+C ببند):

Terminal window
nc -l -N 127.0.0.1 9000

و در ترمینال دیگر یک فرم بفرست، همان کاری که دکمه‌ی «ثبت» در یک صفحه‌ی وب می‌کند:

Terminal window
curl -s -m 1 -X POST -d "name=علی&qty=2" "http://127.0.0.1:9000/order?ref=insta"
خروجی (ترمینال nc)
POST /order?ref=insta HTTP/1.1
Host: 127.0.0.1:9000
User-Agent: curl/8.5.0
Accept: */*
Content-Length: 17
Content-Type: application/x-www-form-urlencoded
name=علی&qty=2

(-m 1 یعنی بعد از یک ثانیه منتظر جواب نمان؛ nc جوابی نمی‌دهد.) دو جا داده فرستاده شد: query در خود آدرس (?ref=insta، معمولاً برای فیلتر و جست‌وجو) و بدنه بعد از خط خالی (برای داده‌ی اصلی). Content-Length: 17 بایت است، نه حرف: «علی» ۶ بایت دارد. Content-Type به سرور می‌گوید بدنه را چطور بخواند: x-www-form-urlencoded همان قالب فرم‌های HTML است (کلید=مقدار&کلید=مقدار). مرورگر حرف‌های فارسی را در این قالب کدگذاری درصدی هم می‌کند؛ curl فقط وقتی بخواهی (--data-urlencode "name=علی" می‌فرستد name=%D8%B9%D9%84%DB%8C).

برنامه‌ها و اپ‌ها بیشتر JSON می‌فرستند (قالب داده‌ای که در دوره‌ی پایتون مفصل می‌بینی). دوباره nc -l -N 127.0.0.1 9000 و:

Terminal window
curl -s -m 1 -H "Content-Type: application/json" -d '{"sku": "NW-SW", "qty": 1}' http://127.0.0.1:9000/api/orders
خروجی (ترمینال nc)
POST /api/orders HTTP/1.1
Host: 127.0.0.1:9000
User-Agent: curl/8.5.0
Accept: */*
Content-Type: application/json
Content-Length: 26
{"sku": "NW-SW", "qty": 1}

-H یک هدر اضافه می‌کند. به این‌جور آدرس‌ها که برنامه‌ها (نه آدم‌ها) با JSON با آن‌ها حرف می‌زنند API می‌گویند. اپ موبایل فروشگاه با همین شکل درخواست‌ها با سرور حرف می‌زند.

مثال ۶: HTTP یادش نمی‌ماند، کوکی یادش می‌اندازد

Section titled “مثال ۶: HTTP یادش نمی‌ماند، کوکی یادش می‌اندازد”

HTTP بی‌حالت (stateless) است: هر درخواست مستقل است و سرور از درخواست قبلی تو چیزی نمی‌داند. پس چطور سایت می‌داند وارد شده‌ای یا سبد خریدت چه دارد؟ با کوکی: سرور در پاسخ یک هدر Set-Cookie می‌فرستد، مرورگر آن را نگه می‌دارد و در هر درخواست بعدی به همان سایت در هدر Cookie برمی‌گرداند. یک سرور کوچک که با کوکی بازدیدهای هر نفر را جدا می‌شمارد، ~/cookie_server.py:

cookie_server.py
from http.server import BaseHTTPRequestHandler, HTTPServer
from http.cookies import SimpleCookie
class Counter(BaseHTTPRequestHandler):
def do_GET(self):
cookie = SimpleCookie(self.headers.get("Cookie", ""))
visits = int(cookie["visits"].value) + 1 if "visits" in cookie else 1
body = f"این بازدید شماره‌ی {visits} توست.\n"
self.send_response(200)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.send_header("Set-Cookie", f"visits={visits}; Max-Age=3600")
self.end_headers()
self.wfile.write(body.encode("utf-8"))
HTTPServer(("127.0.0.1", 8002), Counter).serve_forever()
Terminal window
python3 ~/cookie_server.py

در ترمینال دیگر:

Terminal window
curl -s -i http://127.0.0.1:8002/
خروجی
HTTP/1.0 200 OK
Server: BaseHTTP/0.6 Python/3.12.3
Date: Wed, 21 Oct 2026 20:57:30 GMT
Content-Type: text/plain; charset=utf-8
Set-Cookie: visits=1; Max-Age=3600
این بازدید شماره‌ی 1 توست.

(-i هدرهای پاسخ را هم چاپ می‌کند.) سرور گفت «این را نگه دار: visits=1، برای یک ساعت». curl به‌طور پیش‌فرض کوکی نگه نمی‌دارد، پس هر بار «بازدید ۱» است:

Terminal window
curl -s http://127.0.0.1:8002/
curl -s http://127.0.0.1:8002/
خروجی
این بازدید شماره‌ی 1 توست.
این بازدید شماره‌ی 1 توست.

با -c (کوکی‌ها را در فایل ذخیره کن) و -b (از فایل بفرست)، curl مثل مرورگر رفتار می‌کند:

Terminal window
curl -s -c jar.txt -b jar.txt http://127.0.0.1:8002/
curl -s -c jar.txt -b jar.txt http://127.0.0.1:8002/
curl -s -c jar.txt -b jar.txt http://127.0.0.1:8002/
خروجی
این بازدید شماره‌ی 1 توست.
این بازدید شماره‌ی 2 توست.
این بازدید شماره‌ی 3 توست.

حالا سرور تو را «می‌شناسد»، فقط به این دلیل که خودت هر بار گفتی کی هستی. و این یعنی می‌توانی دروغ هم بگویی:

Terminal window
curl -s -H "Cookie: visits=999" http://127.0.0.1:8002/
خروجی
این بازدید شماره‌ی 1000 توست.

کوکی در دست کلاینت است و کاربر می‌تواند عوضش کند (همان قانون درس قبل: به کلاینت اعتماد نکن). برای همین سایت‌های واقعی در کوکی چیزی مثل «من مدیرم» یا «اعتبارم ۱ میلیون» نمی‌گذارند؛ فقط یک شناسه‌ی تصادفی و طولانی جلسه (session ID) می‌گذارند و اطلاعات واقعی را روی سرور، کنار همان شناسه، نگه می‌دارند. اگر کسی شناسه‌ی جلسه‌ی تو را بدزدد، خود تو می‌شود؛ یکی از دلیل‌های اصلی HTTPS (درس بعد) همین است.

مثال ۷: همین‌ها در مرورگر

Section titled “مثال ۷: همین‌ها در مرورگر”

در مرورگر (Chrome، Firefox، Edge) کلید F12 را بزن (یا راست‌کلیک ← Inspect) و برو به تب Network. صفحه‌ای را باز کن (یا http://localhost:8000 را رفرش کن). هر ردیف یک درخواست HTTP است: مسیر، متد، کد وضعیت، نوع و اندازه. روی یکی کلیک کن: همان هدرهای درخواست و پاسخ مثال ۱ را می‌بینی. یک صفحه‌ی خبری معمولی صد تا دویست درخواست دارد: HTML، فایل‌های CSS و جاوااسکریپت، عکس‌ها، فونت‌ها و APIها. وقتی صفحه کند است یا جایی از آن خالی می‌ماند، این تب اولین جایی است که نگاه می‌کنی: ردیف‌های قرمز (۴xx، ۵xx) و ردیف‌هایی که زمان زیادی گرفته‌اند.

پشت پرده: از HTTP/1.1 تا HTTP/3

Section titled “پشت پرده: از HTTP/1.1 تا HTTP/3”
یک بازدید صفحه: اول DNS، بعد اتصال TCP (و برای HTTPS دست‌دادن TLS)، بعد درخواست و پاسخ HTML، و بعد درخواست‌های جداگانه برای هر فایلی که HTML به آن اشاره می‌کند.
  • HTTP/1.1 (۱۹۹۷): متن خوانا، همان چیزی که در این درس دیدی. روی یک اتصال، درخواست‌ها یکی‌یکی و به نوبت.
  • HTTP/2 (۲۰۱۵): همان مفهوم‌ها (متد، هدر، کد)، ولی دودویی و فشرده، و ده‌ها درخواست هم‌زمان روی یک اتصال. برای همین در curl -v یک سایت واقعی ممکن است HTTP/2 200 ببینی.
  • HTTP/3 (۲۰۲۲): همان، روی UDP (با پروتکل QUIC) به‌جای TCP، برای شروع سریع‌تر و کار بهتر روی شبکه‌های موبایل ناپایدار. (یادت هست /etc/services برای ۴۴۳ هر دو tcp و udp داشت؟)

خبر خوب: معنی متدها، کدها و هدرها در هر سه یکی است. چیزی که این‌جا یاد گرفتی، همه‌جا کار می‌کند.

هدر کجا معنی
Host درخواست اسم سایت
User-Agent درخواست نرم‌افزار کلاینت
Content-Type هر دو نوع بدنه (text/html، application/json، …)
Content-Length هر دو اندازه‌ی بدنه به بایت
Location پاسخ آدرس مقصد ریدایرکت (با ۳xx)
Set-Cookie / Cookie پاسخ / درخواست گذاشتن و برگرداندن کوکی
Cache-Control پاسخ تا کی می‌شود این را کش کرد
Authorization درخواست مدرک ورود (توکن) برای APIها
گزینه‌ی curl کار
-v نمایش درخواست و پاسخ کامل
-I فقط هدرها (HEAD)
-i هدرهای پاسخ + بدنه
-L دنبال کردن ریدایرکت
-X POST تعیین متد
-d "k=v" فرستادن بدنه (فرم)
-H "Name: value" اضافه کردن هدر
-c file -b file ذخیره و فرستادن کوکی
-s -o /dev/null -w "%{http_code}\n" فقط کد وضعیت

۱) نگاه نکردن به کد وضعیت

Section titled “۱) نگاه نکردن به کد وضعیت”

«صفحه سفید است» ممکن است ۲۰۰ با محتوای خالی باشد، یا ۵۰۰. راه‌حل: اول کد را ببین (curl -I، تب Network)؛ رقم اول مسیر را تعیین می‌کند.

۲) دنبال نکردن ریدایرکت

Section titled “۲) دنبال نکردن ریدایرکت”

curl بدون -L فقط ۳۰۱ را نشان می‌دهد و برنامه‌ای که ریدایرکت را دنبال نکند، صفحه‌ی خالی می‌بیند (سناریو). راه‌حل: -L در curl و بررسی هدر Location.

JSON بدون -H "Content-Type: application/json" فرستاده می‌شود و سرور آن را فرم فرض می‌کند و نمی‌فهمد (معمولاً ۴۰۰ یا ۴۱۵). راه‌حل: همیشه نوع بدنه را بگو.

لینک‌های ?action=delete را ربات‌ها و پیش‌بارگذاری مرورگر «کلیک» می‌کنند. راه‌حل: هر تغییری با POST، PUT یا DELETE.

۵) اطلاعات مهم در کوکی یا query

Section titled “۵) اطلاعات مهم در کوکی یا query”

قیمت، نقش کاربر یا رمز در کوکی یا URL قابل‌دیدن و تغییر است (و URLها در لاگ‌ها و تاریخچه می‌مانند). راه‌حل: فقط شناسه‌ی تصادفی در کوکی؛ داده‌ی واقعی روی سرور؛ و همه روی HTTPS.

تمرینآسان

با curl و سرور http.server در ~/site، کد وضعیت این‌ها را بگیر و برای هر کدام بگو یعنی چه: /، /notes.txt، /missing.png، /images (بدون اسلش آخر).

دیدن جواب
Terminal window
for p in / /notes.txt /missing.png /images; do
curl -s -o /dev/null -w "$p -> %{http_code}\n" "http://localhost:8000$p"
done
خروجی
/ -> 200
/notes.txt -> 200
/missing.png -> 404
/images -> 301

۲۰۰ موفق (برای / سرور خودکار index.html را داد)، ۴۰۴ چنین فایلی نیست، ۳۰۱ ریدایرکت به /images/. ($p در حلقه‌ی bash هر بار یکی از مسیرهاست.)

تمرینمتوسط

URL زیر را تکه‌تکه کن و بگو هر تکه به کدام لایه یا برنامه می‌رسد: http://localhost:8000/products/search?q=کتاب&page=2#top. بعد با nc -l -N 127.0.0.1 8000 (به‌جای سرور) و curl ببین کدام بخش‌ها واقعاً به سرور فرستاده می‌شوند.

دیدن جواب
Terminal window
nc -l -N 127.0.0.1 8000 # ترمینال ۱
curl -s -m 1 "http://localhost:8000/products/search?q=کتاب&page=2#top" # ترمینال ۲
خروجی (ترمینال nc)
GET /products/search?q=کتاب&page=2 HTTP/1.1
Host: localhost:8000
User-Agent: curl/8.5.0
Accept: */*
  • http: پروتکل؛ curl بر اساس آن تصمیم گرفت رمزنگاری نکند و پورت پیش‌فرض ۸۰ باشد.
  • localhost: برای DNS/hosts و در هدر Host.
  • 8000: پورت TCP.
  • /products/search?q=کتاب&page=2: مسیر و query که در خط درخواست به سرور رسید.
  • #top: نرسید. fragment فقط برای مرورگر است («به بخش top صفحه اسکرول کن»).

(مرورگر «کتاب» را قبل از فرستادن به %DA%A9%D8%AA%D8%A7%D8%A8 تبدیل می‌کرد؛ curl آن را همان‌طور خام فرستاد.)

تمرینسخت

با curl یک «جلسه»ی کامل با cookie_server.py شبیه‌سازی کن: سه بازدید با یک فایل کوکی (a.txt) و دو بازدید با فایل کوکی دیگر (b.txt). نشان بده سرور دو «کاربر» جدا می‌بیند. بعد محتوای a.txt را نگاه کن و بگو چرا این طراحی شمارنده ناامن است و طراحی درست چه شکلی است.

دیدن جواب
Terminal window
rm -f a.txt b.txt
for i in 1 2 3; do curl -s -c a.txt -b a.txt http://127.0.0.1:8002/; done
for i in 1 2; do curl -s -c b.txt -b b.txt http://127.0.0.1:8002/; done
tail -1 a.txt
خروجی
این بازدید شماره‌ی 1 توست.
این بازدید شماره‌ی 2 توست.
این بازدید شماره‌ی 3 توست.
این بازدید شماره‌ی 1 توست.
این بازدید شماره‌ی 2 توست.
127.0.0.1 FALSE / FALSE 1792619850 visits 3

دو فایل کوکی مثل دو مرورگر جدا (یا دو آدم) هستند. خط آخر فایل: سایت، مسیر، زمان انقضا (عدد ثانیه از ۱۹۷۰) و خود کوکی visits=3. ناامن است چون مقدار واقعی در دست کلاینت است و می‌شود عوضش کرد (مثال ۶، visits=999). اگر این عدد «امتیاز» یا «اعتبار» بود، کاربر به خودش هر چقدر بخواهد امتیاز می‌داد. طراحی درست: سرور یک شناسه‌ی تصادفی و غیرقابل‌حدس (مثلاً ۳۲ کاراکتر) در کوکی می‌گذارد (session=k8Zq...) و عدد بازدید را روی سرور (در حافظه یا دیتابیس) کنار همان شناسه نگه می‌دارد. کلاینت فقط یک «شماره‌ی کمد» دارد، نه خود کمد.

آزمونک
  1. در خط «GET /notes.txt HTTP/1.1»، /notes.txt چیست؟

  2. کد 404 به کدام خانواده تعلق دارد و یعنی چه؟

  3. پاسخ 301 با هدر Location: https://... یعنی…

  4. کدام بخش URL هرگز به سرور فرستاده نمی‌شود؟

  5. HTTP «بی‌حالت» است یعنی…

  6. چرا نباید «نقش: مدیر» را مستقیم در کوکی گذاشت؟

  • HTTP متن است: درخواست (متد، مسیر، هدرها، بدنه) و پاسخ (کد وضعیت، هدرها، بدنه)، با یک خط خالی بین هدر و بدنه.
  • URL: scheme، host، port، path، query و fragment (که به سرور نمی‌رسد).
  • کدها با رقم اول: 2xx موفق، 3xx برو جای دیگر، 4xx تقصیر درخواست، 5xx تقصیر سرور.
  • متدها: GET بخوان (بدون تغییر)، POST بساز، PUT/PATCH عوض کن، DELETE حذف کن.
  • HTTP بی‌حالت است؛ کوکی (Set-Cookie، Cookie) سرور را «یادآور» می‌کند، ولی در دست کلاینت است.
  • curl -v و تب Network مرورگر، دو ابزار اصلی دیدن HTTP.
برگه‌ی تقلب این درس
دستورکاری که می‌کند
curl -v URLدرخواست و پاسخ کامل
curl -I URLفقط هدرها
curl -L URLدنبال کردن ریدایرکت
curl -s -o /dev/null -w "%{http_code}\n" URLفقط کد وضعیت
curl -X POST -d "a=1" URLفرستادن فرم
curl -H 'Content-Type: application/json' -d '{...}' URLفرستادن JSON
curl -c jar -b jar URLنگه داشتن کوکی
F12 → Networkدیدن درخواست‌ها در مرورگر