Let's Go بهبودهای سرور و امنیت › زمان‌های قطع اتصال
قبلی · فهرست · بعدی
فصل 9.6.

زمان‌های قطع اتصال (Connection Timeouts)

در این بخش، نحوه پیکربندی زمان‌های قطع اتصال (Connection Timeouts) را بررسی می‌کنیم. این شامل محدودیت‌های زمانی (Time Limits) برای خواندن (Reading)، نوشتن (Writing) و اتصال (Connection) می‌شود.

برای شروع، بیایید تنظیمات زمان‌سنج (Timer Settings) را در پیکربندی سرور (Server Configuration) خود اضافه کنیم:

File: cmd/web/main.go
package main

...

func main() {
    ...
    
    tlsConfig := &tls.Config{
        CurvePreferences: []tls.CurveID{tls.X25519, tls.CurveP256},
    }

    srv := &http.Server{
        Addr:      *addr,
        Handler:   app.routes(),
        ErrorLog:  slog.NewLogLogger(logger.Handler(), slog.LevelError),
        TLSConfig: tlsConfig,
        // Add Idle, Read and Write timeouts to the server.
        IdleTimeout:  time.Minute,
        ReadTimeout:  5 * time.Second,
        WriteTimeout: 10 * time.Second,
    }

    logger.Info("starting server", "addr", srv.Addr)

    err = srv.ListenAndServeTLS("./tls/cert.pem", "./tls/key.pem")
    logger.Error(err.Error())
    os.Exit(1)
}

...

هر سه این زمان‌های قطع اتصال — IdleTimeout, ReadTimeout و WriteTimeout — تنظیمات سراسری سرور هستند که بر روی اتصال زیرین عمل می‌کنند و به همه درخواست‌ها بدون توجه به هندلر یا URL آنها اعمال می‌شوند.

تنظیم IdleTimeout

به طور پیش‌فرض، Go اتصالات پایدار را بر روی همه اتصالات پذیرفته شده فعال می‌کند. این به کاهش تأخیر (به ویژه برای اتصالات HTTPS) کمک می‌کند زیرا یک کلاینت می‌تواند از همان اتصال برای چندین درخواست بدون نیاز به تکرار دست‌دهی TLS استفاده کند.

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

هیچ راهی برای افزایش این پیش‌فرض وجود ندارد (مگر اینکه خودتان یک net.Listener بنویسید)، اما می‌توانید آن را از طریق تنظیم IdleTimeout کاهش دهید. در مورد ما، IdleTimeout را به 1 دقیقه تنظیم کرده‌ایم، به این معنی که همه اتصالات پایدار به طور خودکار پس از 1 دقیقه عدم فعالیت بسته می‌شوند.

تنظیم ReadTimeout

در کد ما، تنظیم ReadTimeout را به 5 ثانیه تنظیم کرده‌ایم. این به این معنی است که اگر هدرها یا بدنه درخواست هنوز 5 ثانیه پس از پذیرش درخواست در حال خواندن باشند، Go اتصال زیرین را می‌بندد. از آنجا که این یک بسته شدن 'سخت' بر روی اتصال است، کاربر هیچ پاسخی HTTP(S) دریافت نخواهد کرد.

تنظیم یک دوره کوتاه ReadTimeout به کاهش خطر از حملات کلاینت‌های کند — مانند Slowloris — که می‌توانند در غیر این صورت یک اتصال را به طور نامحدود با ارسال درخواست‌های ناقص و ناتمام HTTP(S) باز نگه دارند، کمک می‌کند.

تنظیم WriteTimeout

تنظیم WriteTimeout اتصال زیرین را می‌بندد اگر سرور ما تلاش کند پس از یک دوره معین (در کد ما، 10 ثانیه) به اتصال بنویسد. اما این بسته به پروتکل مورد استفاده کمی متفاوت عمل می‌کند.

مهم است که به خاطر داشته باشید که نوشتن‌هایی که توسط یک هندلر انجام می‌شود، بافر شده و به عنوان یک واحد زمانی که هندلر بازمی‌گردد به اتصال نوشته می‌شوند. بنابراین، ایده WriteTimeout به طور کلی جلوگیری از هندلرهای طولانی‌مدت نیست، بلکه جلوگیری از این است که داده‌هایی که هندلر بازمی‌گرداند، بیش از حد طول بکشد تا نوشته شود.


اطلاعات اضافی

تنظیم ReadHeaderTimeout

http.Server همچنین یک تنظیم ReadHeaderTimeout ارائه می‌دهد که ما در برنامه خود از آن استفاده نکرده‌ایم. این به روشی مشابه ReadTimeout عمل می‌کند، به جز اینکه به خواندن هدرهای HTTP(S) اعمال می‌شود. بنابراین، اگر ReadHeaderTimeout را به 3 ثانیه تنظیم کنید، یک اتصال بسته خواهد شد اگر هدرهای درخواست هنوز 3 ثانیه پس از پذیرش درخواست در حال خواندن باشند. با این حال، خواندن بدنه درخواست می‌تواند پس از گذشت 3 ثانیه ادامه یابد، بدون اینکه اتصال بسته شود.

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

برای برنامه وب Snippetbox ما هیچ عملیاتی وجود ندارد که نیاز به زمان‌های قطع اتصال خواندن در مسیرهای مختلف داشته باشد — خواندن هدرها و بدنه‌های درخواست برای همه مسیرهای ما باید به راحتی در 5 ثانیه کامل شود، بنابراین ما به استفاده از ReadTimeout ادامه خواهیم داد.

تنظیم MaxHeaderBytes

http.Server شامل یک فیلد MaxHeaderBytes است که می‌توانید از آن برای کنترل حداکثر تعداد بایتی که سرور هنگام تجزیه هدرهای درخواست می‌خواند، استفاده کنید. به طور پیش‌فرض، Go حداکثر طول هدر 1 مگابایت را مجاز می‌داند.

اگر می‌خواهید حداکثر طول هدر را به 0.5 مگابایت محدود کنید، به عنوان مثال، می‌توانید بنویسید:

srv := &http.Server{
    Addr:           *addr,
    MaxHeaderBytes: 524288,
    ...
}

اگر MaxHeaderBytes تجاوز کند، کاربر به طور خودکار یک پاسخ 431 Request Header Fields Too Large دریافت خواهد کرد.

اینجا یک نکته وجود دارد: Go همیشه 4096 بایت اضافی فضای سر به عددی که تنظیم کرده‌اید اضافه می‌کند. اگر نیاز دارید که MaxHeaderBytes یک عدد دقیق یا بسیار کم باشد، باید این را در نظر بگیرید.

واژه‌نامه اصطلاحات فنی

اصطلاح فارسی معادل انگلیسی توضیح
زمان‌های قطع اتصال Connection Timeouts محدودیت‌های زمانی اتصال
محدودیت‌های زمانی Time Limits محدودیت‌های زمانی عملیات
خواندن Reading دریافت داده از اتصال
نوشتن Writing ارسال داده به اتصال
اتصال Connection ارتباط شبکه‌ای
تنظیمات زمان‌سنج Timer Settings پیکربندی محدودیت‌های زمانی
پیکربندی سرور Server Configuration تنظیمات سرور
حملات کند Slow Attacks حملات با اتصال‌های طولانی
مدیریت منابع Resource Management مدیریت منابع سیستم
امنیت شبکه Network Security حفاظت از ارتباطات شبکه