خانه / آموزش‌ها / آموزش ساخت وب‌سرور HTTP در پایتون با ماژول http.server

آموزش ساخت وب‌سرور HTTP در پایتون با ماژول http.server

🐍 HomeOfPython
|
📅 1404/10/16

سطح مقدماتی (Beginner Level)

ماژول http.server در کتابخانه استاندارد پایتون، ابزاری قدرتمند اما ساده برای راه‌اندازی سرورهای وب است. این ماژول به شما اجازه می‌دهد بدون نیاز به نصب هیچ ابزار اضافی (مانند Flask یا Django)، سیستم خود را به یک وب‌سرور تبدیل کنید. این ابزار برای اشتراک‌گذاری فایل‌ها در شبکه محلی یا تست کردن فایل‌های HTML/JS بسیار کاربردی است.

ساختار کلی سرور

۱. اجرای سریع از طریق خط فرمان (CLI)

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

مثال اول: اجرای سرور پیش‌فرض

دستور زیر سرور را روی پورت پیش‌فرض ۸۰۰۰ اجرا می‌کند:

python
# این دستور باید در ترمینال اجرا شود
# python -m http.server

مثال دوم: تغییر پورت

اگر پورت ۸۰۰۰ اشغال است، می‌توانید شماره پورت را مشخص کنید:

python
# اجرای سرور روی پورت 9000
# python -m http.server 9000

۲. ساخت سرور با اسکریپت پایتون

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

  1. HTTPServer: مسئول شنود (Listen) روی پورت و دریافت درخواست‌ها.
  2. SimpleHTTPRequestHandler: مسئول پاسخ‌دهی به درخواست‌ها (بیشتر برای سرو کردن فایل‌های استاتیک).

مثال اول: اسکریپت پایه سرور

این کد دقیقاً همان کاری را می‌کند که دستور خط فرمان انجام می‌داد، اما اکنون درون کد کنترل داریم.

python
# Static code (Blocks execution indefinitely)
import http.server
import socketserver

PORT = 8000

# تعریف هندلر (مدیریت‌کننده درخواست‌ها)
handler = http.server.SimpleHTTPRequestHandler

# ایجاد سرور
with socketserver.TCPServer(("", PORT), handler) as httpd:
    print(f"Server started at http://localhost:{PORT}")
    # سرور را برای همیشه روشن نگه دار
    httpd.serve_forever()

مثال دوم: تغییر دایرکتوری سرویس‌دهی

گاهی اوقات می‌خواهید فایل‌های پوشه‌ای غیر از پوشه فعلی را نمایش دهید.

python
# Static code (Configuration snippet)
import http.server
import socketserver
from functools import partial

PORT = 8080
DIRECTORY = "public_html"  # فرض کنید این پوشه وجود دارد

handler = partial(http.server.SimpleHTTPRequestHandler, directory=DIRECTORY)

# ادامه کد برای اجرای سرور...
# with socketserver.TCPServer(("", PORT), handler) as httpd: ...

۳. درک مفهوم Request و Response ساده

وقتی مرورگر آدرسی را باز می‌کند، یک Request ارسال می‌کند. سرور پایتون این درخواست را می‌گیرد و اگر از SimpleHTTPRequestHandler استفاده کنید، به دنبال فایلی با آن نام می‌گردد و محتویات آن را به عنوان Response برمی‌گرداند.

در اینجا یک مثال تعاملی ساده از تولید محتوای HTML که سرور باید بفرستد را می‌بینیم:

Python

سطح پیشرفته (Professional Level)

در سطح حرفه‌ای، استفاده از SimpleHTTPRequestHandler کافی نیست چون فقط فایل سرو می‌کند. برای ساخت API یا پردازش داده‌ها، باید کلاس BaseHTTPRequestHandler را به ارث ببریم و متدها را بازنویسی (Override) کنیم.

۱. سفارشی‌سازی هندلر (Custom Request Handler)

برای کنترل دقیق روی درخواست‌ها، باید کلاسی بسازید و متدهای do_GET یا do_POST را پیاده‌سازی کنید.

نکات کلیدی:

  • باید هدرها (Headers) را خودتان ارسال کنید.
  • پاسخ باید به صورت بایت (bytes) باشد، نه رشته (string).
  • کد وضعیت HTTP (مثل 200 یا 404) باید صریحاً اعلام شود.

مثال اول: پاسخ Hello World سفارشی

این کد به هر درخواستی پاسخ متنی ساده می‌دهد.

python
# Static code (Server definition)
from http.server import BaseHTTPRequestHandler, HTTPServer

class MyHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        # 1. ارسال کد وضعیت 200 (موفق)
        self.send_response(200)
        
        # 2. ارسال هدرها
        self.send_header("Content-type", "text/plain; charset=utf-8")
        self.end_headers()
        
        # 3. نوشتن بدنه پاسخ (باید بایت باشد)
        message = "سلام از طرف سرور پایتون!".encode("utf-8")
        self.wfile.write(message)

# برای اجرا:
# server = HTTPServer(('localhost', 8080), MyHandler)
# server.serve_forever()

۲. مدیریت مسیرها (Routing) و Query Parameters

یک سرور واقعی باید بسته به آدرس (self.path) کارهای متفاوتی انجام دهد. همچنین باید بتواند پارامترهای URL را بخواند.

نمودار مسیریابی

مثال اول: پارس کردن URL

برای جدا کردن مسیر از پارامترها از urllib.parse استفاده می‌کنیم.

Python

مثال دوم: پیاده‌سازی Routing ساده در سرور

این یک الگوی رایج برای مدیریت صفحات مختلف است.

python
# Static code (Routing logic inside handler)
from http.server import BaseHTTPRequestHandler
from urllib.parse import urlparse

class RoutingHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        # تجزیه آدرس
        parsed_path = urlparse(self.path)
        path = parsed_path.path
        
        if path == "/":
            self.send_response(200)
            self.end_headers()
            self.wfile.write(b"Home Page")
            
        elif path == "/about":
            self.send_response(200)
            self.end_headers()
            self.wfile.write(b"About Us Page")
            
        elif path == "/api/data":
            self.send_response(200)
            self.send_header("Content-type", "application/json")
            self.end_headers()
            self.wfile.write(b'{"status": "ok", "value": 100}')
            
        else:
            self.send_response(404)
            self.end_headers()
            self.wfile.write(b"404 Not Found")

۳. مدیریت درخواست‌های POST

در متد do_POST، شما باید داده‌های ارسال شده توسط کاربر (Body) را بخوانید. نکته مهم این است که باید طول محتوا (Content-Length) را از هدرها بخوانید تا بدانید چقدر از rfile (فایل خواندنی) بخوانید.

مثال فنی: خواندن JSON از درخواست POST

python
# Static code (POST handling snippet)
import json

class APIHandler(BaseHTTPRequestHandler):
    def do_POST(self):
        # 1. پیدا کردن طول محتوا
        content_length = int(self.headers.get('Content-Length', 0))
        
        # 2. خواندن داده‌ها
        post_data = self.rfile.read(content_length)
        
        # 3. پردازش داده‌ها (مثلا دیکد کردن JSON)
        try:
            data = json.loads(post_data.decode('utf-8'))
            response_msg = f"Received data for user: {data.get('username')}"
            status = 200
        except json.JSONDecodeError:
            response_msg = "Invalid JSON"
            status = 400

        # 4. ارسال پاسخ
        self.send_response(status)
        self.end_headers()
        self.wfile.write(response_msg.encode('utf-8'))

۴. چالش‌های همزمانی (Concurrency)

به طور پیش‌فرض، HTTPServer تک‌رشته‌ای (Single-threaded) است. یعنی اگر یک درخواست ۱۰ ثانیه طول بکشد، بقیه درخواست‌ها صف می‌شوند. برای حل این مشکل در محیط‌های توسعه، از ThreadingHTTPServer استفاده می‌کنیم.

مثال: سرور چندنخی (Multi-threaded)

python
# Static code (Threading mixin)
from http.server import HTTPServer, SimpleHTTPRequestHandler
from socketserver import ThreadingMixIn

# روش اول: استفاده از کلاس آماده در پایتون ۳.۷ به بعد
from http.server import ThreadingHTTPServer

# روش دوم: ساخت کلاس ترکیبی (برای نسخه‌های قدیمی‌تر یا کنترل بیشتر)
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
    """Handle requests in a separate thread."""
    daemon_threads = True

# نحوه اجرا
# server = ThreadingHTTPServer(('0.0.0.0', 8000), SimpleHTTPRequestHandler)
# server.serve_forever()

نکات امنیتی و عملکردی

  1. هرگز در پروداکشن استفاده نکنید: ماژول http.server برای امنیت و پرفورمنس بالا طراحی نشده است. برای محیط واقعی از Gunicorn یا Uvicorn در کنار فریم‌ورک‌هایی مثل Flask یا FastAPI استفاده کنید.
  2. Directory Traversal: کلاس SimpleHTTPRequestHandler سعی می‌کند جلوی دسترسی به فایل‌های سیستمی بالاتر از ریشه را بگیرد، اما همچنان آسیب‌پذیر است.
  3. Blocking Operations: در کد هندلر از عملیات سنگین CPU یا I/O طولانی خودداری کنید چون کل ترد را مشغول می‌کند.