بازگشت به دوره

آشنایی با پروتکل HTTP

می‌دانیم که شبکه‌ی اینترنت از تعداد بسیار زیادی کامپیوتر به هم متصل تشکیل شده است. به طور کلی برای برقراری ارتباط بین کامپیوترهای یک شبکه، نیاز به یک زبان مشترک یا استاندارد است. در شبکه‌های مختلف از استاندارهای مختلفی برای این منظور استفاده می‌شود. به این استانداردها در دنیای شبکه اصطلاحاً پروتکل (قرارداد) گفته می‌شود.

به عنوان مثال شبکه‌ی اینترنت بر پایه‌ی پروتکل TCP/IP کار می‌کند. البته TCP/IP تنها پروتکل مورد استفاده در اینترنت نیست. در واقع TCP/IP یک پروتکل پایه است که بسیاری از پروتکل‌های دیگر مانند FTP، POP، SMTP، SSH و ... بر پایه‌ی آن کار می‌کنند.

در این بخش قصد داریم به بررسی یکی از پروتکل‌های مورد استفاده در اینترنت، یعنی پروتکل HTTP بپردازیم. این پروتکل برای ارائه‌ی سرویس‌های مرتبط با وب به وجود آمده است. در واقع تمام وبسایت‌هایی که می‌شناسید بر پایه‌ی همین پروتکل کار می‌کنند. البته در این بخش قصد نداریم وارد جزئیات این پروتکل شویم. و تنها به اندازه‌ای که برای ادامه‌ی مباحث این فصل نیاز داریم به بررسی این پروتکل خواهیم پرداخت.

 

مدل Client-Server

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

توجه کنید که Server یا Client بودنِ کامپیوترها، با توجه به نرم‌افزاری که اجرا می‌کنند مشخص می‌شود. یعنی سخت‌افزار به کار رفته در کامپیوترهای Server و Client تقریباً یکسان است. هرچند سخت‌افزار Server ها معمولاً قدرتمندتر است. اما هر دو کامپیوتر را می‌توان با نصب و اجرای نرم‌افزارهای متفاوت به Server یا Client تبدیل کرد.

نکته : کلمه‌ی Client از نظر لغوی به معنی "مشتری" است. اما در متون تخصصی، با توجه به محل استفاده از کلمه‌ی Client، از کلماتی مانند "سرویس‌گیرنده" یا "کاربر" استفاده می‌شود. در این کتاب بیشتر از کلمه‌ی "کاربر" به عنوان معادل فارسی Client استفاده خواهیم کرد.

نکته : کلمه‌ی Server نیز از نظر لغوی به معنی "سرویس‌دهنده" یا "خدمتگزار" است. اما به دلیل استفاده‌ی روزمره از این کلمه در زبان فارسی، در این کتاب از همان کلمه‌ی "سرور" به عنوان معادل فارسی Server استفاده خواهیم کرد.

نحوه‌ی برقراری ارتباط بین کاربر و سرور در پروتکل HTTP به صورت ساده شده در شکل زیر نشان داده شده است.

اگر به جهت فلش‌ها توجه کنید، می‌بینید که همیشه کامپیوتر کاربر آغاز کننده‌ی ارتباط است. زیرا درخواست (Request) توسط کاربر ارسال می‌شود. پس از ارسال درخواست توسط کاربر و دریافت آن توسط سرور، پاسخی (Response) متناسب با درخواست برای کاربر ارسال می‌شود.

در کامپیوتر کاربر می‌توان از نرم‌افزارهای مختلفی برای ارسال درخواست به سرور استفاده کرد. اما مشهورترین نرم‌افزارهایی که در کامپیوتر کاربر به کار می‌روند مرورگرها (Browsers) هستند. کامپیوتر سرور نیز باید برنامه‌ی خاصی را اجرا کند که "وب‌سرور" (Web Server) نامیده می‌شود. امروزه انواع مختلفی از وب‌سرورها وجود دارد که از مشهورترین آنها می‌توان به Apache و Nginx و یا IIS اشاره کرد.

حال فرض کنید که می‌خواهید صفحه‌ای از یک وبسایت را توسط مرورگر باز کنید. حتماً تا به حال بارها این کار را انجام داده‌اید و احتمالاً این یکی از کارهای روزمره‌ی شما است. برای انجام این کار ابتدا یک مرورگر را باز می‌کنید (مثلاً Google Chrome). سپس در نوار آدرس (Address Bar)، آدرس صفحه‌ی مورد نظرتان را وارد کرده و دکمه‌ی Enter را می‌زنید. فرض کنید آدرس صفحه‌ی مورد نظرتان به شکل زیر است.


