ينفذ Bun معيار WHATWG fetch، مع بعض الامتدادات لتلبية احتياجات JavaScript من جانب الخادم.
ينفذ Bun أيضًا node:http، لكن fetch موصى به بدلاً من ذلك.
إرسال طلب HTTP
لإرسال طلب HTTP، استخدم fetch
const response = await fetch("http://example.com");
console.log(response.status); // => 200
const text = await response.text(); // أو response.json()، response.formData()، إلخ.يعمل fetch أيضًا مع عناوين HTTPS.
const response = await fetch("https://example.com");يمكنك أيضًا تمرير كائن Request إلى fetch.
const request = new Request("http://example.com", {
method: "POST",
body: "Hello, world!",
});
const response = await fetch(request);إرسال طلب POST
لإرسال طلب POST، مرر كائنًا مع تعيين خاصية method إلى "POST".
const response = await fetch("http://example.com", {
method: "POST",
body: "Hello, world!",
});يمكن أن يكون body سلسلة أو كائن FormData أو ArrayBuffer أو Blob، وأكثر من ذلك. راجع توثيق MDN لمزيد من المعلومات.
بروكسي الطلبات
لبروكسي طلب، مرر كائنًا مع تعيين خاصية proxy إلى سلسلة URL:
const response = await fetch("http://example.com", {
proxy: "http://proxy.com",
});يمكنك أيضًا استخدام تنسيق كائن لإرسال رؤوس مخصصة إلى خادم الوكيل:
const response = await fetch("http://example.com", {
proxy: {
url: "http://proxy.com",
headers: {
"Proxy-Authorization": "Bearer my-token",
"X-Custom-Proxy-Header": "value",
},
},
});يتم إرسال headers مباشرة إلى الوكيل في طلبات CONNECT (لأهداف HTTPS) أو في طلب الوكيل (لأهداف HTTP). إذا قدمت رأس Proxy-Authorization، فإنه يتجاوز أي بيانات اعتماد في عنوان URL للوكيل.
رؤوس مخصصة
لتعيين رؤوس مخصصة، مرر كائنًا مع تعيين خاصية headers إلى كائن.
const response = await fetch("http://example.com", {
headers: {
"X-Custom-Header": "value",
},
});يمكنك أيضًا تعيين الرؤوس باستخدام كائن Headers.
const headers = new Headers();
headers.append("X-Custom-Header", "value");
const response = await fetch("http://example.com", {
headers,
});أجوبة الاستجابة
لقراءة جسم الاستجابة، استخدم إحدى الطرق التالية:
response.text(): Promise<string>: يرجع promise يحل مع جسم الاستجابة كسلسلة.response.json(): Promise<any>: يرجع promise يحل مع جسم الاستجابة ككائن JSON.response.formData(): Promise<FormData>: يرجع promise يحل مع جسم الاستجابة ككائنFormData.response.bytes(): Promise<Uint8Array>: يرجع promise يحل مع جسم الاستجابة كـUint8Array.response.arrayBuffer(): Promise<ArrayBuffer>: يرجع promise يحل مع جسم الاستجابة كـArrayBuffer.response.blob(): Promise<Blob>: يرجع promise يحل مع جسم الاستجابة كـBlob.
تدفق أجوبة الاستجابة
يمكنك استخدام iterators غير المتزامنة لتدفق جسم الاستجابة.
const response = await fetch("http://example.com");
for await (const chunk of response.body) {
console.log(chunk);
}يمكنك أيضًا الوصول بشكل مباشر إلى كائن ReadableStream.
const response = await fetch("http://example.com");
const stream = response.body;
const reader = stream.getReader();
const { value, done } = await reader.read();تدفق أجوبة الطلبات
يمكنك أيضًا تدفق البيانات في أجوبة الطلبات باستخدام ReadableStream:
const stream = new ReadableStream({
start(controller) {
controller.enqueue("Hello");
controller.enqueue(" ");
controller.enqueue("World");
controller.close();
},
});
const response = await fetch("http://example.com", {
method: "POST",
body: stream,
});عند استخدام streams مع HTTP(S):
- يتم تدفق البيانات مباشرة إلى الشبكة بدون تخزين الجسم بالكامل في الذاكرة
- إذا فُقد الاتصال، يُلغى الـ stream
- لا يتم تعيين رأس
Content-Lengthتلقائيًا ما لم يكن للـ stream حجم معروف
عند استخدام streams مع S3:
- لطلبات PUT/POST، يستخدم Bun تلقائيًا تحميل متعدد الأجزاء
- يتم استهلاك الـ stream في قطع وتحميلها بشكل متوازٍ
- يمكن مراقبة التقدم من خلال خيارات S3
جلب URL مع مهلة
لجلب URL مع مهلة، استخدم AbortSignal.timeout:
const response = await fetch("http://example.com", {
signal: AbortSignal.timeout(1000),
});إلغاء طلب
لإلغاء طلب، استخدم AbortController:
const controller = new AbortController();
const response = await fetch("http://example.com", {
signal: controller.signal,
});
controller.abort();مقابس مجال Unix
لجلب URL باستخدام مقبس مجال Unix، استخدم خيار unix: string:
const response = await fetch("https://hostname/a/path", {
unix: "/var/run/path/to/unix.sock",
method: "POST",
body: JSON.stringify({ message: "Hello from Bun!" }),
headers: {
"Content-Type": "application/json",
},
});TLS
لاستخدام شهادة عميل، استخدم خيار tls:
await fetch("https://example.com", {
tls: {
key: Bun.file("/path/to/key.pem"),
cert: Bun.file("/path/to/cert.pem"),
// ca: [Bun.file("/path/to/ca.pem")],
},
});التحقق من TLS مخصص
لتخصيص التحقق من TLS، استخدم خيار checkServerIdentity في tls
await fetch("https://example.com", {
tls: {
checkServerIdentity: (hostname, peerCertificate) => {
// إرجاع Error إذا كانت الشهادة غير صالحة
},
},
});هذا مشابه لكيفية عمله في وحدة net في Node.
تعطيل التحقق من TLS
لتعطيل التحقق من TLS، اضبط rejectUnauthorized إلى false:
await fetch("https://example.com", {
tls: {
rejectUnauthorized: false,
},
});هذا مفيد بشكل خاص لتجنب أخطاء SSL عند استخدام شهادات ذاتية التوقيع، لكن هذا يعطل التحقق من TLS ويجب استخدامه بحذر.
خيارات الطلب
بالإضافة إلى خيارات fetch القياسية، يوفر Bun العديد من الامتدادات:
const response = await fetch("http://example.com", {
// التحكم في فك ضغط الاستجابة التلقائي (الافتراضي: true)
// يدعم gzip، deflate، brotli (br)، و zstd
decompress: true,
// تعطيل إعادة استخدام الاتصال لهذا الطلب
keepalive: false,
// مستوى تسجيل التصحيح
verbose: true, // أو "curl" لمخرجات أكثر تفصيلاً
});دعم البروتوكول
بالإضافة إلى HTTP(S)، يدعم fetch في Bun العديد من البروتوكولات الإضافية:
عناوين S3 - s3://
يدعم Bun الجلب من دلاء S3 مباشرة.
// استخدام متغيرات البيئة للبيانات الاعتمادية
const response = await fetch("s3://my-bucket/path/to/object");
// أو تمرير البيانات الاعتمادية بشكل صريح
const response = await fetch("s3://my-bucket/path/to/object", {
s3: {
accessKeyId: "YOUR_ACCESS_KEY",
secretAccessKey: "YOUR_SECRET_KEY",
region: "us-east-1",
},
});ملاحظة: فقط طريقتا PUT و POST تدعمان أجوبة الطلبات عند استخدام S3. للتحميلات، يستخدم Bun تلقائيًا تحميل متعدد الأجزاء للأجوبة المتدفقة.
يمكنك قراءة المزيد عن دعم Bun لـ S3 في توثيق S3.
عناوين الملفات - file://
يمكنك جلب ملفات محلية باستخدام بروتوكول file::
const response = await fetch("file:///path/to/file.txt");
const text = await response.text();على Windows، يتم تطبيع المسارات تلقائيًا:
// كلاهما يعمل على Windows
const response = await fetch("file:///C:/path/to/file.txt");
const response2 = await fetch("file:///c:/path\\to/file.txt");عناوين البيانات - data:
يدعم Bun مخطط عنوان data::
const response = await fetch("data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==");
const text = await response.text(); // "Hello, World!"عناوين Blob - blob:
يمكنك جلب blobs باستخدام عناوين تم إنشاؤها بواسطة URL.createObjectURL():
const blob = new Blob(["Hello, World!"], { type: "text/plain" });
const url = URL.createObjectURL(blob);
const response = await fetch(url);معالجة الأخطاء
يتضمن تنفيذ fetch في Bun العديد من حالات الأخطاء المحددة:
- استخدام جسم طلب مع طرق GET/HEAD سيرمي خطأ (وهو متوقع لواجهة برمجة تطبيقات fetch)
- محاولة استخدام كلا خياري
proxyوunixمعًا سيرمي خطأ - فشل التحقق من شهادة TLS عندما يكون
rejectUnauthorizedtrue (أو undefined) - قد ترمي عمليات S3 أخطاء محددة تتعلق بالمصادقة أو الأذونات
معالجة Content-Type
يضبط Bun تلقائيًا رأس Content-Type لأجوبة الطلبات عندما لا يتم توفيره صراحة:
- لكائنات
Blob، يستخدمtypeالخاص بالـ blob - لـ
FormData، يضبط حدود multipart المناسبة
التصحيح
للمساعدة في التصحيح، يمكنك تمرير verbose: true إلى fetch:
const response = await fetch("http://example.com", {
verbose: true,
});سيطبع هذا رؤوس الطلب والاستجابة إلى طرفيتك:
[fetch] > HTTP/1.1 GET http://example.com/
[fetch] > Connection: keep-alive
[fetch] > User-Agent: Bun/1.3.3
[fetch] > Accept: */*
[fetch] > Host: example.com
[fetch] > Accept-Encoding: gzip, deflate, br, zstd
[fetch] < 200 OK
[fetch] < Content-Encoding: gzip
[fetch] < Age: 201555
[fetch] < Cache-Control: max-age=604800
[fetch] < Content-Type: text/html; charset=UTF-8
[fetch] < Date: Sun, 21 Jul 2024 02:41:14 GMT
[fetch] < Etag: "3147526947+gzip"
[fetch] < Expires: Sun, 28 Jul 2024 02:41:14 GMT
[fetch] < Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
[fetch] < Server: ECAcc (sac/254F)
[fetch] < Vary: Accept-Encoding
[fetch] < X-Cache: HIT
[fetch] < Content-Length: 648ملاحظة: verbose: boolean ليس جزءًا من واجهة برمجة تطبيقات fetch القياسية للويب وهو خاص بـ Bun.
الأداء
قبل إرسال طلب HTTP، يجب إجراء بحث DNS. يمكن أن يستغرق هذا وقتًا طويلاً، خاصة إذا كان خادم DNS بطيئًا أو اتصال الشبكة ضعيفًا.
بعد بحث DNS، يجب توصيل مقبس TCP وقد تحتاج إلى إجراء مصافحة TLS. يمكن أن يستغرق هذا أيضًا وقتًا طويلاً.
بعد اكتمال الطلب، يمكن أن يستغرق استهلاك جسم الاستجابة أيضًا وقتًا طويلاً والذاكرة.
في كل خطوة من الخطوات، يوفر Bun واجهات برمجة تطبيقات لمساعدتك في تحسين أداء تطبيقك.
الجلب المسبق لـ DNS
للجلب المسبق لإدخال DNS، يمكنك استخدام واجهة برمجة تطبيقات dns.prefetch. واجهة برمجة التطبيقات هذه مفيدة عندما تعرف أنك ستحتاج للاتصال بمضيف قريبًا وتريد تجنب بحث DNS الأولي.
import { dns } from "bun";
dns.prefetch("bun.com");التخزين المؤقت لـ DNS
افتراضيًا، يخزن Bun ويهدر استعلامات DNS في الذاكرة لمدة تصل إلى 30 ثانية. يمكنك عرض إحصائيات التخزين المؤقت عن طريق استدعاء dns.getCacheStats():
لمعرفة المزيد حول التخزين المؤقت لـ DNS في Bun، راجع توثيق التخزين المؤقت لـ DNS.
الاتصال المسبق بمضيف
للاتصال المسبق بمضيف، يمكنك استخدام واجهة برمجة تطبيقات fetch.preconnect. واجهة برمجة التطبيقات هذه مفيدة عندما تعرف أنك ستحتاج للاتصال بمضيف قريبًا وتريد بدء بحث DNS الأولي، واتصال مقبس TCP، ومصافحة TLS مبكرًا.
import { fetch } from "bun";
fetch.preconnect("https://bun.com");ملاحظة: استدعاء fetch مباشرة بعد fetch.preconnect لن يجعل طلبك أسرع. الاتصال المسبق يساعد فقط إذا كنت تعرف أنك ستحتاج للاتصال بمضيف قريبًا، لكنك لست مستعدًا لإجراء الطلب بعد.
الاتصال المسبق عند بدء التشغيل
للاتصال المسبق بمضيف عند بدء التشغيل، يمكنك تمرير --fetch-preconnect:
bun --fetch-preconnect https://bun.com ./my-script.tsهذا يشبه إلى حد ما <link rel="preconnect"> في HTML.
هذه الميزة غير منفذة على Windows بعد. إذا كنت مهتمًا باستخدام هذه الميزة على Windows، يرجى تقديم مشكلة ويمكننا تنفيذ الدعم لها على Windows.
تجميع الاتصالات و HTTP keep-alive
يعيد Bun استخدام الاتصالات لنفس المضيف تلقائيًا. هذا معروف بتجميع الاتصالات. يمكن أن يقلل هذا بشكل كبير من الوقت المستغرق لإنشاء اتصال. لا تحتاج إلى فعل أي شيء لتفعيل هذا؛ إنه تلقائي.
حد الاتصال المتزامن
افتراضيًا، يحدد Bun الحد الأقصى لعدد طلبات fetch المتزامنة إلى 256. نفعل هذا لعدة أسباب:
- يحسن استقرار النظام العام. أنظمة التشغيل لديها حد أعلى لعدد مقابس TCP المفتوحة المتزامنة، عادة في الآلاف المنخفضة. الاقتراب من هذا الحد يتسبب في تصرف كمبيوترك بالكامل بشكل غريب. تتعطل التطبيقات وتنهار.
- يشجع إعادة استخدام اتصال HTTP Keep-Alive. لطلبات HTTP قصيرة العمر، غالبًا ما تكون أبطأ خطوة هي إعداد الاتصال الأولي. إعادة استخدام الاتصالات يمكن أن توفر الكثير من الوقت.
عند تجاوز الحد، يتم وضع الطلبات في قائمة الانتظار وإرسالها بمجرد انتهاء الطلب التالي.
يمكنك زيادة الحد الأقصى لعدد الاتصالات المتزامنة عبر متغير البيئة BUN_CONFIG_MAX_HTTP_REQUESTS:
BUN_CONFIG_MAX_HTTP_REQUESTS=512 bun ./my-script.tsالقيمة القصوى لهذا الحد محددة حاليًا بـ 65,336. رقم المنفذ الأقصى هو 65,535، لذا من الصعب جدًا على أي كمبيوتر واحد تجاوز هذا الحد.
تخزين الاستجابة المؤقت
يبذل Bun قصارى جهده لتحسين أداء قراءة جسم الاستجابة. أسرع طريقة لقراءة جسم الاستجابة هي استخدام إحدى هذه الطرق:
response.text(): Promise<string>response.json(): Promise<any>response.formData(): Promise<FormData>response.bytes(): Promise<Uint8Array>response.arrayBuffer(): Promise<ArrayBuffer>response.blob(): Promise<Blob>
يمكنك أيضًا استخدام Bun.write لكتابة جسم الاستجابة إلى ملف على القرص:
import { write } from "bun";
await write("output.txt", response);تفاصيل التنفيذ
- تجميع الاتصالات مفعل افتراضيًا لكن يمكن تعطيله لكل طلب مع
keepalive: false. يمكن أيضًا استخدام رأس"Connection: close"لتعطيل keep-alive. - تحميلات الملفات الكبيرة محسنة باستخدام استدعاء نظام
sendfileالخاص بنظام التشغيل في ظل ظروف محددة:- يجب أن يكون الملف أكبر من 32KB
- يجب ألا يستخدم الطلب وكيلًا
- على macOS، فقط الملفات العادية (وليس الأنابيب أو المقابس أو الأجهزة) يمكنها استخدام
sendfile - عندما لا تتحقق هذه الشروط، أو عند استخدام S3/تحميلات متدفقة، يعود Bun إلى قراءة الملف إلى الذاكرة
- هذا التحسين فعال بشكل خاص لطلبات HTTP (وليس HTTPS) حيث يمكن إرسال الملف مباشرة من النواة إلى مكدس الشبكة
- عمليات S3 تتعامل تلقائيًا مع توقيع الطلبات ودمج رؤوس المصادقة
ملاحظة: العديد من هذه الميزات هي امتدادات خاصة بـ Bun لواجهة برمجة تطبيقات fetch القياسية.