پرش به مطلب اصلی

HttpServer

HttpServer بخش سرور HttpClient است. درخواست ورودی HTTP را تجزیه می‌کند، فریم‌بندی HTTP را برمی‌دارد و فقط body درخواست را به نود بعدی می‌دهد. در مسیر برگشت نیز payload دریافتی از نود بعدی را به body پاسخ تبدیل می‌کند.

این نود از HTTP/1.1، اتصال مستقیم HTTP/2، ارتقای HTTP/1.1 به h2c، توکن سفارشی upgrade، حالت split در HTTP/1.1 و WebSocket پشتیبانی می‌کند.

HttpServer خودش TLS را terminate نمی‌کند. اگر ترافیک ورودی HTTPS است، باید TlsServer را پیش از HttpServer قرار دهید.

جایگاه رایج

TcpListener -> HttpServer -> SomeServiceTunnel
TcpListener -> TlsServer -> HttpServer -> SomeServiceTunnel

نمونه یک مسیر کامل:

Client side:
TcpListener -> MuxClient -> HttpClient -> TlsClient -> TcpConnector

Server side:
TcpListener -> TlsServer -> HttpServer -> MuxServer -> TcpConnector

نمونه تنظیم

{
"name": "http-server",
"type": "HttpServer",
"settings": {
"http-version": "both",
"upgrade": true,
"host": "example.com",
"path": "/api/tunnel",
"method": "POST",
"status": 200,
"headers": {
"server": "waterwall"
}
},
"next": "service"
}

تنظیمات ضروری

این نود تنظیم اختصاصی اجباری ندارد، اما آبجکت settings باید وجود داشته باشد و خالی نباشد.

پیش‌فرض‌های مهم:

گزینهپیش‌فرض
http-versionboth
upgradeدر حالت both برابر true
path/
methodPOST
status200
websocketfalse
http1-modesingle

تنظیمات اختیاری

گزینهپیش‌فرضتوضیح
http-versionbothحالت پروتکل؛ مقدارهای عددی و نام‌های مستعار متنی را می‌پذیرد.
upgradeفقط در حالت both برابر trueدرخواست‌های upgrade در HTTP/1.1 را می‌پذیرد.
upgrade-protocolh2cتوکن upgrade. اگر غیر از h2c باشد، بعد از 101 مسیر raw می‌شود.
upgrade-request-headersنداردheaderهایی که باید در درخواست ارتقا وجود داشته باشند.
upgrade-response-headersنداردheaderهای اضافه برای پاسخ 101 Switching Protocols.
hostنداردhost مورد انتظار. اگر تنظیم شود، مقدار ناسازگار رد خواهد شد.
path/مسیر مورد انتظار.
methodPOSTمتد مورد انتظار در حالت غیر WebSocket.
status200کد وضعیت پاسخ. بازه معتبر فعلی 100 تا 599 است.
headersنداردheaderهای اضافه پاسخ. مقدارها باید string باشند.
content-typeنداردمقدار Content-Type از جدول داخلی واتروال.
websocketfalsehandshakeِ WebSocket را می‌پذیرد و سپس payload را در فریم‌های WebSocket منتقل می‌کند.
websocket-originنداردمقدار مورد انتظار Origin.
websocket-subprotocolنداردsubprotocol مورد انتظار که در پاسخ تکرار می‌شود.
full-duplexfalseپایان request body را بلافاصله به upstream Finish واتروال تبدیل نمی‌کند.
http1-modesingleشکل HTTP/1.1: مقدار single یا split. گزینه قدیمی http1-split هم پذیرفته می‌شود.
splitمقادیر پیش‌فرض داخلیتنظیمات جفت کردن دو درخواست در حالت split.
no-split-upload-buffering-limitfalseگزینه مخصوص تست برای برداشتن سقف بافر سمت upload پیش از متصل شدن download.
verbosefalseلاگ بیشتر برای تشخیص پروتکل، handshake و فریم‌بندی.

حالت‌های HTTP

مقدار http-versionمعنی
1, 1.1, http1, http1.1اجبار به HTTP/1.1.
2, 2.0, http2, h2اجبار به HTTP/2 مستقیم.
both, any, auto, 1.1+2تشخیص HTTP/2 مستقیم یا HTTP/1.1، همراه با upgrade اختیاری.

