پست

Http

HTTP The Definitive Guide

Http

توضیحات

جامع‌ترین مرجع موجود درباره پروتکل HTTP که در سال ۲۰۰۲ توسط O’Reilly منتشر شد و نوشته دیوید گورلی و برایان توتی به همراه سه نویسنده دیگر است. کتاب در ۲۱ فصل و ۶۵۶ صفحه، نه‌تنها HTTP را توضیح می‌دهد بلکه تمام فناوری‌های هسته‌ای وابسته به آن - از مدیریت اتصال TCP، پراکسی و Cache، احراز هویت، HTTPS تا Redirect و Load Balancing - را با دیاگرام‌های دقیق پوشش می‌دهد. این کتاب بیشتر یک «چرا و چطور» است تا یک مرجع header-by-header.

نظر

  • امتیاز : 08/10
  • به دیگران توصیه می‌کنم : بله
  • دوباره می‌خوانم : بله
  • ایده برجسته : مکانیزم HTTP Connection Management - کتاب به‌خوبی نشان می‌دهد که چرا HTTP/1.1 با Keep-Alive و Pipeline، و چرا HTTP/2 با Multiplexing، هر کدام تلاشی برای حل مشکل «Head-of-Line Blocking» بودند. این درک لایه‌ای از تکامل پروتکل، به مهندس کمک می‌کند که بهینه‌سازی performance را نه از روی حفظ بلکه از روی درک انجام دهد.
  • تاثیر در من : وقتی روی API های .NET Core کار می‌کنم، حالا می‌دانم دقیقاً چه اتفاقی پشت هر Request می‌افتد - از TCP handshake تا Cache-Control header؛ این درک عمیق‌تر در Debug کردن مشکلات شبکه و طراحی درست Middleware ها تأثیر مستقیم داشته.
  • نکات مثبت : کامل‌ترین توضیح HTTP/1.1 که در قالب یک کتاب یافت می‌شود؛ دیاگرام‌های فراوان که مفاهیم انتزاعی پروتکل را ملموس می‌کنند؛ پوشش عمیق موضوعاتی که اکثر کتاب‌های دیگر از آن‌ها عبور می‌کنند مثل Web Robots، Content Negotiation و Internationalization
  • نکات منفی : HTTP/2 و HTTP/3 را پوشش نمی‌دهد چون قبل از آن‌ها نوشته شده؛ نمونه کدها به Perl و C نوشته شده‌اند که امروز بی‌ربط به نظر می‌رسند؛ برای کسی که فقط دنبال کار با API های مدرن است، عمق کتاب بیش از نیاز است

مشخصات

  • نویسنده : دیوید گورلی و برایان توتی (David Gourley & Brian Totty)
  • انتشارات : O’Reilly
  • صفحه مشخصات :

بخش‌هایی از کتاب

فصل اول - بخش اول: HTTP چیست و چرا اهمیت دارد؟

معرفی و جایگاه HTTP

نویسندگان کتاب با یک ادعای ساده اما قوی شروع می‌کنند: «HTTP زبان مشترک اینترنت مدرن است.» این جمله را جدی بگیرید. وقتی می‌گویند «زبان مشترک»، منظورشان این است که تمام Web Browsers، Web Servers و Web Applications - بدون استثنا - برای تبادل اطلاعات روی HTTP تکیه می‌کنند.

نکته‌ای که اینجا مهم است و بسیاری از توسعه‌دهندگان نادیده می‌گیرند: HTTP یک Application-layer Protocol است. این یعنی HTTP اصلاً نگران جزئیات شبکه نیست - آن جزئیات را به TCP/IP واگذار کرده. این تقسیم مسئولیت (Separation of Concerns) در طراحی HTTP، یکی از تصمیمات معماری بنیادی این پروتکل است.

HTTP به‌عنوان یک Multimedia Courier

نویسندگان HTTP را «پیک چندرسانه‌ای اینترنت» می‌نامند. چرا؟ چون HTTP روزانه میلیاردها فایل از انواع مختلف - تصویر JPEG، صفحه HTML، فایل متنی، فیلم MPEG، فایل صوتی WAV و Java Applet - را با سرعت، راحتی و قابلیت اطمینان جابه‌جا می‌کند.

یک ویژگی حیاتی که کتاب روی آن تأکید می‌کند: HTTP از Reliable Data-Transmission Protocols استفاده می‌کند. این تضمین می‌دهد که داده شما در مسیر انتقال خراب یا تغییر نمی‌کند. برای شما به‌عنوان یک توسعه‌دهنده .NET Core، این یعنی لایه HTTP زیرساخت انتقال را تضمین می‌کند و شما می‌توانید تمام انرژی خود را روی منطق اپلیکیشن متمرکز کنید.

Web Clients و Web Servers

کتاب ساختار اصلی را این‌گونه تعریف می‌کند:

  • Web Server (HTTP Server): داده‌های اینترنت را نگه می‌دارد و در پاسخ به درخواست‌ها ارائه می‌دهد
  • Web Client (HTTP Client): درخواست‌هایی به سمت سرور می‌فرستد و پاسخ دریافت می‌کند
  • رایج‌ترین Client روزمره: Web Browser (مانند Chrome یا Firefox)

نویسندگان یک مثال عملی می‌زنند: وقتی آدرس http://www.oreilly.com/index.html را در مرورگر تایپ می‌کنید، مرورگر یک HTTP Request به سرور www.oreilly.com ارسال می‌کند. سرور فایل /index.html را پیدا کرده و در قالب یک HTTP Response - به همراه نوع فایل، طول آن و سایر اطلاعات - برمی‌گرداند.

نکته معماری: Client و Server با هم «اجزای پایه‌ای World Wide Web» را تشکیل می‌دهند. این یک معماری Client-Server خالص است.


فصل اول - بخش دوم: Resources، MIME Types و URI/URL

Web Resource چیست؟

نویسندگان تعریف دقیقی از Resource ارائه می‌دهند: «هر منبعی که محتوای وب تولید کند.»

دو نوع Resource وجود دارد:

  • Static Resource: فایل‌هایی که روی فایل‌سیستم سرور ذخیره شده‌اند - HTML، JPEG، Word، PDF، MP4 و هر فرمت دیگری. اینها ثابت‌اند؛ همیشه همان محتوا را برمی‌گردانند.
  • Dynamic Resource: برنامه‌هایی که محتوا را در لحظه تولید می‌کنند - بر اساس هویت کاربر، زمان روز، داده‌های درخواست‌شده یا اطلاعات پایگاه داده.

مثال‌های Dynamic Resource که کتاب ذکر می‌کند: تصویر زنده از یک دوربین، سیستم معامله سهام، جستجوی پایگاه داده ملک، یا فروشگاه آنلاین.

نتیجه‌گیری کتاب: Resource یعنی هر چیزی که منبع محتوا باشد - یک اسپردشیت فروش شرکت، یک Gateway برای جستجوی کتابخانه، یا یک موتور جستجو.

MIME Type - برچسب فرمت داده

از آنجا که اینترنت هزاران نوع داده مختلف دارد، HTTP به یک مکانیزم برچسب‌گذاری نیاز داشت. اینجاست که MIME Type (Multipurpose Internet Mail Extensions) وارد می‌شود.

MIME در اصل برای حل مشکل انتقال پیام‌ها بین سیستم‌های ایمیل مختلف طراحی شده بود. HTTP این استاندارد را «قرض گرفت» و برای برچسب‌گذاری محتوای چندرسانه‌ای خود استفاده کرد.

ساختار MIME Type:

1
نوع‌اصلی/نوع‌فرعی
MIME Typeتوضیح
text/htmlسند HTML
text/plainمتن ساده ASCII
image/jpegتصویر JPEG
image/gifتصویر GIF
video/quicktimeفیلم Apple QuickTime
application/vnd.ms-powerpointارائه PowerPoint

وقتی Browser یک Object از سرور دریافت می‌کند، به MIME Type نگاه می‌کند تا بفهمد چطور آن را نمایش دهد. اکثر مرورگرها صدها نوع محتوا را می‌شناسند: نمایش تصویر، پارس HTML، پخش صدا از بلندگو، یا راه‌اندازی Plugin خارجی.

URI - آدرس پستی اینترنت

هر Resource روی سرور یک نام منحصربه‌فرد دارد که به آن URI (Uniform Resource Identifier) می‌گویند. نویسندگان URI را به «آدرس پستی اینترنت» تشبیه می‌کنند - به‌طور یکتا یک Resource را در سراسر جهان شناسایی و مکان‌یابی می‌کند.

URI دو «طعم» دارد:

URL - مکان دقیق Resource

URL (Uniform Resource Locator) رایج‌ترین شکل URI است. URL مکان دقیق یک Resource را روی یک سرور خاص توصیف می‌کند.

ساختار استاندارد URL از سه بخش تشکیل شده:

1
2
3
4
http://www.joes-hardware.com/specials/saw-blade.gif
  │              │                      │
 Scheme      Server Address         Resource Path