http://www.example.com/page1.html

می‌خواهیم ببینیم که پس از وارد کردن آدرس فوق در نوار آدرس و زدن دکمه‌ی Enter دقیقاً چه اتفاقی می‌افتد؟ و مرورگر چگونه این صفحه‌ی وب را از وبسایت مورد نظر درخواست کرده و نتیجه را به کاربر نمایش می‌دهد؟ خواهیم دید که برای انجام این کار توسط مرورگر، کارهای زیادی در پشت صحنه انجام می‌شود که کاربر متوجه آنها نمی‌شود. در واقع پروتکل HTTP ارتباط بین مرورگر و سرور را برقرار می‌کند. که در ادامه به بررسی چگونگی آن می‌پردازیم.

 

درخواست‌های HTTP

برای آغاز ارتباط بین یک کاربر و یک سرور، ابتدا باید یک درخواست HTTP یا HTTP Request از سمت کاربر به سرور ارسال شود. یک درخواست HTTP یک رشته‌ی متنی است که از اجزا مختلفی تشکیل می‌شود. قالب کلی یک درخواست HTTP به شکل زیر است.


[Method] [Path] [Version]
[Headers]

[Body]
در ادامه به بررسی هر یک از اجزاء تشکیل‌دهنده‌ی یک درخواست HTTP می‌پردازیم.  

Method

درخواست‌های HTTP را به روش‌های مختلفی می‌توان ارسال کرد. در بخش [Method] باید روش ارسال درخواست تعیین شود. در مجموع ۹ روش برای ارسال درخواست‌های HTTP وجود دارد. اما در این کتاب فقط از دو روش GET و POST استفاده خواهیم کرد. زیرا سایر روش‌ها کاربرد چندانی ندارند و بیش از ۹۹ درصد درخواست‌های HTTP با همین دو روش ارسال می‌شوند. تفاوت این دو روش نیز در ادامه توضیح داده خواهد شود.

 

Path

فرض کنید هدف از ارسال درخواست HTTP، دریافت یک صفحه‌ی وب به آدرس زیر باشد.


http://www.example.com/page1.html

این آدرس از ۳ بخش اصلی تشکیل شده است. بخش اول نوع پروتکل را مشخص می‌کند که همان http است. بخش دوم نام دامنه (Domain) و زیر دامنه (Subdomain) را مشخص می‌کند که برابر با www.example.com است. و بخش سوم نام و مسیر فایل را مشخص می‌کند که در این مثال برابر با /page1.html است. این بخش، همان چیزی است که باید به جای بخش [Path] در درخواست HTTP قرار داده شود.

 

Version

پروتکل HTTP دارای نسخه‌های متفاوتی است. در حال حاضر نسخه‌های 1.1 و 2 بیشترین کاربرد را دارند. در بخش [Version] از درخواست HTTP، باید نسخه‌ی مورد استفاده از پروتکل HTTP مشخص شود. البته تا جایی که به بحث ما مربوط می‌شود، این دو نسخه تفاوت خاصی با یکدیگر ندارند.

حال دوباره مثال قبل را در نظر بگیرید. برای باز کردن صفحه‌ی وبی که آدرس آن نشان داده شد. خط اول درخواست HTTP به شکل زیر خواهد بود.


GET /page1.html HTTP/1.1

معنی خط بالا این است که قصد داریم یک درخواست HTTP از نوع GET ارسال کنیم. تا صفحه‌ی وبی را که نام و مسیر آن در سرور برابر با /page1.html است را دریافت کنیم. همچنین در ارسال این درخواست از نسخه‌ی 1.1 پروتکل HTTP استفاده شده است.

همانطور که مشاهده می‌کنید، نام دامنه‌ی مورد نظر در درخواست فوق تعیین نشده است. واضح است که تا زمانی که دامنه‌ی مورد نظر تعیین نشود، امکان ارسال درخواست وجود ندارد. اما چگونه باید نام دامنه (و زیر دامنه) را مشخص کرد؟

 

Headers

در بخش [Headers] می‌توان داده‌های دیگری را به سرور ارسال کرد. این داده‌ها به صورت جفت‌های کلید-مقدار (Key-Value) ارسال می‌شوند. به هر یک از این جفت‌ها یک هدر (Header) گفته می‌شود و هر هدر را باید در یک خط مجزا قرار داد. تعدادی هدر استاندارد در پروتکل HTTP وجود دارد. یعنی در پروتکل HTTP معنی خاصی برای آنها در نظر گرفته شده است. اما در صورت نیاز می‌توان به هر تعداد هدر سفارشی نیز در درخواست‌های HTTP قرار داد. البته این کار خیلی مرسوم نیست و معمولاً فقط از هدرهای استاندارد استفاده می‌شود. دو نمونه از هدرهای استاندارد و پرکاربرد به شرح زیر هستند.

