انتقل إلى المحتوى

HTTP الذكي

يتواصل Pitmaster مع خوادم git عبر HTTPS باستخدام بروتوكول HTTP الذكي. وهذه وسيلة النقل الأساسية عبر الشبكة. ويستخدم وظائف التدفق الأصلية في PHP (fopen مع سياق تدفق) لطلبات HTTP، لذا يجب تفعيل allow_url_fopen.

SmartHttpClient

PHP
use Pitmaster\Protocol\SmartHttpClient;

$http = new SmartHttpClient();
// Or with custom timeout:
$http = new SmartHttpClient(timeout: 60);

تكون المهلة افتراضيًا قيمة الثابت PITMASTER_HTTP_TIMEOUT (عددًا صحيحًا)، أو 30 ثانية إذا لم يكن معرّفًا.

اكتشاف المراجع

PHP
$discovery = $http->discoverRefs('https://github.com/user/repo.git');

foreach ($discovery->refs() as $name => $id) {
    echo "{$id->hex} {$name}\n";
}

// HEAD symref (e.g., 'refs/heads/main')
$defaultBranch = $discovery->headSymref();

// Specific ref
$mainId = $discovery->ref('refs/heads/main');

// Server capabilities (a Capability object, or null)
$caps = $discovery->capabilities();

ينفذ discoverReceivePackRefs() الأمر نفسه من أجل git-receive-pack. يُرسل طلب اكتشاف المراجع إلى:

الكود
GET <url>/info/refs?service=git-upload-pack

الاستجابة قائمة مراجع مرمّزة بصيغة pkt-line، مع الإمكانات في السطر الأول.

حزمة الرفع (الجلب)

PHP
$response = $http->uploadPack($url, $requestBody);

يرسل طلب POST إلى <url>/git-upload-pack يتضمن متن التفاوض على المطلوب والمتاح. ويعيد متن الاستجابة الخام (أسطر pkt، وNAK، وبيانات النطاق الجانبي). ينشئ UploadPackClient الطلب ويستخرج الحزمة:

PHP
use Pitmaster\Protocol\UploadPackClient;

$client = new UploadPackClient($http);
$packData = $client->fetch($url, $wants, $haves);   // ObjectId[] wants and haves

حزمة الاستلام (الدفع)

PHP
$response = $http->receivePack($url, $requestBody);

يرسل طلب POST إلى <url>/git-receive-pack يتضمن أوامر تحديث المراجع وبيانات الحزمة.

ترميز PktLine

يستخدم بروتوكول git تأطير pkt-line لجميع الاتصالات. يسبق كل سطر طوله الإجمالي، بما في ذلك بادئة الطول ذات 4 بايتات، المرمّز بأربعة أرقام ست عشرية.

PHP
use Pitmaster\Protocol\PktLine;

// Encode a single line
$encoded = PktLine::encode("want abc123def456\n");
// "0016want abc123def456\n"

// Flush packet (end of section)
$flush = PktLine::flush();
// "0000"

// Delimiter packet (protocol v2)
$delim = PktLine::delimiter();
// "0001"

// Decode a stream
$lines = PktLine::decode($rawData);
// Each element is: string (data), null (flush), or false (delimiter)

قواعد الطول

  • الحد الأدنى لسطر البيانات: 0005 (حمولة بحجم 1 بايت)
  • الحد الأقصى لسطر البيانات: fff0 (حمولة بحجم 65516 بايت)
  • حزمة التفريغ: 0000
  • حزمة الفاصل: 0001
  • نهاية الاستجابة: 0002 (في البروتوكول v2؛ يتعامل معها ProtocolV2::decode()، بينما يرفضها PktLine::decode())
PHP
PktLine::MAX_PAYLOAD;  // 65516 (65520 - 4)

اكتشاف المراجع

تحلّل الفئة RefDiscovery إعلان المراجع الصادر عن الخادم.

PHP
use Pitmaster\Protocol\RefDiscovery;

$discovery = RefDiscovery::parse($pktLines);

// All refs
$refs = $discovery->refs();
// ['refs/heads/main' => ObjectId, 'refs/tags/v1.0' => ObjectId, ...]

// Single ref
$id = $discovery->ref('refs/heads/main');

// HEAD symbolic ref (from capabilities)
$symref = $discovery->headSymref();
// 'refs/heads/main' or null

// Server capabilities
$caps = $discovery->capabilities();
// Capability object (see below), or null

تنسيق إعلان المراجع

يتضمن السطر الأول من الإعلان الإمكانات:

الكود
<hash> HEAD\0<capability list>\n
<hash> refs/heads/main\n
<hash> refs/tags/v1.0\n
0000

الإمكانات