(Protocol)  (آدرس اینترنتی سرور)   (مسیر Resource)
  • Scheme: پروتکل دسترسی (معمولاً http://)
  • Server Address: آدرس اینترنتی سرور (مثل www.joes-hardware.com)
  • Resource Path: مسیر Resource روی سرور (مثل /specials/saw-blade.gif)

چند مثال از کتاب:

URLتوضیح
http://www.oreilly.com/index.htmlصفحه اصلی O’Reilly
http://www.yahoo.com/images/logo.gifلوگوی Yahoo
ftp://joe:[email protected]/locking-pliers.gifفایل تصویر با FTP احراز هویت‌شده

URN - هویت مستقل از مکان

URN (Uniform Resource Name) یک نام یکتا برای یک محتوای خاص است - مستقل از اینکه آن Resource الان کجا قرار دارد.

مثال از کتاب:

1
urn:ietf:rfc:2141

این URN سند استاندارد اینترنتی «RFC 2141» را نام‌گذاری می‌کند، حتی اگر آن سند به مکان دیگری منتقل شود یا در چندین جا کپی باشد.

وضعیت فعلی URN: هنوز در مرحله آزمایشی است و به‌طور گسترده پذیرفته نشده. دلیل اصلی: نیاز به یک زیرساخت پشتیبان برای Resolve کردن مکان Resource دارد که هنوز ایجاد نشده. بنابراین در عمل و در بقیه کتاب، URI و URL به‌جای هم استفاده می‌شوند.

یک نکته معماری مهم

رابطه این مفاهیم را ذهن نگه دارید:

1
2
3
URI (مفهوم کلی شناسه)
 ├── URL (شناسه بر اساس مکان) ← مورد استفاده فعلی
 └── URN (شناسه بر اساس نام)  ← هنوز آزمایشی

این تمایز بعداً در فصل‌های بعدی کتاب اهمیت پیدا می‌کند، به‌ویژه وقتی درباره Caching و Resource Identity صحبت می‌شود.


فصل اول - بخش سوم: Transactions، Messages و Connections

HTTP Transaction چیست؟

یک HTTP Transaction از دو بخش تشکیل می‌شود:

  1. Request Command: از Client به سمت Server ارسال می‌شود
  2. Response Result: از Server به سمت Client برمی‌گردد

این تبادل از طریق بلوک‌های فرمت‌شده داده به نام HTTP Messages انجام می‌شود.

HTTP Methods - دستوراتی که به سرور می‌گویند چه کند

هر HTTP Request Message یک Method دارد که به سرور می‌گوید چه عملیاتی انجام دهد. پنج Method رایج که کتاب معرفی می‌کند:

HTTP Methodتوضیح
GETیک Resource نام‌دار را از سرور به Client ارسال کن
PUTداده‌های Client را در یک Resource نام‌دار روی سرور ذخیره کن
DELETEیک Resource نام‌دار را از سرور حذف کن
POSTداده‌های Client را به یک Gateway Application سرور بفرست
HEADفقط HTTP Header های Response را برای Resource نام‌دار بفرست

Status Codes - نتیجه Transaction را گزارش می‌دهند

هر HTTP Response Message یک Status Code سه‌رقمی دارد که به Client می‌گوید درخواست موفق بوده یا نیاز به اقدام دیگری است.

Status Codeتوضیح
200 OKسند با موفقیت برگردانده شد
302 Redirectبه جای دیگری برو تا Resource را بگیری
404 Not Foundاین Resource پیدا نشد

یک نکته مهم که کتاب تأکید می‌کند: HTTP علاوه بر Status Code عددی، یک Reason Phrase متنی هم می‌فرستد - صرفاً برای توصیف. اما تمام پردازش‌ها فقط بر اساس عدد انجام می‌شوند. به همین دلیل این چهار Response از نظر HTTP کاملاً یکسان هستند:

1
2
3
4
200 OK
200 Document attached
200 Success
200 All's cool, dude

Web Pages - مجموعه‌ای از Resources، نه یک Resource

یک نکته معماری مهم که بسیاری از توسعه‌دهندگان نادیده می‌گیرند: یک اپلیکیشن اغلب چندین HTTP Transaction برای انجام یک کار صادر می‌کند.

وقتی مرورگر یک صفحه وب گرافیکی را بارگذاری می‌کند:

  1. ابتدا یک Transaction برای دریافت HTML Skeleton (ساختار صفحه) انجام می‌دهد
  2. سپس برای هر تصویر، هر Graphics Pane و هر Java Applet، یک Transaction جداگانه صادر می‌کند
  3. این Resource های جاسازی‌شده حتی ممکن است روی سرورهای مختلف باشند

نتیجه: یک «صفحه وب» در واقع یک مجموعه از Resources است، نه یک Resource واحد.

ساختار HTTP Messages

HTTP Messages رشته‌های ساده و Line-Oriented از کاراکترها هستند. چون متن ساده (Plain Text) هستند نه باینری، خواندن و نوشتن آنها برای انسان آسان است.

دو نوع Message وجود دارد:

  • Request Message: از Web Client به Web Server
  • Response Message: از Server به Client

هر Message از سه بخش تشکیل می‌شود:

1
2
3
4
5
6
GET /tools.html HTTP/1.0                    ← Start Line
User-agent: Mozilla/4.75                    ← Header Fields
Host: www.joes-hardware.com                 ← Header Fields
Accept: text/html, image/gif                ← Header Fields
                                            ← Blank Line (پایان Headers)
[بدنه اختیاری]                              ← Body

Start Line

اولین خط Message است. در Request می‌گوید «چه کاری انجام بده»؛ در Response می‌گوید «چه اتفاقی افتاد».

Header Fields

صفر یا بیشتر Header Field بعد از Start Line می‌آیند. هر Header از یک نام و یک مقدار تشکیل شده که با دو‌نقطه (:) از هم جدا می‌شوند. Headers با یک Blank Line تمام می‌شوند.

Body

بعد از Blank Line، یک Body اختیاری قرار دارد که می‌تواند هر نوع داده‌ای باشد - برخلاف Start Line و Headers که متنی و ساختاریافته‌اند، Body می‌تواند داده باینری دلخواه باشد (تصویر، ویدیو، فایل صوتی، نرم‌افزار).

Connections - چطور Messages جابه‌جا می‌شوند؟

لایه‌بندی شبکه

HTTP یک Application Layer Protocol است و جزئیات شبکه را به TCP/IP واگذار می‌کند.

1
2
3
4
5
HTTP        ← Application Layer
TCP         ← Transport Layer
IP          ← Network Layer
Link Interface ← Data Link Layer
Physical Hardware ← Physical Layer

TCP سه تضمین اساسی ارائه می‌دهد:

  • انتقال بدون خطا (Error-free data transportation)
  • تحویل مرتب - داده دقیقاً به همان ترتیبی که فرستاده شده می‌رسد
  • جریان داده بدون قطعه‌بندی - می‌تواند در هر اندازه‌ای داده ارسال کند

IP Addresses و Port Numbers

قبل از اینکه HTTP Client بتواند پیامی بفرستد، باید یک TCP/IP Connection برقرار کند. برای این کار به دو چیز نیاز دارد:

  • IP Address: آدرس سرور (مثل 207.200.83.29)
  • Port Number: شماره پورت نرم‌افزار روی سرور

از کجا IP Address و Port Number را پیدا می‌کند؟ از URL.

1
2
3
http://207.200.83.29:80/index.html     ← IP Address صریح + Port صریح
http://www.netscape.com:80/index.html  ← Hostname + Port صریح
http://www.netscape.com/index.html     ← Hostname + Port پیش‌فرض (80)

وقتی Port در URL وجود نداشته باشد، مقدار پیش‌فرض 80 در نظر گرفته می‌شود. Hostname از طریق DNS (Domain Name Service) به IP Address تبدیل می‌شود.

مراحل کامل یک HTTP Connection

مراحلی که مرورگر برای نمایش یک صفحه طی می‌کند:

  1. استخراج Hostname از URL
  2. تبدیل Hostname به IP Address از طریق DNS
  3. استخراج Port Number از URL (یا استفاده از پیش‌فرض ۸۰)
  4. برقراری TCP Connection به سرور روی آن پورت
  5. ارسال HTTP Request Message به سرور
  6. خواندن HTTP Response Message از سرور
  7. بستن Connection

فصل اول - بخش چهارم: Protocol Versions و Architectural Components

نسخه‌های HTTP

کتاب چهار نسخه اصلی HTTP را معرفی می‌کند که هر کدام مشکلات نسخه قبلی را حل کردند:

HTTP/0.9

اولین نسخه HTTP، مربوط به سال ۱۹۹۱، بسیار ساده و ابتدایی بود. فقط از Method GET پشتیبانی می‌کرد و هیچ MIME Type، HTTP Header یا Version Number ای نداشت. هدف اولیه‌اش فقط انتقال داده HTML بود.

HTTP/1.0

اولین نسخه‌ای که به‌طور گسترده استفاده شد. در این نسخه Version Number، HTTP Headers، Method های اضافه و پشتیبانی از MIME Type اضافه شدند. این باعث شد HTTP بتواند انواع داده‌های مختلف را منتقل کند.

نکته: HTTP/1.0 یک استاندارد رسمی نبود - بیشتر مستندسازی رفتارهای رایجی بود که در عمل استفاده می‌شدند.

HTTP/1.1

این نسخه اولین استاندارد رسمی HTTP است. روی بهینه‌سازی Performance، رفع ابهامات معماری و پشتیبانی از ویژگی‌های پیشرفته‌تر تمرکز کرد. مهم‌ترین چیزی که اضافه کرد Persistent Connections بود - Connection ها دیگر بعد از هر Request بسته نمی‌شدند.

این نسخه همان چیزی است که بقیه کتاب عمدتاً روی آن تمرکز دارد.

HTTP-NG (HTTP/2)

نسخه‌ای که در زمان نوشتن کتاب هنوز در مرحله طراحی بود. هدفش بهبود چشمگیر Performance از طریق Modular Architecture و Distributed Object بود.

Architectural Components of the Web

تا اینجا فقط درباره Client و Server صحبت کردیم. اما کتاب تأکید می‌کند که وب از اجزای معماری بیشتری تشکیل شده است.

Proxies

یک Proxy بین Client و Server قرار می‌گیرد. درخواست‌های HTTP از Client را دریافت کرده و آنها را به سرور می‌فرستد.

Proxy ها اغلب برای امنیت استفاده می‌شوند - می‌توانند همه Traffic را فیلتر کنند. مثلاً در یک محیط کارپوراتیو، Proxy می‌تواند محتوای خطرناک را شناسایی و مسدود کند قبل از اینکه به Client برسد.

Caches

یک Web Cache یا Caching Proxy نوع خاصی از Proxy است. نسخه‌های Local از اسناد پرتقاضا را نگه می‌دارد تا در دفعه بعدی نیازی به دریافت از سرور اصلی نباشد.

مزیت اصلی: کاهش چشمگیر Traffic و Latency. به‌جای اینکه هر Client همان محتوا را از سرور دور دریافت کند، Cache نزدیک‌تر آن را تحویل می‌دهد.

Gateways

Gateway ها سرورهای خاصی هستند که به‌عنوان واسط برای سرورهای دیگر عمل می‌کنند. اغلب برای تبدیل HTTP Traffic به پروتکل دیگری استفاده می‌شوند.

یک مثال رایج: HTTP/FTP Gateway - یک Gateway که درخواست‌های HTTP را دریافت کرده و فایل‌ها را از طریق FTP Fetch می‌کند. از دیدگاه Client، این Gateway فقط یک Web Server معمولی به نظر می‌رسد.

Tunnels

Tunnel ها اتصالات کوری هستند که داده خام را بین دو Connection انتقال می‌دهند بدون اینکه به محتوا نگاه کنند.

رایج‌ترین کاربرد: انتقال SSL Traffic رمزنگاری‌شده از طریق یک HTTP Connection - به این ترتیب SSL Traffic می‌تواند از میان یک HTTP Firewall عبور کند، حتی اگر Firewall محتوا را نفهمد.

Agents

Agent ها Client هایی هستند که به‌صورت خودکار و بدون نظارت انسانی درخواست‌های HTTP صادر می‌کنند.

انواع Agent ها:

  • Web Browser (آشناترین نوع)
  • Web Spider / Web Crawler: وب را برای ایجاد ایندکس موتورهای جستجو می‌گردند
  • Web Robot: وظایف خودکار مختلف انجام می‌دهند

جمع‌بندی معماری کلی وب

همه این اجزا در کنار هم یک تصویر کامل می‌سازند:

1
2
3
4
5
6
7
8
9
10
11
12
13
[Agent/Browser]
      │
      │ HTTP Request
      ▼
   [Proxy] ──── [Cache]
      │
      │ HTTP Request (فیلترشده)
      ▼
  [Gateway]
      │
      │ پروتکل دیگر (FTP، SSL، ...)
      ▼
  [Origin Server]

نویسندگان این فصل را با یک جمله قدرتمند تمام می‌کنند: «این پایان شروع است.» یعنی همه چیزی که در این فصل گفته شد فقط یک نمای کلی بود - هر کدام از این مفاهیم در فصل‌های بعدی با عمق کامل بررسی می‌شوند.


فصل دوم - بخش اول: URLs و ناوبری در منابع اینترنت

چرا URL اهمیت دارد؟

قبل از URL، دسترسی به منابع اینترنت یک کار پیچیده بود. کاربران باید پروتکل‌های مختلف را می‌دانستند، آدرس‌های متفاوت را حفظ می‌کردند و برای هر سرویس رویکرد متفاوتی داشتند.

URL این مشکل را حل کرد: یک فرمت یکپارچه و استاندارد که به کاربر و نرم‌افزار می‌گوید یک Resource کجاست و چطور باید به آن دسترسی داشت. نویسندگان URL را «نقطه شروع ناوبری در اینترنت» می‌نامند.

ساختار کامل URL Syntax

کتاب ساختار کامل URL را به این شکل تعریف می‌کند:

1
scheme://user:password@host:port/path;params?query#frag

هر بخش را جداگانه بررسی می‌کنیم:

Scheme - «چطور دسترسی داشته باشیم»

اولین بخش URL است و پروتکل دسترسی را مشخص می‌کند. Case-Insensitive است یعنی HTTP و http یکسان هستند. با :// از بقیه URL جدا می‌شود.

مثال: http، ftp، mailto، rtsp

Host و Port - «کجا برویم»

Host آدرس سرور را مشخص می‌کند - می‌تواند Hostname باشد (مثل www.joes-hardware.com) یا IP Address مستقیم (مثل 207.200.83.29).

Port شماره پورت TCP است. اگر حذف شود، مقدار پیش‌فرض پروتکل استفاده می‌شود - برای HTTP پیش‌فرض 80 است.

User و Password - «احراز هویت»

برخی سرورها قبل از دسترسی به داده نیاز به احراز هویت دارند. اگر Username بدون Password نوشته شود، مرورگر Password را درخواست می‌کند. اگر هیچ‌کدام نباشند، بعضی Client ها Username پیش‌فرض anonymous را ارسال می‌کنند.

1
2
3
ftp://joe:[email protected]/locking-pliers.gif
      ───  ──────
      User Password

Path - «چه Resource ای می‌خواهیم»

مسیر محلی Resource روی سرور است. از / شروع می‌شود و با / بخش‌هایش از هم جدا می‌شوند.

1
/specials/saw-blade.gif

Parameters - «اطلاعات ورودی اضافه»

بعضی پروتکل‌ها به اطلاعات ورودی بیشتری نیاز دارند. Parameters با ; از Path جدا می‌شوند و می‌توانند چندتایی باشند.

1
http://www.joes-hardware.com/hammers;sale=false/index.html;graphics=true

در این مثال دو Parameter داریم:

  • sale=false برای بخش hammers
  • graphics=true برای index.html

Query String - «فیلتر کردن داده»

برای ارسال پارامترهای Query به Gateway Application ها (مثل پایگاه داده یا موتور جستجو) استفاده می‌شود. با ? شروع می‌شود و جفت‌های key=value با & از هم جدا می‌شوند.

1
http://www.joes-hardware.com/inventory-check.cgi?item=12731&color=blue

Fragment - «بخش خاصی از Resource»

برای اشاره به یک بخش یا قسمت خاص از Resource استفاده می‌شود. با # شروع می‌شود.

1
http://www.joes-hardware.com/tools.html#drills

نکته مهم معماری: Fragment هرگز به سرور ارسال نمی‌شود. Browser آن را برای خودش نگه می‌دارد و بعد از دریافت کل سند، به آن بخش از صفحه Scroll می‌کند.

URL Shortcuts - میان‌برهای URL

کتاب دو نوع Shortcut معرفی می‌کند که Browser ها از آنها پشتیبانی می‌کنند:

Relative URLs

به‌جای نوشتن آدرس کامل، می‌توان آدرس نسبی نوشت. Browser آدرس کامل را با ترکیب Base URL (آدرس صفحه فعلی) و Relative URL می‌سازد.

مثال: اگر در صفحه http://www.joes-hardware.com/tools.html باشیم:

1
<a href="./hammers.html">Hammers</a>

Browser این را به آدرس کامل تبدیل می‌کند:

1
http://www.joes-hardware.com/hammers.html

فرآیند تبدیل Relative URL به Absolute URL را Resolving می‌گویند.

Expandomatic URLs

Browser ها می‌توانند URL را به‌صورت خودکار کامل کنند - بدون اینکه کاربر آدرس کامل تایپ کند.

دو نوع Auto-Expansion وجود دارد:

  • Hostname Expansion: اگر yahoo تایپ کنید، Browser اضافه می‌کند www. و .com تا www.yahoo.com بشود
  • History Expansion: Browser تاریخچه بازدیدها را چک می‌کند و URL های مشابه پیشنهاد می‌دهد

هشدار کتاب: این رفتار Auto-Expansion در Proxy ها می‌تواند مشکل‌ساز باشد، چون Proxy URL اصلی تایپ‌شده را می‌بیند نه URL کامل‌شده را.


فصل دوم - بخش دوم: کاراکترهای خاص و انواع Scheme ها

مشکل کاراکترهای خاص در URL

URL باید روی همه پلتفرم‌ها و پروتکل‌ها قابل استفاده باشد. این یعنی باید هم قابل حمل (Portable) باشد و هم کامل - بتواند هر Resource ای را نام‌گذاری کند.

این دو هدف با هم در تضاد هستند. چرا؟ چون سیستم‌های مختلف کاراکترهای مختلفی را مجاز می‌دانند. برای حل این مشکل، طراحان URL یک مکانیزم Escape تعریف کردند.

URL Character Set - کاراکترهای مجاز

URL از US-ASCII به‌عنوان مجموعه کاراکتر پایه استفاده می‌کند. دلیلش سادگی و سازگاری با سیستم‌های قدیمی است.

کاراکترها در URL به سه دسته تقسیم می‌شوند:

دستهتوضیحمثال 
Unreserved Charactersهمیشه مجاز، نیازی به Encode ندارندA-Z، a-z، 0-9، -، _، .، ~ 
Reserved Charactersمعنای خاص دارند؛ اگر به‌عنوان داده استفاده شوند باید Encode شوند/، ?، #، :، @، &، = 
Unsafe Charactersممکن است در انتقال تغییر کنند یا حذف شوندفاصله، <، >، {، }، ``

Percent Encoding - راه‌حل کاراکترهای غیرمجاز

هر کاراکتری که در URL مجاز نباشد باید با Percent Encoding کدگذاری شود. فرمت آن ساده است:

1
% + دو رقم هگزادسیمال از کد ASCII کاراکتر

چند مثال رایج:

کاراکترEncoded
فاصله (Space)%20
%%25
/ (به‌عنوان داده)%2F
@ (به‌عنوان داده)%40

مثال عملی:

1
http://www.joes-hardware.com/more%20tools.html

این URL در واقع فایلی به نام more tools.html (با فاصله) را درخواست می‌کند.

نکته مهم برای توسعه‌دهنده: در .NET Core متد Uri.EscapeDataString() برای Encode کردن و Uri.UnescapeDataString() برای Decode کردن استفاده می‌شود. هرگز URL را دستی Encode نکنید - جزئیاتی مثل کاراکترهای Reserved که نباید Encode شوند، به‌راحتی فراموش می‌شوند.

انواع Scheme ها

کتاب مهم‌ترین URL Scheme های موجود را معرفی می‌کند. هر Scheme قوانین و ساختار خاص خودش را دارد:

http - پرکاربردترین Scheme

1
http://host:port/path?query#frag

Port پیش‌فرض: 80. اگر Port نباشد، همان ۸۰ فرض می‌شود.

https - نسخه امن HTTP

1
https://host:port/path?query#frag

Port پیش‌فرض: 443. دقیقاً مثل http است اما از SSL/TLS برای رمزنگاری Connection استفاده می‌کند.

ftp - File Transfer Protocol

1
ftp://user:password@host:port/path

برای دانلود و آپلود فایل روی FTP Server استفاده می‌شود. Port پیش‌فرض: 21.

mailto - ارسال ایمیل

1
mailto:addr-spec

مثال:

این Scheme کمی با بقیه فرق دارد - به یک Resource روی شبکه اشاره نمی‌کند بلکه یک آدرس ایمیل است.

file - فایل محلی

1
file://host/path

برای دسترسی به فایل‌های محلی روی همان دستگاه استفاده می‌شود. اگر host حذف شود، همان دستگاه محلی فرض می‌شود:

1
file:///C:/Documents/presentation.ppt

rtsp و rtspu - Streaming Media

1
2
rtsp://host:port/path
rtspu://host:port/path

برای Real Time Streaming Protocol استفاده می‌شود. rtsp از TCP و rtspu از UDP استفاده می‌کند.

news - Usenet Newsgroup

1
2
news:newsgroup
news:message-id

برای دسترسی به Usenet Newsgroup ها. یک ویژگی جالب: news URL اطلاعات کافی برای مکان‌یابی Resource ندارد - نمی‌گوید از کدام سرور باید خواند.

telnet - دسترسی به سرویس‌های تعاملی

1
telnet://user:password@host:port/

برای ارتباط با سرویس‌های Interactive روی شبکه استفاده می‌شود.

آینده URL - از URL به URN

کتاب در پایان این فصل یک مشکل بنیادی URL را مطرح می‌کند: URL به مکان وابسته است.

اگر یک Resource جابه‌جا شود، URL قدیمی دیگر کار نمی‌کند و همه Linkهایی که به آن اشاره می‌کردند «شکسته» می‌شوند. این مشکل معروف به Link Rot است.

راه‌حل پیشنهادی: URN (Uniform Resource Name) - یک نام ثابت برای Resource که مستقل از مکان آن است. با URN می‌توان Resource را به هر جایی منتقل کرد بدون اینکه Link های موجود بشکنند.

اما چرا هنوز URN جایگزین URL نشده؟ کتاب دلیل را صریح بیان می‌کند: برای کار کردن URN به یک Resolution Infrastructure نیاز است - یک سیستم که بتواند نام URN را به مکان فعلی Resource تبدیل کند. ساخت و پیاده‌سازی چنین زیرساختی در مقیاس اینترنت بسیار پیچیده است.


فصل سوم - بخش اول: ساختار و جریان HTTP Messages

Messages چگونه جریان دارند؟

نویسندگان فصل سوم را با یک استعاره شروع می‌کنند: HTTP Messages مثل آب در لوله جریان دارند. اصطلاح Inbound و Outbound برای توصیف جهت جریان Message استفاده می‌شود.

  • Inbound: Message به سمت Origin Server حرکت می‌کند
  • Outbound: Message از Origin Server به سمت User Agent (Browser) برمی‌گردد

یک قانون مهم: همه Messages - چه Request چه Response - Downstream جریان دارند. یعنی همه Message ها از فرستنده به سمت گیرنده حرکت می‌کنند، صرف‌نظر از اینکه Request باشند یا Response.

دو نوع Message

HTTP دقیقاً دو نوع Message دارد و نه چیز دیگری:

  • Request Message: از Client به Server - درخواست انجام یک عملیات
  • Response Message: از Server به Client - نتیجه آن عملیات

ساختار کامل یک Message

هر HTTP Message از سه بخش اصلی تشکیل شده:

1
2
3
4
<start-line>          ← یک خط - توصیف Message
<headers>             ← صفر یا بیشتر - متادیتا
<blank line>          ← یک خط خالی اجباری (CRLF)
<body>                ← اختیاری - داده واقعی

ساختار Request Message:

1
2
3
4
5
GET /specials/saw-blade.gif HTTP/1.0    ← Request Line
Host: www.joes-hardware.com            ← Header
Accept: image/*                        ← Header
                                       ← Blank Line
                                       ← (Body ندارد)

ساختار Response Message:

1
2
3
4
5
HTTP/1.0 200 OK                        ← Response Line (Status Line)
Content-type: image/gif                ← Header
Content-length: 8572                   ← Header
                                       ← Blank Line
[binary content of image]              ← Body

Start Line - اولین خط هر Message

Request Line

در Request Message، Start Line را Request Line می‌نامند. از سه بخش تشکیل شده که با فاصله از هم جدا می‌شوند:

1
2
3
Method   Request-URL   HTTP-Version
  │           │              │
 GET    /tools.html      HTTP/1.0
  • Method: عملیاتی که سرور باید انجام دهد
  • Request-URL: آدرس کامل یا مسیر محلی Resource
  • HTTP-Version: نسخه پروتکل که فرستنده از آن استفاده می‌کند

Status Line (Response Line)

در Response Message، Start Line را Status Line می‌نامند:

1
2
3
HTTP-Version   Status-Code   Reason-Phrase
     │              │               │
 HTTP/1.0          200             OK
  • HTTP-Version: نسخه‌ای که سرور پشتیبانی می‌کند
  • Status-Code: عدد سه‌رقمی نتیجه عملیات
  • Reason-Phrase: توضیح متنی Status Code - فقط برای انسان، نه برای ماشین

Headers - متادیتای Message

Header ها اطلاعات اضافی درباره Request یا Response فراهم می‌کنند. هر Header یک Name-Value Pair است که با : از هم جدا می‌شوند:

1
2
Content-type: text/html
Content-length: 403

قوانین مهم Header ها:

  • هر Header باید در یک خط باشد
  • اگر یک Header خط بعدی را نیاز داشت، می‌توان با فاصله یا Tab در خط بعدی ادامه داد (به این Folding می‌گویند)
  • Headers با یک Blank Line تمام می‌شوند
  • Header Name ها Case-Insensitive هستند - Content-Type و content-type یکسان‌اند

دسته‌بندی Header ها

کتاب Header ها را به پنج دسته تقسیم می‌کند:

دستهتوضیحمثال
General Headersهم در Request هم در Response استفاده می‌شوندDate، Connection
Request Headersفقط در Request MessageHost، Accept، User-Agent
Response Headersفقط در Response MessageServer، WWW-Authenticate
Entity Headersاطلاعات درباره BodyContent-Type، Content-Length
Extension HeadersHeader های سفارشی که در استاندارد نیستندهر X-Custom-Header

Body - داده واقعی

Body بخش اختیاری Message است که داده واقعی را حمل می‌کند.

  • Request Body: داده‌ای که Client به سرور می‌فرستد - مثل داده فرم در POST
  • Response Body: محتوایی که سرور به Client برمی‌گرداند - HTML، تصویر، JSON و…

یک تفاوت اساسی با Start Line و Headers: Body می‌تواند داده باینری دلخواه باشد. تصویر، ویدیو، فایل صوتی، نرم‌افزار - هیچ محدودیتی در نوع داده وجود ندارد.

نکته Performance: اگر Body داده باینری دارد، هرگز نباید آن را به‌عنوان متن parse کنید. در .NET Core برای خواندن Body به‌صورت Stream از Request.Body استفاده کنید تا از کپی غیرضروری داده در حافظه جلوگیری شود.


فصل سوم - بخش اول: ساختار HTTP Messages

جریان Messages

کتاب با یک تعریف دقیق شروع می‌کند: HTTP Message ها بلوک‌های پایه‌ای داده هستند که بین HTTP Application ها ردوبدل می‌شوند.

پیام‌ها در یک جهت مشخص جاری می‌شوند - نویسندگان عمداً از کلمه «flow» استفاده می‌کنند:

  • Inbound: به سمت Origin Server
  • Outbound: از Origin Server به سمت Client (بعد از پردازش)
  • Downstream: صرف‌نظر از Request یا Response، همه پیام‌ها به سمت Downstream جاری می‌شوند

نکته معماری: Sender همیشه نسبت به Receiver در Upstream قرار دارد. این مفهوم بعداً در فصل Proxies و Caching اهمیت پیدا می‌کند.

اجزای یک HTTP Message

هر HTTP Message از سه بخش تشکیل می‌شود:

1
2
3
4
5
GET /specials/saw-blade.gif HTTP/1.0    ← Start Line
Host: www.joes-hardware.com             ← Headers
Accept: text/*                          ← Headers
                                        ← Blank Line (CRLF)

Start Line

در Request Message سه چیز مشخص می‌کند: Method، Request URL و نسخه HTTP.

1
2
3
GET /specials/saw-blade.gif HTTP/1.0
─── ─────────────────────── ────────
Method      Request URL      Version

در Response Message سه چیز مشخص می‌کند: نسخه HTTP، Status Code و Reason Phrase.

1
2
3
HTTP/1.0 200 OK
──────── ─── ──
Version  Code Reason Phrase

Header Fields

صفر یا بیشتر Header به شکل Name: Value بعد از Start Line می‌آیند. Headers با یک Blank Line (CRLF خالی) تمام می‌شوند.

کتاب Headers را به پنج دسته تقسیم می‌کند:

دستهتوضیح
General Headersهم در Request و هم در Response استفاده می‌شوند
Request Headersفقط در Request - اطلاعات بیشتر درباره درخواست
Response Headersفقط در Response - اطلاعات بیشتر درباره پاسخ
Entity Headersاطلاعات درباره Body (طول، نوع محتوا و …)
Extension HeadersHeader های غیراستاندارد که توسط توسعه‌دهنده تعریف می‌شوند

یک Header می‌تواند به چند خط تقسیم شود اگر خط بعدی با فاصله یا Tab شروع شود:

1
2
3
4
5
HTTP/1.0 200 OK
Content-Type: image/gif
Content-Length: 8572
Server: Test Server
    Version 1.0

Body

بعد از Blank Line قرار می‌گیرد. می‌تواند هر نوع داده‌ای باشد - متن، HTML، تصویر، ویدیو، JSON، XML یا هر فرمت دیگری.

در Request، Body داده‌ای است که به سرور ارسال می‌شود (مثلاً داده‌های یک Form). در Response، Body محتوای درخواست‌شده است.

HTTP Methods - جزئیات کامل

GET

رایج‌ترین Method. از سرور می‌خواهد یک Resource را برگرداند. Body ندارد.

1
2
3
GET /seasonal/index-fall.html HTTP/1.1
Host: www.joes-hardware.com
Accept: *

پاسخ سرور:

1
2
3
4
5
6
7
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 617

<HTML>
<HEAD><TITLE>Joe's Hardware - Autumn</TITLE></HEAD>
...

دقیقاً مثل GET است اما سرور فقط Headers را برمی‌گرداند، نه Body را.

کاربردهای اصلی:

  • بررسی وجود یک Resource بدون دانلود محتوا
  • بررسی تغییر Resource از آخرین بار (برای Caching)
  • بررسی نوع و اندازه محتوا قبل از دانلود
1
2
3
HEAD /seasonal/index-fall.html HTTP/1.1
Host: www.joes-hardware.com
Accept: *

پاسخ سرور (بدون Body):

1
2
3
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 617

PUT

داده‌های ارسال‌شده از Client را روی سرور ذخیره می‌کند. محتوای Body را در Resource مشخص‌شده در URL می‌نویسد.

نکته امنیتی که کتاب تأکید می‌کند: چون PUT به کاربر اجازه تغییر محتوای سرور را می‌دهد، بسیاری از Web Server ها آن را Disable می‌کنند یا نیاز به احراز هویت دارند.

POST

برای ارسال داده به یک Gateway Application (مثل یک Form HTML یا یک API Endpoint) استفاده می‌شود. داده در Body قرار می‌گیرد.

1
2
3
4
5
6
POST /inventory-check.cgi HTTP/1.1
Host: www.joes-hardware.com
Content-Type: application/x-www-form-urlencoded
Content-length: 27

product=1&quantity=5&price=150

TRACE

یک Request تشخیصی است. درخواست را از طریق تمام Proxy ها و Gateway های مسیر ردیابی می‌کند. سرور نهایی تمام Message دریافت‌شده را عیناً در Body Response برمی‌گرداند - به این ترتیب Client می‌تواند ببیند پیامش در مسیر چطور تغییر کرده.

1
2
3
TRACE /product-list.txt HTTP/1.1
Host: www.joes-hardware.com
Accept: text/*

پاسخ سرور:

1
2
3
4
5
6
7
HTTP/1.1 200 OK
Content-Type: message/http
Content-length: 96

TRACE /product-list.txt HTTP/1.1
Host: www.joes-hardware.com
Accept: text/*

محدودیت مهم: TRACE نباید Body داشته باشد.

OPTIONS

می‌پرسد سرور برای یک Resource خاص از چه Method هایی پشتیبانی می‌کند.

1
2
3
OPTIONS * HTTP/1.1
Host: www.joes-hardware.com
Accept: *

پاسخ سرور:

1
2
HTTP/1.1 200 OK
Allow: GET, POST, PUT, OPTIONS

DELETE

از سرور می‌خواهد یک Resource را حذف کند. اما نویسندگان یک نکته مهم را ذکر می‌کنند: سرور مجاز است این درخواست را نادیده بگیرد. Client نمی‌تواند مطمئن باشد که حذف واقعاً انجام شده.


فصل سوم - بخش دوم: Status Codes و Headers

Status Codes - دسته‌بندی کامل

Status Code ها در پنج دسته کلی تقسیم می‌شوند. رقم اول کد، دسته را مشخص می‌کند:

دستهمحدودهمعنا
Informational100-199درخواست دریافت شد، پردازش ادامه دارد
Success200-299درخواست با موفقیت دریافت، فهمیده و پذیرفته شد
Redirection300-399برای تکمیل درخواست، اقدام بیشتری لازم است
Client Error400-499درخواست دارای خطای Syntax یا قابل اجرا نیست
Server Error500-599سرور در اجرای یک درخواست معتبر موفق نشد

دسته 100 - Informational

100 Continue

یک Status Code چالش‌برانگیز است. وقتی Client می‌خواهد یک Request با Body بزرگ ارسال کند، ابتدا یک درخواست اولیه با Header Expect: 100-continue می‌فرستد تا مطمئن شود سرور آماده دریافت است.

اگر سرور آماده باشد، 100 Continue می‌فرستد و Client Body را ارسال می‌کند. اگر سرور آماده نباشد، یک Error Code مناسب می‌فرستد و Client از ارسال Body جلوگیری می‌کند - این باعث صرفه‌جویی در Bandwidth می‌شود.

دسته 200 - Success

200 OK

رایج‌ترین Response. درخواست موفق بوده. Body شامل Resource درخواست‌شده است.

201 Created

برای درخواست‌های PUT یا POST که یک Resource جدید ایجاد کردند. سرور باید Location Header را با URL Resource جدید ارسال کند.

202 Accepted

درخواست پذیرفته شده اما پردازش هنوز کامل نشده. سرور تضمینی نمی‌دهد که پردازش حتماً کامل شود.

204 No Content

Response دارای Status Line و Headers است اما Body ندارد. اغلب بعد از یک عملیات موفق که نیازی به برگرداندن داده نیست استفاده می‌شود.

206 Partial Content

بخشی از یک Resource برگردانده شده. این Status Code مربوط به Range Request هاست - وقتی Client فقط بخشی از یک فایل بزرگ را درخواست می‌کند. Header های Content-Range و Date الزامی هستند.

دسته 300 - Redirection

301 Moved Permanently

Resource به آدرس جدیدی منتقل شده. سرور آدرس جدید را در Header Location برمی‌گرداند. Client و هر Proxy باید URL های ذخیره‌شده را به‌روز کنند.

302 Found

مشابه 301 اما موقتی است. Resource فعلاً در آدرس دیگری است اما در آینده به آدرس اصلی برمی‌گردد. Client نباید URL ذخیره‌شده را تغییر دهد.

303 See Other

به Client می‌گوید Resource را از URL دیگری با Method GET بگیرد. معمولاً بعد از یک POST استفاده می‌شود تا از مشکل Double Submit (زدن دوباره دکمه Submit) جلوگیری شود.

304 Not Modified

وقتی Client یک Conditional Request می‌فرستد (با Header هایی مثل If-Modified-Since)، اگر Resource تغییر نکرده باشد، سرور 304 برمی‌گرداند بدون Body. Client از نسخه Cache‌شده خودش استفاده می‌کند.

307 Temporary Redirect

مشابه 302 است اما با یک تفاوت مهم: Method و Body درخواست اصلی تغییر نمی‌کنند. در 302، بعضی Client ها POST را به GET تبدیل می‌کردند که رفتار اشتباهی بود.

دسته 400 - Client Error

400 Bad Request

Request دارای Syntax اشتباه است یا سرور نمی‌تواند آن را بفهمد.

401 Unauthorized

درخواست به احراز هویت نیاز دارد. سرور باید Header WWW-Authenticate را با نوع احراز هویت موردنیاز ارسال کند.

403 Forbidden

سرور درخواست را فهمیده اما از اجرای آن امتناع می‌کند - بدون توضیح دلیل. برخلاف 401، احراز هویت کمکی نمی‌کند.

404 Not Found

سرور Resource درخواست‌شده را پیدا نکرد. رایج‌ترین Error Code ای است که کاربران با آن روبه‌رو می‌شوند.

405 Method Not Allowed

Method مورداستفاده برای این Resource مجاز نیست. سرور باید در Header Allow لیست Method های مجاز را برگرداند.

408 Request Timeout

Client در زمان مشخص‌شده Request را کامل نکرد.

409 Conflict

Request با وضعیت فعلی Resource در تضاد است - مثلاً آپلود یک فایل قدیمی‌تر از نسخه روی سرور.

410 Gone

مشابه 404 اما با یک تفاوت: سرور قطعاً می‌داند که Resource وجود داشته و برای همیشه حذف شده.

415 Unsupported Media Type

سرور نوع داده‌ای که Client فرستاده را پشتیبانی نمی‌کند.

دسته 500 - Server Error

500 Internal Server Error

عمومی‌ترین Server Error. سرور با یک خطای غیرمنتظره مواجه شده.

501 Not Implemented

Client از یک قابلیت یا Method استفاده کرده که سرور از آن پشتیبانی نمی‌کند.

502 Bad Gateway

یک Proxy یا Gateway پاسخ نامعتبری از سرور بعدی در زنجیره دریافت کرده.

503 Service Unavailable

سرور موقتاً قادر به پاسخگویی نیست - معمولاً به دلیل حجم بالا یا در حال نگهداری (Maintenance). اگر مشخص باشد چه مدت طول می‌کشد، سرور باید Header Retry-After ارسال کند.

504 Gateway Timeout

مشابه 408 اما برای Proxy یا Gateway - سرور بعدی در زنجیره در زمان مقرر پاسخ نداد.

Headers - جزئیات کامل

General Headers

در هر دو Request و Response استفاده می‌شوند:

Headerتوضیح
Connectionنحوه مدیریت Connection بعد از Transaction
Dateتاریخ و زمان ساخت Message
MIME-Versionنسخه MIME استفاده‌شده
Trailerمجموعه Header هایی که در Trailer یک Chunked Transfer هستند
Transfer-Encodingنوع Encoding که روی Body اعمال شده
ViaProxy هایی که Message از آنها عبور کرده
Cache-Controlدستورالعمل‌های Caching

Request Headers

Headerتوضیح
Client-IPIP Address کاربر
Fromآدرس ایمیل کاربر
HostHostname و Port سرور مقصد
RefererURL صفحه‌ای که لینک فعلی از آن آمده
UA-CPUنوع CPU کاربر
User-Agentاطلاعات Browser یا Client
Acceptنوع‌های محتوایی که Client می‌پذیرد
Accept-CharsetCharacter Set هایی که Client می‌پذیرد
Accept-EncodingEncoding هایی که Client می‌پذیرد
Accept-Languageزبان‌هایی که Client ترجیح می‌دهد
Authorizationداده احراز هویت
CookieToken هایی که سرور قبلاً ارسال کرده
If-Modified-Sinceفقط اگر بعد از این تاریخ تغییر کرده برگردان
If-None-Matchفقط اگر ETag تغییر کرده برگردان

Response Headers

Headerتوضیح
Ageچه مدت است که Response در Cache بوده
Serverنوع و نسخه نرم‌افزار سرور
AllowMethod های مجاز برای این Resource
LocationURL Redirect برای Redirection
Set-CookieToken ای که سرور می‌خواهد Client ذخیره کند
WWW-Authenticateنوع احراز هویت موردنیاز

Entity Headers

Headerتوضیح
Content-Typeنوع MIME محتوای Body
Content-Lengthطول Body به Byte
Content-EncodingEncoding اعمال‌شده روی Body
Content-Languageزبان محتوای Body
Content-Locationآدرس جایگزین برای Resource
Content-MD5Checksum MD5 از محتوا برای تأیید صحت
Content-Rangeمحدوده Byte های ارسال‌شده در یک Partial Response
Last-Modifiedآخرین تاریخ تغییر Resource
Expiresتاریخ انقضای محتوا
ETagیک شناسه یکتا برای نسخه فعلی Resource

فصل چهارم - بخش اول: TCP Connections و Performance

چرا Connection Management مهم است؟

نویسندگان این فصل را با یک ادعای صریح شروع می‌کنند: اکثر توسعه‌دهندگان HTTP درک درستی از TCP Connections ندارند. این سوءتفاهم مستقیماً روی Performance اپلیکیشن تأثیر می‌گذارد.

HTTP پیام‌هایش را از طریق TCP Connection منتقل می‌کند. قبل از اینکه بتوان یک HTTP Request فرستاد، باید یک TCP Connection بین Client و Server برقرار شود.

TCP Connection - زیرساخت انتقال HTTP

TCP داده را به صورت جریان پیوسته‌ای از Byte ها منتقل می‌کند. این Byte ها در قطعاتی به نام Segment بسته‌بندی می‌شوند و از طریق IP منتقل می‌شوند. هر Segment در یک IP Packet قرار می‌گیرد.

هر IP Packet شامل این اجزاست:

  • IP Packet Header: آدرس IP مبدأ و مقصد، اندازه و سایر Flag ها
  • TCP Segment Header: شماره Port مبدأ و مقصد، Flag های کنترلی و اطلاعات ترتیب داده
  • TCP Data Chunk: بخشی از داده واقعی

TCP Performance - کجا زمان تلف می‌شود؟

کتاب دقیقاً مشخص می‌کند که در یک HTTP Transaction زمان صرف چه کارهایی می‌شود:

1
2
3
4
DNS Lookup → TCP Connection → HTTP Request → Processing → HTTP Response
     │               │               │             │              │
  ~ms تا s       Three-Way       Header+Body    Server Side   Header+Body
               Handshake          ارسال          پردازش        دریافت

مهم‌ترین تأخیرها:

1. TCP Connection Setup - Three-Way Handshake

قبل از اینکه یک Byte از داده HTTP منتقل شود، باید TCP Handshake انجام شود:

1
2
3
4
5
6
7
8
Client                          Server
  │                               │
  │──── SYN ──────────────────────▶│   (۱) Client می‌گوید: «می‌خواهم وصل شوم»
  │                               │
  │◀─── SYN + ACK ────────────────│   (۲) Server می‌گوید: «قبول دارم»
  │                               │
  │──── ACK + HTTP Request ───────▶│   (۳) Client تأیید می‌کند + Request را می‌فرستد
  │                               │

این Handshake یک Round-Trip کامل قبل از هر Request اضافه می‌کند. برای Connection های طولانی این هزینه یک‌بار پرداخت می‌شود، اما برای Connection های کوتاه هر بار باید تکرار شود.

2. TCP Slow Start - آغاز کند اتصال

TCP برای جلوگیری از ازدحام شبکه (Congestion) از مکانیزمی به نام Slow Start استفاده می‌کند. یک Connection جدید نمی‌تواند فوراً با حداکثر سرعت داده بفرستد - باید ابتدا با سرعت کم شروع کند و به تدریج سرعت را افزایش دهد.

نتیجه عملی: Connection های قدیمی از Connection های جدید سریع‌تر هستند. یک Connection که مدتی در حال انتقال داده بوده، Slow Start را پشت سر گذاشته و می‌تواند با سرعت کامل کار کند.

این یکی از دلایل اصلی است که Persistent Connection ها در HTTP/1.1 اضافه شدند.

3. Nagle Algorithm و TCP_NODELAY

TCP یک الگوریتم دارد به نام Nagle Algorithm که Packet های کوچک را با هم ترکیب می‌کند تا تعداد Packet های شبکه را کاهش دهد.

اما این الگوریتم برای HTTP مشکل‌ساز است چون:

  • HTTP Header ها معمولاً کوچک هستند
  • Nagle Algorithm آنها را نگه می‌دارد تا با داده بیشتری ترکیب کند
  • این باعث تأخیر می‌شود

راه‌حل: فعال کردن TCP_NODELAY که Nagle Algorithm را غیرفعال می‌کند.

4. TIME_WAIT و Port Exhaustion

وقتی یک TCP Connection بسته می‌شود، آدرس IP و شماره Port به مدت 2MSL (حدود 2 دقیقه) در حالت TIME_WAIT باقی می‌مانند.

این برای جلوگیری از تداخل بین Connection های قدیمی و جدید است. اما در سرورهای پرترافیک می‌تواند منجر به Port Exhaustion شود - کمبود Port در دسترس برای Connection های جدید.

HTTP Connection Handling - مدیریت Connection ها

Connection Header

HTTP یک Header به نام Connection دارد که می‌تواند سه نوع Token داشته باشد:

  • نام یک HTTP Header: آن Header فقط برای این Connection معتبر است و نباید به Downstream فرستاده شود
  • close: بعد از این Transaction، Connection بسته می‌شود
  • keep-alive: می‌خواهیم Connection را باز نگه داریم (HTTP/1.0)

نکته مهم: هر Proxy که Connection Header را دریافت می‌کند باید آن را پردازش و حذف کند قبل از اینکه Message را به جلو بفرستد.

انواع Connection - چهار رویکرد

1. Sequential Connections - ساده‌ترین، کندترین

1
2
3
Connection 1:  [Connect][Request][Response][Close]
Connection 2:                                      [Connect][Request][Response][Close]
Connection 3:                                                                          [Connect]...

برای هر درخواست یک Connection جدید باز می‌شود. کند است چون هر بار باید Handshake و Slow Start انجام شود.

2. Parallel Connections - چند Connection همزمان

چند HTTP Transaction به‌صورت همزمان روی Connection های مختلف انجام می‌شوند.

1
2
3
4
Connection 1: [Connect][──── Request 1 ────]
Connection 2: [Connect][──── Request 2 ────]
Connection 3: [Connect][──── Request 3 ────]
Connection 4: [Connect][──── Request 4 ────]

مزایا:

  • زمان کل کاهش می‌یابد
  • اگر یک Connection کند باشد، بقیه ادامه می‌دهند

معایب:

  • هر Connection به Memory و منابع نیاز دارد
  • اگر Bandwidth محدود باشد، Parallel Connection ها با هم رقابت می‌کنند و ممکن است نتیجه بدتری بدهند
  • سرور باید بار Connection های بیشتری را تحمل کند

محدودیت عملی: مرورگرها معمولاً تعداد Parallel Connection ها را به 6-8 محدود می‌کنند.

3. Persistent Connections - مهم‌ترین بهبود HTTP/1.1

بعد از انجام یک Transaction، Connection باز می‌ماند و برای Request های بعدی استفاده می‌شود.

1
Connection:  [Connect][Request 1][Response 1][Request 2][Response 2][Request 3][Response 3][Close]

مزایا:

  • هزینه Handshake فقط یک‌بار پرداخت می‌شود
  • TCP Slow Start اثر کمتری دارد
  • تعداد Connection های باز کمتر است

دو نوع Persistent Connection:

Keep-Alive (HTTP/1.0): باید صریحاً با Header Connection: keep-alive درخواست شود. سرور می‌تواند این پارامترها را تنظیم کند:

1
2
Connection: keep-alive
Keep-Alive: max=5, timeout=120
  • max=5: حداکثر ۵ Transaction دیگر روی این Connection
  • timeout=120: اگر ۱۲۰ ثانیه بی‌استفاده باشد بسته شود

Persistent (HTTP/1.1): در HTTP/1.1 تمام Connection ها به‌صورت پیش‌فرض Persistent هستند. برای بستن Connection باید صریحاً Connection: close فرستاده شود.

4. Pipelined Connections - ارسال چند Request بدون انتظار

Pipelining اجازه می‌دهد چند Request پشت سر هم فرستاده شوند بدون اینکه منتظر Response بمانیم.

1
2
Client: [Request 1][Request 2][Request 3]───────▶
Server:                        ◀─────────────────[Response 1][Response 2][Response 3]

محدودیت‌های مهم:

  • فقط روی Persistent Connection ها کار می‌کند
  • Response ها باید دقیقاً به همان ترتیب Request ها برگردند
  • اگر سرور Connection را ببندد، Client باید Request های بی‌پاسخ را دوباره بفرستد
  • فقط برای Idempotent Method ها مناسب است (مثل GET و HEAD، نه POST)

فصل چهارم - بخش دوم: Connection Close و رفتارهای پیچیده TCP

بستن Connection - ساده به نظر می‌رسد، اما نیست

نویسندگان این بخش را «رازهای Connection Close» می‌نامند. دلیلش این است که بستن یک TCP Connection ظاهراً ساده است، اما جزئیاتی دارد که می‌توانند منجر به از دست رفتن داده شوند.

At Will Close - بستن دلخواه Connection

هر دو طرف Client و Server می‌توانند در هر زمانی Connection را ببندند - حتی در وسط یک Transaction. اما کتاب تأکید می‌کند که این کار باید با احتیاط انجام شود.

سرورها معمولاً Connection را در این موارد می‌بندند:

  • بعد از ارسال آخرین Response
  • وقتی Connection برای مدت طولانی بی‌استفاده بوده (Idle Timeout)
  • وقتی تعداد Transaction های مجاز (max در Keep-Alive) تمام شده

دو نوع TCP Close

TCP دو مکانیزم برای بستن Connection دارد که تفاوت مهمی بین آنهاست:

Full Close

هر دو کانال ارسال و دریافت بسته می‌شوند.

1
2
3
4
5
Client ──────────────────── Server
  │   ◀── FIN ──────────────  │   (Server کانال ارسالش را می‌بندد)
  │   ──── ACK ──────────────▶│
  │   ──── FIN ──────────────▶│   (Client کانال ارسالش را می‌بندد)
  │   ◀── ACK ───────────────  │

Half Close

فقط یک کانال بسته می‌شود - معمولاً کانال ارسال (Output).

1
2
3
Client ──── Half Close Output ───▶ Server
  │         (من دیگر نمی‌فرستم)        │
  │   ◀─── داده هنوز می‌تواند بیاد ────│

Half Close در HTTP اهمیت دارد چون به یک طرف اجازه می‌دهد بگوید «من کارم تمام شد» اما هنوز آماده دریافت داده باشد.

Reset Error - خطرناک‌ترین رفتار

اگر یک طرف داده‌ای به یک Connection بسته‌شده بفرستد، طرف مقابل یک TCP Reset برمی‌گرداند.

مشکل اینجاست: TCP Reset می‌تواند Buffer های دریافت را پاک کند. این یعنی اگر سرور یک Full Close انجام داده باشد و Client هنوز داده می‌فرستد، وقتی Reset دریافت می‌شود، ممکن است داده‌ای که سرور قبلاً فرستاده و هنوز در Buffer است از بین برود - حتی اگر آن داده کاملاً معتبر بوده باشد.

Graceful Connection Close - روش صحیح بستن

برای جلوگیری از Reset Error، کتاب یک رویه مشخص توصیه می‌کند:

مراحل Graceful Close:

  1. Half Close کردن Output: فقط کانال ارسال را ببند
  2. منتظر ماندن: صبر کن تا طرف مقابل هم Output خودش را ببندد، یا یک Timeout رخ دهد
  3. Full Close کردن: حالا Connection را کاملاً ببند
1
2
3
4
5
طرفی که می‌خواهد ببندد:
  │── Half Close Output ──▶   (من دیگر داده نمی‌فرستم)
  │◀── داده‌های باقی‌مانده ──   (هنوز می‌خوانم)
  │◀── Half Close Output ──   (طرف مقابل هم تمام کرد)
  │── Full Close ──────────▶  (حالا می‌توان کاملاً بست)

نکته عملی: در .NET Core این رفتار از طریق Socket.Shutdown(SocketShutdown.Send) قبل از Socket.Close() پیاده‌سازی می‌شود.

جمع‌بندی Performance - راهکارهای عملی

کتاب در پایان فصل یک جمع‌بندی از راهکارهای بهبود Performance ارائه می‌دهد:

Parallel Connections

برای صفحاتی با منابع زیاد (تصویر، CSS، JS) مناسب است. اما تعداد زیاد Connection همزمان می‌تواند روی سرور فشار بیاورد.

مناسب برای: بارگذاری اولیه صفحات وب

Persistent Connections

بهترین راه‌حل برای اکثر سناریوها. هزینه Handshake و Slow Start را برای چندین Request جبران می‌کند.

مناسب برای: API Call های متعدد به یک سرور (مثلاً در یک Service مبتنی بر .NET Core که به چند Endpoint مراجعه می‌کند)

Persistent + Parallel ترکیب

در عمل مرورگرهای مدرن از ترکیب هر دو استفاده می‌کنند: چند Persistent Connection موازی.

Pipelined Connections

کمترین Latency را دارد اما پیچیده‌ترین پیاده‌سازی را هم نیاز دارد. فقط برای Method های Idempotent مناسب است.

یک دیدگاه انتقادی

نویسندگان در پایان فصل صادقانه اعتراف می‌کنند که مستندات HTTP/1.1 درباره Connection Management ناقص و گاهی متناقض هستند. بسیاری از رفتارهای واقعی در دنیای عملی از طریق آزمون و خطا کشف شده‌اند، نه از طریق مستندات رسمی.

این توضیح می‌دهد که چرا پیاده‌سازی‌های مختلف (Browser ها، Proxy ها، Server ها) گاهی رفتارهای متفاوتی دارند - همه آنها استاندارد را به شکل خود تفسیر کرده‌اند.


فصل پنجم - بخش اول: Web Servers و معماری داخلی آنها

Web Server چیست؟

نویسندگان از همان ابتدا تمایز مهمی می‌کشند: Web Server می‌تواند هم به نرم‌افزار و هم به سخت‌افزار اشاره داشته باشد.

  • نرم‌افزار: برنامه‌ای که HTTP Request ها را پردازش می‌کند و HTTP Response برمی‌گرداند
  • سخت‌افزار: دستگاه فیزیکی که این نرم‌افزار روی آن اجرا می‌شود

Web Server ها در اشکال و اندازه‌های مختلف وجود دارند:

  • Embedded Web Server: داخل دستگاه‌هایی مثل پرینتر یا Router - بسیار کوچک و محدود
  • General-Purpose Web Server: مثل Apache، Nginx، IIS - برای اکثر کاربردها
  • E-Commerce Platform Server: بهینه‌شده برای فروشگاه‌های آنلاین
  • Secure Server: با SSL/TLS داخلی

هفت مرحله پردازش یک Request

کتاب چرخه کامل پردازش یک Request در Web Server را به هفت مرحله تقسیم می‌کند. این مدل یکی از مهم‌ترین بخش‌های فصل است:

1
2
3
4
5
6
7
Step 1: Accept Connection      ← اتصال را بپذیر
Step 2: Receive Request        ← Request را بخوان
Step 3: Process Request        ← Request را پردازش کن
Step 4: Map and Access Resource← منبع را پیدا کن
Step 5: Build Response         ← Response بساز
Step 6: Send Response          ← Response بفرست
Step 7: Log Transaction        ← ثبت کن

مرحله اول: پذیرش Connection

Web Server دائماً روی Port 80 (یا Port دیگری) گوش می‌دهد. وقتی یک Client اتصال TCP برقرار می‌کند، چند حالت ممکن است:

حالت عادی: Server اتصال را قبول می‌کند، IP Client را ثبت می‌کند و آماده دریافت Request می‌شود.

Reverse DNS Lookup: Server می‌تواند IP Address را به Hostname تبدیل کند تا بفهمد از کجا آمده. اما کتاب هشدار می‌دهد: این کار می‌تواند کند باشد و Performance را تحت تأثیر قرار دهد. بسیاری از Server های پرترافیک این قابلیت را غیرفعال می‌کنند.

ident Protocol: یک مکانیزم قدیمی که به Server اجازه می‌دهد Username کاربر پشت Connection را بپرسد. امروزه به دلایل امنیتی و عدم پشتیبانی Firewall ها تقریباً منسوخ شده.

مرحله دوم: دریافت Request

Server باید Message ورودی را از روی شبکه بخواند. این کار ساده به نظر می‌رسد، اما چند چالش دارد:

Connection های پرسرعت

Web Server های پرترافیک ممکن است صدها یا هزاران Connection همزمان داشته باشند. برای مدیریت این همزمانی سه معماری رایج وجود دارد:

Single-Threaded: یک Thread به نوبت همه Connection ها را پردازش می‌کند. ساده اما کند - یک Request کند، همه را کند می‌کند.

Multi-Process / Multi-Threaded: برای هر Connection یک Process یا Thread جداگانه. ساده اما در تعداد زیاد Connection، مصرف Memory بالا می‌رود.

Multiplexed I/O: یک Thread چندین Connection را همزمان Observe می‌کند. فقط وقتی یک Connection آماده داده است، پردازش می‌شود. این روش در Server های مدرن (مثل Nginx و Node.js) رایج است.

مرحله سوم: پردازش Request

Server باید Request Message را Parse کند:

  • Method، URL و نسخه HTTP را از Start Line استخراج کند
  • Header های Request را بخواند (هر Header در یک خط، با : جدا شده)
  • Body را بخواند (اگر Content-Length یا Transfer-Encoding: chunked باشد)

مرحله چهارم: Map و دسترسی به Resource

این مرحله یکی از پیچیده‌ترین‌هاست. Server باید URL را به یک Resource واقعی تبدیل کند.

Docroot - ریشه محتوا

ساده‌ترین نگاشت: URL Path مستقیماً به یک مسیر فایل در Document Root (Docroot) تبدیل می‌شود.

1
2
3
Request:  GET /specials/saw-blade.gif HTTP/1.0
Docroot:  /usr/local/httpd/files
Result:   /usr/local/httpd/files/specials/saw-blade.gif

نکته امنیتی مهم: اگر یک Path Traversal Attack رخ دهد (../../etc/passwd)، مهاجم می‌تواند به فایل‌های خارج از Docroot دسترسی پیدا کند. Web Server های خوب این الگوها را شناسایی و رد می‌کنند.

Virtual Hosting - چند سایت روی یک سرور

یک Web Server می‌تواند برای چندین وب‌سایت مختلف روی یک IP Address هاست کند. این کار از طریق HTTP/1.1 Header Host ممکن است:

1
2
3
4
5
6
7
Request به همان سرور:

GET /index.html HTTP/1.1          GET /index.html HTTP/1.1
Host: www.joes-hardware.com       Host: www.marys-antiques.com
        │                                    │
        ▼                                    ▼
/docs/joe/index.html          /docs/mary/index.html

Directory Listing

اگر URL به یک Directory اشاره کند نه یک فایل، Server می‌تواند:

  • یک Index File (مثل index.html) برگرداند اگر وجود داشته باشد
  • یک صفحه HTML با لیست محتوای Directory بسازد
  • یک 403 Forbidden برگرداند

Dynamic Resource - اجرای برنامه

اگر URL به یک CGI Script یا یک Dynamic Resource اشاره کند، Server برنامه را اجرا می‌کند و خروجی آن را به Client برمی‌گرداند.

مرحله پنجم: ساختن Response

بعد از پیدا کردن Resource، Server باید Response Message بسازد:

انتخاب Status Code

  • Resource پیدا شد: 200 OK
  • Resource پیدا نشد: 404 Not Found
  • Resource جابه‌جا شده: 301 Moved Permanently
  • خطای سرور: 500 Internal Server Error

تنظیم MIME Type

Server باید نوع محتوا را مشخص کند. این کار از چند طریق انجام می‌شود:

  • mime.types File: بر اساس پسوند فایل (.html → text/html، .jpg → image/jpeg)
  • Magic Typing: بررسی محتوای فایل برای تشخیص نوع آن - دقیق‌تر اما کندتر
  • Explicit Typing: مدیر سرور نوع را صریحاً تنظیم کرده
  • Type Negotiation: اگر چند نسخه از یک Resource وجود دارد، سرور بهترین را انتخاب می‌کند

مرحله ششم: ارسال Response

Server باید Response را از طریق همان TCP Connection برگرداند. برای Persistent Connection ها، Server باید مراقب باشد که Header های صحیح Content-Length را بفرستد تا Client بداند Body کجا تمام می‌شود.

مرحله هفتم: Logging

آخرین مرحله ثبت Transaction است. Web Server ها معمولاً اطلاعات زیر را ذخیره می‌کنند:

  • IP Address Client
  • تاریخ و زمان Request
  • URL درخواست‌شده
  • HTTP Method
  • Status Code پاسخ
  • اندازه Response به Byte
  • Referer Header
  • User-Agent Header

فصل ششم - بخش اول: Proxies و نقش آنها در وب

Web Intermediary - واسط شبکه

Proxy یک واسط (Intermediary) بین Client و Server است. یک Proxy هم به‌عنوان Server (برای Client) و هم به‌عنوان Client (برای Server) عمل می‌کند.

این تعریف ساده، یک معماری قدرتمند می‌سازد - چون Proxy می‌تواند هر چیزی که از کنارش رد می‌شود را بخواند، تغییر دهد یا مسدود کند.

چرا از Proxy استفاده می‌کنیم؟

کتاب دلایل متعددی برای استفاده از Proxy ذکر می‌کند:

امنیت (Security)

Proxy می‌تواند به‌عنوان یک Firewall عمل کند. در شبکه‌های کارپوراتیو، تمام Traffic از Proxy عبور می‌کند و Proxy می‌تواند محتوای خطرناک را شناسایی و مسدود کند.

فیلترینگ محتوا (Content Filtering)

مدارس و سازمان‌ها از Proxy برای مسدود کردن سایت‌های نامناسب استفاده می‌کنند. Proxy می‌تواند بر اساس URL، MIME Type یا محتوا تصمیم بگیرد.

Document Access Control

Proxy می‌تواند کنترل دسترسی متمرکز ایجاد کند - یک نقطه واحد برای مدیریت دسترسی به منابع مختلف.

Caching

Proxy نسخه‌های Local از محتوای پرتقاضا را نگه می‌دارد. وقتی Client همان محتوا را دوباره درخواست می‌کند، Proxy از Cache پاسخ می‌دهد بدون اینکه به Origin Server مراجعه کند - این Bandwidth و زمان را کاهش می‌دهد.

Anonymity

Proxy می‌تواند HTTP Header هایی مثل From، Referer، Cookie و User-Agent را حذف کند تا هویت Client پنهان بماند.

Transcoding

Proxy می‌تواند محتوا را تبدیل کند - مثلاً تصاویر را فشرده‌تر کند برای دستگاه‌های موبایل، یا محتوای HTML را برای دستگاه‌های با صفحه کوچک تنظیم کند.

Private vs Shared Proxy

Private Proxy

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

Shared (Public) Proxy

بین چندین Client مشترک است. Caching Proxy ها معمولاً Shared هستند چون هرچه کاربران بیشتری از یک Cache استفاده کنند، نرخ Cache Hit بالاتر می‌رود و کارایی بیشتر می‌شود.

Proxy در مقابل Gateway

کتاب یک تمایز مهم بین Proxy و Gateway می‌کشد:

ویژگیProxyGateway
پروتکلهر دو طرف از HTTP استفاده می‌کننددو طرف ممکن است پروتکل متفاوت داشته باشند
نقشواسط بین دو HTTP Client/Serverمترجم بین دو پروتکل مختلف
مثالCaching Proxy، Security ProxyHTTP/FTP Gateway، HTTP/SSL Gateway

در عمل این تمایز همیشه تیز نیست - بعضی Proxy ها قابلیت‌های Gateway هم دارند.

جایگاه Proxy در شبکه

Proxy ها می‌توانند در جاهای مختلفی در مسیر بین Client و Server قرار بگیرند:

Egress Proxy

در خروجی شبکه محلی قرار می‌گیرد. هدف: کنترل Traffic خروجی از سازمان - فیلترینگ، Logging و امنیت.

1
[کاربران داخلی] ──▶ [Egress Proxy] ──▶ [اینترنت]

Ingress Proxy (Reverse Proxy)

در ورودی سرور قرار می‌گیرد. از دیدگاه Client مثل یک Web Server واقعی به نظر می‌رسد. هدف: Load Balancing، Caching و امنیت سرور.

1
[اینترنت] ──▶ [Reverse Proxy] ──▶ [سرورهای داخلی]

Surrogates (Reverse Proxies)

نوع خاصی از Reverse Proxy که جلوی یک یا چند Web Server قرار می‌گیرد و وانمود می‌کند که خود سرور است. درخواست‌ها را Cache می‌کند و فقط در صورت نیاز به سرور اصلی می‌فرستد.

Network Exchange Proxy

در نقاط اتصال شبکه‌های بزرگ قرار می‌گیرد تا Traffic سنگین را مدیریت کند.

تنظیمات Proxy در Client

Client چطور می‌داند درخواست‌هایش را به Proxy بفرستد؟ کتاب چهار روش ذکر می‌کند:

Manual Configuration

کاربر آدرس و Port Proxy را دستی در تنظیمات مرورگر وارد می‌کند.

Browser Preconfiguration

سازمان مرورگرها را از قبل با تنظیمات Proxy پیکربندی می‌کند - کاربر نیازی به تنظیم دستی ندارد.

Proxy Auto-Configuration - PAC

یک فایل JavaScript با پسوند .pac که به مرورگر می‌گوید برای هر URL از کدام Proxy استفاده کند.

1
2
3
4
5
6
function FindProxyForURL(url, host) {
    if (host == "www.joes-hardware.com") {
        return "PROXY cache.joes-hardware.com:8080";
    }
    return "DIRECT";
}

Web Proxy Autodiscovery - WPAD

مرورگر به‌صورت خودکار فایل PAC را پیدا می‌کند از طریق DHCP یا DNS - بدون اینکه کاربر آدرسی وارد کند.

رفتار Proxy با HTTP Messages

Request به Proxy

وقتی Client از طریق Proxy کار می‌کند، URL در Request Message کامل می‌شود - نه فقط Path:

بدون Proxy:

1
2
GET /specials/saw-blade.gif HTTP/1.0
Host: www.joes-hardware.com

با Proxy:

1
2
GET http://www.joes-hardware.com/specials/saw-blade.gif HTTP/1.0
Host: www.joes-hardware.com

این تفاوت مهم است: Proxy باید بداند به کجا Request را بفرستد، پس URL کامل لازم است.

Via Header - ردیابی مسیر

هر Proxy که یک Message از کنارش رد می‌شود باید یک Via Header اضافه کند:

1
Via: 1.1 proxy.joes-hardware.com

اگر چند Proxy در مسیر باشند، هر کدام نام خودشان را اضافه می‌کنند:

1
Via: 1.1 proxy1.joes-hardware.com, 1.1 proxy2.joes-hardware.com

این Header چند کاربرد دارد:

  • تشخیص Loop: اگر Proxy نام خودش را در Via دید، یک Loop وجود دارد
  • تشخیص Protocol: نسخه HTTP هر Proxy مشخص است
  • Debugging: می‌توان دید Request از کدام مسیر رفته

TRACE Method و Proxy ها

متد TRACE ابزار عالی برای دیدن مسیر دقیق Request از طریق Proxy هاست. هر Proxy نام خودش را به Via Header اضافه می‌کند و Server نهایی کل Message دریافت‌شده را برمی‌گرداند.

اما یک مشکل وجود دارد: اگر Proxy ها در مسیر حساس باشند (مثلاً یک Proxy محرمانه)، نمی‌خواهند نامشان فاش شود. برای این حالت HTTP یک Header داریم:

1
Max-Forwards: 2

این Header تعداد Proxy هایی که می‌توانند Request را Forward کنند را محدود می‌کند.


فصل ششم - بخش دوم: Proxy Authentication و Proxy Interoperation

Proxy Authentication - احراز هویت در Proxy

Proxy ها می‌توانند دسترسی به محتوا را کنترل کنند. اگر کاربر احراز هویت نشده باشد، Proxy می‌تواند محتوا را مسدود کند تا کاربر اعتبارنامه ارائه دهد.

چرخه احراز هویت در Proxy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Client                    Proxy                    Server
  │                         │                         │
  │── GET /resource ────────▶│                         │
  │                         │                         │
  │◀── 407 Proxy Auth Req ──│  (Proxy می‌گوید: احراز هویت لازم است)
  │    Proxy-Authenticate:  │
  │    Basic realm="..."    │
  │                         │
  │── GET /resource ────────▶│
  │   Proxy-Authorization:  │
  │   Basic [credentials]   │
  │                         │──── Request ───────────▶│
  │                         │◀─── Response ───────────│
  │◀── 200 OK ──────────────│

دو Header کلیدی:

Headerجهتتوضیح
Proxy-Authenticateاز Proxy به Clientنوع احراز هویت موردنیاز را مشخص می‌کند
Proxy-Authorizationاز Client به Proxyاعتبارنامه کاربر را حمل می‌کند

تفاوت مهم با Server Authentication: در Server Authentication از 401 Unauthorized و Header های WWW-Authenticate / Authorization استفاده می‌شود. در Proxy Authentication از 407 Proxy Authentication Required و Header های Proxy-Authenticate / Proxy-Authorization استفاده می‌شود. این دو مکانیزم مستقل از هم هستند و می‌توانند همزمان فعال باشند.

مشکل Proxy Authentication در زنجیره‌های چندگانه

وقتی چند Proxy در مسیر وجود دارد، هر Proxy می‌تواند احراز هویت مستقل خودش را داشته باشد:

1
Client ──▶ [Proxy 1: نیاز به Auth] ──▶ [Proxy 2: نیاز به Auth] ──▶ Server

Client باید با هر Proxy به‌طور جداگانه احراز هویت کند. این می‌تواند تجربه کاربری پیچیده‌ای ایجاد کند.

Proxy Interoperation - همکاری Proxy با دیگران

مشکل Header های ناشناخته

یک Proxy ممکن است با Header هایی روبه‌رو شود که آنها را نمی‌شناسد. قانون کتاب صریح است:

Proxy باید Header های ناشناخته را بدون تغییر Forward کند.

این قانون برای Extensibility (قابلیت توسعه) HTTP حیاتی است. اگر Proxy ها Header های ناشناخته را حذف کنند، هیچ Header جدیدی نمی‌توانست در اینترنت منتشر شود.

Blind Relay - مشکل Keep-Alive

یکی از معروف‌ترین مشکلات Proxy، Blind Relay است. یک Proxy ساده که فقط Byte ها را بدون فهمیدن آنها منتقل می‌کند می‌تواند مشکل جدی ایجاد کند:

1
Client ──── Connection: keep-alive ────▶ [Proxy ساده] ──── Connection: keep-alive ────▶ Server

مشکل اینجاست: Proxy این Header را عیناً به Server می‌فرستد. Server فکر می‌کند Proxy می‌خواهد Connection را باز نگه دارد و منتظر می‌ماند. اما Proxy نمی‌داند این Header چه معنایی دارد و Connection را می‌بندد.

نتیجه: Server و Proxy در یک Deadlock گیر می‌کنند - Server منتظر Request بعدی است، Proxy منتظر Response است.

راه‌حل: Proxy باید Connection Header را پردازش کند و قبل از Forward کردن حذف کند. این یکی از دلایلی است که Proxy نویسی پیچیده است.

Hop-by-Hop Headers - فقط برای یک Connection

برخی Header ها فقط بین دو نقطه مجاور (یک Hop) معنا دارند و نباید در طول زنجیره Forward شوند. به اینها Hop-by-Hop Headers می‌گویند:

  • Connection
  • Keep-Alive
  • Proxy-Authenticate
  • Proxy-Authorization
  • TE
  • Trailers
  • Transfer-Encoding
  • Upgrade

هر Proxy باید این Header ها را پردازش و سپس حذف کند قبل از اینکه Message را به Hop بعدی بفرستد.

در مقابل، End-to-End Headers باید در تمام طول زنجیره تا Server نهایی حفظ شوند - مثل Content-Type، Authorization و Cache-Control.

SSL و Proxy - مشکل رمزنگاری

یک چالش مهم: وقتی Client از HTTPS استفاده می‌کند، تمام داده رمزنگاری شده است. Proxy نمی‌تواند محتوا را بخواند یا تغییر دهد.

راه‌حل HTTP برای این مشکل: CONNECT Method

1
2
3
4
5
6
7
8
Client ──── CONNECT www.joes-hardware.com:443 HTTP/1.0 ────▶ [Proxy]
            (لطفاً یک Tunnel باز کن)

[Proxy] ──── 200 Connection Established ────▶ Client
            (Tunnel آماده است)

Client ═══════════ SSL Traffic رمزنگاری‌شده ══════════════ Server
            (Proxy فقط Byte ها را منتقل می‌کند بدون خواندن)

با CONNECT، Proxy یک TCP Tunnel کور ایجاد می‌کند. داده‌های رمزنگاری‌شده بدون هیچ تغییری از این Tunnel عبور می‌کنند.

جمع‌بندی - چرا Proxy پیچیده است؟

نویسندگان در پایان فصل یک نکته صادقانه مطرح می‌کنند: پیاده‌سازی یک Proxy صحیح بسیار سخت‌تر از آن چیزی است که به نظر می‌رسد. دلایل اصلی:

  • باید هم رفتار Client و هم رفتار Server را پیاده‌سازی کند
  • باید Header های Hop-by-Hop را شناسایی و حذف کند
  • باید با Client هایی که HTTP/1.0 می‌فهمند و Client هایی که HTTP/1.1 می‌فهمند به‌درستی کار کند
  • باید مشکلات Blind Relay را مدیریت کند
  • باید Loop Detection داشته باشد

فصل هفتم - بخش اول: Caching و مبانی آن

چرا Cache وجود دارد؟

نویسندگان فصل را با چهار مشکل واقعی شبکه شروع می‌کنند که Cache برای حل آنها طراحی شده:

۱. Redundant Data Transfers - انتقال‌های تکراری

اگر صد کاربر مختلف یک صفحه محبوب را باز کنند، همان محتوا صد بار از Server دانلود می‌شود. این یعنی صد بار همان Bandwidth مصرف می‌شود و صد بار همان فشار روی Server می‌آید. Cache این تکرار را حذف می‌کند.

۲. Bandwidth Bottlenecks - گلوگاه پهنای باند

بسیاری از شبکه‌های محلی (LAN) پهنای باند بیشتری نسبت به اتصال اینترنتی خود دارند. اگر محتوا در یک Cache محلی باشد، با سرعت LAN تحویل داده می‌شود نه با سرعت محدودتر اینترنت.

۳. Flash Crowds - ازدحام ناگهانی

گاهی یک رویداد (مثل خبر مهم یا تبلیغ تلویزیونی) باعث می‌شود تعداد زیادی کاربر همزمان به یک سایت هجوم ببرند. بدون Cache، این ازدحام می‌تواند Server را از پا دربیاورد. Cache این بار را توزیع می‌کند.

۴. Distance Delays - تأخیر فاصله

حتی با سرعت نور، ارسال داده از آمریکا به ایران چند صد میلی‌ثانیه طول می‌کشد. Cache محلی این فاصله فیزیکی را حذف می‌کند.

Cache Hit و Cache Miss

دو حالت اصلی وجود دارد وقتی Client یک Request می‌فرستد:

Cache Hit: محتوای درخواست‌شده در Cache موجود است و مستقیم از Cache تحویل داده می‌شود - بدون مراجعه به Origin Server.

Cache Miss: محتوا در Cache نیست. Cache باید به Origin Server مراجعه کند، محتوا را بگیرد، در Cache ذخیره کند و به Client تحویل دهد.

Revalidation - بررسی تازگی Cache

حتی اگر محتوا در Cache باشد، ممکن است منقضی شده باشد. Cache می‌تواند از Origin Server بپرسد آیا محتوا تغییر کرده:

Revalidation Hit: سرور تأیید می‌کند محتوا تغییر نکرده (304 Not Modified). Cache همان نسخه قبلی را تحویل می‌دهد - فقط یک Request کوچک رفت و بدون Body برگشت.

Revalidation Miss: سرور می‌گوید محتوا تغییر کرده (200 OK). Cache نسخه جدید را دریافت، ذخیره و تحویل می‌دهد.

Object Deleted: سرور می‌گوید محتوا دیگر وجود ندارد (404 Not Found). Cache نسخه قبلی را حذف می‌کند.

Hit Rate - نرخ موفقیت Cache

Cache Hit Rate (یا Document Hit Rate) درصد Request هایی است که Cache توانسته بدون مراجعه به Server پاسخ دهد.

اما این معیار کامل نیست. یک فایل ۱۰۰ کیلوبایتی که Miss می‌شود خیلی بیشتر از ۱۰۰ فایل ۱ کیلوبایتی که Hit می‌شوند روی شبکه تأثیر می‌گذارد. به همین دلیل کتاب یک معیار دیگر معرفی می‌کند:

Byte Hit Rate: درصد Byte هایی که از Cache تأمین شده‌اند. این معیار بهتر نشان می‌دهد Cache چقدر Bandwidth صرفه‌جویی کرده.

Cache Topologies - چیدمان Cache ها

Private Cache - Cache اختصاصی

فقط برای یک کاربر. مرورگرها یک Cache خصوصی روی دیسک و Memory دارند. سریع و شخصی است اما محتوا فقط برای یک کاربر قابل استفاده است.

Shared (Public) Cache - Cache مشترک

بین چندین کاربر مشترک است. هرچه کاربران بیشتری از یک Cache Proxy استفاده کنند، نرخ Hit بالاتر می‌رود.

1
2
3
[کاربر ۱] ─┐
[کاربر ۲] ─┤──▶ [Shared Cache Proxy] ──▶ [اینترنت]
[کاربر ۳] ─┘

Cache Hierarchy - سلسله‌مراتب Cache

در سازمان‌های بزرگ معمولاً چند لایه Cache وجود دارد:

1
2
3
4
5
6
7
8
9
10
[مرورگر کاربر]
      │ Miss
      ▼
[Cache محلی شعبه]
      │ Miss
      ▼
[Cache مرکزی سازمان]
      │ Miss
      ▼
[Origin Server]

هر لایه Cache های لایه‌های پایین‌تر را «گرم» نگه می‌دارد. اگر Cache محلی Miss بخورد، به Cache مرکزی می‌رود و اگر آن هم Miss بخورد، به Origin Server می‌رود.

Mesh Networks - شبکه توری Cache

در این معماری پیشرفته‌تر، Cache ها می‌توانند با هم صحبت کنند - قبل از رفتن به Origin Server، یک Cache از Cache های هم‌سطح خود می‌پرسد آیا محتوا را دارند.

به این رفتار Cache Peering می‌گویند و پروتکل ICP (Internet Cache Protocol) برای آن استفاده می‌شود.

مراحل پردازش Cache

کتاب پردازش هر Request در Cache را به هفت مرحله تقسیم می‌کند:

1
2
3
4
5
6
7
۱. دریافت Request
۲. Parse کردن URL
۳. جستجو در Cache
۴. بررسی Freshness
۵. ساختن Response
۶. ارسال Response
۷. Logging

مرحله سوم: جستجو در Cache

Cache URL را به‌عنوان کلید برای جستجو استفاده می‌کند. اگر یک نسخه Cache‌شده پیدا شد، بررسی Freshness آغاز می‌شود.

مرحله چهارم: بررسی Freshness

این مرحله پیچیده‌ترین بخش Caching است. Cache باید تصمیم بگیرد آیا نسخه ذخیره‌شده هنوز تازه (Fresh) است یا منقضی (Stale) شده.

Freshness - تازگی محتوا

Document Expiration

Server می‌تواند صریحاً به Cache بگوید محتوا تا چه زمانی معتبر است. دو Header برای این کار وجود دارد:

Expires Header (HTTP/1.0):

1
Expires: Fri, 05 Jul 2002, 05:00:00 GMT

یک تاریخ و زمان مطلق مشخص می‌کند. مشکل: بستگی به همگام بودن ساعت Server و Cache دارد.

Cache-Control: max-age Header (HTTP/1.1):

1
Cache-Control: max-age=484200

مدت زمان نسبی به ثانیه مشخص می‌کند - از لحظه‌ای که محتوا Cache شده. این رویکرد بهتر است چون به همگام‌سازی ساعت وابسته نیست.

Age محتوا چطور محاسبه می‌شود؟

Cache باید بداند محتوا چقدر پیر است. این از طریق Header Age انجام می‌شود:

1
Age: 600

یعنی این Response 600 ثانیه پیش از Origin Server دریافت شده.


فصل هفتم - بخش دوم: Revalidation، Cache-Control و Controlling Cachability

Server Revalidation - بررسی تازگی با سرور

وقتی Cache تشخیص می‌دهد محتوا ممکن است منقضی شده باشد، باید از Origin Server بپرسد آیا تغییری رخ داده. به این فرآیند Revalidation می‌گویند.

دو Header برای Revalidation وجود دارد:

If-Modified-Since - بر اساس زمان

1
2
GET /page.html HTTP/1.1
If-Modified-Since: Wed, 22 Nov 2001 18:26:32 GMT

Cache می‌پرسد: «آیا این فایل بعد از این تاریخ تغییر کرده؟»

  • اگر تغییر نکرده: سرور 304 Not Modified برمی‌گرداند - بدون Body
  • اگر تغییر کرده: سرور 200 OK با محتوای جدید برمی‌گرداند

If-None-Match - بر اساس ETag

گاهی تاریخ کافی نیست. مثلاً:

  • یک فایل هر چند ثانیه بازنویسی می‌شود اما محتوایش تغییر نمی‌کند
  • یک سرور ساعت دقیقی ندارد
  • محتوا در کمتر از یک ثانیه تغییر کرده

راه‌حل: ETag (Entity Tag) - یک شناسه یکتا برای نسخه خاصی از محتوا.

1
2
HTTP/1.1 200 OK
ETag: "v2.6"

Cache این ETag را نگه می‌دارد و دفعه بعد می‌پرسد:

1
2
GET /page.html HTTP/1.1
If-None-Match: "v2.6"

اگر ETag تغییر نکرده باشد، سرور 304 Not Modified برمی‌گرداند.

کدام بهتر است؟ کتاب توضیح می‌دهد که HTTP/1.1 ترجیح می‌دهد از ETag استفاده شود، اما هر دو روش می‌توانند همزمان استفاده شوند. اگر هر دو فرستاده شوند، سرور باید هر دو را تأیید کند.

Cache-Control Directives - دستورات کنترل Cache

HTTP/1.1 یک سیستم جامع برای کنترل رفتار Cache معرفی کرد. این Directive ها می‌توانند توسط هر دو طرف - Server و Client - فرستاده شوند.

Directive های Request (Client به Cache)

Directiveتوضیح
no-cacheمحتوا را از Cache نده - اول با سرور Revalidate کن
no-storeهیچ نسخه‌ای از این Response را ذخیره نکن
max-age=<s>محتوایی قبول دارم که کمتر از s ثانیه پیر باشد
max-stale=<s>حتی محتوای منقضی‌شده را قبول دارم، اما حداکثر تا s ثانیه بعد از انقضا
min-fresh=<s>محتوایی می‌خواهم که حداقل s ثانیه دیگر تازه باشد
only-if-cachedفقط از Cache پاسخ بده - اگر ندارد 504 Gateway Timeout بده
no-transformمحتوا را تغییر نده (مثلاً تصاویر را فشرده نکن)

Directive های Response (Server به Cache)

Directiveتوضیح
publicاین Response را می‌توان Cache کرد حتی اگر Authenticated باشد
privateاین Response فقط برای یک کاربر خاص است - Shared Cache نباید آن را ذخیره کند
no-cacheبدون Revalidation با سرور، این محتوا را به Client نده
no-storeاین Response را اصلاً ذخیره نکن
no-transformمحتوا را تغییر نده
must-revalidateقبل از استفاده از نسخه منقضی‌شده، حتماً Revalidate کن
proxy-revalidateمثل must-revalidate اما فقط برای Shared Cache ها
max-age=<s>محتوا تا s ثانیه تازه است
s-maxage=<s>مثل max-age اما فقط برای Shared Cache ها

Controlling Cachability - چه کسی کنترل می‌کند؟

کتاب سه لایه کنترل Caching را معرفی می‌کند:

لایه اول: Server کنترل می‌کند

روش اول - Cache-Control Header:

1
2
3
Cache-Control: no-store
Cache-Control: no-cache
Cache-Control: must-revalidate, max-age=3600

روش دوم - Expires Header:

1
Expires: Thu, 01 Dec 2024 16:00:00 GMT

روش سوم - هیچ Expiration نگذاشتن: اگر Server هیچ Freshness Header نفرستد، Cache می‌تواند از Heuristic Expiration استفاده کند.

Heuristic Expiration - تخمین Freshness

وقتی سرور هیچ اطلاعاتی درباره Freshness نمی‌دهد، Cache خودش تخمین می‌زند. رایج‌ترین الگوریتم:

1
Freshness Lifetime = (Date - Last-Modified) × 0.1

یعنی اگر آخرین تغییر فایل ۱۰ روز پیش بوده، Cache آن را برای ۱ روز (۱۰٪ از ۱۰ روز) تازه در نظر می‌گیرد.

هشدار: این فقط یک تخمین است. برای محتوای حساس، همیشه باید صریحاً Cache-Control Header فرستاده شود.

لایه دوم: Client کنترل می‌کند

Client می‌تواند Cache را دور بزند:

1
2
Cache-Control: no-cache
Pragma: no-cache

وقتی کاربر در مرورگر Ctrl+F5 می‌زند، مرورگر این Header ها را ارسال می‌کند.

لایه سوم: مدیر Cache کنترل می‌کند

مدیر سرور می‌تواند قوانین Caching را در تنظیمات Cache Proxy تعریف کند - مستقل از آنچه Server یا Client می‌گویند.

Cache و تبلیغات آنلاین - یک تنش واقعی

نویسندگان یک تضاد جالب را مطرح می‌کنند: شرکت‌های تبلیغاتی می‌خواهند بدانند هر بنر تبلیغاتی دقیقاً چند بار دیده شده تا پول دریافت کنند. اما Cache این شمارش را خراب می‌کند - اگر بنر از Cache تحویل داده شود، Server اصلی خبر ندارد.

راه‌حل‌هایی که کتاب ذکر می‌کند:

  • استفاده از no-cache برای تبلیغات تا همیشه از Server اصلی بارگذاری شوند
  • استفاده از URL های منحصربه‌فرد برای هر Request تا Cache نتواند آنها را یکسان ببیند
  • استفاده از Cookie برای ردیابی بدون دور زدن Cache

Vary Header - Cache بر اساس بیشتر از URL

گاهی یک URL می‌تواند محتوای متفاوتی برای کاربران مختلف داشته باشد - مثلاً بر اساس زبان یا نوع دستگاه. Cache باید این تفاوت را در نظر بگیرد.

1
Vary: Accept-Language

این Header به Cache می‌گوید: «برای این URL، نسخه‌های مختلف را بر اساس Accept-Language جداگانه Cache کن.»

1
2
URL: /index.html + Accept-Language: fa  →  نسخه فارسی Cache می‌شود
URL: /index.html + Accept-Language: en  →  نسخه انگلیسی Cache می‌شود

فصل هشتم - بخش اول: Gateways و یکپارچه‌سازی با پروتکل‌های دیگر

Gateway چیست؟

نویسندگان با یک تعریف دقیق شروع می‌کنند: Gateway یک واسط است که یک طرف از HTTP صحبت می‌کند و طرف دیگر از پروتکل متفاوتی.

تفاوت اصلی با Proxy: Proxy هر دو طرف از HTTP استفاده می‌کنند. Gateway یک مترجم پروتکل است.

1
Client ──── HTTP ────▶ [Gateway] ──── پروتکل دیگر ────▶ Server

Gateway به HTTP اجازه می‌دهد با دنیای خارج از HTTP ارتباط برقرار کند - پایگاه داده، سرویس‌های FTP، برنامه‌های Legacy و هر سیستم دیگری.

نامگذاری Gateway ها

کتاب یک فرمت نامگذاری برای Gateway ها معرفی می‌کند:

1
<پروتکل طرف Client> / <پروتکل طرف Server>
نامتوضیح
HTTP/FTPاز Client درخواست HTTP می‌گیرد، از FTP Server فایل می‌گیرد
HTTP/HTTPSHTTP را به HTTPS تبدیل می‌کند
HTTPS/HTTPConnection امن را Terminate می‌کند و HTTP ساده به سرور می‌فرستد
HTTP/NNTPبه Usenet Newsgroup ها دسترسی می‌دهد

Protocol Gateways - انواع مهم

HTTP/FTP Gateway

رایج‌ترین نوع Protocol Gateway. Client یک Request HTTP معمولی می‌فرستد، Gateway آن را به دستورات FTP تبدیل می‌کند:

1
2
3
4
5
Client ──── GET ftp://ftp.joes-hardware.com/pub/files/file.txt HTTP/1.0 ────▶ [HTTP/FTP Gateway]
                                                                                       │
                                                                              FTP RETR file.txt
                                                                                       │
                                                                              FTP Server

Gateway باید این کارها را انجام دهد:

  • یک FTP Connection برقرار کند
  • Login کند (از User و Password در URL یا Anonymous)
  • دستور TYPE I برای Binary Transfer بفرستد
  • دستور PASV برای Passive Mode بفرستد
  • دستور RETR برای دانلود فایل بفرستد

HTTPS/HTTP Gateway - SSL Termination

در این معماری رایج، یک Gateway در لبه شبکه SSL را Terminate می‌کند و HTTP ساده به سرورهای داخلی می‌فرستد:

1
Internet ──── HTTPS ────▶ [SSL Termination Gateway] ──── HTTP ────▶ [سرورهای داخلی]

مزایا:

  • سرورهای داخلی نیازی به مدیریت SSL ندارند
  • بار رمزنگاری روی یک نقطه متمرکز است
  • مدیریت Certificate ها ساده‌تر است

معایب:

  • داده بین Gateway و سرورهای داخلی رمزنگاری نشده است - اگر شبکه داخلی امن نباشد، مشکل‌ساز است

Resource Gateways - CGI و Application Server ها

مهم‌ترین نوع Gateway از دیدگاه توسعه‌دهنده: Gateway هایی که HTTP را به اجرای کد تبدیل می‌کنند.

CGI - Common Gateway Interface

اولین و ساده‌ترین استاندارد برای اجرای برنامه‌های سمت Server.

وقتی یک Request به URL یک CGI Script می‌رسد:

1
2
3
4
5
۱. Web Server یک Process جدید می‌سازد
۲. متغیرهای محیطی (Environment Variables) را از HTTP Request پر می‌کند
۳. Script اجرا می‌شود
۴. خروجی Script به‌عنوان HTTP Response به Client برمی‌گردد
۵. Process بسته می‌شود

مشکل اصلی CGI: برای هر Request یک Process جدید ساخته می‌شود. در ترافیک بالا این بسیار سنگین است - هزاران Process همزمان روی سرور.

FastCGI - حل مشکل CGI

FastCGI یک Process دائمی نگه می‌دارد. به‌جای ساختن Process جدید برای هر Request، درخواست‌ها از طریق یک Connection دائمی به Process همیشه‌زنده فرستاده می‌شوند.

1
2
3
4
5
6
7
CGI:      [Request 1] → Process → Close
          [Request 2] → Process → Close
          [Request 3] → Process → Close

FastCGI:  [Request 1] ─┐
          [Request 2] ──┤──▶ [یک Process دائمی]
          [Request 3] ─┘

Server Extension API ها

Web Server های مدرن API هایی ارائه می‌دهند که به توسعه‌دهنده اجازه می‌دهند کد را مستقیم داخل Web Server اجرا کنند - بدون Process جداگانه:

  • ISAPI در IIS (مرتبط با .NET)
  • Apache Modules در Apache
  • Nginx Modules در Nginx

این روش از CGI بسیار سریع‌تر است چون Overhead ساختن Process وجود ندارد.

Application Interfaces و Web Services

نویسندگان در این بخش به آینده نگاه می‌کنند و پیش‌بینی می‌کنند که HTTP به یک زبان مشترک برای ارتباط بین برنامه‌ها تبدیل خواهد شد.

دو استاندارد مهم که در زمان نوشتن کتاب (۲۰۰۲) در حال ظهور بودند:

SOAP (Simple Object Access Protocol): پیام‌های XML را از طریق HTTP منتقل می‌کند تا برنامه‌ها بتوانند با هم صحبت کنند - صرف‌نظر از اینکه روی چه پلتفرمی یا به چه زبانی نوشته شده‌اند.

XML-RPC: فراخوانی توابع از راه دور (Remote Procedure Call) از طریق HTTP و XML.

دیدگاه انتقادی: نویسندگان صادقانه اشاره می‌کنند که این استانداردها هنوز در حال تکامل هستند و مشخص نیست کدام‌یک جا خواهد افتاد. از منظر امروز می‌دانیم که REST API ها جایگزین SOAP شدند و JSON جایگزین XML شد - اما اصل ایده کاملاً درست بود: HTTP به زبان مشترک Web Services تبدیل شد.

Tunnels - انتقال کور داده

Tunnel یک مکانیزم HTTP است که اجازه می‌دهد داده‌های غیر HTTP از طریق یک HTTP Connection منتقل شوند - بدون اینکه Tunnel محتوا را بخواند یا تغییر دهد.

CONNECT Method - ایجاد Tunnel

رایج‌ترین کاربرد Tunnel: عبور دادن SSL Traffic از میان یک Proxy که فقط HTTP می‌فهمد.

1
2
3
4
5
6
7
Client ──── CONNECT home.netscape.com:443 HTTP/1.0 ────▶ [Proxy]

[Proxy] ──── 200 Connection Established ────▶ Client
             (اکنون یک Tunnel کور داریم)

Client ══════════════ SSL Data ══════════════ Server
       (Proxy فقط Byte ها را منتقل می‌کند)

مراحل دقیق:

  1. Client یک CONNECT Request به Proxy می‌فرستد با Hostname و Port مقصد
  2. Proxy یک TCP Connection به آن Host و Port برقرار می‌کند
  3. اگر موفق شد، 200 Connection Established به Client می‌فرستد
  4. از این لحظه، Proxy فقط Byte ها را بدون بررسی بین Client و Server منتقل می‌کند

نکته امنیتی: CONNECT Method می‌تواند خطرناک باشد. اگر Proxy به هر Host و Port اجازه Tunnel بدهد، کاربران می‌توانند از آن برای دور زدن فیلترها استفاده کنند. Proxy های هوشمند فقط به Port 443 اجازه CONNECT می‌دهند.

Relays - ساده‌ترین نوع واسط

Relay ساده‌ترین نوع HTTP Intermediary است. فقط Connection را برقرار می‌کند و داده را بدون هیچ پردازشی منتقل می‌کند.

تفاوت با Tunnel: Relay از HTTP آگاه است اما فقط Start Line و بعضی Header ها را پردازش می‌کند - نه محتوا.

مشکل اصلی Relay: همان مشکل Blind Relay که در فصل Proxy دیدیم. اگر Relay Connection: keep-alive را بدون پردازش Forward کند، می‌تواند Deadlock ایجاد کند.


فصل هشتم - بخش اول: Gateways و یکپارچه‌سازی با پروتکل‌های دیگر

Gateway چیست؟

نویسندگان با یک تعریف دقیق شروع می‌کنند: Gateway یک واسط است که یک طرف از HTTP صحبت می‌کند و طرف دیگر از پروتکل متفاوتی.

تفاوت اصلی با Proxy: Proxy هر دو طرف از HTTP استفاده می‌کنند. Gateway یک مترجم پروتکل است.

1
Client ──── HTTP ────▶ [Gateway] ──── پروتکل دیگر ────▶ Server

Gateway به HTTP اجازه می‌دهد با دنیای خارج از HTTP ارتباط برقرار کند - پایگاه داده، سرویس‌های FTP، برنامه‌های Legacy و هر سیستم دیگری.

نامگذاری Gateway ها

کتاب یک فرمت نامگذاری برای Gateway ها معرفی می‌کند:

1
<پروتکل طرف Client> / <پروتکل طرف Server>
نامتوضیح
HTTP/FTPاز Client درخواست HTTP می‌گیرد، از FTP Server فایل می‌گیرد
HTTP/HTTPSHTTP را به HTTPS تبدیل می‌کند
HTTPS/HTTPConnection امن را Terminate می‌کند و HTTP ساده به سرور می‌فرستد
HTTP/NNTPبه Usenet Newsgroup ها دسترسی می‌دهد

Protocol Gateways - انواع مهم

HTTP/FTP Gateway

رایج‌ترین نوع Protocol Gateway. Client یک Request HTTP معمولی می‌فرستد، Gateway آن را به دستورات FTP تبدیل می‌کند:

1
2
3
4
5
Client ──── GET ftp://ftp.joes-hardware.com/pub/files/file.txt HTTP/1.0 ────▶ [HTTP/FTP Gateway]
                                                                                       │
                                                                              FTP RETR file.txt
                                                                                       │
                                                                              FTP Server

Gateway باید این کارها را انجام دهد:

  • یک FTP Connection برقرار کند
  • Login کند (از User و Password در URL یا Anonymous)
  • دستور TYPE I برای Binary Transfer بفرستد
  • دستور PASV برای Passive Mode بفرستد
  • دستور RETR برای دانلود فایل بفرستد

HTTPS/HTTP Gateway - SSL Termination

در این معماری رایج، یک Gateway در لبه شبکه SSL را Terminate می‌کند و HTTP ساده به سرورهای داخلی می‌فرستد:

1
Internet ──── HTTPS ────▶ [SSL Termination Gateway] ──── HTTP ────▶ [سرورهای داخلی]

مزایا:

  • سرورهای داخلی نیازی به مدیریت SSL ندارند
  • بار رمزنگاری روی یک نقطه متمرکز است
  • مدیریت Certificate ها ساده‌تر است

معایب:

  • داده بین Gateway و سرورهای داخلی رمزنگاری نشده است - اگر شبکه داخلی امن نباشد، مشکل‌ساز است

Resource Gateways - CGI و Application Server ها

مهم‌ترین نوع Gateway از دیدگاه توسعه‌دهنده: Gateway هایی که HTTP را به اجرای کد تبدیل می‌کنند.

CGI - Common Gateway Interface

اولین و ساده‌ترین استاندارد برای اجرای برنامه‌های سمت Server.

وقتی یک Request به URL یک CGI Script می‌رسد:

1
2
3
4
5
۱. Web Server یک Process جدید می‌سازد
۲. متغیرهای محیطی (Environment Variables) را از HTTP Request پر می‌کند
۳. Script اجرا می‌شود
۴. خروجی Script به‌عنوان HTTP Response به Client برمی‌گردد
۵. Process بسته می‌شود

مشکل اصلی CGI: برای هر Request یک Process جدید ساخته می‌شود. در ترافیک بالا این بسیار سنگین است - هزاران Process همزمان روی سرور.

FastCGI - حل مشکل CGI

FastCGI یک Process دائمی نگه می‌دارد. به‌جای ساختن Process جدید برای هر Request، درخواست‌ها از طریق یک Connection دائمی به Process همیشه‌زنده فرستاده می‌شوند.

1
2
3
4
5
6
7
CGI:      [Request 1] → Process → Close
          [Request 2] → Process → Close
          [Request 3] → Process → Close

FastCGI:  [Request 1] ─┐
          [Request 2] ──┤──▶ [یک Process دائمی]
          [Request 3] ─┘

Server Extension API ها

Web Server های مدرن API هایی ارائه می‌دهند که به توسعه‌دهنده اجازه می‌دهند کد را مستقیم داخل Web Server اجرا کنند - بدون Process جداگانه:

  • ISAPI در IIS (مرتبط با .NET)
  • Apache Modules در Apache
  • Nginx Modules در Nginx

این روش از CGI بسیار سریع‌تر است چون Overhead ساختن Process وجود ندارد.

Application Interfaces و Web Services

نویسندگان در این بخش به آینده نگاه می‌کنند و پیش‌بینی می‌کنند که HTTP به یک زبان مشترک برای ارتباط بین برنامه‌ها تبدیل خواهد شد.

دو استاندارد مهم که در زمان نوشتن کتاب (۲۰۰۲) در حال ظهور بودند:

SOAP (Simple Object Access Protocol): پیام‌های XML را از طریق HTTP منتقل می‌کند تا برنامه‌ها بتوانند با هم صحبت کنند - صرف‌نظر از اینکه روی چه پلتفرمی یا به چه زبانی نوشته شده‌اند.

XML-RPC: فراخوانی توابع از راه دور (Remote Procedure Call) از طریق HTTP و XML.

دیدگاه انتقادی: نویسندگان صادقانه اشاره می‌کنند که این استانداردها هنوز در حال تکامل هستند و مشخص نیست کدام‌یک جا خواهد افتاد. از منظر امروز می‌دانیم که REST API ها جایگزین SOAP شدند و JSON جایگزین XML شد - اما اصل ایده کاملاً درست بود: HTTP به زبان مشترک Web Services تبدیل شد.

Tunnels - انتقال کور داده

Tunnel یک مکانیزم HTTP است که اجازه می‌دهد داده‌های غیر HTTP از طریق یک HTTP Connection منتقل شوند - بدون اینکه Tunnel محتوا را بخواند یا تغییر دهد.

CONNECT Method - ایجاد Tunnel

رایج‌ترین کاربرد Tunnel: عبور دادن SSL Traffic از میان یک Proxy که فقط HTTP می‌فهمد.

1
2
3
4
5
6
7
Client ──── CONNECT home.netscape.com:443 HTTP/1.0 ────▶ [Proxy]

[Proxy] ──── 200 Connection Established ────▶ Client
             (اکنون یک Tunnel کور داریم)

Client ══════════════ SSL Data ══════════════ Server
       (Proxy فقط Byte ها را منتقل می‌کند)

مراحل دقیق:

  1. Client یک CONNECT Request به Proxy می‌فرستد با Hostname و Port مقصد
  2. Proxy یک TCP Connection به آن Host و Port برقرار می‌کند
  3. اگر موفق شد، 200 Connection Established به Client می‌فرستد
  4. از این لحظه، Proxy فقط Byte ها را بدون بررسی بین Client و Server منتقل می‌کند

نکته امنیتی: CONNECT Method می‌تواند خطرناک باشد. اگر Proxy به هر Host و Port اجازه Tunnel بدهد، کاربران می‌توانند از آن برای دور زدن فیلترها استفاده کنند. Proxy های هوشمند فقط به Port 443 اجازه CONNECT می‌دهند.

Relays - ساده‌ترین نوع واسط

Relay ساده‌ترین نوع HTTP Intermediary است. فقط Connection را برقرار می‌کند و داده را بدون هیچ پردازشی منتقل می‌کند.

تفاوت با Tunnel: Relay از HTTP آگاه است اما فقط Start Line و بعضی Header ها را پردازش می‌کند - نه محتوا.

مشکل اصلی Relay: همان مشکل Blind Relay که در فصل Proxy دیدیم. اگر Relay Connection: keep-alive را بدون پردازش Forward کند، می‌تواند Deadlock ایجاد کند.


فصل نهم - بخش اول: Web Robots و Crawlers

Web Robot چیست؟

نویسندگان تعریف دقیقی ارائه می‌دهند: Web Robot برنامه‌ای است که HTTP Transaction های خودکار انجام می‌دهد - بدون هیچ تعامل انسانی در حین اجرا.

Robot ها انواع مختلفی دارند:

نوعتوضیح
Crawler / Spiderوب را می‌گردد و لینک‌ها را دنبال می‌کند
Web Wandererاندازه و ساختار وب را اندازه‌گیری می‌کند
Indexerمحتوا را برای موتور جستجو ایندکس می‌کند
Harvesterاطلاعات خاصی (مثل آدرس ایمیل) جمع‌آوری می‌کند
Checkerلینک‌های شکسته را پیدا می‌کند
Mirror Makerنسخه کامل یک سایت را کپی می‌کند

Crawling - چطور وب را می‌گردند؟

شروع از Seed URL ها

هر Crawler از یک یا چند Seed URL شروع می‌کند. این URL ها نقطه آغازین هستند. Crawler صفحه را دانلود می‌کند، تمام لینک‌های داخل آن را استخراج می‌کند و آنها را به صف اضافه می‌کند.

1
2
3
4
5
6
7
8
9
10
11
Seed URLs
    │
    ▼
[صف URL ها]
    │
    ▼
دانلود صفحه
    │
    ├── استخراج لینک‌های جدید ──▶ [صف URL ها]
    │
    └── پردازش محتوا (ایندکس، ذخیره، ...)

مشکل Loop - دور باطل

اگر Crawler مراقب نباشد، می‌تواند در یک حلقه بی‌نهایت گیر کند:

1
صفحه A ──▶ صفحه B ──▶ صفحه A ──▶ صفحه B ──▶ ...

راه‌حل: Crawler باید یک ساختار داده برای URL های بازدیدشده نگه دارد. رایج‌ترین روش‌ها:

  • Hash Table: جستجوی سریع O(1) - مناسب برای Crawler های کوچک
  • Checksum: به‌جای ذخیره URL کامل، Checksum آن را ذخیره می‌کند - فضای کمتر
  • Bloom Filter: یک ساختار داده Probabilistic که فضای بسیار کمتری مصرف می‌کند اما ممکن است False Positive داشته باشد

URL Normalization

URL های مختلف ممکن است به یک Resource اشاره کنند:

1
2
3
4
http://www.joes-hardware.com/index.html
http://www.joes-hardware.com/index.html#section1
HTTP://WWW.JOES-HARDWARE.COM/index.html
http://www.joes-hardware.com:80/index.html

Crawler باید این URL ها را نرمالیزه کند تا از بازدید تکراری جلوگیری کند.

Robotic HTTP - رفتار صحیح HTTP در Robot ها

شناسایی خود - User-Agent Header

هر Robot باید خودش را با User-Agent Header معرفی کند:

1
User-Agent: Slurp/2.0 ([email protected]; http://www.inktomi.com/slurp.html)

این اطلاعات به مدیر سایت کمک می‌کند بفهمد چه Robot هایی سایتش را بازدید کرده‌اند و اگر مشکلی باشد با آنها تماس بگیرد.

Virtual Hosting - Host Header

اگر یک سرور چندین Virtual Host داشته باشد، Robot باید Host Header را حتماً بفرستد، وگرنه سرور نمی‌داند به کدام سایت می‌خواهد دسترسی داشته باشد.

1
2
3
GET /index.html HTTP/1.1
Host: www.joes-hardware.com
User-Agent: MyRobot/1.0

Conditional GET - احترام به Cache

Robot های خوب از Conditional GET استفاده می‌کنند تا در صورت عدم تغییر محتوا، آن را دوباره دانلود نکنند:

1
2
GET /page.html HTTP/1.1
If-Modified-Since: Sat, 22 Jun 2002 12:00:00 GMT

اگر صفحه تغییر نکرده باشد، سرور 304 Not Modified برمی‌گرداند و Robot از دانلود مجدد جلوگیری می‌کند.

Misbehaving Robots - Robot های بدرفتار

نویسندگان صادقانه از مشکلاتی که Robot ها می‌توانند ایجاد کنند صحبت می‌کنند:

Looping - دور باطل

Robot بدون تشخیص Loop، مدام همان صفحات را بازدید می‌کند و سرور را زیر درخواست‌های تکراری دفن می‌کند.

Overloading - بار اضافه

بعضی Robot ها Request ها را بدون هیچ تأخیری می‌فرستند - مثل یک DDoS ناخواسته. یک سرور کوچک ممکن است تحت این فشار از کار بیفتد.

Harvesting - جمع‌آوری مخرب

Robot هایی که آدرس‌های ایمیل را برای Spam جمع می‌کنند، یا قیمت‌های رقبا را برای کپی کردن می‌خوانند.

Excluding Robots - پروتکل robots.txt

برای حل مشکل Robot های ناخواسته، یک استاندارد غیررسمی اما پذیرفته‌شده وجود دارد: Robots Exclusion Protocol یا همان فایل robots.txt.

قوانین استفاده از robots.txt

هر Robot قبل از هر چیز باید این فایل را بخواند:

1
2
GET /robots.txt HTTP/1.1
Host: www.joes-hardware.com

اگر سرور 404 Not Found برگرداند، Robot می‌تواند همه جای سایت برود. اگر فایل وجود داشت، باید قوانین آن را رعایت کند.

ساختار فایل robots.txt

1
2
3
4
5
6
7
8
9
10
11
User-agent: *
Disallow: /tmp/
Disallow: /logs/
Disallow: /admin/

User-agent: Googlebot
Disallow: /private/
Allow: /public/

User-agent: BadBot
Disallow: /

قوانین ساختار:

  • User-agent: * - برای همه Robot ها اعمال می‌شود
  • User-agent: Googlebot - فقط برای Googlebot اعمال می‌شود
  • Disallow: - این مسیر ممنوع است
  • Allow: - این مسیر مجاز است (برای Override کردن یک Disallow کلی‌تر)
  • Disallow: خالی - یعنی هیچ محدودیتی وجود ندارد

نکته مهم درباره robots.txt

کتاب تأکید می‌کند: robots.txt یک قرارداد اجتماعی است، نه یک مکانیزم امنیتی.

یک Robot بدخواه می‌تواند کاملاً این فایل را نادیده بگیرد. اگر محتوایی واقعاً محرمانه است، باید با احراز هویت از آن محافظت شود - نه فقط با robots.txt.

Meta Tag - کنترل Crawling در سطح صفحه

علاوه بر robots.txt که در سطح سرور کار می‌کند، HTML یک Meta Tag برای کنترل Crawling در سطح صفحه دارد:

1
2
3
4
<meta name="robots" content="noindex">
<meta name="robots" content="nofollow">
<meta name="robots" content="noindex, nofollow">
<meta name="robots" content="all">
مقدارتوضیح
noindexاین صفحه را ایندکس نکن
nofollowلینک‌های این صفحه را دنبال نکن
noindex, nofollowهیچ‌کدام از این دو کار را نکن
allهر دو کار مجاز است (پیش‌فرض)

Search Engines - موتورهای جستجو

نویسندگان توضیح می‌دهند که موتورهای جستجو از چند جزء اصلی تشکیل شده‌اند:

1
2
3
[Crawler] ──▶ [Repository] ──▶ [Indexer] ──▶ [Search Index]
                                                     │
                                           [Query Engine] ◀── کاربر
  • Crawler: وب را می‌گردد و صفحات را جمع‌آوری می‌کند
  • Repository: صفحات جمع‌آوری‌شده را ذخیره می‌کند
  • Indexer: کلمات کلیدی و لینک‌ها را استخراج و ایندکس می‌کند
  • Query Engine: جستجوی کاربر را در ایندکس اجرا می‌کند

فصل یازدهم - بخش اول: Client Identification و روش‌های شناسایی کاربر

چرا شناسایی کاربر مهم است؟

HTTP یک پروتکل Stateless است - هر Request کاملاً مستقل از Request های قبلی است. سرور به‌طور پیش‌فرض هیچ حافظه‌ای از تعاملات قبلی ندارد.

اما اپلیکیشن‌های واقعی نیاز دارند کاربر را بشناسند:

  • فروشگاه آنلاین باید سبد خرید را برای هر کاربر جداگانه نگه دارد
  • سایت خبری باید ترجیحات کاربر را به خاطر بسپارد
  • سیستم بانکی باید مطمئن شود فقط کاربر مجاز به حساب دسترسی دارد

این تضاد بین Stateless بودن HTTP و نیاز اپلیکیشن به State چالش اصلی این فصل است.

روش اول: HTTP Headers

بعضی Header ها اطلاعاتی درباره کاربر حمل می‌کنند:

Headerتوضیحمحدودیت
Fromآدرس ایمیل کاربرمرورگرها آن را برای حفظ حریم خصوصی ارسال نمی‌کنند
User-Agentنوع و نسخه مرورگرهمه کاربران یک مرورگر را شناسایی نمی‌کند
RefererURL صفحه‌ای که از آن آمدهفقط رفتار کاربر را نشان می‌دهد، هویت نه

این Header ها برای آمار و تحلیل مفید هستند اما برای شناسایی یکتای کاربر کافی نیستند.

روش دوم: Client IP Address

ساده‌ترین روش: استفاده از IP Address کاربر.

مشکلات جدی این روش:

  • NAT: چندین کاربر پشت یک Router یک IP مشترک دارند - همه آنها یکسان به نظر می‌رسند
  • DHCP: IP Address کاربر هر بار ممکن است تغییر کند
  • Proxy: اگر کاربر از Proxy استفاده کند، سرور IP Proxy را می‌بیند نه IP کاربر
  • حریم خصوصی: ردیابی کاربر از طریق IP مشکلات حقوقی ایجاد می‌کند

نتیجه: IP Address برای شناسایی کاربر غیرقابل‌اعتماد است.

روش سوم: User Login - احراز هویت صریح

اگر سرور نیاز به شناسایی داشته باشد، می‌تواند از کاربر بخواهد Login کند.

1
2
3
4
5
6
7
8
9
Client ──── GET /private/page.html HTTP/1.1 ────▶ Server

Server ◀──── 401 Unauthorized ────────────────── Server
             WWW-Authenticate: Basic realm="My Site"

Client ──── GET /private/page.html HTTP/1.1 ────▶ Server
            Authorization: Basic dXNlcjpwYXNz

Server ◀──── 200 OK ───────────────────────────── Server

وقتی کاربر یک‌بار Login کند، مرورگر به‌صورت خودکار Authorization Header را با هر Request بعدی به همان سرور می‌فرستد.

مشکلات:

  • هر سایت جداگانه Username و Password می‌خواهد
  • کاربر باید برای هر سایت ثبت‌نام کند
  • مدیریت این همه اعتبارنامه برای کاربر دشوار است

روش چهارم: Fat URLs

یک روش خلاقانه اما مشکل‌دار: اطلاعات شناسایی کاربر را داخل URL جاسازی کنید.

1
2
3
http://www.joes-hardware.com/storefront?user_id=XYZ987
http://www.joes-hardware.com/tools.html?user_id=XYZ987
http://www.joes-hardware.com/checkout?user_id=XYZ987

مشکلات جدی:

  • زشتی URL: URL های طولانی و ناخوانا برای کاربر
  • نشت اطلاعات: اگر کاربر URL را برای دیگری بفرستد، Session اشتراک‌گذاری می‌شود
  • Cache شکست: هر کاربر URL منحصربه‌فرد دارد - Cache نمی‌تواند آنها را با هم Cache کند
  • از دست دادن Session: اگر کاربر URL را تایپ کند بدون User ID، Session از دست می‌رود

روش پنجم: Cookies - بهترین راه‌حل

Cookie مکانیزمی است که به سرور اجازه می‌دهد اطلاعاتی را روی مرورگر کاربر ذخیره کند و در Request های بعدی آنها را بازیابی کند.

1
Set-Cookie: user_id=XYZ987; domain=.joes-hardware.com; path=/; expires=Wed, 09-Jun-2025 10:18:14 GMT
بخشتوضیح
user_id=XYZ987نام و مقدار Cookie
domain=.joes-hardware.comاین Cookie فقط برای این Domain فرستاده شود
path=/این Cookie برای همه مسیرهای سایت فرستاده شود
expires=...تاریخ انقضا - بعد از این تاریخ Cookie حذف می‌شود

اولین بازدید - کاربر ناشناس:

1
2
3
4
5
6
Client ──── GET /index.html HTTP/1.1 ────────────────────────▶ Server
            (هیچ Cookie ای نیست)

Client ◀─── 200 OK ──────────────────────────────────────────  Server
            Set-Cookie: id=XYZ987; domain=joes-hardware.com
            (سرور یک شناسه به کاربر می‌دهد)

بازدیدهای بعدی:

1
2
3
4
5
6
Client ──── GET /tools.html HTTP/1.1 ────────────────────────▶ Server
            Cookie: id=XYZ987
            (مرورگر Cookie را خودکار می‌فرستد)

Client ◀─── 200 OK ──────────────────────────────────────────  Server
            (سرور کاربر را می‌شناسد)

Session Cookie: موقتی است. وقتی مرورگر بسته شود، حذف می‌شود. تاریخ انقضا ندارد.

Persistent Cookie: روی دیسک ذخیره می‌شود و بعد از بستن مرورگر هم باقی می‌ماند. با expires یا max-age تعریف می‌شود.

Secure Flag

اگر Secure Flag روی Cookie باشد، مرورگر آن را فقط از طریق HTTPS می‌فرستد:

1
Set-Cookie: session_id=ABC123; Secure

HttpOnly Flag

با این Flag، JavaScript نمی‌تواند به Cookie دسترسی داشته باشد - از XSS (Cross-Site Scripting) جلوگیری می‌کند:

1
Set-Cookie: session_id=ABC123; HttpOnly

Cookie ها فقط به Domain و Path مشخص‌شده فرستاده می‌شوند:

1
Set-Cookie: pref=dark; domain=.joes-hardware.com; path=/tools

این Cookie فقط به سابدامین‌های joes-hardware.com و فقط برای مسیر /tools فرستاده می‌شود.

نویسندگان یک بحث انتقادی مهم مطرح می‌کنند: Cookie ها می‌توانند برای ردیابی رفتار کاربر در سطح گسترده استفاده شوند.

Third-Party Cookie: اگر یک صفحه از سرور دیگری (مثلاً یک شبکه تبلیغاتی) محتوا بارگذاری کند، آن سرور هم می‌تواند Cookie تنظیم کند. این Cookie در سایت‌های مختلف فرستاده می‌شود و رفتار کاربر را در سراسر وب ردیابی می‌کند.


فصل دوازدهم و سیزدهم - Authentication: Basic و Digest

Authentication چیست؟

نویسندگان بین دو مفهوم تمایز می‌کشند:

  • Authentication (احراز هویت): تأیید اینکه «تو کی هستی»
  • Authorization (مجوزدهی): تأیید اینکه «آیا اجازه داری این کار را بکنی»

HTTP دو مکانیزم استاندارد برای Authentication دارد: Basic و Digest. هر دو از یک چرخه چهار مرحله‌ای استفاده می‌کنند.

چرخه Authentication

1
2
3
4
5
6
7
8
9
10
11
12
13
Client ──── Request ────────────────────────────────────────▶ Server
            (بدون اعتبارنامه)

Client ◀─── 401 Unauthorized ──────────────────────────────── Server
            WWW-Authenticate: Basic realm="Family"
            (سرور می‌گوید: احراز هویت لازم است)

Client ──── Request ────────────────────────────────────────▶ Server
            Authorization: Basic [اعتبارنامه]
            (کاربر Username و Password را می‌فرستد)

Client ◀─── 200 OK ─────────────────────────────────────────── Server
            (دسترسی مجاز است)

Basic Authentication - ساده اما ناامن

نحوه کارکرد

سرور ابتدا یک Realm مشخص می‌کند - یک ناحیه حفاظت‌شده با نام:

1
WWW-Authenticate: Basic realm="Corporate Financials"

Client اعتبارنامه را به این شکل می‌سازد:

1
2
3
4
۱. ترکیب: username:password  →  "joe:joepasswd"
۲. Base64 Encode:            →  "am9lOmpvZXBhc3N3ZA=="
۳. ارسال:
   Authorization: Basic am9lOmpvZXBhc3N3ZA==

نکته مهم: Base64 یک Encoding است، نه Encryption. هر کسی می‌تواند آن را Decode کند. این یعنی Basic Authentication اعتبارنامه را به‌صورت Plain Text روی شبکه می‌فرستد.

مشکلات امنیتی Basic Authentication

کتاب چهار مشکل جدی را صریح بیان می‌کند:

اول - رمزنگاری ضعیف: Base64 هیچ امنیتی ندارد. هر کسی که ترافیک شبکه را ببیند می‌تواند فوری Username و Password را استخراج کند.

دوم - نبود محافظت در برابر Replay Attack: اگر مهاجم یک Request را ضبط کند، می‌تواند دقیقاً همان Request را دوباره بفرستد و دسترسی پیدا کند.

سوم - نبود محافظت از محتوا: Basic Authentication فقط هویت را تأیید می‌کند. بعد از تأیید، محتوای Request و Response هنوز Plain Text است.

چهارم - Proxy و Intermediary: هر Proxy در مسیر می‌تواند اعتبارنامه را ببیند.

نتیجه نویسندگان: Basic Authentication فقط در ترکیب با HTTPS/TLS قابل استفاده است. تنها راه امن کردن آن این است که کل Connection رمزنگاری شود.

Digest Authentication - نسخه امن‌تر

ایده اصلی

Digest Authentication یک اصل ساده دارد: هرگز Password را روی شبکه نفرست. به‌جای آن، یک Digest (خلاصه) از Password فرستاده می‌شود.

Nonce - کلید ضد Replay Attack

سرور یک مقدار تصادفی به نام Nonce می‌سازد و به Client می‌فرستد. Client این Nonce را در محاسبه Digest استفاده می‌کند:

1
2
3
4
Server ──── 401 Unauthorized ────────────────────────────────▶ Client
            WWW-Authenticate: Digest
                realm="[email protected]"
                nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093"

نحوه محاسبه Digest

Client با این الگوریتم Digest را محاسبه می‌کند:

1
2
3
HA1 = MD5(username:realm:password)
HA2 = MD5(method:digestURI)
Response = MD5(HA1:nonce:HA2)

مثال عملی:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
username = "Mufasa"
password = "Circle Of Life"
realm    = "[email protected]"
nonce    = "dcd98b7102dd2f0e8b11d0f600bfb0c093"
URI      = "/dir/index.html"
method   = "GET"

HA1      = MD5("Mufasa:[email protected]:Circle Of Life")
         = "939e7578ed9e3c518a452acee763bce9"

HA2      = MD5("GET:/dir/index.html")
         = "39aff3a2bab80126081ab5c0a1f5a3b8"

Response = MD5("939e7578...:dcd98b71...:39aff3a2...")
         = "6629fae49393a05397450978507c4ef1"

Request نهایی:

1
2
3
4
5
6
GET /dir/index.html HTTP/1.1
Authorization: Digest username="Mufasa",
               realm="[email protected]",
               nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
               uri="/dir/index.html",
               response="6629fae49393a05397450978507c4ef1"

Quality of Protection - qop

HTTP/1.1 یک لایه اضافه به Digest اضافه کرد به نام qop (Quality of Protection):

qop=auth

فقط احراز هویت - همان محاسبه پایه که بالا دیدیم.

qop=auth-int

علاوه بر احراز هویت، یکپارچگی پیام هم تأیید می‌شود. HA2 شامل Body پیام هم می‌شود:

1
HA2 = MD5(method:digestURI:MD5(entityBody))

این تضمین می‌کند که محتوای Request در مسیر تغییر نکرده.

Nonce Count - ضد Replay Attack

با qop، Client یک Nonce Count (nc) می‌فرستد که نشان می‌دهد این Nonce چندمین بار استفاده می‌شود:

1
nc=00000001   ← اولین استفاده از این Nonce

سرور می‌تواند تشخیص دهد اگر همان nc دوباره دیده شود، یک Replay Attack رخ داده.

مقایسه Basic و Digest

ویژگیBasicDigest
ارسال PasswordBase64 (قابل Decode)هرگز - فقط Hash
Replay Attackآسیب‌پذیرمحافظت‌شده با Nonce
یکپارچگی پیامنداردبا qop=auth-int
پیچیدگی پیاده‌سازیبسیار سادهمتوسط
امنیت بدون HTTPSهیچنسبی
امنیت با HTTPSقابل قبولخوب

محدودیت‌های Digest Authentication

نویسندگان صادقانه بیان می‌کنند که Digest هم بی‌نقص نیست:

اول - Man-in-the-Middle: اگر مهاجم بتواند ترافیک را Intercept کند، می‌تواند سرور جعلی بسازد و اعتبارنامه را بدزدد.

دوم - MD5 قدیمی است: MD5 در زمان نوشتن کتاب (2002) قابل قبول بود. امروز MD5 کاملاً شکسته شده - برای هر کاربرد امنیتی باید از SHA-256 یا بالاتر استفاده شود.

سوم - پذیرش محدود: در عمل Digest Authentication به‌طور گسترده پیاده‌سازی نشد. اکثر سایت‌ها به‌جای آن از Form-Based Authentication + HTTPS + Session Cookie استفاده کردند.

دیدگاه امروزی: در سال 2026، هیچ‌کدام از این دو مکانیزم برای اپلیکیشن‌های مدرن کافی نیستند. استاندارد فعلی OAuth 2.0 / OpenID Connect است که روی HTTPS و Token های JWT کار می‌کند.


فصل چهاردهم - بخش اول: رمزنگاری دیجیتال و مبانی HTTPS

چرا HTTP ناامن است؟

نویسندگان با یک واقعیت تلخ شروع می‌کنند: HTTP داده را به‌صورت Plain Text منتقل می‌کند. هر کسی در مسیر شبکه - ISP، Proxy، مهاجم - می‌تواند:

  • محتوای کامل Request و Response را بخواند
  • اطلاعات حساس (Password، شماره کارت اعتباری) را بدزدد
  • محتوا را تغییر دهد بدون اینکه Client یا Server متوجه شوند
  • هویت Server را جعل کند

HTTPS این مشکلات را با اضافه کردن یک لایه SSL/TLS زیر HTTP حل می‌کند.

سه هدف اصلی رمزنگاری

کتاب رمزنگاری را برای رسیدن به سه هدف معرفی می‌کند:

هدفتوضیحسوال
Confidentiality (محرمانگی)فقط گیرنده مجاز محتوا را بخواندچه کسی می‌تواند بخواند؟
Integrity (یکپارچگی)محتوا در مسیر تغییر نکرده باشدآیا تغییر کرده؟
Authentication (احراز هویت)طرف مقابل واقعاً همان کسی است که ادعا می‌کندبا چه کسی صحبت می‌کنم؟

رمزنگاری متقارن - Symmetric-Key Cryptography

مفهوم پایه

فرستنده و گیرنده از یک کلید مشترک برای رمزنگاری و رمزگشایی استفاده می‌کنند:

1
2
Plain Text ──[Key]──▶ Cipher Text ──[Key]──▶ Plain Text
           رمزنگاری                رمزگشایی

الگوریتم‌های رایج: DES، 3DES، AES، RC4

مشکل اصلی - توزیع کلید

اگر Alice و Bob می‌خواهند ارتباط امن داشته باشند، باید ابتدا کلید را با هم به اشتراک بگذارند. اما چطور؟ اگر کانال ارتباطی امن نیست، کلید هم در خطر است.

این مشکل به نام Key Distribution Problem شناخته می‌شود و یکی از چالش‌های بنیادی رمزنگاری است.

رمزنگاری نامتقارن - Public-Key Cryptography

ایده انقلابی

به‌جای یک کلید مشترک، هر طرف دو کلید دارد:

  • Public Key: عمومی است - هر کسی می‌تواند آن را داشته باشد
  • Private Key: کاملاً محرمانه است - فقط صاحبش می‌داند

رابطه بین آنها: چیزی که با Public Key رمزنگاری شده، فقط با Private Key متناظر رمزگشایی می‌شود - و بالعکس.

1
2
3
4
5
Alice می‌خواهد به Bob پیام امن بفرستد:

۱. Alice Public Key بابرا دریافت می‌کند (عمومی است، مشکلی نیست)
۲. Alice پیام را با Public Key باب رمزنگاری می‌کند
۳. فقط باب با Private Key خودش می‌تواند آن را رمزگشایی کند

الگوریتم RSA

رایج‌ترین الگوریتم Public-Key. امنیت آن بر اساس سختی تجزیه اعداد بزرگ به عوامل اول است.

مشکل Performance

رمزنگاری نامتقارن از نظر محاسباتی بسیار کندتر از متقارن است - تا 1000 برابر کندتر.

راه‌حل عملی SSL/TLS: از Public-Key فقط برای تبادل کلید استفاده می‌شود. بعد از اینکه کلید متقارن به‌صورت امن تبادل شد، ادامه ارتباط با رمزنگاری متقارن انجام می‌شود.

Digital Signatures - امضای دیجیتال

مفهوم

امضای دیجیتال ثابت می‌کند که یک پیام واقعاً از طرف مشخصی آمده و تغییر نکرده.

نحوه کارکرد

1
2
3
4
5
6
7
8
9
امضا کردن (توسط فرستنده):
۱. محاسبه Hash از پیام          →  "abc123"
۲. رمزنگاری Hash با Private Key →  [امضا]
۳. ارسال: پیام + امضا

تأیید (توسط گیرنده):
۱. رمزگشایی امضا با Public Key  →  "abc123"
۲. محاسبه Hash از پیام دریافتی  →  "abc123"
۳. مقایسه دو Hash               →  یکسان = پیام معتبر است

اگر پیام در مسیر تغییر کرده باشد، Hash محاسبه‌شده با Hash رمزگشایی‌شده یکسان نخواهد بود.

Digital Certificates - گواهی دیجیتال

مشکل - چطور Public Key را تأیید کنیم؟

اگر مهاجم بتواند Public Key جعلی ارائه دهد، می‌تواند هویت سرور را جعل کند. نیاز به یک مرجع قابل‌اعتماد داریم.

Certificate Authority - CA

CA یک سازمان مورداعتماد است که هویت سرورها را تأیید می‌کند. CA یک Digital Certificate صادر می‌کند که شامل:

  • نام سرور (Common Name)
  • Public Key سرور
  • تاریخ انقضا
  • امضای دیجیتال CA
1
2
3
4
5
[Server] ──── ارسال اطلاعات هویت ────▶ [CA]
              (اسناد، مدارک شرکت)

[Server] ◀─── Certificate ────────────── [CA]
              (CA هویت را تأیید و امضا کرد)

زنجیره اعتماد - Chain of Trust

1
2
3
4
5
    │
    └── امضا کرده ──▶ [Intermediate CA]
                           │
                           └── امضا کرده ──▶ [Certificate سرور]

مرورگر Root CA های مورداعتماد را از قبل می‌شناسد. وقتی Certificate سرور را می‌بیند، زنجیره را تا Root CA بررسی می‌کند.

ساختار یک Certificate

1
2
3
4
5
6
7
8
9
10
11
12
Certificate:
  Version: 3
  Serial Number: 1234567890
  Issuer: CN=VeriSign Class 3 CA
  Validity:
    Not Before: Jan  1 00:00:00 2002
    Not After:  Dec 31 23:59:59 2003
  Subject: CN=www.joes-hardware.com
           O=Joe's Hardware, Inc.
           C=US
  Public Key: RSA (2048 bit)
  Signature: [امضای CA]

HTTPS - HTTP روی SSL/TLS

تفاوت ظاهری

1
2
HTTP:   http://www.joes-hardware.com/index.html   (Port 80)
HTTPS:  https://www.joes-hardware.com/index.html  (Port 443)

SSL Handshake - برقراری اتصال امن

قبل از اینکه یک Byte از داده HTTP منتقل شود، باید SSL Handshake انجام شود:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Client                                          Server
  │                                               │
  │──── ClientHello ──────────────────────────────▶│
  │     (نسخه SSL، Cipher Suite های پشتیبانی‌شده،│
  │      یک عدد تصادفی)                           │
  │                                               │
  │◀─── ServerHello ──────────────────────────────│
  │     (Cipher Suite انتخاب‌شده، عدد تصادفی)    │
  │                                               │
  │◀─── Certificate ──────────────────────────────│
  │     (گواهی دیجیتال سرور)                     │
  │                                               │
  │  [Client Certificate سرور را تأیید می‌کند]   │
  │                                               │
  │──── ClientKeyExchange ─────────────────────────▶│
  │     (Pre-Master Secret رمزنگاری‌شده با        │
  │      Public Key سرور)                         │
  │                                               │
  │  [هر دو Session Key متقارن را می‌سازند]       │
  │                                               │
  │──── ChangeCipherSpec + Finished ───────────────▶│
  │◀─── ChangeCipherSpec + Finished ───────────────│
  │                                               │
  │══════════ داده HTTP رمزنگاری‌شده ══════════════│

Session Key - کلید موقت

بعد از Handshake، هر دو طرف یک Session Key مشترک دارند که:

  • برای همین Session است - در Session بعدی متفاوت خواهد بود
  • با الگوریتم متقارن (مثل AES) داده را رمزنگاری می‌کند
  • بسیار سریع‌تر از RSA است

Tunneling HTTPS از طریق Proxy

وقتی Client از طریق Proxy به یک HTTPS سرور وصل می‌شود، یک مشکل وجود دارد: Proxy نمی‌تواند داده رمزنگاری‌شده را بخواند.

راه‌حل: CONNECT Method که در فصل هشتم دیدیم:

1
2
3
4
5
6
7
8
Client ──── CONNECT www.joes-hardware.com:443 HTTP/1.0 ────▶ [Proxy]

[Proxy] اتصال TCP به سرور برقرار می‌کند

[Proxy] ──── 200 Connection Established ────▶ Client

Client ════════ SSL Handshake و داده رمزنگاری‌شده ════════ Server
       (Proxy فقط Byte ها را منتقل می‌کند)

فصل پانزدهم - بخش اول: Entities و Encodings

پیام در مقابل Entity - تمایز مهم

نویسندگان از همان ابتدا یک تمایز مفهومی مهم می‌کشند:

  • HTTP Message: ظرف حمل‌ونقل است - Start Line، Headers و Body
  • HTTP Entity: محتوای واقعی است - چیزی که حمل می‌شود

رابطه آنها: پیام مثل جعبه است، Entity مثل بار داخل جعبه.

1
2
3
4
5
6
7
8
HTTP Message
┌─────────────────────────────────┐
│ HTTP/1.1 200 OK                 │ ← Start Line
│ Content-Type: text/html         │ ← Entity Headers
│ Content-Length: 348             │ ← Entity Headers
│                                 │ ← Blank Line
│ <html>...</html>                │ ← Entity Body
└─────────────────────────────────┘

Entity Headers - توصیف محتوا

Entity Headers اطلاعاتی درباره محتوای Body ارائه می‌دهند:

Headerتوضیح
Content-Typeنوع MIME محتوا
Content-Lengthطول Body به Byte
Content-Languageزبان محتوا
Content-EncodingEncoding اعمال‌شده (مثل gzip)
Content-Locationآدرس جایگزین برای این Resource
Content-Rangeمحدوده Byte در Partial Response
Content-MD5Checksum MD5 برای تأیید صحت
Last-Modifiedآخرین زمان تغییر
Expiresزمان انقضای محتوا
AllowMethod های مجاز
ETagشناسه یکتا برای این نسخه

Content-Length - طول دقیق Body

چرا Content-Length مهم است؟

TCP یک جریان پیوسته از Byte هاست. بدون Content-Length، گیرنده نمی‌داند Body کجا تمام می‌شود و پیام بعدی کجا شروع می‌شود.

این به‌خصوص در Persistent Connection ها حیاتی است - چون چندین پیام پشت سر هم روی یک Connection می‌آیند.

استثناها - کِی Content-Length اجباری نیست؟

کتاب سه حالت را ذکر می‌کند:

  • Response به HEAD: Body ندارد، پس Content-Length لازم نیست
  • Status Code 1xx، 204، 304: این Response ها Body ندارند
  • Transfer-Encoding: chunked: طول به شکل دیگری مشخص می‌شود

Content-Length و Truncation

اگر Entity Body کوتاه‌تر از مقدار Content-Length باشد، Client باید خطا گزارش دهد. اگر بلندتر باشد، Client اضافی را نادیده می‌گیرد.

Content-Type - نوع محتوا

Charset

برای محتوای متنی، Content-Type باید Charset هم داشته باشد:

1
2
Content-Type: text/html; charset=utf-8
Content-Type: text/plain; charset=iso-8859-1

اگر Charset مشخص نشود، گیرنده باید خودش Charset را حدس بزند - که می‌تواند اشتباه باشد.

Multipart - چند بخش در یک Body

گاهی یک Entity Body شامل چند بخش جداگانه است. برای مثال در Form Upload:

1
2
3
4
5
6
7
8
9
10
11
12
Content-Type: multipart/form-data; boundary=AaBb01

--AaBb01
Content-Disposition: form-data; name="username"

Joe
--AaBb01
Content-Disposition: form-data; name="file"; filename="report.pdf"
Content-Type: application/pdf

[PDF Binary Data]
--AaBb01--

boundary یک رشته یکتاست که بخش‌های مختلف را از هم جدا می‌کند.

Content Encoding - فشرده‌سازی محتوا

چرا Encoding مهم است؟

قبل از ارسال، محتوا می‌تواند تبدیل شود - معمولاً فشرده‌سازی برای کاهش حجم.

1
[محتوای اصلی] ──[Encode]──▶ [محتوای فشرده] ──▶ شبکه ──▶ [Decode] ──▶ [محتوای اصلی]

انواع Content Encoding

Encodingتوضیح
gzipرایج‌ترین - معمولاً ۶۰-۸۰٪ حجم HTML را کاهش می‌دهد
deflateمبتنی بر الگوریتم zlib
compressقدیمی - کمتر استفاده می‌شود
identityهیچ تبدیلی نشده

مذاکره Encoding با Accept-Encoding

Client به سرور می‌گوید چه Encoding هایی پشتیبانی می‌کند:

1
2
GET /index.html HTTP/1.1
Accept-Encoding: gzip, deflate;q=0.8, identity;q=0.5

سرور بهترین Encoding را انتخاب می‌کند و در Header اعلام می‌کند:

1
2
HTTP/1.1 200 OK
Content-Encoding: gzip

Transfer-Encoding و Chunked Encoding

تفاوت با Content-Encoding

 Content-EncodingTransfer-Encoding
سطحروی محتوا (Entity)روی انتقال (Transport)
ماندگاریدائمی - Cache هم فشرده نگه می‌داردموقت - فقط برای این Transfer
هدفکاهش حجممدیریت ارسال

مشکلی که Chunked Encoding حل می‌کند

گاهی سرور نمی‌داند محتوا چقدر بزرگ است - مثلاً وقتی محتوا به‌صورت Dynamic تولید می‌شود. در این حالت نمی‌توان Content-Length را از قبل مشخص کرد.

Chunked Encoding راه‌حل است: محتوا به Chunk های کوچک تقسیم می‌شود و هر Chunk با اندازه‌اش ارسال می‌شود.

ساختار Chunked Encoding

1
2
3
4
5
6
7
8
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: text/html

19\r\n                          ← اندازه Chunk به هگزادسیمال (25 byte)
I'm a chunked message\r\n       ← محتوای Chunk
0\r\n                           ← Chunk با اندازه صفر = پایان
\r\n

یک مثال واقعی با چند Chunk:

1
2
3
4
5
6
7
8
4\r\n
Wiki\r\n
6\r\n
pedia \r\n
E\r\n
in\r\n\r\nchunks.\r\n
0\r\n
\r\n

نتیجه: Wikipedia in\r\n\r\nchunks.

Range Requests - درخواست بخشی از محتوا

کاربرد

گاهی Client فقط بخشی از یک Resource را می‌خواهد:

  • ادامه دانلود ناتمام: اگر دانلود قطع شود، فقط بخش باقی‌مانده درخواست شود
  • Streaming: بارگذاری تدریجی یک فایل ویدیویی بزرگ
  • Random Access: خواندن بخش خاصی از یک فایل بزرگ

درخواست Range

1
2
GET /bigfile.zip HTTP/1.1
Range: bytes=4000-8000

انواع Range:

1
2
3
4
5
Range: bytes=0-499       ← اولین ۵۰۰ Byte
Range: bytes=500-999     ← Byte های ۵۰۰ تا ۹۹۹
Range: bytes=-500        ← آخرین ۵۰۰ Byte
Range: bytes=500-        ← از Byte 500 تا انتها
Range: bytes=0-0,-1      ← اولین و آخرین Byte

پاسخ سرور

اگر سرور از Range پشتیبانی کند:

1
2
3
4
5
HTTP/1.1 206 Partial Content
Content-Range: bytes 4000-8000/65535
Content-Length: 4001

[داده درخواست‌شده]

اگر سرور از Range پشتیبانی نکند، کل فایل را با 200 OK برمی‌گرداند.

Entity Digests - تأیید یکپارچگی

Content-MD5

سرور می‌تواند یک MD5 Checksum از Body محاسبه کند و در Header بگذارد:

1
Content-MD5: Q2hlY2sgSW50ZWdyaXR5IQ==

Client بعد از دریافت، همین محاسبه را تکرار می‌کند. اگر نتایج یکسان بود، محتوا در مسیر تغییر نکرده.

محدودیت: Content-MD5 در Header قرار دارد. اگر مهاجم محتوا را تغییر دهد، می‌تواند MD5 را هم تغییر دهد. برای امنیت واقعی باید از HTTPS استفاده شود.


فصل شانزدهم - بخش اول: Internationalization و پشتیبانی HTTP از محتوای چندزبانه

چرا Internationalization مهم است؟

وب یک رسانه جهانی است. کاربران در ایران، ژاپن، روسیه و آلمان همگی از همان HTTP استفاده می‌کنند اما زبان، خط و Character Set کاملاً متفاوتی دارند.

HTTP باید بتواند:

  • محتوای متنی به هر زبانی را منتقل کند
  • به Client بگوید محتوا به چه زبان و Encoding ای است
  • به Client اجازه دهد زبان مورد نظرش را اعلام کند

Character Set چیست؟

Character Set یک نگاشت بین کاراکترهای قابل نمایش و اعداد دیجیتال است.

مثال ساده:

1
2
3
4
در ASCII:
'A' → 65
'B' → 66
'a' → 97

اما ASCII فقط ۱۲۸ کاراکتر دارد - برای انگلیسی کافی است اما برای فارسی، عربی، چینی یا ژاپنی کاملاً ناکافی است.

تکامل Character Set ها

ASCII - نقطه شروع

اولین استاندارد. ۷ بیت، ۱۲۸ کاراکتر. فقط حروف لاتین، اعداد و نشانه‌های رایج.

ISO-8859 - خانواده Latin

برای حل محدودیت ASCII، مجموعه‌ای از استانداردهای ۸ بیتی ساخته شد:

استانداردپوشش
ISO-8859-1 (Latin-1)اروپای غربی
ISO-8859-2اروپای مرکزی
ISO-8859-6عربی
ISO-8859-8عبری

مشکل اصلی: هر استاندارد فقط یک زبان را پوشش می‌دهد. نمی‌توان در یک سند هم عربی و هم ژاپنی داشت.

Unicode - راه‌حل جهانی

Unicode یک Character Set جهانی است که هدفش پوشش همه زبان‌های دنیا در یک استاندارد واحد است.

1
2
3
4
5
'A'    → U+0041
'ا'    → U+0627
'あ'   → U+3042
'中'   → U+4E2D
'😀'   → U+1F600

فعلاً بیش از 140,000 کاراکتر از بیش از 150 خط را پوشش می‌دهد.

Encoding - چطور Unicode را ذخیره کنیم؟

Unicode یک Character Set است - می‌گوید هر کاراکتر چه عددی دارد. اما Encoding می‌گوید آن عدد چطور به Byte تبدیل شود.

UTF-8 - رایج‌ترین Encoding

UTF-8 یک Encoding متغیر طول است:

1
2
3
4
کاراکترهای ASCII:    1 Byte   (سازگار با ASCII)
کاراکترهای اروپایی: 2 Byte
کاراکترهای آسیایی:  3 Byte
Emoji و موارد خاص:  4 Byte

مثال:

1
2
3
4
'A'  → 0x41           (1 Byte)
'é'  → 0xC3 0xA9      (2 Byte)
'ا'  → 0xD8 0xA7      (2 Byte)
'あ' → 0xE3 0x81 0x82  (3 Byte)

مزیت اصلی UTF-8: کاملاً با ASCII سازگار است. هر فایل ASCII معتبر، یک فایل UTF-8 معتبر هم هست.

UTF-16 - برای زبان‌های آسیایی بهینه‌تر

از ۲ یا ۴ Byte برای هر کاراکتر استفاده می‌کند. برای زبان‌هایی مثل چینی و ژاپنی که اکثر کاراکترهایشان در UTF-8 سه Byte نیاز دارند، کارآمدتر است.

Content-Type و Charset در HTTP

سرور باید هم نوع محتوا و هم Encoding آن را اعلام کند:

1
2
3
Content-Type: text/html; charset=utf-8
Content-Type: text/plain; charset=iso-8859-6
Content-Type: application/json; charset=utf-8

اگر Charset مشخص نباشد؟

HTTP/1.1 یک پیش‌فرض تعریف کرده: اگر Charset مشخص نشود، ISO-8859-1 فرض می‌شود.

هشدار: این پیش‌فرض برای توسعه‌دهندگان فارسی‌زبان خطرناک است. اگر سرور Charset را مشخص نکند و محتوا UTF-8 باشد، مرورگر ممکن است متن فارسی را به‌درستی نمایش ندهد. همیشه Charset را صریحاً مشخص کنید.

Accept-Charset - ترجیح Client

Client می‌تواند Charset های مورد پشتیبانی خود را اعلام کند:

1
Accept-Charset: iso-8859-1, utf-8;q=0.9, *;q=0.1
  • iso-8859-1 با اولویت ۱.۰ (پیش‌فرض)
  • utf-8 با اولویت ۰.۹
  • هر Charset دیگری با اولویت ۰.۱

Language Tags - برچسب زبان

Content-Language Header

سرور زبان محتوا را اعلام می‌کند:

1
2
3
4
Content-Language: fa
Content-Language: en-US
Content-Language: zh-CN
Content-Language: ar-SA

ساختار Language Tag

Language Tag از بخش‌های جداشده با - تشکیل می‌شود:

1
2
3
4
5
6
fa         ← فارسی (زبان اصلی)
fa-IR      ← فارسی ایران (زبان + منطقه)
en-US      ← انگلیسی آمریکا
en-GB      ← انگلیسی بریتانیا
zh-CN      ← چینی ساده‌شده (چین)
zh-TW      ← چینی سنتی (تایوان)

Accept-Language Header

Client زبان‌های ترجیحی خود را اعلام می‌کند:

1
Accept-Language: fa, en;q=0.9, fr;q=0.5

سرور می‌تواند بر اساس این اطلاعات محتوای مناسب را انتخاب کند.

Internationalized URIs

مشکل

URI ها از ASCII طراحی شده‌اند. اما URL هایی به فارسی، عربی یا چینی چطور؟

راه‌حل - Percent Encoding

کاراکترهای غیر ASCII ابتدا به UTF-8 تبدیل می‌شوند و سپس هر Byte با %XX نمایش داده می‌شود:

1
2
3
4
5
6
7
8
9
10
فارسی: "کتاب"

UTF-8 Bytes:
ک → 0xDA 0xA9
ت → 0xD8 0xAA
ا → 0xD8 0xA7
ب → 0xD8 0xA8

Encoded URL:
%DA%A9%D8%AA%D8%A7%D8%A8

IDN - Internationalized Domain Names

یک مشکل مشابه برای Domain Name ها وجود داشت. IDN (Internationalized Domain Names) استانداردی است که اجازه می‌دهد Domain Name به زبان‌های غیر لاتین تعریف شود.

از طریق Punycode کاراکترهای غیر ASCII به یک فرمت ASCII قابل استفاده در DNS تبدیل می‌شوند:

1
"مثال.إختبار" → "xn--mgbh0fb.xn--kgbechtv"

Content Negotiation - انتخاب بهترین نسخه

HTTP یک مکانیزم برای مذاکره محتوا دارد. سرور می‌تواند چندین نسخه از یک Resource داشته باشد و بهترین را بر اساس ترجیحات Client انتخاب کند.

1
2
3
4
5
6
7
8
9
10
Client:
GET /index.html HTTP/1.1
Accept-Language: fa, en;q=0.8
Accept-Charset: utf-8

Server:
دارای: index.fa.html (فارسی، UTF-8)
       index.en.html (انگلیسی، ISO-8859-1)

انتخاب: index.fa.html ← بهترین تطابق با ترجیح Client

سرور در Response توضیح می‌دهد چه انتخابی کرده:

1
2
3
4
HTTP/1.1 200 OK
Content-Language: fa
Content-Type: text/html; charset=utf-8
Vary: Accept-Language, Accept-Charset

Vary Header به Cache می‌گوید: «این Response برای همه کاربران یکسان نیست - بر اساس Accept-Language و Accept-Charset ممکن است متفاوت باشد.»


فصل هفدهم - Content Negotiation و Transcoding

مشکلی که Content Negotiation حل می‌کند

یک URL واحد ممکن است برای کاربران مختلف باید محتوای متفاوتی برگرداند:

  • کاربر فرانسوی → صفحه به فرانسه
  • کاربر با مرورگر موبایل → صفحه سبک‌تر و ساده‌تر
  • کاربر با اتصال کند → تصاویر با کیفیت پایین‌تر

Content Negotiation مکانیزمی است که به سرور اجازه می‌دهد بهترین نسخه را برای هر Client انتخاب کند - بدون اینکه URL تغییر کند.

سه روش Content Negotiation

روش اول: Server-Driven Negotiation

Client ترجیحاتش را از طریق Accept Header ها اعلام می‌کند و سرور تصمیم می‌گیرد:

1
2
3
4
5
GET /index.html HTTP/1.1
Accept:          text/html, application/xml;q=0.9, */*;q=0.8
Accept-Language: fa, en;q=0.7
Accept-Charset:  utf-8, iso-8859-1;q=0.5
Accept-Encoding: gzip, deflate

مزایا:

  • Client نیازی به دانستن URL های مختلف ندارد
  • منطق انتخاب متمرکز در سرور است

معایب:

  • سرور باید Header های زیادی بررسی کند
  • Cache باید نسخه‌های مختلف را جداگانه Cache کند
  • سرور نمی‌تواند همه ترجیحات Client را بداند - مثلاً اندازه صفحه نمایش

روش دوم: Client-Driven Negotiation (Agent-Driven)

سرور یک لیست از گزینه‌های موجود می‌فرستد و Client انتخاب می‌کند:

1
2
3
4
5
6
7
8
9
10
11
Client ──── GET /index.html ────────────────────────────▶ Server

Server ◀─── 300 Multiple Choices ──────────────────────── Server
            Links:
            - /index.fr.html (فرانسه)
            - /index.en.html (انگلیسی)
            - /index.fa.html (فارسی)

Client ──── GET /index.fa.html ─────────────────────────▶ Server

Server ◀─── 200 OK ─────────────────────────────────────── Server

مزایا:

  • Client اطلاعات بیشتری دارد - می‌داند دقیقاً چه می‌خواهد
  • Cache ساده‌تر است - هر URL یک نسخه مشخص دارد

معایب:

  • یک Round-Trip اضافه - Latency بیشتر
  • اتوماسیون سخت‌تر است

روش سوم: Transparent Negotiation

یک Proxy هوشمند در مسیر، هم Accept Header های Client را می‌داند و هم گزینه‌های سرور را. خودش بهترین انتخاب را می‌کند - بدون دخالت Client یا Server.

1
Client ──── Request + Accept Headers ────▶ [Proxy هوشمند] ──── بهترین URL ────▶ Server

مزیت: هیچ Round-Trip اضافه‌ای نیست و Cache بهینه است.

q-Value - اولویت‌بندی ترجیحات

در Accept Header ها، q (Quality Value) اولویت هر گزینه را مشخص می‌کند. مقدار بین 0.0 (کمترین اولویت) تا 1.0 (بیشترین اولویت) است:

1
Accept-Language: fa, en;q=0.9, fr;q=0.6, *;q=0.1

ترتیب اولویت:

  1. fa - اولویت 1.0 (پیش‌فرض)
  2. en - اولویت 0.9
  3. fr - اولویت 0.6
  4. هر زبان دیگری - اولویت 0.1

Vary Header و Cache

وقتی سرور Content Negotiation انجام می‌دهد، باید به Cache اطلاع دهد که این Response برای همه کاربران یکسان نیست. این کار از طریق Vary Header انجام می‌شود:

1
2
3
4
HTTP/1.1 200 OK
Content-Language: fa
Content-Type: text/html; charset=utf-8
Vary: Accept-Language, Accept-Encoding

این Vary Header به Cache می‌گوید: «اگر می‌خواهی این Response را Cache کنی، آن را جداگانه برای هر ترکیب Accept-Language و Accept-Encoding Cache کن.»

تأثیر Vary روی Cache Hit Rate

1
2
3
4
Request 1: Accept-Language: fa  →  Cache با کلید (URL + fa)
Request 2: Accept-Language: en  →  Cache با کلید (URL + en)
Request 3: Accept-Language: fa  →  Cache Hit!
Request 4: Accept-Language: fr  →  Cache Miss - باید از سرور بگیرد

Transcoding - تبدیل محتوا

Transcoding یک قدم فراتر از Content Negotiation است. به‌جای انتخاب از بین نسخه‌های از پیش آماده، محتوا در لحظه تبدیل می‌شود.

Format Conversion

یک Proxy می‌تواند فرمت محتوا را تغییر دهد:

1
2
3
GIF → JPEG        (برای کاهش حجم)
HTML → WML        (برای دستگاه‌های موبایل قدیمی)
High-res → Low-res (برای اتصالات کند)

Encoding Conversion

محتوا از یک Encoding به Encoding دیگر تبدیل می‌شود:

1
ISO-8859-1 → UTF-8

Language Translation

نویسندگان اشاره می‌کنند که ترجمه خودکار محتوا از طریق Transcoding ممکن است - اما در سال ۲۰۰۲ هنوز در مرحله تحقیق بود.

دیدگاه امروزی: امروزه با وجود API های ترجمه مثل Google Translate API، این ایده کاملاً قابل پیاده‌سازی است.

Transcoding در دستگاه‌های موبایل

یکی از کاربردهای مهم Transcoding: تطبیق محتوا برای دستگاه‌های مختلف.

1
2
3
4
5
6
7
8
9
Origin Server
    │
    │  HTML کامل با تصاویر بزرگ
    ▼
[Transcoding Proxy]
    │
    ├──▶ Desktop Browser    → HTML کامل
    ├──▶ Mobile Browser     → HTML ساده‌شده + تصاویر فشرده
    └──▶ دستگاه قدیمی      → متن ساده

Proxy بر اساس User-Agent Header نوع دستگاه را تشخیص می‌دهد و محتوا را مناسب آن دستگاه تبدیل می‌کند.

Feature Negotiation - فراتر از Content

نویسندگان یک مفهوم پیشرفته‌تر را مطرح می‌کنند: Feature Negotiation - مذاکره درباره قابلیت‌های Client، نه فقط فرمت محتوا.

مثال: اگر Client از JavaScript، Flash یا فونت‌های خاص پشتیبانی می‌کند، سرور می‌تواند نسخه غنی‌تری از صفحه بفرستد.

این ایده در زمان نوشتن کتاب هنوز در حال استانداردسازی بود.

دیدگاه امروزی: در دنیای مدرن این مشکل با Responsive Design و Progressive Enhancement در سمت Client حل شده - به‌جای اینکه سرور تصمیم بگیرد، CSS و JavaScript در مرورگر محتوا را تطبیق می‌دهند.


فصل هجدهم - Web Hosting و مدیریت ترافیک

Virtual Hosting - چند سایت روی یک سرور

مشکل

اگر هر سایت به یک سرور اختصاصی نیاز داشت، هزینه‌ها بسیار بالا می‌رفت. Virtual Hosting اجازه می‌دهد چندین سایت روی یک سرور فیزیکی اجرا شوند.

چطور سرور می‌فهمد کدام سایت را بسرویس‌دهد؟

در HTTP/1.0 مشکلی وجود داشت: هر دو سایت یک IP داشتند و Request فقط شامل Path بود - نه Hostname کامل:

1
GET /index.html HTTP/1.0

سرور نمی‌دانست این Request برای کدام سایت است.

راه‌حل HTTP/1.1 - Host Header:

1
2
GET /index.html HTTP/1.1
Host: www.joes-hardware.com

HTTP/1.1 این Header را اجباری کرد. سرور از آن استفاده می‌کند تا بفهمد Request برای کدام Virtual Host است.

چهار روش پیاده‌سازی Virtual Hosting

روش اول - Virtual Hosting با Host Header (HTTP/1.1):

ساده‌ترین و رایج‌ترین روش. همه سایت‌ها یک IP دارند و از Host Header برای تمایز استفاده می‌شود.

1
2
3
IP: 192.168.1.1
├── Host: www.joes-hardware.com  →  /docs/joe/
└── Host: www.marys-antiques.com →  /docs/mary/

روش دوم - Virtual Hosting با IP های مختلف:

هر سایت IP جداگانه دارد. گران‌تر (IP Address محدود است) اما با HTTP/1.0 هم کار می‌کند.

1
2
192.168.1.1 → www.joes-hardware.com
192.168.1.2 → www.marys-antiques.com

روش سوم - Virtual Hosting با Port های مختلف:

هر سایت روی Port متفاوتی اجرا می‌شود. مشکل: کاربر باید Port را در URL بنویسد - ناراحت‌کننده است.

1
2
192.168.1.1:80   → www.joes-hardware.com
192.168.1.1:8080 → www.marys-antiques.com

روش چهارم - ترکیب:

در عمل، سرورهای مدرن از ترکیب این روش‌ها استفاده می‌کنند.

Making Web Sites Reliable - پایداری سرویس

Mirror Sites - آینه‌سازی

برای محتوای پرتقاضا، چندین سرور یکسان در مکان‌های جغرافیایی مختلف قرار می‌گیرند:

1
2
3
کاربر ایران    →  سرور آسیا
کاربر اروپا    →  سرور اروپا
کاربر آمریکا  →  سرور آمریکا

Client به نزدیک‌ترین سرور هدایت می‌شود - Latency کمتر، پهنای باند بهتر.

HTTP Redirects - هدایت به سرور مناسب

ساده‌ترین روش: سرور اصلی Client را به Mirror مناسب Redirect می‌کند:

1
2
3
4
5
6
Client ──── GET /bigfile.zip ────────────▶ Master Server

Master Server ──── 302 Found ────────────▶ Client
                   Location: http://asia.joes-hardware.com/bigfile.zip

Client ──── GET /bigfile.zip ────────────▶ Asia Mirror

مشکل: یک Round-Trip اضافه - Client باید ابتدا به Master Server برود.

DNS-Based Load Balancing

DNS یک نام را به چندین IP نگاشت می‌کند. هر بار که یک DNS Query انجام می‌شود، یک IP متفاوت برمی‌گردد (Round-Robin):

1
2
3
4
www.joes-hardware.com → DNS
                         ├── 192.168.1.1  (Request اول)
                         ├── 192.168.1.2  (Request دوم)
                         └── 192.168.1.3  (Request سوم)

مشکل: DNS نمی‌داند کدام سرور سالم است. اگر یک سرور از کار بیفتد، DNS هنوز IP آن را برمی‌گرداند.

Web Traffic Management - مدیریت ترافیک

HTTP Load Balancer

یک Load Balancer جلوی چندین سرور قرار می‌گیرد و درخواست‌ها را بین آنها توزیع می‌کند:

1
2
3
                    ┌──▶ Server 1
Client ──▶ [LB] ───┼──▶ Server 2
                    └──▶ Server 3

الگوریتم‌های رایج توزیع بار:

الگوریتمتوضیح
Round-Robinبه نوبت به هر سرور
Least Connectionsبه سروری که کمترین Connection دارد
Weightedبر اساس ظرفیت هر سرور
IP HashClient های یکسان همیشه به سرور یکسان

Sticky Sessions - ماندن روی یک سرور

بعضی اپلیکیشن‌ها نیاز دارند تمام Request های یک کاربر به همان سرور بروند - مثلاً وقتی Session داده‌ها روی حافظه سرور نگه داشته می‌شوند.

Load Balancer از Cookie یا IP Address برای تشخیص کاربر و هدایت به سرور مناسب استفاده می‌کند.

Surrogate Servers و Content Distribution

Surrogate (Reverse Proxy)

یک سرور که جلوی Origin Server قرار می‌گیرد. از دیدگاه Client، Surrogate همان Origin Server است.

1
2
3
4
Client ──── GET /index.html ────▶ [Surrogate]
                                        │ Cache Miss
                                        ▼
                                  [Origin Server]

تفاوت با Forward Proxy: Forward Proxy از طرف Client کار می‌کند. Surrogate از طرف Server کار می‌کند.

CDN - Content Delivery Network

CDN یک شبکه گسترده از Surrogate ها در سراسر دنیاست. محتوا در نزدیک‌ترین Node به کاربر Cache می‌شود:

1
2
3
4
5
Origin Server (ایران)
      │
      ├──▶ CDN Node آسیا    ◀── کاربر ژاپن
      ├──▶ CDN Node اروپا   ◀── کاربر آلمان
      └──▶ CDN Node آمریکا  ◀── کاربر برزیل

مزایا:

  • محتوا از نزدیک‌ترین نقطه تحویل داده می‌شود - Latency کمتر
  • Origin Server از بار مستقیم کاربران محافظت می‌شود
  • در برابر ترافیک ناگهانی (Flash Crowd) مقاوم است

Server Tracking - شناسایی سرور درست

IP Anycast

یک IP Address به چندین سرور در مکان‌های مختلف تخصیص داده می‌شود. شبکه به‌صورت خودکار ترافیک را به نزدیک‌ترین سرور هدایت می‌کند.

1
2
3
4
همه سرورها IP یکسان دارند: 1.2.3.4

کاربر آسیا   → Routing Table → سرور آسیا (1.2.3.4)
کاربر اروپا  → Routing Table → سرور اروپا (1.2.3.4)

این روش امروز در DNS Root Server ها و CDN های بزرگ (مثل Cloudflare) استفاده می‌شود.

خلاصه معماری یک سایت پرترافیک

کتاب در پایان فصل معماری کاملی از یک سایت واقعی ترسیم می‌کند:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
کاربر
  │
  ▼
[DNS با Round-Robin]
  │
  ▼
[Load Balancer]
  │
  ├── [Surrogate / Cache Layer]
  │         │ Cache Miss
  │         ▼
  │   [Application Servers]
  │         │
  │         ▼
  │   [Database Cluster]
  │
  └── [Static Content Servers]
        (تصاویر، CSS، JS)

هر لایه یک هدف مشخص دارد:

  • DNS: توزیع اولیه ترافیک بین Data Center ها
  • Load Balancer: توزیع ترافیک بین سرورهای یک Data Center
  • Surrogate: کاهش بار روی Application Server ها
  • Application Server: پردازش منطق کسب‌وکار
  • Database: ذخیره داده‌های پایدار

فصل نوزدهم - WebDAV: نوشتن روی وب

WebDAV چیست؟

WebDAV (Web-based Distributed Authoring and Versioning) یک توسعه روی HTTP/1.1 است که اجازه می‌دهد کاربران از راه دور محتوا را روی Web Server ایجاد، ویرایش، حذف و مدیریت کنند - نه فقط بخوانند.

HTTP اصلی فقط برای خواندن طراحی شده بود:

  • GET - بخوان
  • POST - فرم بفرست

WebDAV متدهای جدیدی اضافه کرد که وب را به یک سیستم فایل توزیع‌شده تبدیل می‌کند.

متدهای جدید WebDAV

مدیریت محتوا

متدتوضیح
PUTیک فایل جدید ایجاد یا یک فایل موجود را جایگزین کن
DELETEیک فایل یا Directory را حذف کن
COPYیک Resource را از یک URL به URL دیگر کپی کن
MOVEیک Resource را از یک URL به URL دیگر منتقل کن
MKCOLیک Collection (Directory) جدید بساز

مدیریت Properties

متدتوضیح
PROPFINDProperties یک Resource را بخوان
PROPPATCHProperties یک Resource را تغییر بده

Locking - قفل کردن

متدتوضیح
LOCKیک Resource را قفل کن تا دیگران نتوانند تغییر دهند
UNLOCKقفل را بردار

Properties - ویژگی‌های Resource

WebDAV مفهوم Properties را معرفی می‌کند - متادیتای قابل خواندن و نوشتن که به هر Resource متصل می‌شود.

Live Properties

Properties ای که توسط سرور مدیریت می‌شوند و به‌صورت خودکار به‌روز می‌شوند:

1
2
3
4
<DAV:getcontentlength>   ← اندازه فایل
<DAV:getlastmodified>    ← آخرین زمان تغییر
<DAV:getcontenttype>     ← نوع MIME
<DAV:displayname>        ← نام نمایشی

Dead Properties

Properties ای که توسط کاربر تنظیم می‌شوند - سرور فقط آنها را ذخیره می‌کند:

1
2
<author>Mohammad Hossein Karami</author>
<category>Technical Documentation</category>

PROPFIND - خواندن Properties

1
2
3
4
5
6
7
8
9
PROPFIND /files/report.html HTTP/1.1
Host: www.joes-hardware.com
Content-Type: application/xml
Depth: 0

<?xml version="1.0" encoding="utf-8" ?>
<D:propfind xmlns:D="DAV:">
  <D:allprop/>
</D:propfind>

پاسخ سرور:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
HTTP/1.1 207 Multi-Status
Content-Type: application/xml

<?xml version="1.0" encoding="utf-8" ?>
<D:multistatus xmlns:D="DAV:">
  <D:response>
    <D:href>/files/report.html</D:href>
    <D:propstat>
      <D:prop>
        <D:getcontentlength>4525</D:getcontentlength>
        <D:getlastmodified>Mon, 12 Jan 2025 09:25:00 GMT</D:getlastmodified>
        <D:getcontenttype>text/html</D:getcontenttype>
      </D:prop>
      <D:status>HTTP/1.1 200 OK</D:status>
    </D:propstat>
  </D:response>
</D:multistatus>

Status Code 207: یک Status Code جدید که WebDAV اضافه کرده - Multi-Status. چون یک Request ممکن است روی چندین Resource اعمال شود و هر کدام نتیجه متفاوتی داشته باشند.

Collections - مجموعه‌ها

در WebDAV، Collection معادل Directory است - یک Resource که می‌تواند شامل Resource های دیگر باشد.

MKCOL - ساختن Collection

1
2
MKCOL /files/new-project/ HTTP/1.1
Host: www.joes-hardware.com

پاسخ:

1
HTTP/1.1 201 Created

Depth Header - عمق عملیات

بسیاری از WebDAV Method ها یک Depth Header دارند که مشخص می‌کند عملیات چقدر عمیق اعمال شود:

1
2
3
Depth: 0    ← فقط روی Resource خودش
Depth: 1    ← Resource + فرزندان مستقیم
Depth: infinity ← Resource + تمام زیرمجموعه‌ها

مثال: PROPFIND با Depth: infinity برای خواندن Properties همه فایل‌های داخل یک Directory و زیرDirectory های آن.

Locking - جلوگیری از ویرایش همزمان

یکی از مشکلات اصلی ویرایش توزیع‌شده: اگر دو نفر همزمان یک فایل را ویرایش کنند، یکی از تغییرات از دست می‌رود. WebDAV این مشکل را با Locking حل می‌کند.

انواع Lock

Exclusive Lock: فقط یک نفر می‌تواند Lock داشته باشد - دیگران نمی‌توانند تغییر دهند:

1
2
3
4
5
6
7
8
9
10
11
LOCK /files/report.html HTTP/1.1
Host: www.joes-hardware.com
Timeout: Second-3600
Content-Type: application/xml

<?xml version="1.0" encoding="utf-8" ?>
<D:lockinfo xmlns:D="DAV:">
  <D:lockscope><D:exclusive/></D:lockscope>
  <D:locktype><D:write/></D:locktype>
  <D:owner>Mohammad Hossein</D:owner>
</D:lockinfo>

پاسخ سرور شامل یک Lock Token است:

1
2
HTTP/1.1 200 OK
Lock-Token: <opaquelocktoken:e71d4fae-5dec-11d9-b0fc>

Shared Lock: چند نفر می‌توانند همزمان Lock داشته باشند - برای خواندن مشترک.

Lock Token - کلید قفل

برای هر عملیاتی که روی Resource قفل‌شده انجام می‌شود، باید Lock Token فرستاده شود:

1
2
PUT /files/report.html HTTP/1.1
If: (<opaquelocktoken:e71d4fae-5dec-11d9-b0fc>)

اگر Token اشتباه باشد یا Lock منقضی شده باشد، سرور 423 Locked برمی‌گرداند.

COPY و MOVE

COPY - کپی کردن Resource

1
2
3
4
COPY /files/report.html HTTP/1.1
Destination: http://www.joes-hardware.com/files/report-backup.html
Depth: infinity
Overwrite: T
  • Destination: آدرس مقصد
  • Depth: infinity برای کپی کامل یک Collection با تمام محتوایش
  • Overwrite: T اگر مقصد وجود دارد، جایگزین شود

MOVE - انتقال Resource

1
2
MOVE /files/old-report.html HTTP/1.1
Destination: http://www.joes-hardware.com/archive/old-report.html

تفاوت با COPY: بعد از انتقال، Resource اصلی حذف می‌شود.

Versioning - نسخه‌بندی (DeltaV)

WebDAV یک Extension به نام DeltaV دارد که نسخه‌بندی فایل‌ها را اضافه می‌کند - مثل Git اما روی HTTP.

مفاهیم اصلی:

1
2
3
4
5
Version History
    │
    ├── Version 1  ← نسخه اولیه
    ├── Version 2  ← بعد از ویرایش اول
    └── Version 3  ← بعد از ویرایش دوم (نسخه فعلی)

با DeltaV می‌توان:

  • به نسخه‌های قبلی برگشت
  • تفاوت بین نسخه‌ها را دید
  • تاریخچه تغییرات را ردیابی کرد

WebDAV در دنیای واقعی

نویسندگان کاربردهای عملی WebDAV را ذکر می‌کنند:

  • Microsoft Office: ذخیره مستقیم اسناد روی WebDAV Server
  • macOS: اتصال به WebDAV Server مثل یک درایو شبکه
  • Content Management Systems: پایه بسیاری از CMS های اولیه
  • Subversion (SVN): سیستم کنترل نسخه که روی WebDAV ساخته شده

دیدگاه امروزی: WebDAV در دنیای مدرن جای خود را تا حدودی به REST API ها و سرویس‌های Cloud Storage (مثل Google Drive API یا OneDrive API) داده. اما هنوز در بسیاری از سیستم‌های Enterprise و ابزارهای مدیریت فایل مثل Nextcloud استفاده می‌شود.

جمع‌بندی کل کتاب

این کتاب یک سفر کامل از پایه‌ترین مفاهیم تا پیچیده‌ترین جزئیات HTTP بود:

بخش اول - مبانی: HTTP Message ها، URL ها، Connection های TCP و معماری Web Server ها

بخش دوم - معماری شبکه: Proxy ها، Cache ها، Gateway ها، Tunnel ها و Web Robots

بخش سوم - امنیت و هویت: Cookie ها، Basic/Digest Authentication و HTTPS/TLS

بخش چهارم - محتوا: Encoding ها، Internationalization، Content Negotiation و Transcoding

بخش پنجم - توسعه وب: Virtual Hosting، Load Balancing، CDN و WebDAV

این کتاب در سال ۲۰۰۲ نوشته شده اما اصول بنیادین آن هنوز پایه HTTP/2 و HTTP/3 هستند. اگر می‌خواهی گام بعدی را بردار، پیشنهاد می‌کنم کتاب «High Performance Browser Networking» نوشته Ilya Grigorik را بخوانی - که مکمل مستقیم این کتاب است و HTTP/2، QUIC و بهینه‌سازی‌های مدرن شبکه را پوشش می‌دهد.