HTTP/1.1 معمولی

با http1-mode: "single" برای هر line در WaterWall یک درخواست و پاسخ HTTP/1.1 استفاده می‌شود:

  • headerهای درخواست در HttpServer خوانده می‌شوند.
  • body درخواست به payloadِ WaterWall تبدیل می‌شود.
  • payload برگشتی از نود بعدی به body پاسخ تبدیل می‌شود.
  • پاسخ با Transfer-Encoding: chunked ساخته می‌شود.
  • هنگام Finish در downstream، chunk پایانی فرستاده و سپس Finishِ WaterWall منتشر می‌شود.

Parser درخواست می‌تواند body از نوع chunked، بدنه دارای Content-Length و درخواست بدون body را بخواند. Trailerهای chunked در خود نود خوانده می‌شوند.

اگر full-duplex برابر false باشد، پایان body درخواست به Finish در upstream تبدیل می‌شود. با مقدار true، پایان درخواست فقط در state داخلی ثبت می‌شود و line سمت سرویس تا بسته شدن transport باز می‌ماند.

حالت HTTP/1.1 split

در حالت split دو درخواست HTTP/1.1 به یک line در WaterWall متصل می‌شوند:

  • درخواست upload: body آن به نود بعدی داده می‌شود.
  • درخواست download: body پاسخ آن، payload برگشتی از نود بعدی را حمل می‌کند.
  • دو نیمه با id، direction و token اختیاری به هم جفت می‌شوند.

این حالت باید با حالت split در HttpClient هماهنگ باشد.

{
"name": "http-server",
"type": "HttpServer",
"settings": {
"http-version": 1,
"http1-mode": "split",
"path": "/tunnel",
"split": {
"upload-method": "POST",
"download-method": "GET",
"id-placement": "query",
"id-name": "sid",
"direction-placement": "query",
"direction-name": "part",
"token": "shared-token",
"token-placement": "header",
"token-name": "X-Tunnel-Token"
},
"headers": {
"Cache-Control": "no-store"
}
},
"next": "service"
}

فیلدهای رایج داخل split:

گزینهپیش‌فرضتوضیح
upload-methodمتد سطح اصلیمتد مورد انتظار سمت upload.
download-methodGETمتد مورد انتظار سمت download.
upload-pathpath اصلیمسیر سمت upload.
download-pathpath اصلیمسیر سمت download.
id-placementqueryمحل خواندن شناسه مشترک.
id-namewwidنام فیلد شناسه.
direction-placementqueryمحل خواندن marker مربوط به جهت.
direction-namewwdirنام marker مربوط به جهت.
upload-valueuploadمقدار جهت برای upload.
download-valuedownloadمقدار جهت برای download.
tokenنداردtoken مشترک اختیاری.
token-placementheaderمحل خواندن token.
token-nameX-Waterwall-Tokenنام فیلد token.
هشدار

Split mode فقط با http-version = 1 معتبر است و با websocket ترکیب نمی‌شود.

no-split-upload-buffering-limit سقف ایمنی معمول بافر سمت upload را برمی‌دارد. این گزینه بیشتر برای تست و شرایط کنترل‌شده مناسب است، نه استفاده عادی.

HTTP/2

در اتصال مستقیم HTTP/2، HttpServer یک stream منطقی برای line در WaterWall می‌پذیرد:

  • فریم‌های DATA درخواست به payloadِ upstream تبدیل می‌شوند.
  • payload برگشتی به فریم DATA پاسخ تبدیل می‌شود.
  • END_STREAM سمت درخواست به Finish در upstream تبدیل می‌شود، مگر اینکه full-duplex فعال باشد.
  • Finish در downstream به END_STREAM پاسخ تبدیل می‌شود.

جزئیات مهم:

  • مقدار MAX_CONCURRENT_STREAMS برابر 1 است
  • window اولیه stream برابر 1 MiB است.
  • بیشترین اندازه فریم 32 KiB است.
  • streamهای اضافه با REFUSED_STREAM رد می‌شوند.
  • در حالت WebSocket، تنظیم SETTINGS_ENABLE_CONNECT_PROTOCOL فعال می‌شود.

