การเขียนสคริปต์ JavaScript
Chute รองรับการเขียนสคริปต์ JavaScript สำหรับการแก้ไขคำขอ/การตอบกลับขั้นสูง การจับคู่กฎที่กำหนดเอง การแก้ไข DNS และงานตามกำหนดเวลา สคริปต์ใช้เอนจิน JavaScriptCore ของ Apple และทำตาม API สคริปต์ที่เข้ากันได้กับ Surge
สคริปต์ถูกกำหนดในส่วน [Script] ของไฟล์การกำหนดค่า
การกำหนดค่า
[Script]
MyScript = type=http-request, script-path=/path/to/script.js, pattern=^https?://example\.com, requires-body=true, max-size=262144, timeout=10, argument=myArg, debug=true
CronJob = type=cron, script-path=/path/to/cron.js, cron-expression=* * * * *, wake-system=true
พารามิเตอร์สคริปต์
| พารามิเตอร์ | จำเป็น | ค่าเริ่มต้น | คำอธิบาย |
|---|---|---|---|
type |
ใช่ | http-request |
ประเภททริกเกอร์สคริปต์ (ดูด้านล่าง) |
script-path |
ใช่ | — | เส้นทางไฟล์ในเครื่องหรือ URL HTTP(S) ไปยังสคริปต์ JS |
pattern |
ไม่ | (ตรงทั้งหมด) | รูปแบบ regex URL เพื่อกรองเมื่อสคริปต์ทริกเกอร์ |
requires-body |
ไม่ | ตรวจจับอัตโนมัติ | บังคับให้สคริปต์รับเนื้อหาคำขอ/การตอบกลับเต็มรูปแบบ |
max-size |
ไม่ | 131072 (128KB) | ขนาดเนื้อหาสูงสุดเป็นไบต์สำหรับสคริปต์ที่เข้าถึงเนื้อหา |
timeout |
ไม่ | 5.0 วินาที | เวลาหมดการทำงานต่อสคริปต์ |
argument |
ไม่ | — | อาร์กิวเมนต์สตริงที่กำหนดเอง พร้อมใช้งานเป็น $argument ใน JS |
debug |
ไม่ | false | ข้ามแคชการคอมไพล์เพื่อการดีบัก |
cron-expression |
ไม่ | — | นิพจน์กำหนดเวลา cron (สำหรับประเภท cron เท่านั้น) |
wake-system |
ไม่ | false | ปลุกระบบเพื่อดำเนินการสคริปต์ cron (เฉพาะ iOS) |
enable |
ไม่ | true | เปิดหรือปิดใช้งานสคริปต์นี้ |
script-update-interval |
ไม่ | 0 | วินาทีระหว่างการอัปเดตสคริปต์ระยะไกล (-1 = ไม่เลย, 0 = เฉพาะเมื่อเริ่มต้น) |
ประเภทสคริปต์
| สตริงประเภท | Enum | คำอธิบาย |
|---|---|---|
http-request |
HTTP Request | ดักจับและแก้ไขคำขอ HTTP ก่อนอัปสตรีม |
http-response |
HTTP Response | ดักจับและแก้ไขการตอบกลับ HTTP ก่อนไคลเอนต์ |
http-request-before-send |
HTTP Request Before Send | แก้ไขคำขอหลังจากรวบรวมเนื้อหาเต็ม ก่อนส่ง |
rule |
Rule | ตรรกะการจับคู่กฎที่กำหนดเอง |
dns |
DNS | การแก้ไข DNS ที่กำหนดเอง |
cron |
Cron | สคริปต์ตามกำหนดเวลา |
event |
Event | ตัวจัดการเหตุการณ์ระบบ (เช่น network-changed) |
การตรวจจับเนื้อหาอัตโนมัติ: หากซอร์สสคริปต์มี
$request.bodyหรือ$response.bodyเนื้อหาจะถูกให้โดยอัตโนมัติสูงสุดmax-sizeใช้requires-body=trueเพื่อบังคับพฤติกรรมนี้
การอ้างอิง API JavaScript
สคริปต์ทำงานในสภาพแวดล้อม JavaScriptCore แบบแซนด์บ็อกซ์พร้อมวัตถุส่วนกลางต่อไปนี้
$request (อ่านอย่างเดียว)
พร้อมใช้งานใน: http-request, http-response, http-request-before-send, rule
| คุณสมบัติ | ประเภท | คำอธิบาย |
|---|---|---|
.url |
String | URL คำขอแบบเต็ม |
.method |
String | เมธอด HTTP (GET, POST ฯลฯ) หรือ QUERY สำหรับ DNS |
.headers |
Object | ส่วนหัวคำขอเป็นคู่ key-value |
.body |
String หรือ null | เนื้อหาคำขอ (ถอดรหัส UTF-8) |
.hostname |
String | ชื่อโฮสต์เป้าหมาย |
.destPort |
Number | พอร์ตปลายทาง |
.processPath |
String | เส้นทางกระบวนการที่ร้องขอ (เฉพาะ macOS) |
.userAgent |
String | ค่าส่วนหัว User-Agent |
.sourceIP |
String | ที่อยู่ IP ต้นทาง |
.listenPort |
Number | พอร์ตการฟังของพร็อกซี |
.requestId |
String | ID คำขอที่ไม่ซ้ำ |
.dnsResult |
String | ที่อยู่ IP ที่แก้ไขแล้ว |
.srcPort |
Number | พอร์ตต้นทาง |
.protocol |
String | โปรโตคอลที่ตรวจพบ: http, https, tcp, dns |
$response (อ่านอย่างเดียว)
พร้อมใช้งานใน: http-response
| คุณสมบัติ | ประเภท | คำอธิบาย |
|---|---|---|
.status |
Number | รหัสสถานะ HTTP |
.headers |
Object | ส่วนหัวการตอบกลับเป็นคู่ key-value |
.body |
String หรือ null | เนื้อหาการตอบกลับ (ถอดรหัส UTF-8) |
$done(value) — ตัวจัดการการเสร็จสมบูรณ์
ต้องถูกเรียกหนึ่งครั้งอย่างแน่นอนเมื่อสิ้นสุดสคริปต์เพื่อส่งสัญญาณการเสร็จสมบูรณ์ การทำงานของสคริปต์บล็อกจนกว่า $done() จะถูกเรียกหรือหมดเวลา
$done({}) // ส่งผ่าน — ไม่มีการแก้ไข
$done() // ยกเลิกการเชื่อมต่อ
$done({matched: true}) // ผลการจับคู่กฎ (เฉพาะสคริปต์กฎ)
$done({address: "1.2.3.4"}) // ผลลัพธ์ DNS (เฉพาะสคริปต์ dns)
ค่าส่งคืนของสคริปต์ HTTP Request:
$done({
url: "https://new.example.com/path", // เขียน URL ใหม่
headers: {"X-Custom": "value"}, // แก้ไขส่วนหัว
body: "new request body", // แก้ไขเนื้อหา
response: { // ส่งคืนการตอบกลับสังเคราะห์ (ข้ามอัปสตรีม)
status: 200,
headers: {"Content-Type": "text/html"},
body: "<html>Blocked</html>"
}
})
เมื่อระบุ response คำขอจะถูกตัดตอน: Chute ส่งคืนการตอบกลับสังเคราะห์โดยตรงไปยังไคลเอนต์โดยไม่ติดต่อเซิร์ฟเวอร์อัปสตรีม สิ่งนี้มีประโยชน์สำหรับการบล็อก การจำลอง API หรือการส่งคืนเนื้อหาที่แคช
ค่าส่งคืนของสคริปต์ HTTP Response:
$done({
status: 200, // แก้ไขรหัสสถานะ
headers: {"X-Custom": "value"}, // แก้ไขส่วนหัวการตอบกลับ
body: "new response body", // แก้ไขเนื้อหาการตอบกลับ
url: "https://other.example.com" // ทริกเกอร์การเปลี่ยนเส้นทาง 302
})
เมื่อระบุ url Chute ส่งคืนการเปลี่ยนเส้นทาง 302 ไปยัง URL ที่กำหนดแทนการตอบกลับเดิม
ค่าส่งคืนของสคริปต์ DNS:
$done({address: "1.2.3.4"}) // IP เดียว
$done({addresses: ["1.2.3.4", "5.6.7.8"]}) // หลาย IP
$done({address: "10.0.0.1", ttl: 300}) // พร้อม TTL ที่กำหนดเอง (วินาที, ค่าเริ่มต้น 60)
$done({server: "8.8.8.8"}) // ส่งต่อไปยังเซิร์ฟเวอร์ DNS ที่ระบุ
$httpClient — ไคลเอนต์ HTTP แบบ Async
ทำคำขอ HTTP จากภายในสคริปต์ คำขอทั้งหมดถูกยกเลิกเมื่อ $done() หรือหมดเวลา
$httpClient.get(url, function(error, response, data) {
if (error) {
console.log("Request failed: " + error)
} else {
console.log("Status: " + response.status)
console.log("Response: " + data)
}
})
$httpClient.post(url, {headers: {...}, body: "...", timeout: 5}, callback)
$httpClient.put(url, options, callback)
$httpClient.del(url, options, callback)
$httpClient.head(url, options, callback)
$httpClient.options(url, options, callback)
$httpClient.patch(url, options, callback)
ลายเซ็น Callback: callback(error, response, data)
error: สตริงข้อผิดพลาดหรือnullresponse:{status: Number, headers: Object}หรือnulldata: สตริงเนื้อหาการตอบกลับ UTF-8 หรือnull
$persistentStore — ที่เก็บคีย์-ค่า
ที่เก็บคีย์-ค่าแบบถาวรที่คงอยู่ข้ามการเริ่มต้นสคริปต์และกระบวนการ สนับสนุนโดย NSUserDefaults
$persistentStore.write(data, key) // เก็บค่า
$persistentStore.read(key) // ดึงค่า
$persistentStore.remove(key) // ลบค่า
$notification — การแจ้งเตือนในเครื่อง
ส่งการแจ้งเตือนระบบในเครื่อง
$notification.post("Title", "Subtitle", "Notification body text")
$network — ข้อมูลเครือข่าย
ข้อมูลสถานะเครือข่ายแบบอ่านอย่างเดียว
$network.dns // อาร์เรย์ของ IP เซิร์ฟเวอร์ DNS
$network.wifi // {ssid: "WiFiName", bssid: "aa:bb:cc:dd:ee:ff"}
$environment — ข้อมูลรันไทม์
$environment.system // "iOS" หรือ "macOS"
$environment.appVersion // สตริงเวอร์ชันแอป
$utils — ยูทิลิตี้
$utils.geoip("1.2.3.4") // รหัสประเทศ (เช่น "US")
$utils.ipasn("1.2.3.4") // หมายเลข ASN (เช่น "13335")
$utils.ungzip(data) // คลายการบีบอัดข้อมูล gzip
$klne — API ควบคุมพร็อกซี
ควบคุมรันไทม์พร็อกซีจากสคริปต์
$klne.policyGroups // รับกลุ่มนโยบายทั้งหมด
$klne.selectPolicy("Group", "Proxy") // สลับนโยบายสำหรับกลุ่ม
$klne.getActiveConnections() // แสดงรายการการเชื่อมต่อที่ใช้งาน
$klne.closeConnection("id") // ปิดการเชื่อมต่อ
$klne.flushDNS() // ล้างแคช DNS
$klne.startURLTest("Group") // ทริกเกอร์การทดสอบ URL สำหรับกลุ่ม
$klne.reloadConfiguration() // โหลดการกำหนดค่าทั้งหมดใหม่
$klne.setOutboundMode("rule") // ตั้งค่าโหมด: "global", "proxy", "direct", "rule"
$klne.setHTTPCaptureEnabled(true) // เปิด/ปิด MITM
$script — ข้อมูลเมตาสคริปต์
$script.name // ชื่อสคริปต์จากการกำหนดค่า
$script.type // สตริงประเภทสคริปต์
$script.startTime // การประทับเวลา Epoch
ตัวแปรส่วนกลางต่อการทำงาน
ตัวแปรต่อไปนี้ถูกฉีดต่อการทำงานของสคริปต์และเฉพาะเจาะจงกับประเภทสคริปต์บางประเภท
$argument — อาร์กิวเมนต์สคริปต์
ค่าสตริงจากพารามิเตอร์ argument= ในการกำหนดค่าสคริปต์ พร้อมใช้งานใน: http-request, http-response, http-request-before-send, rule, dns, cron
console.log("Argument: " + $argument)
$domain — โดเมน DNS (เฉพาะสคริปต์ DNS)
ชื่อโดเมนที่กำลังถูกสอบถาม พร้อมใช้งานเฉพาะในสคริปต์ dns
var domain = $domain // เช่น "example.com"
$cronexp — นิพจน์ Cron (เฉพาะสคริปต์ Cron)
นิพจน์กำหนดเวลา cron จากการกำหนดค่าสคริปต์ พร้อมใช้งานเฉพาะในสคริปต์ cron
console.log("Schedule: " + $cronexp) // เช่น "*/30 * * * *"
$event — ข้อมูลเหตุการณ์ (เฉพาะสคริปต์ Event)
ข้อมูลเกี่ยวกับเหตุการณ์ที่ทริกเกอร์ ปัจจุบันรองรับเฉพาะ network-changed
console.log("Event: " + $event.name) // "network-changed"
console — การบันทึก
console.log("Debug message") // บันทึกละเอียด
console.warn("Warning message") // บันทึกคำเตือน
console.error("Error message") // บันทึกคำเตือนพร้อมคำนำหน้า [JS-ERROR]
setTimeout(fn, seconds) — ตัวจับเวลา
กำหนดเวลาฟังก์ชันให้ทำงานหลังจากดีเลย์
setTimeout(function() {
console.log("Delayed execution")
}, 2.5) // 2.5 วินาที
$script(subScriptPath) — ตัวโหลดสคริปต์ย่อย
โหลดและประเมินไฟล์ JavaScript อื่น
$script("/path/to/helper.js")
$script("https://example.com/remote-script.js")
// ส่งข้อมูลระหว่างสคริปต์โดยใช้ $persistentStore
รายละเอียดประเภทสคริปต์
สคริปต์ HTTP Request
ทำงานเมื่อได้รับส่วนหัวคำขอ สามารถแก้ไข URL ส่วนหัว และเนื้อหาก่อนที่คำขอจะถูกส่งต่อ
[Script]
ModifyHeaders = type=http-request, script-path=modify.js, pattern=^https://api\.example\.com
สคริปต์ HTTP Response
ทำงานเมื่อได้รับส่วนหัวการตอบกลับ สามารถแก้ไขสถานะ ส่วนหัว และเนื้อหาก่อนส่งคืนไปยังไคลเอนต์
[Script]
ModifyResponse = type=http-response, script-path=response.js, pattern=^https://api\.example\.com
สคริปต์ HTTP Request Before Send
ทำงานหลังจากรวบรวมเนื้อหาคำขอเต็มรูปแบบ ก่อนส่งไปยังอัปสตรีม มีประโยชน์สำหรับการแก้ไขเนื้อหาคำขอ POST/PUT
[Script]
BeforeSend = type=http-request-before-send, script-path=before-send.js, pattern=^https://api\.example\.com, requires-body=true
สคริปต์ Rule
การจับคู่กฎที่กำหนดเอง สคริปต์ต้องเรียก $done({matched: true}) หรือ $done({matched: false})
[Rule]
SCRIPT,MyRuleScript,DIRECT
[Script]
MyRuleScript = type=rule, script-path=rule.js
สคริปต์ DNS
การแก้ไข DNS ที่กำหนดเอง รับ $domain และส่งคืนที่อยู่ที่แก้ไขแล้ว
// dns.js
var domain = $domain
if (domain === "internal.example.com") {
$done({address: "10.0.0.1", ttl: 300})
} else {
$done({}) // ส่งผ่านไปยังการแก้ไข DNS ปกติ
}
สคริปต์ Cron
การทำงานตามกำหนดเวลาโดยใช้นิพจน์ cron ช่วงขั้นต่ำคือ 60 วินาที
[Script]
HourlyTask = type=cron, script-path=hourly.js, cron-expression=0 * * * *
รองรับไวยากรณ์ cron แบบย่อ (เช่น */30 * * * * สำหรับทุก 30 นาที, ค่าที่คั่นด้วยคอมมา)
สคริปต์ Event
ถูกทริกเกอร์โดยเหตุการณ์ระบบ ปัจจุบันรองรับเหตุการณ์ network-changed (ยิงเมื่อ Wi-Fi หรือเครือข่ายเซลลูลาร์เปลี่ยน)
[Script]
NetChange = type=event, script-path=network-changed.js
วัตถุ $event พร้อมใช้งาน:
$event.name // "network-changed"
ตัวอย่างการใช้งานจริง
เปลี่ยนเส้นทางอุปกรณ์มือถือ
สคริปต์ http-request ที่เปลี่ยนเส้นทางผู้ใช้มือถือตาม User-Agent:
[Script]
MobileRedirect = type=http-request, script-path=mobile-redirect.js, pattern=^https://example\\.com
// mobile-redirect.js
var ua = $request.headers["User-Agent"] || ""
if (/Mobile|Android|iPhone/.test(ua)) {
$done({
response: {
status: 302,
headers: {"Location": "https://m.example.com" + $request.url.replace(/.*example\\.com/, "")},
body: ""
}
})
} else {
$done({})
}
บล็อกเนื้อหาในการตอบกลับ API
สคริปต์ http-response ที่ลบโฆษณาและเนื้อหาสปอนเซอร์จากการตอบกลับ JSON API:
[Script]
RemoveAds = type=http-response, script-path=remove-ads.js, pattern=^https://api\\.example\\.com/feed, requires-body=true
// remove-ads.js
var body = JSON.parse($response.body)
if (body.ads) {
delete body.ads
}
if (body.recommendations) {
body.recommendations = body.recommendations.filter(function(r) {
return !r.sponsored
})
}
$done({body: JSON.stringify(body)})
แก้ไขเนื้อหาคำขอก่อนส่ง
สคริปต์ http-request-before-send ที่ทำความสะอาดเพย์โหลด POST:
[Script]
SanitizePayload = type=http-request-before-send, script-path=sanitize.js, pattern=^https://api\\.example\\.com/submit, requires-body=true
// sanitize.js
var body = JSON.parse($request.body)
body.clientSecret = "[REDACTED]"
body.timestamp = Math.floor(Date.now() / 1000)
$done({body: JSON.stringify(body)})
กฎกำหนดเอง: การจัดเส้นทางตามเวลา
สคริปต์ rule ที่เลือกพร็อกซีที่แตกต่างกันขึ้นอยู่กับช่วงเวลาของวัน:
[Rule]
SCRIPT,TimeBasedRule,ProxyA
[Script]
TimeBasedRule = type=rule, script-path=time-rule.js
// time-rule.js
var hour = new Date().getHours()
if (hour >= 9 && hour < 18) {
$done({matched: false}) // ส่งต่อไปยังกฎถัดไปในช่วงเวลาทำงาน
} else {
$done({matched: true}) // ใช้ ProxyA ในช่วงนอกเวลาทำงาน
}
DNS กำหนดเองสำหรับโดเมนภายใน
สคริปต์ dns ที่แก้ไขชื่อโฮสต์ภายในเป็น IP ในเครื่อง:
[Script]
InternalDNS = type=dns, script-path=internal-dns.js
// internal-dns.js
var internalHosts = {
"gitlab.local": "10.0.0.10",
"registry.local": "10.0.0.11",
"monitor.local": "10.0.0.12"
}
if (internalHosts[$domain]) {
$done({address: internalHosts[$domain], ttl: 3600})
} else {
$done({}) // ส่งผ่านไปยัง DNS ปกติ
}
สลับนโยบายอัตโนมัติเมื่อเปลี่ยนเครือข่าย
สคริปต์ event ที่สลับไปยังกลุ่มนโยบายแบบประหยัดเมื่อใช้เซลลูลาร์:
[Script]
NetSwitch = type=event, script-path=network-switch.js
// network-switch.js
if (!$network.wifi.ssid) {
// ใช้เซลลูลาร์ — ใช้กลุ่มข้อมูลต่ำ
$klne.selectPolicy("MainGroup", "LowDataProxy")
console.log("Switched to cellular profile")
} else if ($network.wifi.ssid === "Office") {
$klne.selectPolicy("MainGroup", "DIRECT")
console.log("Switched to office profile")
}
ตรวจสอบสุขภาพเป็นระยะ
สคริปต์ cron ที่ตรวจสอบสุขภาพพร็อกซีทุก 30 นาที:
[Script]
HealthCheck = type=cron, script-path=health-check.js, cron-expression=*/30 * * * *
// health-check.js
$httpClient.head("https://www.google.com/generate_204", {timeout: 10},
function(error, response, data) {
if (error || response.status !== 204) {
console.error("Health check failed: " + (error || "status " + response.status))
$notification.post("Chute Alert", "Health Check", "Cannot reach Google")
} else {
console.log("Health check OK")
}
}
)
$done()
เพิ่มข้อมูลการตอบกลับ API ด้วยข้อมูลภายนอก
สคริปต์ http-response ที่เพิ่มข้อมูลผู้ใช้โดยเรียก API รอง:
[Script]
EnrichUsers = type=http-response, script-path=enrich.js, pattern=^https://api\\.example\\.com/users, requires-body=true
// enrich.js
var users = JSON.parse($response.body)
var pending = users.length
if (pending === 0) { $done({}) }
users.forEach(function(user, index) {
$httpClient.get("https://internal-api.example.com/avatar/" + user.id,
function(error, resp, data) {
if (!error && resp.status === 200) {
users[index].avatar = JSON.parse(data).url
}
pending--
if (pending === 0) {
$done({body: JSON.stringify(users)})
}
}
)
})
โมเดลการทำงาน
- สคริปต์ทั้งหมดทำงานบนคิวอนุกรมเฉพาะเพื่อความปลอดภัยของเธรด
- การทำงานของสคริปต์แต่ละครั้งมีเวลาหมดต่อสคริปต์ หาก
$done()ไม่ถูกเรียกภายในเวลาหมด สคริปต์จะถูกปฏิบัติเป็นส่งผ่าน - พูล JSContext (ขนาด 3) ถูกใช้เพื่อประสิทธิภาพ; คอนเท็กซ์ถูกใช้ซ้ำข้ามการทำงาน
- การคอมไพล์สคริปต์ถูกแคชโดยค่าเริ่มต้น; ใช้
debug=trueเพื่อข้ามแคช - บน iOS/tvOS สคริปต์อยู่ภายใต้ขีดจำกัดหน่วยความจำ 10MB; บน macOS 512MB
- สคริปต์ระยะไกล (เส้นทาง HTTP/HTTPS) ถูกดึงเมื่อเริ่มต้นและอาจถูกดึงใหม่ที่
script-update-intervalวินาที
การรวมสคริปต์โมดูล
สคริปต์ยังสามารถกำหนดในไฟล์ โมดูล (.sgmodule) ภายใต้ส่วน [Script]
```