آیا می خواهید یک توسعه دهنده فول استک بهتری باشید؟ اصول وب و HTTP را بشناسید – انجمن DEV

ساخت سرور HTTP از ابتدا با Nodejs و Typescript
این روزها می توانید با کمک ابزارهای مدرن، یک سرور کاملاً کاربردی را در عرض چند دقیقه بچرخانید.
پس چرا کسی زحمت ساخت یک سرور HTTP را از ابتدا بدهد؟
برای من، این آزمایشی است که برای سه ماه آینده اجرا می کنم: تسلط بر اصول و انجام کار عمیق.
همچنین یک حقیقت ساده در مورد کار در فناوری وجود دارد: اگر من در مورد پیشرفت به عنوان یک مهندس تمام پشته جدی هستم، مطمئناً باید اصول اولیه را درک کنم، نه فقط با انتزاعات و چارچوب ها کار کنم. بهعلاوه، نوعی وضوح وجود دارد که از مشاهده نحوه تناسب همه قطعات با هم به دست میآید: از TCP تا درخواستهای مسیریابی. فراتر از چالش فنی، در نهایت رضایت ساده از ساختن چیزی سرگرم کننده و ملموس وجود دارد.
چرا سرور HTTP خودم را بسازم؟
-
یادگیری اصول اولیه: من فکر می کنم که درک نحوه عملکرد سرورهای HTTP در سطح گرانول کمک می کند تا کار من به عنوان یک مهندس تمام پشته محکم شود. استقرار سرور با استفاده از کتابخانه ها یا خدمات از پیش ساخته شده یک چیز است. دانستن نحوه رسیدگی به درخواستها و پاسخها چیز دیگری است.
-
ساختن چیزی ملموس: رضایت بی نظیری در ساخت ابزاری وجود دارد که در خدمت هدفی واقعی باشد، مانند ارائه یک صفحه وب که یک صفحه html را با تصاویر و متن ارائه می دهد، تنها با استفاده از اصول اولیه. این یک راه قدرتمند برای تعمیق مهارت های فنی شما در حین کار بر روی چیزی لذت بخش است.
اول، اصول اولیه. سرور HTTP چیست؟
در هسته خود، سرور HTTP برنامهای است که به درخواستهای HTTP ورودی از مشتریان (مانند یک مرورگر یا مصرفکننده API) گوش میدهد، آن درخواستها را پردازش میکند و پاسخها را ارسال میکند. از TCP (پروتکل کنترل انتقال) برای ایجاد یک اتصال قابل اعتماد، اطمینان از اینکه بسته های داده منظم و دست نخورده می رسند.
TCP در مقابل UDP
من کوهنوردی می کنم و یک قیاس به من رسید تا پروتکل های TCP و UDP را متمایز کنم.
-
TCP: به کوهنوردانی که روی یک یخچال طناب زده اند فکر کنید. همه با امنیت و نظم به قله می رسند. این TCP است: اطمینان از اینکه داده ها (کلیبرها) به ترتیب و دست نخورده می رسند.
-
UDP: UDP مانند کوهنوردانی است که به صورت انفرادی بدون طناب صعود می کنند. معمولاً سریعتر است، اما هیچ تضمینی وجود ندارد که همه به آنجا برسند. سریع، اما کمتر قابل اعتماد.
ویژگی های کلیدی سرور HTTP من
-
رسیدگی به درخواست های GET: با یک «سلام، دنیا!» ساده پاسخ میدهد. برای درخواست های اساسی
-
پشتیبانی از پروتکل HTTP/1.1: ملزومات این پروتکل پرکاربرد را پیاده سازی می کند.
-
ارائه فایل های استاتیک: فایل های تصویری ذخیره شده در سرور را واکشی و تحویل می دهد
public/images/
دایرکتوری -
مسیریابی: با استفاده از مسیریابی اولیه را پیاده سازی می کند
if-else
عباراتی برای رسیدگی به نقاط پایانی مختلف مانند/api
یا/
. -
رسیدگی به خطا: از پاسخهای مناسب برای درخواستهای نادرست، مانند بازگشت اطمینان میدهد
400 Bad Request
برای خطاهای تجزیه
چیزی که یاد گرفتم
سوکت سوکت سوکت
سوکت ها نحوه صحبت سرورها و کلاینت ها با یکدیگر هستند. قبل از این پروژه، من در مورد سوکت ها همانطور که بیشتر ما در مورد ماشین ها می دانیم، پدال گاز را فشار می دهیم و ماشین حرکت می کند، می دانستم. این جادو است! اکنون، من دیدم که چگونه هیچ چیز جادویی در مورد آن وجود ندارد، به خصوص زمانی که من آن را نوشتم createDataHandler
تابع
createDataHandler
تابعی است که تکه های داده TCP ورودی را برای درخواست های HTTP مدیریت می کند. یک بافر را حفظ می کند: رشته ای که داده ها را از تکه های ورودی جمع می کند.
- برای هر قطعه، داده های باینری را به یک رشته تبدیل می کند و آن را به بافر اضافه می کند.
- سپس بررسی می کند که آیا بافر حاوی آن است یا خیر
\r\n\r\n
، که پایان هدرهای HTTP را نشان می دهد. -
اگر نشانگر را پیدا کند، درخواست را پردازش کرده و پاسخی را ارسال می کند.
این به من دید واضحی از نحوه عملکرد HTTP در پایین ترین سطح داد: هر درخواست ورودی به عنوان داده خام شروع می شود و سرور باید آن را به چیزی قابل استفاده تجزیه کند. سوکت ها طناب های نامرئی هستند که این تکه ها را به جلو و عقب می کشند و
createDataHandler
تابع واسطه ای است که چیزی را از داده ها معقول می کند.نوشتن این تابع به من نشان داد که قبل از اینکه یک سرور حتی شروع به پاسخ دادن به یک درخواست کند چقدر اتفاق می افتد. همچنین به من یاد داد که چقدر چیزها می توانند شکننده باشند. اگر بافر به درستی مدیریت نشود، به عنوان مثال، با عدم شناسایی انتهای هدرها، سرور فقط خراب می شود. اینها چیزهایی هستند که من هرگز هنگام استفاده از فریمورک ها از آنها قدردانی نکرده ام.
مسیریابی به این سادگی نیست
مسیریابی دستی چالش برانگیزتر از آن چیزی بود که من انتظار داشتم، به خصوص در مورد مدیریت انواع محتوا. چارچوبهایی مانند Express.js این کار را بسیار آسان میکنند، اما در پشت صحنه، اتفاقات زیادی میافتد:
-
مدیریت نوع محتوا: سرور من فقط از JSON برای بدنه های درخواست پشتیبانی می کرد و باید نوع MIME صحیح را به صورت دستی برای پاسخ ها تنظیم می کردم. افزودن پشتیبانی برای انواع محتوای بیشتر، مانند
multipart/form-data
برای آپلود فایل یاapplication/x-www-form-urlencoded
برای ارسال فرم، به منطق تجزیه اضافی نیاز دارد. -
مدیریت سرصفحه ها: در یک تنظیمات اولیه، شما مسئول تنظیم هدرهای ضروری مانند
Content-Type
وContent-Length
. این مقادیر را اشتباه بگیرید، و مشتری یا پاسخ را نمیفهمد یا منتظر اطلاعات بیشتر میماند. این همان اتفاقی بود که سرور من تصاویر/jpeg نوع داده را انتخاب نکرد. -
تجزیه فرمت های بدنه: هر نوع محتوا منطق خاص خود را می خواهد. به عنوان مثال، بدنه های JSON باید در اشیاء جاوا اسکریپت تجزیه شوند، در حالی که
multipart/form-data
نیاز به مدیریت جریان های فایل دارد. این پیچیدگی با اضافه کردن پشتیبانی برای فرمت های بیشتر به سرعت افزایش می یابد.Express.js بیشتر این موارد را خودکار می کند. را تشخیص می دهد
Content-Type
هدر، بدنه را بر اساس آن تجزیه می کند و نوع MIME مناسب را برای پاسخ ها تنظیم می کند. انجام این کار به صورت دستی به من قدردانی عمیقی از میزان صرفه جویی در چارچوب های کاری داد.
در حالی که مسیریابی دستی من برای موارد اساسی کار می کند، واضح است که مقیاس کردن آن برای رسیدگی به سناریوهای پیچیده تر به تلاش بیشتری نیاز دارد.
همیشه سعی کنید نرم افزار خود را خراب کنید
نرم افزار به ندرت در اولین بار کامل می شود، یک راه عالی برای آزمایش سرور من این بود که اجرا کنم:
hey -n 1000 -m GET http://localhost:8080/
بوم. سرویس خرابی ها پس از 800 درخواست.
این دستور 1000 درخواست GET را به طور همزمان ارسال می کند و استرس دنیای واقعی را شبیه سازی می کند. این یک مسئله مهم را آشکار کرد: من به درستی با تایم اوت های سوکت کار نمی کردم. سرور من هنگ می کرد زیرا سوکت های بیکار را نمی بست.
برای رفع این مشکل، من تصمیم گرفتم پس از هر درخواست، سوکت را ببندم، که برای محدوده این پروژه عالی عمل می کند. اما اگر بخواهم از اتصالات مداوم پشتیبانی کنم، باید از تماس خودداری کنم socket.end()
و منطق اضافی را پیاده سازی کنید. در حال حاضر، بستن اتصال پس از هر درخواست ساده ترین و ایمن ترین روش است. تمیز، قابل پیش بینی است و از نشت منابع جلوگیری می کند.
بعد چه می شود؟
چیزهای بیشتری وجود دارد که می توانم به این سرور اضافه کنم، به همین دلیل است که شروع یک پروژه از صفر بسیار خوب است. من میتوانم هر لقمهای از آن را در اختیار داشته باشم و میتوانم در صورت نیاز/کنجکاوی به آن اضافه کنم.
- HTTPS: در حال حاضر، HTTP ساده است. افزودن SSL/TLS آن را ایمن می کند.
- وب سوکت ها: برای ارتباط بیدرنگ، مانند برنامههای چت.
- احراز هویت: ورود کاربران و جلسات را مدیریت کنید.
- مسیریابی بهتر: برای انواع محتوای بیشتر پشتیبانی اضافه کنید و انواع MIME را به صورت پویا بر اساس پسوند فایل یا درخواست محتوا تنظیم کنید.
- ذخیره سازی: با ذخیره داده های درخواستی مکرر عملکرد را بهبود بخشید.
چرا مهم است – فرا یادگیری
نوشتن یک سرور HTTP از ابتدا راهی برای تعمیق درک اصلی من از وب و سرورها بود. برای اینکه همه چیز را بدیهی ندانم و مهارت های مهندسی خود را به عنوان یک توسعه دهنده فول استک بهبود بخشم. همچنین جالب بود که ببینم چگونه محدودیت ها (استفاده نکردن از چارچوبی مانند Express) شما را مجبور می کند انتقادی فکر کنید و مشکلات را خلاقانه حل کنید، که البته درک من را عمیق تر می کند. و لذتی منحصر به فرد در ساختن چیزی کاربردی از ابتدا وجود دارد! 😃
می توانید پروژه را در GitHub بررسی کنید!