Upgrade

با http-version: "both" و upgrade: true، نود می‌تواند درخواست‌های upgrade را بپذیرد.

برای h2c، درخواست باید metadata لازم را داشته باشد:

  • Connection شامل Upgrade و HTTP2-Settings
  • Upgrade: h2c
  • HTTP2-Settings

پس از پذیرش upgrade، پاسخ 101 Switching Protocols فرستاده می‌شود و نود وارد HTTP/2 می‌شود. درخواست اصلی ارتقایافته stream شماره 1 است و طراحی فعلی برای payload منتظر یک stream تونل پس از upgrade می‌ماند.

اگر upgrade-protocol توکنی سفارشی و غیر از h2c باشد، پس از handshake موفق مسیر به عبور دوطرفه بایت‌های خام تبدیل می‌شود.

WebSocket

با websocket: true، ابتدا handshakeِ WebSocket بررسی می‌شود و سپس payload در فریم‌های WebSocket جابه‌جا می‌شود.

در HTTP/1.1:

  • متد باید GET باشد
  • headerهای upgrade WebSocket باید وجود داشته باشند
  • Sec-WebSocket-Key باید معتبر باشد
  • Sec-WebSocket-Version باید 13 باشد
  • request body پذیرفته نمی‌شود
  • اگر host، path، websocket-origin یا websocket-subprotocol تنظیم شده باشند، باید با درخواست منطبق باشند.

در HTTP/2:

  • request باید extended CONNECT باشد
  • مقدار :protocol باید websocket باشد
  • :path و authority/host در صورت تنظیم باید منطبق باشند.
  • Sec-WebSocket-Version باید 13 باشد

بعد از handshake:

  • فریم‌های سمت کلاینت باید mask شده باشند.
  • فریم‌های سمت سرور بدون mask فرستاده می‌شوند.
  • payload برگشتی WaterWall به فریم binary تبدیل می‌شود.
  • فریم‌های text، binary و continuation از peer به payload خام تبدیل می‌شوند.
  • ping و pong داخل نود مدیریت می‌شوند
  • پیش از Finish یک فریم close فرستاده می‌شود.

رفتار Finish و Lifecycle

HttpServer پیش از انتشار بعضی رویدادهای Finish در WaterWall، بایت‌های پایانی پروتکل را می‌فرستد:

  • HTTP/1.1 آخرین chunk پاسخ را می‌فرستد.
  • HTTP/2 یک END_STREAM برای پاسخ می‌فرستد.
  • WebSocket یک فریم close می‌فرستد.

پیاده‌سازی ابتدا جهت مربوط را finished علامت می‌زند، سپس state محلی HTTP را از بین می‌برد و در پایان Finish واقعی WaterWall را منتشر می‌کند. این ترتیب مانع از آن می‌شود که backpressure ناشی از بایت‌های پایانی پروتکل، callbackهای pause/resume را به سمتی برگرداند که قبلاً finish شده است.

در حالت split، HttpServer همان line منطقی WaterWall را می‌سازد و مدیریت می‌کند که دو نیمه upload و download در HTTP را به هم وصل می‌کند. نیمه‌های transport بسته می‌شوند، بی‌آنکه مانند اتصال‌های مستقل سرویس با آن‌ها رفتار شود.

Buffer Padding

HttpServer برای مسیرهای فریم‌بندی، 16 بایت left padding اعلام می‌کند. هنگام ترکیب آن با نودهایی که بایت پروتکل اضافه می‌کنند، این فضا را در نظر بگیرید.

اشتباه‌های رایج

  • قرار دادن HttpServer مستقیم بعد از TcpListener وقتی ترافیک ورودی TLS رمزنگاری شده است. ابتدا TlsServer بگذارید.
  • انتظار عبور header به نود بعدی؛ نود بعدی فقط payloadِ body را می‌گیرد.
  • ترکیب حالت split با WebSocket یا HTTP/2.
  • انتظار تبدیل چند stream مستقل HTTP/2 به چند line در WaterWall.
  • استفاده از extension سفارشی WebSocket؛ مسیر WebSocket فقط فریم‌های بدون extension را منتقل می‌کند.