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

پروتکل API

APIServer دسترسی به operationهای صریحاً ثبت‌شدهٔ nodeها را روی stream بایتی قابل اعتماد و دارای احراز هویت فراهم می‌کند. این صفحه قالب مشترک ارتباط را تعریف می‌کند. تنظیمات gateway و مجوزها در مرجع APIServer هستند؛ نام operationها، بدنهٔ درخواست، پاسخ و تضمین تغییر state در مرجع node هدف تعریف می‌شوند.

برای اجرای کلاینت، از مثال Python شروع کنید. برای فرمان‌های حساب از CLI مدیریت استفاده کنید. هر دو از ماژول transport کتابخانهٔ استاندارد ww_api.py استفاده می‌کنند. ماژول ww_api_cli.py گزینه‌های اتصال، خواندن محدود token و راهنمای نتیجهٔ نامعلومِ نوشتن را مشترک می‌کند. transport به argparse وابسته نیست؛ اعتبارسنجی schema بدنهٔ AuthenticationServer در ww_admin.py باقی می‌ماند. هنگام کپی مثال عمومی، دایرکتوری همسایهٔ client/ نیز لازم است. مثال عمومی بایت‌های نتیجهٔ decodeشده را مستقیم در stdout می‌نویسد. برای پیاده‌سازی node، پیاده‌سازی operationهای API را بخوانید.

transport و frame​

زنجیره‌های پشتیبانی‌شده stream مرتب و قابل اعتماد تحویل می‌دهند:

TcpListener -> APIServer
TcpListener -> HttpServer -> APIServer
UdpListener -> TcpOverUdpServer -> APIServer

APIServer socket باز نمی‌کند و transport را رمزنگاری نمی‌کند. دسترسی راه دور به stream حفاظت‌شده نیاز دارد. اتصال مستقیم UdpListener -> APIServer پشتیبانی نمی‌شود: prefix طول، گم‌شدن یا جابه‌جایی datagram را جبران نمی‌کند. TcpOverUdpServer پیش از رسیدن داده به APIServer، stream قابل اعتماد را بازسازی می‌کند. ترکیب کامل HTTP و UDP قابل اعتماد در مثال‌های transport آمده است.

تمام درخواست‌ها و پاسخ‌ها frame یکسان دارند:

4-byte unsigned big-endian length | exactly length UTF-8 JSON bytes

طول، prefix را شامل نمی‌شود و باید غیرصفر و در سقف envelope تنظیم‌شده باشد. newline یا NUL انتهایی وجود ندارد. read سوکت، callback payload تونل، chunk HTTP یا packet UDP مرز frame نیست: prefix یا body می‌تواند میان چند read تقسیم شود و یک read می‌تواند چند frame داشته باشد.

در ترکیب HTTP، این بایت‌های frameشده در body حمل می‌شوند. فراخواننده ابتدا پاسخ را dechunk می‌کند، سپس prefix API را می‌خواند و با همان طول کامل‌بودن پاسخ را تشخیص می‌دهد؛ منتظر EOF یا chunk نهایی HTTP نمی‌ماند. HttpServer method و path تنظیم‌شده را بررسی می‌کند. APIServer فقط بایت‌های body را می‌بیند؛ این رابط مسیریابی REST، احراز هویت header یا HTTP status جدا برای هر operation ندارد.

envelope درخواست​

{
"id": "request-42",
"auth": {"client": "monitor", "token": "<configured token>"},
"selector": {"config": "edge-europe", "node": "auth-client"},
"operation": "status",
"content_type": "application/json",
"body_encoding": "utf8",
"body": "{\"include_accounting\":true}"
}

تمام فیلدهای بالا به‌جز selector.config اجباری‌اند.