تحلّل الفئة Capability إمكانات البروتوكول وتديرها.

PHP
use Pitmaster\Protocol\Capability;

$caps = Capability::parse('thin-pack side-band-64k ofs-delta symref=HEAD:refs/heads/main');

$caps->has('thin-pack');           // true
$caps->get('symref');              // 'HEAD:refs/heads/main'
$caps->all();                      // ['thin-pack' => null, ..., 'symref' => 'HEAD:refs/heads/main']

الإمكانات الشائعة

الإمكانية المعنى
multi_ack يدعم الخادم التفاوض متعدد الإقرارات
thin-pack يرسل الخادم حزمًا رقيقة (قد تُحذف الكائنات الأساسية). يطلبها Pitmaster، لكنه لا يستطيع فهرسة الحزم الرقيقة بعد (#16)
side-band يستخدم الخادم النطاق الجانبي لبيانات الحزمة (بحد أقصى 1000 بايت)
side-band-64k يستخدم الخادم نطاقًا جانبيًا بحجم 64K لبيانات الحزمة
ofs-delta قد تستخدم الحزمة كائنات OFS_DELTA
shallow يدعم الخادم الاستنساخ السطحي
no-done لا يتطلب الخادم سطر «انتهاء»
symref=HEAD:refs/heads/main الهدف الرمزي لـ HEAD
agent=git/2.45.0 إصدار git لدى الخادم

فصل بيانات النطاق الجانبي

عندما يستخدم الخادم النطاق الجانبي، تتعدد قنوات بيانات الحزمة لتشمل رسائل التقدم والأخطاء. يشير البايت الأول من حمولة كل سطر pkt-line إلى القناة:

القناة المعنى
1 بيانات الحزمة
2 رسائل التقدم (stderr)
3 رسائل الخطأ الفادح

تزيل Pitmaster الخاصة بـ UploadPackClient بايتات القنوات وتعيد تجميع بيانات الحزمة.

إصدارات البروتوكول

v1 (افتراضي)

البروتوكول التقليدي. يرسل اكتشاف المراجع كل المراجع دفعة واحدة. يتفاوض Pitmaster في جولة واحدة: يرسل المطلوب، ورؤوس كل المراجع المحلية على هيئة have، وdone.

v2

بروتوكول أبسط وأفضل أداءً.

الاختلافات الرئيسية عن v1:

  • اكتشاف المراجع أمر منفصل ls-refs (يمكنه تصفية المراجع)
  • الجلب أمر واحد fetch بمعلمات منظّمة
  • يستخدم حزمًا ذات فواصل (0001) للفصل بين الأقسام

يستخدم كل من clone() وfetch() الإصدار v1 دائمًا. يتوفر v2 عند استدعائه صراحةً:

PHP
$discovery = $http->discoverRefsV2($url);       // ls-refs
$packData = (new UploadPackClient($http))->fetchV2($url, $wants, $haves);

تدفق طلب HTTP

الاستنساخ/الجلب

الكود
1. GET  /info/refs?service=git-upload-pack
   Response: pkt-line encoded ref list

2. POST /git-upload-pack
   Request:  want <hash>\n ... have <hash>\n ... done\n
   Response: NAK\n + pack data (possibly side-band wrapped)

الدفع

الكود
1. GET  /info/refs?service=git-receive-pack
   Response: pkt-line encoded ref list

2. POST /git-receive-pack
   Request:  <old-hash> <new-hash> refs/heads/main\0<capabilities>\n
             0000
             PACK<pack data>
   Response: unpack ok\n + ref status lines

معالجة الأخطاء

تؤدي أخطاء الشبكة إلى رمي ProtocolException. عندما يفشل تبادل clone() أو fetch() الذكي عبر HTTP، يعيد Pitmaster المحاولة عبر HTTP البسيط، ثم يعيد رمي الخطأ الأصلي إذا فشلت المحاولة أيضًا. أما الإخفاقات الأخرى (مثل جهة بعيدة غير معروفة أو مقبض بلا عملية أو حزمة يتعذر فهرستها) فترمي أنواعًا أخرى من الاستثناءات:

PHP
use Pitmaster\Exceptions\ProtocolException;

try {
    $repo->fetch();
} catch (ProtocolException $e) {
    echo "Network error: {$e->getMessage()}\n";
}

أخطاء شائعة:

  • تعذّر الوصول إلى الخادم (انتهاء المهلة)
  • المصادقة مطلوبة أو المستودع غير موجود (يُبلّغ عن ذلك باعتباره Unexpected HTTP status 401 أو 403 أو 404)
  • Content-Type غير متوقع في الاستجابة
  • بيانات pkt-line غير صالحة
  • رسالة خطأ في قناة النطاق الجانبي