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-version | both |
upgrade | در حالت both برابر true |
path | / |
method | POST |
status | 200 |
websocket | false |
http1-mode | single |
تنظیمات اختیاری
| گزینه | پیشفرض | توضیح |
|---|---|---|
http-version | both | حالت پروتکل؛ مقدارهای عددی و نامهای مستعار متنی را میپذیرد. |
upgrade | فقط در حالت both برابر true | درخواستهای upgrade در HTTP/1.1 را میپذیرد. |
upgrade-protocol | h2c | توکن upgrade. اگر غیر از h2c باشد، بعد از 101 مسیر raw میشود. |
upgrade-request-headers | ندارد | headerهایی که باید در درخواست ارتقا وجود داشته باشند. |
upgrade-response-headers | ندارد | headerهای اضافه برای پاسخ 101 Switching Protocols. |
host | ندارد | host مورد انتظار. اگر تنظیم شود، مقدار ناسازگار رد خواهد شد. |
path | / | مسیر مورد انتظار. |
method | POST | متد مورد انتظار در حالت غیر WebSocket. |
status | 200 | کد وضعیت پاسخ. بازه معتبر فعلی 100 تا 599 است. |
headers | ندارد | headerهای اضافه پاسخ. مقدارها باید string باشند. |
content-type | ندارد | مقدار Content-Type از جدول داخلی واتروال. |
websocket | false | handshakeِ WebSocket را میپذیرد و سپس payload را در فریمهای WebSocket منتقل میکند. |
websocket-origin | ندارد | مقدار مورد انتظار Origin. |
websocket-subprotocol | ندارد | subprotocol مورد انتظار که در پاسخ تکرار میشود. |
full-duplex | false | پایان request body را بلافاصله به upstream Finish واتروال تبدیل نمیکند. |
http1-mode | single | شکل HTTP/1.1: مقدار single یا split. گزینه قدیمی http1-split هم پذیرفته میشود. |
split | مقادیر پیشفرض داخلی | تنظیمات جفت کردن دو درخواست در حالت split. |
no-split-upload-buffering-limit | false | گزینه مخصوص تست برای برداشتن سقف بافر سمت upload پیش از متصل شدن download. |
verbose | false | لاگ بیشتر برای تشخیص پروتکل، 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-method | GET | متد مورد انتظار سمت download. |
upload-path | path اصلی | مسیر سمت upload. |
download-path | path اصلی | مسیر سمت download. |
id-placement | query | محل خواندن شناسه مشترک. |
id-name | wwid | نام فیلد شناسه. |
direction-placement | query | محل خواندن marker مربوط به جهت. |
direction-name | wwdir | نام marker مربوط به جهت. |
upload-value | upload | مقدار جهت برای upload. |
download-value | download | مقدار جهت برای download. |
token | ندارد | token مشترک اختیاری. |
token-placement | header | محل خواندن token. |
token-name | X-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-SettingsUpgrade: h2cHTTP2-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 را منتقل میکند.