فیلدمعنا
idرشتهٔ غیرخالی همبستگی، حداکثر ۶۴ بایت UTF-8. در پاسخ تکرار می‌شود و مانع اجرای دوبارهٔ درخواست نیست.
auth.clientنام client تنظیم‌شدهٔ API، حداکثر ۲۵۶ بایت UTF-8.
auth.tokentoken ثابت ۳۲ بایتی client، به‌شکل دقیقاً ۶۴ نویسهٔ hexadecimal.
selector.configنام منطقی دقیق و اختیاری config، حداکثر ۲۵۶ بایت UTF-8.
selector.nodeنام دقیق node عمومی سطح بالا، حداکثر ۲۵۶ بایت UTF-8.
operationنام دقیق operation ثبت‌شده، حداکثر ۶۴ بایت UTF-8.
content_typeنوع محتوای body decodeشدهٔ node، حداکثر ۱۲۸ بایت UTF-8؛ باید با نوع درخواست اعلام‌شدهٔ operation یکسان باشد.
body_encodingمقدار utf8 یا base64.
bodyرشتهٔ JSON حاوی درخواست encodeشدهٔ node، حتی وقتی خود درخواست JSON است.

نام‌های metadata به بزرگی و کوچکی حروف حساس‌اند. parser بیرونی کلید ناشناخته یا تکراری، type نادرست، UTF-8 خراب، دادهٔ غیر whitespace پس از پایان و NUL decodeشده در metadata یا body نوع UTF-8 را رد می‌کند. عمق و شمار مقدارهای envelope پیش از تخصیص درخت JSON محدود می‌شوند. این قواعد، schema بدنهٔ node هدف را تعریف نمی‌کنند.

احراز هویت، انتخاب هدف و مجوز​

هر envelope مستقل احراز هویت می‌شود. token API از رمز کاربر ترافیک و اعتبارنامهٔ تولیدکنندهٔ upstream در AuthenticationClient جداست. نام client ناشناخته و token نادرست هر دو unauthorized می‌دهند. احراز هویت پیش از lookup هدف انجام می‌شود.

selector.config نام منطقی name یک config بارشده است؛ نام فایل نیست و config تازه‌ای بار نمی‌کند. با پیکربندی APIServer، تمام configهای بارشده باید نام منطقی غیرخالی و یکتا داشته باشند. childهای داخلی تولیدشده هدف عمومی API نیستند.

حذف selector.config فقط وقتی کار می‌کند که نام دقیق node میان تمام nodeهای اعلام‌شدهٔ سطح بالا در تمام configهای بارشده یک بار وجود داشته باشد. nodeهایی که API ندارند یا برای caller مجاز نیستند نیز شمرده می‌شوند. فیلتر ثبت API یا مجوز نمی‌تواند نام مبهم را یکتا کند؛ برای رفع ambiguous_target نام config را بدهید.

پس از حل هدف، APIServer مجوز دقیق client برای همان config، node و operation را بررسی می‌کند. wildcard یا مجوز ضمنی از operation دیگر وجود ندارد. descriptor فقط‌خواندنی نیز operation را عمومی نمی‌کند. هنگام startup، هدف‌های allowlist و operationهای ثبت‌شده بررسی می‌شوند. وجود نماد قدیمی TunnelApi به‌تنهایی چیزی را از این gateway در دسترس نمی‌گذارد. درخواست بدون مجوز دقیق forbidden می‌گیرد؛ پس از freeze رجیستری، descriptor تغییرناپذیرِ آماده‌شده استفاده می‌شود. read_only فقط metadata توصیفی است و مجوز، rollback یا ایمنی retry را اعمال نمی‌کند.

encoding و نوع محتوای body​

با body_encoding: "utf8"، بایت‌های UTF-8 decodeشدهٔ body بدون تغییر تحویل می‌شوند. با "base64"، base64 سخت‌گیرانه و canonical بایت دلخواه از جمله NUL را حمل می‌کند. base64 encoding است، نه رمزنگاری.

