JavaScript 스크립팅

Chute는 고급 요청/응답 수정, 사용자 정의 규칙 매칭, DNS 해석 및 예약 작업을 위한 JavaScript 스크립팅을 지원합니다. 스크립트는 Apple의 JavaScriptCore 엔진을 사용하며 Surge 호환 스크립트 API를 따릅니다.

스크립트는 설정 파일의 [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 JS 스크립트의 로컬 파일 경로 또는 HTTP(S) URL
pattern 아니오 (모두 일치) 스크립트 트리거 시점을 필터링하는 URL 정규식 패턴
requires-body 아니오 자동 감지 스크립트가 전체 요청/응답 본문을 수신하도록 강제
max-size 아니오 131072 (128KB) 본문 접근 스크립트의 최대 본문 크기(바이트)
timeout 아니오 5.0초 스크립트별 실행 시간 초과
argument 아니오 JS에서 $argument로 사용 가능한 사용자 정의 문자열 인자
debug 아니오 false 디버깅을 위해 컴파일 캐시 건너뛰기
cron-expression 아니오 Cron 일정 표현식 (cron 유형 전용)
wake-system 아니오 false cron 스크립트 실행을 위해 시스템 깨우기 (iOS 전용)
enable 아니오 true 이 스크립트 활성화 또는 비활성화
script-update-interval 아니오 0 원격 스크립트 업데이트 간격(초) (-1 = 안 함, 0 = 시작 시만)

스크립트 유형

유형 문자열 열거형 설명
http-request HTTP 요청 업스트림 전 HTTP 요청 가로채기 및 수정
http-response HTTP 응답 클라이언트 전 HTTP 응답 가로채기 및 수정
http-request-before-send HTTP 요청 전송 전 전체 본문 수집 후, 전송 전 요청 수정
rule 규칙 사용자 정의 규칙 매칭 로직
dns DNS 사용자 정의 DNS 해석
cron Cron 예약/타이머 스크립트
event 이벤트 시스템 이벤트 핸들러 (예: network-changed)

본문 자동 감지: 스크립트 소스에 $request.body 또는 $response.body가 포함되어 있으면, 본문이 자동으로 max-size까지 제공됩니다. 이 동작을 강제하려면 requires-body=true를 사용하세요.


JavaScript API 참조

스크립트는 다음 전역 객체가 사용 가능한 샌드박스 JavaScriptCore 환경에서 실행됩니다.

$request (읽기 전용)

사용 가능: http-request, http-response, http-request-before-send, rule

속성 유형 설명
.url String 전체 요청 URL
.method String HTTP 메서드 (GET, POST 등) 또는 DNS의 경우 QUERY
.headers Object 키-값 쌍으로 된 요청 헤더
.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 키-값 쌍으로 된 응답 헤더
.body String 또는 null 응답 본문 (UTF-8 디코딩)

$done(value) — 완료 핸들러

스크립트 종료 시 완료를 알리기 위해 반드시 정확히 한 번 호출해야 합니다. $done()이 호출되거나 시간 초과가 만료될 때까지 스크립트 실행이 차단됩니다.

$done({})                        // 통과 — 수정 없음
$done()                          // 연결 중단
$done({matched: true})           // 규칙 일치 결과 (규칙 스크립트 전용)
$done({address: "1.2.3.4"})      // DNS 결과 (DNS 스크립트 전용)

HTTP 요청 스크립트 반환 값:

$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 응답 스크립트 반환 값:

$done({
    status: 200,                              // 상태 코드 수정
    headers: {"X-Custom": "value"},           // 응답 헤더 수정
    body: "new response body",                // 응답 본문 수정
    url: "https://other.example.com"          // 302 리디렉션 트리거
})

url이 제공되면 Chute는 원래 응답 대신 주어진 URL로 302 리디렉션을 반환합니다.

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 클라이언트

스크립트 내에서 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(error, response, data)

  • error: 오류 문자열 또는 null
  • response: {status: Number, headers: Object} 또는 null
  • data: 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   // DNS 서버 IP 배열
$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 — 이벤트 정보 (이벤트 스크립트 전용)

트리거 이벤트에 대한 정보입니다. 현재 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 요청 스크립트

요청 헤더가 수신될 때 실행됩니다. 요청이 전달되기 전에 URL, 헤더 및 본문을 수정할 수 있습니다.

[Script]
ModifyHeaders = type=http-request, script-path=modify.js, pattern=^https://api\.example\.com

HTTP 응답 스크립트

응답 헤더가 수신될 때 실행됩니다. 클라이언트에 반환되기 전에 상태, 헤더 및 본문을 수정할 수 있습니다.

[Script]
ModifyResponse = type=http-response, script-path=response.js, pattern=^https://api\.example\.com

HTTP 요청 전송 전 스크립트

전체 요청 본문이 수집된 후, 업스트림으로 전송 직전에 실행됩니다. POST/PUT 요청 본문 수정에 유용합니다.

[Script]
BeforeSend = type=http-request-before-send, script-path=before-send.js, pattern=^https://api\.example\.com, requires-body=true

규칙 스크립트

사용자 정의 규칙 매칭입니다. 스크립트는 $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 * * * *, 쉼표로 구분된 값).

이벤트 스크립트

시스템 이벤트에 의해 트리거됩니다. 현재 network-changed 이벤트(WiFi 또는 셀룰러 네트워크 변경 시 발생)를 지원합니다.

[Script]
NetChange = type=event, script-path=network-changed.js

$event 객체 사용 가능:

$event.name  // "network-changed"

실용 예제

모바일 기기 리디렉션

User-Agent를 기반으로 모바일 사용자를 리디렉션하는 http-request 스크립트:

[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 응답에서 콘텐츠 차단

JSON API 응답에서 광고 및 스폰서 콘텐츠를 제거하는 http-response 스크립트:

[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)})

전송 전 요청 본문 수정

POST 페이로드를 정리하는 http-request-before-send 스크립트:

[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

내부 호스트명을 로컬 IP로 해석하는 dns 스크립트:

[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")
}

주기적 상태 확인

30분마다 프록시 상태를 확인하는 cron 스크립트:

[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 응답 보강

보조 API를 호출하여 사용자 데이터를 보강하는 http-response 스크립트:

[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 초마다 다시 가져옵니다.

모듈 스크립트 통합

스크립트는 [Script] 섹션 아래의 모듈 파일(.sgmodule)에서도 정의할 수 있습니다. ```

S. Smart Rabbit LLC © All Rights Reserved            updated 2026-06-28 02:09:17

results matching ""

    No results matching ""