مجدداً آدرس زیر را در نظر بگیرید.


http://www.example.com/page1.html

حال می‌توانیم درخواست HTTP برای دریافت این صفحه‌ی وب را کامل‌تر کنیم. با استفاده از هدرهای Host و User-Agent می‌توان درخواست HTTP قبلی را به شکل زیر تکمیل کرد.


GET /page1.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0

همانطور که اشاره شد تنها هدری که حتماً باید در تمام درخواست‌های HTTP وجود داشته باشد، هدر Host است. در مثال فوق علاوه بر این هدر اجباری، از هدر اختیاری User-Agent نیز استفاده شده است. البته معمولاً چندین هدر دیگر نیز در هر درخواست HTTP به سرور ارسال می‌شود که فعلاً از آنها صرف‌نظر می‌کنیم. اما می‌توانید فهرست کاملی از این هدرها را به همراه کاربردشان اینجا مشاهده کنید.

 

Body

آخرین بخش از یک درخواست HTTP، بخش [Body] است. در این بخش می‌توان هر نوع داده‌ی اضافی را به سرور ارسال کرد. معمولاً داده‌هایی که توسط کاربر داخل فرم‌ها وارد می‌شوند، در بخش [Body] قرار گرفته و ارسال می‌شوند. لازم به ذکر است که بخش [Body] فقط در درخواست‌هایی که با روش POST ارسال می‌شوند به کار می‌رود و در درخواست‌های GET استفاده نمی‌شود. زیرا داده‌های موجود در فرم‌هایی که با روش GET ارسال می‌شوند، به صورت Query String به انتهای آدرس URL اضافه می‌شوند.

برای درک بهتر این موضوع فرم زیر را در نظر بگیرید. حتماً می‌دانید که در HTML با استفاده از صفت method می‌توان نحوه‌ی ارسال یک فرم را تعیین کرد. در واقع پس از کلیک کردن بر روی دکمه‌ی Submit، مرورگر بر اساس مقدار صفت method تصمیم می‌گیرد که فرم مورد نظر را با کدامیک از روش‌های GET یا POST ارسال کند. لازم به ذکر است که مقدار پیش‌فرض صفت method برابر با get است. در نتیجه تمام فرم‌هایی که صفت method برای آنها تعیین نشده است با روش GET ارسال می‌شوند.


<form method="get" action="http://www.example.com/page1.html">
	<input type="text" name="firstname" /><br />
	<input type="text" name="lastname" /><br />
	<button type="submit">Submit</button>
</form>

فرض کنید کاربر در فیلدهای این فرم به ترتیب مقادیر "Abbas" و "Moqaddam" را وارد کرده و دکمه‌ی Submit را می‌زند. در این صورت درخواست HTTP ارسال شده به سرور به صورت زیر خواهد بود. (از هدرهای اختیاری صرف‌نظر شده است)


GET /page1.html?firstname=Abbas&lastname=Moqaddam HTTP/1.1
Host: www.example.com

مشاهده می‌کنید که داده‌های فرم به صورت Query String به انتهای بخش [Path] اضافه شده‌اند. ضمناً این درخواست فاقد بخش [Body] است. اما اگر همین فرم را اصلاح کرده و مقدار صفت method را در تگ <form> برابر با post قرار دهید. و همان مقادیر قبلی را در فیلدهای فرم وارد کرده و دکمه‌ی Submit را بزنید. درخواستی که توسط مرورگر ایجاد شده و به سرور ارسال می‌شود به شکل زیر خواهد بود.


POST /page1.html HTTP/1.1
Host: www.example.com

firstname=Abbas&lastname=Moqaddam

این درخواست HTTP با درخواست قبلی چند تفاوت دارد.

پس در روش POST، داده‌های ارسالی به سرور باید در قسمت [Body] قرار داده شوند. توجه کنید که حتماً باید بین بخش [Headers] و بخش [Body] یک خط خالی وجود داشته باشد.

نکته : در صورتی که در فرم‌های HTML از فیلد file برای آپلود فایل به سرور استفاده شده باشد. روش ارسال فرم حتماً باید POST باشد. همچنین صفت enctype از تگ <form> حتماً باید برابر با "multipart/form-data" باشد. ضمناً مقدار صفت enctype در زمان ارسال درخواست HTTP در هدر Content-Type قرار می‌گیرد.