APIServer درخواست node را parse، normalize یا دوباره serialize نمی‌کند. برای نمونه، envelope بالا بایت‌های {"include_accounting":true} را به handler status در AuthenticationClient می‌دهد. operation دیگر می‌تواند قالب body و نوع محتوای دیگری تعریف کند. schema پذیرفته‌شده را از مرجع node بخوانید؛ envelope معتبر همچنان ممکن است درخواست node نامعتبر داشته باشد.

envelope پاسخ​

operation dispatchشده، حتی اگر خطای سطح node برگرداند، از این envelope استفاده می‌کند:

{
"id": "request-42",
"error": null,
"result": {
"code": 0,
"content_type": "application/json",
"body_encoding": "utf8",
"body": "{\"can_authenticate\":true}"
}
}

body بالا نمونه است، نه snapshot کامل status. result.code نتیجهٔ handler را حفظ می‌کند: 0 موفقیت و 1 خطاست. operation ثبت‌شده نوع محتوا و encoding پاسخ را اعلام می‌کند و لازم نیست با encoding درخواست برابر باشند. result.body را با body_encoding پاسخ decode کنید و سپس طبق schema node تفسیر کنید. نتیجهٔ خالی handler به body خالی با type و encoding اعلام‌شده تبدیل می‌شود.

مرجع operationها همچنان منبع تعریف رفتار است:

  • status در AuthenticationClient نمای محلی همگام‌سازی و پذیرش client را توضیح می‌دهد.
  • API مدیریتی AuthenticationServer خواندن حساب، صفحه‌بندی، ساخت، ویرایش مشروط و تغییر رمز را توضیح می‌دهد. مقدارهای unsigned 64-bit آن رشتهٔ ده‌دهی JSON هستند.

خطاهای gateway​

خطای gateway دارای result: null است:

{
"id": "request-42",
"error": {"code": "ambiguous_target", "message": "Specify the configuration name."},
"result": null
}

اگر ID همبستگی قابل اعتماد و محدود بازیابی نشده باشد، id برابر null است. پیام‌ها ثابت و محدودند؛ کلاینت باید تصمیم را بر اساس code خطا بگیرد.

کدمعنا
invalid_requestframe یا schema envelope درخواست نامعتبر است.
invalid_encodingencoding body نامعتبر یا پشتیبانی‌نشده است.
unauthorizedاعتبارنامهٔ API نامعتبر است.
forbiddenoperation در allowlist دقیق caller نیست.
target_not_foundnode عمومی انتخاب‌شده پیدا نشد.
ambiguous_targetنام node بدون config با چند node اعلام‌شده مطابقت دارد.
unsupported_content_typeنوع محتوای درخواست با operation مطابقت ندارد.
busyظرفیت gateway یا dispatch اجازهٔ پذیرش کار را نداد.
timeoutیکی از deadlineهای API منقضی شد.
shutting_downپذیرش API بسته شده است.
response_too_largeنتیجهٔ معتبر در سقف پاسخ encodeشده یا اندازهٔ قابل نمایش جا نمی‌شود.
invalid_responsehandler برای نتیجهٔ UTF-8، بایت UTF-8 نامعتبر یا NUL برگردانده است.

کلاینت‌های مشترک نام‌های عمومی unsupported_api و unsupported_operation را نیز می‌شناسند؛ APIServer فعلی آن‌ها را تولید نمی‌کند. descriptor مجاز در startup حل شده است و نبودن مجوز دقیق در زمان اجرا forbidden می‌دهد.

خطاهای busy و shutting_down در gateway رد قطعی پیش از dispatch هستند و handler را فراخوانی نمی‌کنند. کلاینت‌های Python ساخت/ویرایش/تغییر رمز این رد را بدون retry خودکار گزارش می‌دهند. همان code در نتیجهٔ node outcome جداگانهٔ operation است و باید با قرارداد commit همان operation تفسیر شود.

