
pingap v0.1.3.6
وكيل عكسي مثل nginx، مبني على pingora، بسيط وفعّال.
pingap
قبل أن يستقر إصدار pingap، لن يتم قبول أي طلبات سحب (pull requests). إذا كانت لديك أي أسئلة، يرجى إنشاء issue جديد أولاً.

نظرة عامة
Pingap هو وكيل عكسي (reverse proxy) عالي الأداء مبني على Cloudflare Pingora. يبسّط إدارة التشغيل من خلال تمكين إعادة التحميل الديناميكي للتكوين دون أي توقف للخدمة، وذلك عبر ملفات TOML موجزة وواجهة إدارة ويب بديهية.
تكمن قوته الأساسية في نظام إضافات (plugins) قوي، يقدم أكثر من عشرين ميزة جاهزة للاستخدام في مجالات المصادقة (JWT، Key Auth)، والأمان (CSRF، تقييد IP/Referer/UA)، والتحكم في حركة المرور (تحديد المعدل، التخزين المؤقت)، وتعديل المحتوى (إعادة التوجيه، استبدال المحتوى)، والمراقبة (Request ID). هذا يجعل Pingap ليس مجرد وكيل، بل بوابة تطبيقات مرنة وقابلة للتوسع، مصممة للتعامل بسهولة مع السيناريوهات المعقدة بدءًا من حماية واجهات API وصولاً إلى نشر تطبيقات الويب الحديثة.
中文说明 | Documentation · 中文文档 | Examples | Plugins | Crates
flowchart LR
internet("Internet") -- request --> pingap["Pingap"]
pingap -- proxy:pingap.io/api/* --> apiUpstream["10.1.1.1,10.1.1.2"]
pingap -- proxy:cdn.pingap.io --> cdnUpstream["10.1.2.1,10.1.2.2"]
pingap -- proxy:/* --> upstream["10.1.3.1,10.1.3.2"]
الميزات الرئيسية
-
🚀 أداء عالٍ وموثوقية
- مبني بلغة Rust لضمان أمان الذاكرة وأداء من الطراز الأول.
- مدعوم من Cloudflare Pingora، وهي مكتبة شبكات غير متزامنة مجرّبة في المعارك.
- يدعم بروكسي HTTP/1.1 وHTTP/2 وgRPC-web.
-
🔧 ديناميكي وسهل الاستخدام
- تغييرات التكوين دون أي توقف للخدمة مع إعادة التحميل الساخن.
- ملفات تكوين TOML بسيطة وسهلة القراءة.
- واجهة ويب كاملة الميزات لإدارة بديهية وفي الوقت الفعلي.
- يدعم كلاً من الملفات و etcd كخلفيات للتكوين.
- يدعم تسجيل سجل التكوين، مع إمكانية الاستعادة إلى الإصدار السابق بنقرة واحدة.
-
🧩 قابلية توسع قوية
- نظام إضافات غني للتعامل مع مهام البوابة الشائعة.
- توجيه متقدم مع مطابقة المضيف والمسار والتعبيرات النمطية.
- اكتشاف خدمات مدمج عبر قوائم ثابتة أو DNS أو Docker labels.
- HTTPS تلقائي مع Let's Encrypt (يدعم تحديات HTTP-01 وDNS-01).
-
📊 مراقبة حديثة
- مقاييس Prometheus أصلية للمراقبة (أوضاع السحب والدفع).
- دعم OpenTelemetry مدمج للتتبع الموزع.
- سجلات وصول قابلة للتخصيص بدرجة عالية مع أكثر من 30 متغيراً.
- مقاييس أداء مفصلة، بما في ذلك وقت اتصال المنبع (upstream)، ووقت المعالجة، والمزيد.
🚀 البدء
أسهل طريقة للبدء مع Pingap هي باستخدام Docker Compose.
- أنشئ ملف
docker-compose.yml:
# docker-compose.yml
version: '3.8'
services:
pingap:
image: vicanso/pingap:latest # للإنتاج، استخدم إصداراً محدداً مثل vicanso/pingap:0.12.1-full
container_name: pingap-instance
restart: always
ports:
- "80:80"
- "443:443"
volumes:
# قم بتركيب دليل محلي لحفظ جميع التكوينات والبيانات
- ./pingap_data:/opt/pingap
environment:
# قم بالتكوين باستخدام متغيرات البيئة
- PINGAP_CONF=/opt/pingap/conf
- PINGAP_ADMIN_ADDR=0.0.0.0:80/pingap
- PINGAP_ADMIN_USER=pingap
- PINGAP_ADMIN_PASSWORD=<YourSecurePassword> # غيّر هذا!
command:
# ابدأ pingap وفعّل إعادة التحميل الساخن
- pingap
- --autoreload
- أنشئ دليل البيانات وشغّل:
mkdir pingap_data
docker-compose up -d
- الوصول إلى واجهة الإدارة:
مثيل Pingap الخاص بك يعمل الآن! يمكنك الوصول إلى واجهة الإدارة على http://localhost/pingap باستخدام بيانات الاعتماد التي قمت بتعيينها.
تثبيت الثنائي عبر curl
بالنسبة لنظامي Linux وmacOS، يمكنك تثبيت أحدث ثنائي مُجمّع مسبقاً إلى /usr/local/bin/pingap بأمر واحد:
curl -sSL https://raw.githubusercontent.com/vicanso/pingap/main/install.sh | sh
متغيرات البيئة الاختيارية:
PINGAP_FULL=1— تثبيت إصدار-full(جميع الميزات الاختيارية مفعّلة)PINGAP_LIBC=gnu— على Linux، استخدم إصدار glibc بدلاً من إصدار musl الثابت الافتراضيPINGAP_TLS=rustls— على Linux، قم بتثبيت إصدار-rustls-full(خلفية TLS من rustls، جميع الميزات الاختيارية، بدون OpenSSL)؛ راجع خلفية TLS
# إصدار كامل الميزات
curl -sSL https://raw.githubusercontent.com/vicanso/pingap/main/install.sh | PINGAP_FULL=1 sh
الأنظمة المدعومة: Linux x86_64/arm64، Darwin x86_64/arm64. راجع صفحة الإصدارات لجميع الأصول المتاحة.
للحصول على تعليمات أكثر تفصيلاً، بما في ذلك التشغيل من ثنائي، راجع Documentation.
بدء وكيل بدون ملف تكوين
أمر واحد يكفي لخدمة نطاق عبر https وإعادة توجيهه إلى خلفية:
# شهادة مطلوبة من let's encrypt
pingap --domain=pingap.io --upstream=192.168.1.1:3000
# أو استخدم شهادتك الخاصة
pingap --domain=pingap.io --upstream=192.168.1.1:3000 --cert=/etc/ssl/pingap.io
بدون --cert، يطلب Pingap شهادة من Let's Encrypt عبر تحدي HTTP-01، لذا يجب أن يحل pingap.io إلى هذا المضيف ويجب أن يكون المنفذ 80 قابلاً للوصول من الإنترنت. يتم الاحتفاظ بالشهادة الصادرة في ~/.pingap/acme/<domains>.toml وإعادة استخدامها عند إعادة التشغيل — إصدار الشهادات محدود المعدل، لذا لا تحذفها. كل شيء آخر يأتي من سطر الأوامر: تغيير --upstream يسري في التشغيل التالي دون لمس الشهادة.
يقبل --cert الشهادة نفسها أو الدليل الذي يحتويها — يتم اكتشاف التخطيطات الشائعة fullchain.pem / privkey.pem، cert.pem / key.pem و tls.crt / tls.key تلقائياً، استخدم --key لأي شيء آخر. يكون المستمع افتراضياً على 0.0.0.0:443 عند وجود شهادة و 0.0.0.0:80 عند عدم وجود شهادة ولا نطاق، و --addr يتجاوز ذلك. يأخذ --upstream قائمة مفصولة بفواصل من الخلفيات، و --domain قائمة مفصولة بفواصل من المضيفات (احذفه لخدمة كل مضيف عبر http عادي). يتم الرد على الطلبات لمضيف غير مدرج برمز 404.
يتم إنشاء التكوين في كل تشغيل، لذا لا يمكن تعديله عبر واجهة الإدارة: لأي شيء يتجاوز خادماً واحداً استخدم --conf، والذي لا يمكن دمجه مع هذه العلامات.
التكوين الديناميكي
صُمم Pingap للتكيف مع تغييرات التكوين دون توقف.
إعادة التحميل الساخن (--autoreload): لمعظم التغييرات — مثل تحديث المنبع (upstreams) أو المواقع أو الإضافات — يطبق Pingap التكوين الجديد خلال 10 ثوانٍ دون إعادة تشغيل. هذا هو الوضع الموصى به لبيئات الحاويات.
إعادة التشغيل اللطيفة (-a أو --autorestart): للتغييرات الأساسية (مثل تعديل منافذ استماع الخادم)، يقوم هذا الوضع بإعادة تشغيل كاملة دون أي توقف، مما يضمن عدم فقدان أي طلبات.
عملية التسليم مدفوعة بالجاهزية وليس بالتوقيت: يتم بدء الاستبدال بـ -d -u، ويُبلغ عبر مقبس unix بجوار مقبس الترقية لحظة جاهزيته لتولي المستمعين، وعندها فقط يرسل العملية الجارية لنفسها SIGQUIT. إذا خرج الاستبدال، أو ماتت عمليته الخفية، أو انقضى basic.restart_ready_timeout (الافتراضي 1 دقيقة) أولاً، يتم التخلي عن إعادة التشغيل وتستمر العملية الجارية في الخدمة.
🔧 التطوير
make dev
إذا كنت بحاجة إلى واجهة إدارة ويب، يجب تثبيت nodejs وبناء أصول الويب.
# توليد أصل إدارة الويب
cd web
npm i
cd ..
make build-web
خلفية TLS
الإصدار الافتراضي ينهي TLS باستخدام OpenSSL، المُجمّع من المصدر بواسطة crate openssl. للبناء باستخدام rustls بدلاً من ذلك، والذي يلغي بناء مصدر OpenSSL (لا يزال هناك حاجة إلى مترجم C: موفرو التشفير في rustls، ring و aws-lc-rs، يحتويان على C و assembly):
cargo build --release --no-default-features --features tls-rustls
# مع الميزات الاختيارية أيضاً
cargo build --release --no-default-features --features tls-rustls,full
يتجاهل إصدار rustls إعدادات tls_min_version و tls_max_version و tls_cipher_list و tls_ciphersuites الخاصة بكل خادم ويسجل تحذيراً عند تعيينها: فهو يقدم دائماً TLS 1.2 و 1.3 مع مجموعات التشفير الافتراضية في rustls. كل شيء آخر، بما في ذلك شهادات SNI الديناميكية، وإصدار CA ذاتي التوقيع، وACME وخيار ca للمنبع، يتصرف بنفس الطريقة. هناك فرق واحد يجب معرفته عند التحقق من المنبع: rustls (webpki) يرفض شهادة خادم تحمل CA:TRUE، والتي يقبلها OpenSSL، لذا فإن الخلفية التي تستخدم شهادة ذاتية التوقيع سريعة openssl req -x509 تحتاج إلى ورقة (leaf) مناسبة موقعة من CA (أو ورقة ذاتية التوقيع بدون علامة CA) قبل أن يثق بها خيار ca للمنبع. يسجل سجل بدء التشغيل الخلفية التي تم بناء الثنائي بها.
📝 التكوين
server "test" {
addr = "127.0.0.1:6118"
location "github-api" {
path = "/api"
proxy_set_headers = ["Host:api.github.com"]
rewrite = "^/api/(?<path>.+)$ /$1"
upstream "api" {
addrs = ["api.github.com:443"]
discovery = "dns"
sni = "api.github.com"
}
}
location "static" {
plugin "staticServe" {
category = "directory"
path = "~/Downloads"
step = "request"
}
}
}
[upstreams.api]
addrs = ["api.github.com:443"]
discovery = "dns"
sni = "api.github.com"
[plugins.staticServe]
category = "directory"
path = "~/Downloads"
step = "request"
[locations.github-api]
upstream = "api"
path = "/api"
proxy_set_headers = ["Host:api.github.com"]
rewrite = "^/api/(?<path>.+)$ /$1"
[locations.static]
plugins = ["staticServe"]
[servers.test]
addr = "127.0.0.1:6118"
locations = ["github-api", "static"]
يمكنك العثور على التعليمات ذات الصلة هنا: https://pingap.io/crates/config.
🔄 خطوة الوكيل
graph TD;
server["HTTP Server"];
locationA["Location A"];
locationB["Location B"];
locationPluginListA["Proxy Plugin List A"];
locationPluginListB["Proxy Plugin List B"];
upstreamA1["Upstream A1"];
upstreamA2["Upstream A2"];
upstreamB1["Upstream B1"];
upstreamB2["Upstream B2"];
locationResponsePluginListA["Response Plugin List A"];
locationResponsePluginListB["Response Plugin List B"];
start("New Request") --> server
server -- "host:HostA, Path:/api/*" --> locationA
server -- "Path:/rest/*"--> locationB
locationA -- "Exec Proxy Plugins" --> locationPluginListA
locationB -- "Exec Proxy Plugins" --> locationPluginListB
locationPluginListA -- "proxy pass: 10.0.0.1:8001" --> upstreamA1
locationPluginListA -- "proxy pass: 10.0.0.2:8001" --> upstreamA2
locationPluginListA -- "done" --> response
locationPluginListB -- "proxy pass: 10.0.0.1:8002" --> upstreamB1
locationPluginListB -- "proxy pass: 10.0.0.2:8002" --> upstreamB2
locationPluginListB -- "done" --> response
upstreamA1 -- "Exec Response Plugins" --> locationResponsePluginListA
upstreamA2 -- "Exec Response Plugins" --> locationResponsePluginListA
upstreamB1 -- "Exec Response Plugins" --> locationResponsePluginListB
upstreamB2 -- "Exec Response Plugins" --> locationResponsePluginListB
locationResponsePluginListA --> response
locationResponsePluginListB --> response
response["HTTP Response"] --> stop("Logging");
📊 الأداء
CPU: M4 Pro, Thread: 1
Ping بدون سجل وصول
wrk 'http://127.0.0.1:6118/ping' --latency
Running 10s test @ http://127.0.0.1:6118/ping
2 threads and 10 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 66.41us 23.67us 1.11ms 76.54%
Req/Sec 73.99k 2.88k 79.77k 68.81%
Latency Distribution
50% 67.00us
75% 80.00us
90% 91.00us
99% 116.00us
1487330 requests in 10.10s, 194.32MB read
Requests/sec: 147260.15
Transfer/sec: 19.24MB
📦 إصدار Rust
إصدار MSRV الحالي لدينا هو 1.96
📄 الترخيص
هذا المشروع مرخص بموجب Apache License, Version 2.0.