نکته : مزیت اصلی روش GET نسبت به روش POST سرعت بالای آن است. به همین دلیل اکثر درخواست‌های HTTP با روش GET ارسال می‌شوند. همچنین مزیت اصلی روش POST نسبت به روش GET، نداشتن محدودیت در حجم داده‌های ارسالی است. به همین دلیل برای ارسال داده‌های حجیم به سرور (مانند آپلود فایل) همیشه از روش POST استفاده می‌شود.

 

پاسخ‌های HTTP

پس از اینکه یک درخواست HTTP توسط یک سرور دریافت می‌شود. سرور با تفسیر اجزاء مختلف درخواست، پاسخ (Response) مناسب را برای کاربر ارسال می‌کند. پاسخ‌های HTTP در قالب زیر به کاربر ارسال می‌شوند.


[Version] [Status] [Reason]
[Headers]

[Body]

مشاهده می‌کنید که بخش‌هایی از این قالب، با قالب درخواست‌های HTTP مشابه است. در ادامه مفهوم هر یک از این بخش‌ها را تشریح می‌کنیم.

 

Version

دقیقاً همان کاربردی را دارد که در درخواست‌های HTTP دیدیم و نسخه‌ی مورد استفاده از پروتکل HTTP را مشخص می‌کند.

 

Status

پس از ارسال درخواست به یک سرور، وضعیت‌های مختلفی ممکن است به وجود آید. معمول‌ترین حالت این است که پاسخ کاربر بدون هیچ مشکلی برای او ارسال شود. اما ممکن است درخواست کاربر دارای اشکالاتی باشد که امکان پاسخگویی به آن وجود نداشته باشد. مثلاً ممکن است کاربر آدرسی را درخواست کرده باشد که در سرور وجود ندارد. یا صفحه‌ای را درخواست کرده باشد که اجازه‌ی دسترسی به آن صفحه را ندارد.

به ازای هر یک از حالت‌های ممکن در پاسخ‌های HTTP، یک کد عددی وجود دارد. در هر پاسخ HTTP با توجه به وضعیت رخ داده، کد وضعیت مورد نظر در بخش [Status] قرار داده می‌شود. این کد یک عدد ۳ رقمی بین ۱۰۰ تا ۵۹۹ است. البته بیشتر اعداد این بازه هیچ کاربردی ندارند و تنها حدود ۶۰ مورد از اعداد این بازه دارای معنی خاصی هستند. از بین این تعداد نیز، کمتر از ۱۰ مورد کاربرد عملی دارند و سایر موارد تقریباً هیچگاه مورد استفاده قرار نمی‌گیرند. تعدادی از مهمترین و پرکاربردترین کدهای وضعیت به همراه مفهوم هر یک به شرح زیر می‌باشند.

 

Reason

به ازای هر یک از کدهای عددی مورد استفاده در بخش [Status]، یک رشته‌ی متنی کوتاه نیز وجود دارد که توضیح مختصری در مورد وضعیت رخ داده، ارائه می‌دهد. این توضیح مختصر در بخش [Reason] قرار می‌گیرد. نمونه‌هایی از این توضیحات به همراه کد عددی هر یک به شرح زیر می‌باشند.

به عنوان مثال در صورتی که درخواست دریافت شده توسط سرور، بدون هیچ مشکلی پردازش شده و پاسخ آن ارسال شود. خط اول پاسخ HTTP به این صورت خواهد بود.


HTTP/1.1 200 OK

فهرست کامل کدهای وضعیت HTTP را به همراه توضیح هر یک می‌توانید اینجا مشاهده کنید.

 

Headers

دقیقاً مانند درخواست‌های HTTP، در پاسخ‌های HTTP نیز می‌توان داده‌هایی را به صورت جفت‌های کلید-مقدار در بخش [Headers] قرار داد و به کاربر ارسال کرد. در اینجا نیز می‌توان هم از هدرهای استاندارد و هم از هدرهای سفارشی استفاده کرد. البته نوع هدرهایی که در پاسخ‌های HTTP به کار می‌روند با هدرهایی که در درخواست‌های HTTP به کار می‌روند متفاوت بوده و کاربردهای متفاوتی دارند. برخی از هدرهای پرکاربرد در پاسخ‌های HTTP به شرح زیر می‌باشند.

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

 

Body

مهمترین بخش از یک پاسخ HTTP بخش [Body] است. در واقع محتوایی که باید به عنوان نتیجه به کاربر ارسال شود در این قسمت قرار می‌گیرد. این محتوا در بسیاری از موارد در یک قالب متنی مانند HTML ، CSS ، JSON و ... ارسال می‌شود. اما می‌تواند در قالب‌های باینری مانند PDF ، PNG و ... نیز باشد.

 