خطای gateway و خطای node دو لایهٔ جدا هستند. برای مثال، conflict از AuthenticationServer داخل result.body با result.code: 1 است؛ timeout gateway در error قرار می‌گیرد و نتیجهٔ node ندارد. خرابی تحویل پس از dispatch می‌تواند تغییری را که قبلاً commit شده پنهان کند؛ آن تغییر را rollback نمی‌کند. invalid_response پس از dispatch رخ می‌دهد و تغییر ممکن است commit شده باشد. بایت نامعتبر نه در پاسخ و نه در log بازتاب داده نمی‌شود؛ عملیات base64 می‌تواند بایت دلخواه برگرداند. این خطا مجوز retry خودکار نیست.

ترتیب، بستن اتصال و محدودیت‌ها​

هر اتصال در هر لحظه یک operation اجرا می‌کند. درخواست‌های کامل pipelineشده در صف FIFO منتظر می‌مانند و پاسخ‌ها همان ترتیب را حفظ می‌کنند. اتصال‌های جدا می‌توانند هم‌زمان اجرا شوند؛ lockها و پیش‌شرط‌های operation، state مشترک آن‌ها را کنترل می‌کنند.

frame/schema خراب و احراز هویت نامعتبر، پس از حداکثر یک خطای محدود، اتصال را می‌بندند. خطاهای timeout و invalid_encoding در gateway نیز اتصال را می‌بندند. خطاهای هدف، operation و body در درخواست معتبر و مجاز می‌توانند به‌ترتیب FIFO ادامه یابند. اگر حتی خطا در بودجه جا نشود، امتناع منابع می‌تواند مستقیم اتصال را ببندد. پس از شروع پاسخ، خرابی transport یا انقضای خروجی اتصال را می‌بندد و frame دومی در پاسخ نیمه‌کاره قرار نمی‌دهد. انقضای درخواست بعدی در صف پشت پاسخ قبلی نیز باعث بستن اتصال می‌شود، نه فرستادن خطا خارج از ترتیب.

جدول محدودیت‌های APIServer تنظیمات و پیش‌فرض‌ها را تعریف می‌کند. سقف envelope شامل prefix چهار بایتی نیست؛ بودجهٔ حافظه تخصیص‌های نگه‌داشته‌شده، fragmentها، صف‌ها، jobها و حافظهٔ کاری موقت را هم حساب می‌کند. node می‌تواند سقف body یا کار کوچک‌تری داشته باشد. escaping در JSON و base64 اندازهٔ encodeشده را افزایش می‌دهند؛ طول خام body به‌تنهایی ثابت نمی‌کند envelope پاسخ جا می‌شود.

طول اعلام‌شده باعث تخصیص body نرسیده نمی‌شود؛ ظرفیت frame با بایت‌های رسیده رشد می‌کند و هم‌پوشانی تخصیص قدیم/جدید در بودجه حساب می‌شود. سقف سخت ورودی شامل ورودی ناقص کپی‌شده است، ولی watermark فشار این بایت‌ها را کم می‌کند تا پس از تخلیهٔ backlog یا درخواست‌های قدیمی دریافت ادامه یابد. parser زمان اجرا همان قواعد سخت را بدون پیام stderr مخصوص پیکربندی اعمال می‌کند؛ تشخیص خطای startup/configuration حفظ می‌شود.

Pause پیش‌بینانه نیز وقتی درخواست‌های کامل یا فعال قدیمی بودجهٔ frame بعدی را نگه داشته‌اند ورودی را متوقف می‌کند. پیش از معلوم‌شدن هدر، max_request_envelope + 4 و پس از آن اندازهٔ کامل اعلام‌شده ملاک است؛ بایت‌های bufferشده یک بار شمرده می‌شوند. پایان درخواست و تخلیهٔ خروجی hold را بدون ورودی تازه آزاد می‌کنند. frame ناقص به‌تنهایی می‌تواند دریافت را ادامه دهد. Pause توصیه‌ای است؛ ورودی دیررس یا چسبیده می‌تواند از سقف سخت عبور کند و اتصال را ببندد، و deadline اصلی frame مطلق باقی می‌ماند.

