چطوری یه وب سرور بسازیم ؟

.ریچارد فاینمن میگه "When I cannot create it, I do not understand it" ... اگر نتونم چیزی رو بسازم پس نمیتونم بفهممش مدتها به عنوان برنامه نویس وب کلمه http رو شنیدم و باهاش کار کردم اما به طور دقیق نمیدونستم چی کار میکنه چون همیشه از یه فریمورک یا لایبرری استفاده میکردم که این لایه رو برای من می پوشوند . چند وقت پیش شروع به خوندن کتاب Rust Programming Language کردم . پروژه آخر این کتاب نوشتن یک وب سرور اول به صورت Single Thread و بعد به صورت Multi Thread بود که باعث شد ایده ی این پست به ذهنم بخوره .یه وب سرور یه طور خلاصه کاری که میکنه اینه که روی یه پورت خاص که ما بهش میگیم ( عموما 80 یا 81 ) گوش میده و منتظر یه کانکشن میمونه بعد از اینکه 3-step handshake انجام شد حالا کلاینت ما یه درخواست http میفرسته و منتظر جواب میمونه و از اینجاست که کار ما شروع میشه . خب برای درک بهتر شروع کنیم به نوشتن کد .اول یه پروژه جدید با Rust ایجاد میکنیم ...cargo new webserver --binاینجا با کمک cargo ( که در واقع ابزار مدیریت بسته و پروژه Rust هست ) یه پروژه به نام webserver درست کردیم .این پروژه ای هست که ساخته شده ... نسبتا هیچی نداره نه ؟؟خب اولین کاری که باید بکنیم اینه که روی port ی که بهمون میگن منتظر یه کانکشن tcp بمونیم . فعلا این port رو ثابت بگیریم تا بعدا به عنوان ورودی از کاربر بگیریمش .برای اینکه روی یه port گوش بدیم از لایبرری استاندارد Rust و ماژول TcpListener استفاده میکنیم .let listener = TcpListener::bind("127.0.0.1:8080").unwrap();خب با این خط کد ما یه TcpListener درست کردیم که روی لوکال هاست و پورت 8080 گوش میده و منتظر یه کانکشن Tcp میمونه .خب حالا فرض کنیم یه کانکشن اومد چی کارش کنیم ؟for stream in listener.incoming() { let mut stream = stream.unwrap(); خب الان با اومدن هر ریکویست ما اون رو دریافت میکنیم و میتونیم باهاش هرکاری بخوایم بکنیم !اول بیاین بفهمیم یه ریکویست http به صورت خام چه شکلیه ؟ let mut buffer = [0; 512]; stream.read(&mut buffer).unwrap(); let buffer = String::from_utf8_lossy(&buffer[..]) println!("{}", buffer)خب در اینجا ما یه آرایه 512 تایی از اعداد درست میکنیم که در واقع ریکویست ما قبل از تبدیل شدن به رشته توی اون قرار بگیره و بعد ریکویست رو میخونیم و توی این آرایه قرار میدیم و در مرحله بعد اون رو تبدیل به یک String میکنیم .خب حالا وقتی Println انجام میشه یه همچین خروجی خواهیم داشت ...GET / HTTP/1.1Host: localhost:7878Connection: keep-aliveCache-Control: max-age=0Upgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3Accept-Encoding: gzip, deflate, brAccept-Language: en-GB,en;q=0.9,en-US;q=0.8,fa;q=0.7,la;q=0.6,ar;q=0.5Cookie: csrftoken=SLaqNbhHwKU** یه نکته در مورد سینتکس اگر نا آشناس اینه که Rust از یک سیستم جدید در کامپایلرش استفاده میکنه و برای اینکه بتونه کد های مدیریت حافظه رو تولید کنه نیاز داره با بهش کمک کنیم به همین خاطر بعضی وقتا کدای Rust یه خرده عجیب به نظر میان **خب حالا بیایم به ازای هر ریکویست یه جواب ثابت رو بفرستیم .let response = "HTTP/1.1 200 OK\r\n\r\nHelloAgain"; stream.write(response.as_bytes()).unwrap(); stream.flush().unwrap();با اجرای کد و رفتن به صفحه localhost:7878 این صفحه رو میبینید HelloAgain!!!!خب تونستیم که اولین صفحه وب رو بدون استفاده از هیچ لایبرری و صرفا Tcp نشون بدیم مرحله بعد چیه ؟اینکه به جای متن ساده یه صفحه html رو نشون بدیم .برای اینکار باید اول یک فایل html رو بخونیم let contents = fs::read_to_string("hello.html").unwrap(); let response = format!("HTTP/1.1 200 OK\r\n\r\n{}", contents);برای اینکار کافیه کد های بالا رو تغییرات بدیم تا جواب ریکویست شامل متن کامل یک فایل مثلا hello.html باشه . حالا فرض کنیم شما html رو مثلا به این شکل مینویسید <html lang="en"> <head> <meta charset="utf-8"> <title>Hello!</title> </head> <body> <h1>Hello</h1> <p>Salam man be to yare ghadimi</p> </body> </html> این فایل رو در روت پروژه یعنی جایی که فایل های .toml وجود دارن قرار میدیم .خب بازم اگر برنامه مون رو اجرا کنیم اینبار صفحه html بالا رو بهمون نشون میده عالی شد نه ؟البته این برنامه فقط برای این خوبه که بفهمیم http اونقدرا هم پیچیده نیست و چطوری داره کار میکنه وگرنه برنامه ما تا یه وب سرور مثل IIS یا Apache یا Nginx خیلی خیلی فاصله داره .مثل همیشه نظراتتون خوش حالم میکنه .