یک مثال کامل

حال که با بخش‌های مختلف درخواست‌ها و پاسخ‌های HTTP آشنا شدیم. می‌توانیم یک مثال را به شکل کامل بررسی کنیم. فرض کنید قصد داریم صفحه‌ی وب موجود در آدرس زیر را در مرورگر مشاهده کنیم.


http://www.example.com/hello.html

همچنین فرض کنید محتویات فایل hello.html که در سرور ذخیره شده است به شکل زیر می‌باشد.


<html>
<head>
	<title>Hello Page</title>
</head>
<body>
	<h1>Hello World!</h1>
</body>
</html>

در این صورت با وارد کردن آدرس صفحه‌ی وب در نوار آدرس و زدن دکمه‌ی Enter، یک درخواست HTTP به شکل زیر توسط مرورگر ایجاد شده و به سرور ارسال می‌شود. البته در عمل معمولاً تعداد هدرهای اختیاری بیشتر هستند. اما در این مثال فقط از هدر User-Agent استفاده شده است. و فرض شده است که درخواست با نسخه‌ی ۶۸ از مرورگر Firefox ارسال شده است.


GET /hello.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0

توجه کنید که تمام درخواست‌هایی که با وارد کردن آدرس در نوار آدرس مرورگر و زدن دکمه‌ی Enter ایجاد می‌شوند، با روش GET ارسال می‌شوند. اما درخواست‌هایی که در تعامل با صفحه‌ی وب ایجاد می‌شوند (مثلاً ارسال فرم‌ها) ممکن است با روش POST نیز ارسال شوند.

پس از دریافت این درخواست توسط سرور، در صورتی که فایل مورد نظر در سرور وجود داشته باشد. پاسخ زیر به کاربر (مرورگر) ارسال می‌شود. البته تعداد هدرهای پاسخ نیز معمولاً بیشتر از این موارد است. اما فعلاً برای سادگی فقط از ۳ هدر استفاده شده است.


HTTP/1.1 200 OK
Date: Mon, 19 Aug 2019 10:42:14 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 101

<html>
<head>
	<title>Hello Page</title>
</head>
<body>
	<h1>Hello World!</h1>
</body>
</html>

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

خط دوم نشان می‌دهد که پاسخ در چه زمانی توسط سرور تولید شده است. این زمان همیشه در قالب زمان جهانی (GMT) می‌باشد. خط سوم نشان می‌دهد که محتوای ارسال شده به کاربر یک سند متنی از نوع HTML است. و نوع رمزگذاری آن نیز UTF-8 است. خط چهارم نیز نشان‌دهنده‌ی طول رشته‌ای است که به عنوان بدنه‌ی پاسخ (Body) به کاربر ارسال شده است.

سپس یک خط خالی مشاهده می‌کنید که بخش [Headers] را از بخش [Body] جدا می‌کند. و پس از آن بدنه‌ی پاسخ را می‌بینید. بدنه‌ی پاسخ همان چیزی است که مرورگر در نهایت به کاربر نمایش می‌دهد. البته ممکن است این بخش قالب متنی نداشته باشد و مثلاً ممکن است یک فایل JPG باشد. به هر حال محتوای اصلی که توسط مرورگر درخواست شده است در قسمت [Body] به مرورگر ارسال می‌شود.

توجه کنید که مثال بالا یک مثال بسیار ساده است. امروزه به ندرت می‌توان صفحات وبی را یافت که در قالب یک فایل HTML در سرور ذخیره شده باشند. امروزه بیشتر صفحات وب به صورت پویا (Dynamic) تولید می‌شوند. یعنی پس از اینکه درخواستی به سرور ارسال می‌شود. سرور با پردازش درخواست، یک سند HTML را تولید می‌کند و به عنوان پاسخ به کاربر ارسال می‌کند.

امروزه از زبان‌های برنامه‌نویسی مختلفی برای تولید صفحات وب پویا در سرورها استفاده می‌شود. زبان PHP در حال حاضر پرکاربردترین زبان برای این منظور است. و از دیگر زبان‌ها و تکنولوژی‌های مورد استفاده در این زمینه می‌توان به Node.js یا Python یا ASP.NET اشاره کرد.

حال که تا حدودی با نحوه‌ی عملکرد پروتکل HTTP آشنا شدیم. می‌توانیم به موضوع اصلی این فصل، یعنی مبحث Ajax وارد شویم. در بخش بعدی بحث در رابطه با این موضوع جذاب را آغاز خواهیم کرد.