محدودیت‌ها هم‌زمان اعمال می‌شوند: max_jobs=64 ظرفیت هم‌زمان ۶۴ job را تضمین نمی‌کند. با پیش‌فرض فعلی، دامنهٔ مشترک 64 MiB پیش از هزینهٔ قابل‌توجه session تقریباً هفت job کوچک را جا می‌دهد، چون هر کدام allowance کاری 8 MiB و حافظهٔ دیگری رزرو می‌کند. ورودی و خروجی نگه‌داشته‌شده می‌توانند این ظرفیت را کمتر کنند.

توکن API مصرف منابع بدون احراز هویت را متوقف نمی‌کند. دسترسی شبکه را محدود کنید یا peer را در transport احراز هویت کنید؛ دسترسی راه دور به رمزنگاری و بررسی هویت peer نیاز دارد و محدودیت ورودی حملهٔ انبوه اتصال را حذف نمی‌کند.

deadlineبازه
تکمیل frameاز رسیدن نخستین fragment، شامل ماندن در صف؛ ورودی قطره‌ای آن را reset نمی‌کند.
درخواستاز پذیرش درخواست کامل تا انتشار پاسخ، شامل انتظار FIFO و worker.
خروجیاز انتشار پاسخ تا تحویل به transport، شامل زمان Pause.
بی‌کاریزمانی که frame، درخواست یا خروجی در جریان نیست.

تمام محدودیت‌های تنظیم‌شده عدد صحیح مثبت و محدودند؛ صفر حفاظت را غیرفعال نمی‌کند. قطع یا timeout تحویل را abandoned می‌کند، اما handler در حال اجرا تا پایان reservation خود را نگه می‌دارد. gateway نمی‌تواند کار دلخواهی را که قبلاً commit شده قطع و لغو کند.

نوشتن و پاسخ نامعلوم​

id درخواست فقط برای همبستگی است. استفادهٔ دوباره از آن تضمین replay، checkpoint یا deduplication خودکار ایجاد نمی‌کند. کلاینت عمومی یک بار تلاش می‌کند و نوشتن را خودکار تکرار نمی‌کند.

transport پایتون هر درخواست recv را به 64 KiB محدود می‌کند، ولی سقف frame و همان deadline اصلی monotonic برای resolve، اتصال، ارسال و همهٔ chunkهای پاسخ حفظ می‌شوند. short read مهلت را تمدید نمی‌کند. EOF زودرس، prefix بزرگ، پاسخ خراب، timeout یا قطع transport پس از ارسال، تحویل تغییر را نامعلوم می‌کنند؛ رد پیش از dispatch قطعی است.

پیش از retry قرارداد operation هدف را بخوانید. APIهای عمومی تغییر در AuthenticationServer تضمین می‌کنند خطای بازگشتی operation در سطح node یعنی همین فراخوانی commit نشده است. پاسخ گم‌شده، خراب یا دیررس، قطع اتصال یا timeout gateway پس از dispatch نتیجه را نامعلوم می‌کند. موفقیت، حافظهٔ سرور را تأیید می‌کند؛ persistence معمول و refresh کلاینت جدا هستند.

پس از conflict یا نتیجهٔ نامعلوم، دوباره بخوانید و صریحاً دربارهٔ تغییر درخواستی تصمیم بگیرید. token تازه را پنهانی نگیرید و نوشتن را replay نکنید. بازیابی نتیجهٔ ساخت حساب، ID اصلی را حفظ می‌کند؛ وجود عمومی حساب مشخص نمی‌کند چه کسی آن را ساخته است. خواندن عمومی credential را حذف می‌کند، پس پس از گم‌شدن پاسخ تغییر رمز نیز نمی‌تواند رمز ذخیره‌شده را ثابت کند. مثال‌های مدیریت حساب و مرجع هر operation، مراحل تصمیم‌گیری مربوط را توضیح می‌دهند.