การจำลองและการแทรกความผิดพลาด

การเฝ้าดูทราฟฟิกตอบว่าแอปทำอะไร หน้านี้ว่าด้วยอีกครึ่งหนึ่ง: ทำให้เครือข่ายตอบสิ่งที่ปกติมันจะไม่ตอบ เพื่อดูว่าแอปจะจัดการอย่างไร แบ็กเอนด์ที่ยังไม่ได้สร้าง จุดปลายทางที่คืน 500 การตอบกลับที่ใช้เวลาแปดวินาที API ที่หายไปเฉย ๆ

ทุกอย่างในหน้านี้ทำงานบนทุกแพลตฟอร์มที่ Chute รันได้ และทุกอย่างที่แตะ HTTPS ต้องเปิดการถอดรหัสสำหรับโฮสต์นั้นก่อน — คำขอที่เข้ารหัสซึ่ง Chute อ่านไม่ได้ ก็เป็นคำขอที่ Chute ตอบแทนไม่ได้

Chute จำลองอะไรได้และไม่ได้

Chute แทรกแซงที่การเชื่อมต่อและที่ข้อความ HTTP มันไม่มีตัวจัดรูปทราฟฟิก ดังนั้น:

ทำได้ คืนเนื้อหาสำเร็จรูป คืนรหัสสถานะที่เลือก ใส่ความหน่วงคงที่ก่อนคำขอหรือการตอบกลับ ปฏิเสธการเชื่อมต่อไปเลย บังคับไคลเอนต์ HTTP/3 ให้กลับไปใช้ TCP ส่งคำขอไปยังโฮสต์อื่นที่ไม่ใช่โฮสต์ที่ขอ
ทำไม่ได้ จำกัดแบนด์วิดท์ ทิ้งหรือสลับลำดับแพ็กเก็ต เพิ่มจิตเตอร์ ทำให้การเชื่อมต่อแย่ลงกลางคัน หรือจำลอง RTT ที่เจาะจงในระดับชั้นขนส่ง

ไม่มีส่วน [Throttle] ในการกำหนดค่า และไม่มีการจำกัดความเร็วที่ไหนเลย ถ้าคุณต้องการลิงก์ที่ช้าแทนที่จะเป็นการตอบกลับที่ช้า นั่นเป็นเรื่องของตัวจำลองสภาพเครือข่าย (Network Link Conditioner ของ Apple หรือเราเตอร์) ไม่ใช่ของ Chute

เลือกกลไก

สิ่งที่จะจำลอง ใช้ ที่ไหน
เนื้อหาการตอบกลับที่ยังไม่มีอยู่จริง [Map Local] การตอบกลับจำลอง
503 พอดี [URL Rewrite] … reject URL Rewrite
200 ที่ว่างเปล่า รูปเปล่า อ็อบเจกต์หรืออาร์เรย์ JSON ว่าง reject-200, reject-img / reject-tinygif, reject-dict, reject-array URL Rewrite
รหัสสถานะอื่น ๆ — 401, 429, 500 [Map Local] ที่มี status-code= หรือสคริปต์ http-request การตอบกลับจำลอง, การเขียนสคริปต์ JS
ความหน่วง สคริปต์ http-request หรือ http-response การเขียนสคริปต์ JS
จุดปลายทางที่เข้าไม่ถึงเลย กฎ REJECT นโยบายในตัว
คำขอที่ค้างอยู่จนไคลเอนต์ยอมแพ้ กฎ REJECT-DROP นโยบายในตัว
ไคลเอนต์ที่ไม่ยอมถอยไปใช้ TCP block-quic ตัวเลือกเบ็ดเตล็ด
แบ็กเอนด์อื่นหลัง URL เดิม [Host] หรือโหมด header ของ [URL Rewrite] การกำหนด DNS เฉพาะที่

เนื้อหาการตอบกลับสำเร็จรูป

[Map Local] ตอบคำขอที่ตรงจากไฟล์หรือจากข้อมูลที่ฝังไว้ บนสตรีม HTTP/2 ที่ถอดรหัสแล้วจะไม่ถามเซิร์ฟเวอร์จริงเลย ส่วน HTTP/1.1 คำขอยังคงถูกส่งออกไปและแทนที่เฉพาะคำตอบ:

[Map Local]
^https://api\.example\.com/v1/profile.* data="/Users/me/mocks/profile.json"
^https://api\.example\.com/v1/flags.* base64="eyJiZXRhIjogdHJ1ZX0="

สามสิ่งที่ตัดสินว่ามันจะได้ผลหรือไม่:

  • นิพจน์ปกติจับคู่ที่ตำแหน่งใดก็ได้ใน URL เช่นเดียวกับ URL Rewrite: ^https://api\.example\.com/v1/profile ครอบคลุมพาธนั้นและสตริงคิวรีใด ๆ ที่ตามมาอยู่แล้ว จึงไม่ต้องเติม .* ท้าย
  • data= ถูกอ่านโดยอุปกรณ์ที่รัน Chute บน Mac นั่นสะดวก — แก้ไฟล์แล้วคำขอถัดไปก็เห็นการเปลี่ยนแปลง บนโทรศัพท์หรือ Apple TV พาธจาก Mac ของคุณไม่มีความหมาย ให้ใช้ base64= แทน หรือเสิร์ฟไฟล์ผ่าน HTTP แล้วใช้ URL Rewrite
  • สถานะเป็น 200 OK เว้นแต่คุณจะกำหนด status-code= และการเชื่อมต่อจะถูกปิดหลังตอบ ส่วนหัวการตอบกลับใส่ไว้ใน header="Key: value|…"

เนื้อหารองรับตัวแปรเทมเพลต {{ "{{url}}" }}, {{ "{{host}}" }}, {{ "{{path}}" }}, {{ "{{method}}" }} และ {{ "{{ua}}" }} ซึ่งเพียงพอสำหรับทำการจำลองที่สะท้อนกลับว่าถูกถามอะไรมา

รหัสสถานะข้อผิดพลาด

สำหรับ 503 ไม่ต้องใช้สคริปต์ — URL rewrite โหมด reject คืน HTTP/1.1 503:

[URL Rewrite]
^https://api\.example\.com/v1/orders.* _ reject

โหมดพี่น้องครอบคลุมรูปแบบอื่นของ "ไม่มีอะไรใช้ได้": reject-200 (200 พร้อมเนื้อหาว่าง), reject-img และ reject-tinygif (GIF ขนาด 1×1), reject-dict ({} แบบ JSON, 200), reject-array ([] แบบ JSON, 200) ทั้งหมดมีผลกับ HTTPS เฉพาะเมื่อโฮสต์นั้นกำลังถูกถอดรหัสอยู่

สำหรับ รหัสสถานะอื่นใดก็ตาม วิธีที่สั้นที่สุดคือกฎการตอบกลับจำลองที่มี status-code=:

[Map Local]
^https://api\.example\.com/v1/orders data-type=text data="rate limited" status-code=429 header="Retry-After: 30"

หากคำตอบต้องขึ้นกับคำขอ ให้ใช้สคริปต์ http-request ลัดวงจรคำขอแทน:

[Script]
Fail429 = type=http-request, script-path=/Users/me/mocks/fail429.js, pattern=^https://api\.example\.com/v1/orders
// fail429.js — ตอบโดยไม่ติดต่อเซิร์ฟเวอร์
$done({
    response: {
        status: 429,
        headers: {
            "Content-Type": "application/json",
            "Retry-After": "30"
        },
        body: JSON.stringify({ error: "rate_limited" })
    }
})

pattern ของสคริปต์ตรงที่ตำแหน่งใดก็ได้ใน URL เช่นเดียวกับตระกูลการเขียนใหม่ — คำนำหน้าอย่าง ^https://api\.example\.com/v1/orders ก็พอแล้ว ไม่ต้องเติม .* ท้าย

บน HTTP/1.1 บรรทัดสถานะจะมีวลีเหตุผลมาตรฐานของรหัสนั้น (HTTP/1.1 429 Too Many Requests) ส่วนรหัสที่ไม่มีชื่อในทะเบียน เช่น 599 จะไม่มีวลี (HTTP/1.1 599)

ความหน่วง

สคริปต์ที่ลงทะเบียนตัวจับเวลาไว้จะมีชีวิตอยู่ต่อจนกว่าจะเรียก $done() ตัวจับเวลาจึงคือความหน่วง (สคริปต์ที่ไม่ได้ลงทะเบียนอะไรไว้เลยจะไม่รอ: มันผ่านไปทันทีที่คืนค่า)

[Script]
SlowAPI = type=http-response, script-path=/Users/me/mocks/slow.js, pattern=^https://api\.example\.com/v1/, timeout=15
// slow.js — ส่งการตอบกลับจริงคืนช้าไป 8 วินาที
setTimeout(function () {
    $done({})
}, 8)

งบประมาณคือ timeout ของสคริปต์เอง: ค่าเริ่มต้น 5 วินาที และค่าที่เกิน 30 จะถูกตัดเหลือ 30 สคริปต์ที่ยังไม่เรียก $done() เมื่อหมดเวลาจะถูกถือเป็นการปล่อยผ่าน — ข้อความเดินหน้าต่อโดยไม่ถูกแก้ — ความหน่วงที่ยาวกว่า timeout จึงไม่ได้ล้มเหลวอย่างเอิกเกริก มันแค่เลิกหน่วงเท่านั้น ตั้ง timeout ให้มากกว่าความหน่วงที่ต้องการ ดังตัวอย่างข้างบน

ใช้ type=http-request เพื่อหน่วงก่อนติดต่อเซิร์ฟเวอร์ (แอปเห็นการไป-กลับที่ช้า) และ type=http-response เพื่อหน่วงหลังจากนั้น (เซิร์ฟเวอร์เร็ว แต่แอปก็ยังต้องรอ)

จุดปลายทางที่หายไปเฉย ๆ

การจำลองแทนที่การตอบกลับ ส่วนกฎ REJECT ปฏิเสธการเชื่อมต่อ มันทำงานที่ระดับการเชื่อมต่อ จึงครอบคลุมทุกโปรโตคอลไม่ใช่แค่ HTTP และไม่ต้องถอดรหัส:

[Rule]
DOMAIN-SUFFIX,api.example.com,REJECT

รูปแบบย่อยของ REJECT ตอบกลับต่างกัน ซึ่งมีประโยชน์ในที่นี้ด้วย REJECT-DROP ไม่ตอบเลย ไคลเอนต์จึงรอจนหมดเวลา — เหมือนเซิร์ฟเวอร์ที่ตายแล้ว REJECT-200, REJECT-DICT, REJECT-ARRAY, REJECT-TINYGIF / REJECT-IMG และ REJECT-VIDEO ตอบคำขอ HTTP/1 แบบข้อความธรรมดาหรือที่ถอดรหัสแล้วด้วย 200 ที่สร้างขึ้น (ว่าง, {}, [], GIF ขนาด 1×1, MP4 เปล่า) REJECT, REJECT-TINYGIF และ REJECT-IMG จะกลายเป็น REJECT-DROP เป็นเวลา 30 วินาทีเมื่อโฮสต์เดียวถูกปฏิเสธเกิน 50 ครั้งภายใน 30 วินาที ส่วน REJECT-NO-DROP และรูปแบบย่อยอื่น ๆ จะไม่เป็นเช่นนั้น ดูนโยบายในตัว สำหรับคำขอ HTTP นั้น show-error-page-for-reject = true จะแทนการปฏิเสธเปล่า ๆ ด้วยหน้าข้อผิดพลาดที่อ่านได้ ทำให้เห็นชัดในเบราว์เซอร์ว่าการบล็อกนั้นเป็นของคุณ

นี่ยังเป็นวิธีตรวจว่ามีเส้นทางสำรองอยู่จริงหรือไม่ — ปฏิเสธโฮสต์หลักแล้วดูว่าแอปเอื้อมไปหาโฮสต์รองหรือแค่หมุนอยู่กับที่

บังคับไคลเอนต์ให้ลงจาก HTTP/3

QUIC วิ่งบน UDP และ Chute ถอดรหัสมันไม่ได้ แอปที่ใช้ HTTP/3 จึงมองไม่เห็นด้วยกลไกใด ๆ ในหน้านี้ การปฏิเสธโฟลว์ QUIC ของมันทำให้ไคลเอนต์ที่รองรับลองใหม่ผ่าน TCP ซึ่งทุกอย่างทำงานได้:

[General]
block-quic = on

auto ปฏิเสธ QUIC เฉพาะเมื่อโฟลว์มุ่งไปยังพร็อกซี ส่วน on ปฏิเสธทุกที่รวมถึง DIRECT สำหรับทราฟฟิกที่เข้ามาทาง TUN นั้น Chute ตอบโฟลว์ QUIC ที่ถูกปฏิเสธด้วย ICMP Port Unreachable ไคลเอนต์จึงถอยกลับทันทีแทนที่จะรอจนหมดเวลา

ส่งคำขอไปที่อื่น

สองวิธีในสองชั้น:

[Host]
api.example.com = 10.0.0.5

การแมป [Host] ตอบการค้นหา DNS ด้วยที่อยู่ที่คุณเลือก — เครื่อง staging หรือที่อยู่ที่ไม่ไปไหนเลย ถ้าสิ่งที่คุณต้องการคือการเชื่อมต่อที่หมดเวลาแทนที่จะถูกปฏิเสธ มันมีผลกับทุกโปรโตคอลและไม่ต้องถอดรหัส ล้างแคช DNS หลังเปลี่ยน

[URL Rewrite]
^https://api\.example\.com/v1/(.*) https://staging.example.com/v1/$1 header

โหมด header เขียนคำขอใหม่ในที่และแก้ส่วนหัว Host ให้ด้วย วิธีนี้อยู่ระดับ HTTP จึงต้องถอดรหัสสำหรับ HTTPS ไคลเอนต์จะไม่รู้เลยว่ามีการเขียนใหม่ ตราบใดที่คำขอยังคงใช้โฮสต์เดิม คำขอ HTTPS ที่ถอดรหัสแล้วผูกอยู่กับโฮสต์ที่มันถูกส่งไปหา ดังนั้นกฎที่ระบุโฮสต์อื่น — อย่างกฎข้างบน — จะได้รับการตอบด้วย 307 ชี้ไป URL ใหม่ ซึ่งไคลเอนต์จะตามไปเอง และในกรณีอื่นใดที่เขียนปลายทางใหม่ในที่ไม่ได้ Chute ก็ตอบด้วยวิธีเดียวกัน

ยืนยันว่ามันทำงานจริง

กฎที่ไม่เคยตรงเลยหน้าตาเหมือนกฎที่ตรงแล้วไม่ทำอะไรทุกประการ — และนี่คือรูปแบบความล้มเหลวของทุกอย่างในหน้านี้

  • การเขียนใหม่และการจำลอง: หน้า กฎ ของคอนโซลเว็บแสดงรายการกฎ URL Rewrite, Header Rewrite, Body Rewrite และการตอบกลับจำลองทุกข้อที่ทำงานในรอบนี้พร้อมจำนวนครั้ง ถ้าไม่อยู่ในรายการแปลว่ามันไม่เคยตรงเลย ข้อมูลชุดเดียวกันอยู่ใน rewrite_hits ของ GET /api/rules
  • รายการเชื่อมต่อ: เปิดการเชื่อมต่อนั้นในคอนโซลแล้วอ่านแถว การเขียนใหม่ที่ใช้ ซึ่งเรียกชื่อกฎด้วยถ้อยคำของกฎเอง ใน Dashboard แท็บ ทั่วไป ของการเชื่อมต่อจะนับกฎเหล่านี้เป็น ทำงาน ใต้หัวข้อ กฎ และระบุชื่อไว้ในรายการ กฎที่ทำงาน
  • สคริปต์ไม่ปรากฏในตารางนั้น หลักฐานของสคริปต์คือผลลัพธ์ของตัวมันเอง บรรทัด console.log จะลงในบันทึก อ่านได้ที่หน้า บันทึก ของคอนโซลหรือด้วย GET /api/logs บรรทัดเหล่านี้ถูกเขียนที่ระดับ verbose ดังนั้นให้เพิ่ม loglevel เป็น verbose ก่อน — PUT /api/loglevel ทำได้โดยไม่ต้องโหลดใหม่ ส่วน console.warn และ console.error จะแสดงที่ระดับค่าเริ่มต้น แต่ละบรรทัดจะถูกตัดให้เหลือ 256 อักขระ โดยข้อมูลรับรองจะถูกปิดบังไว้

เก็บกวาด

กฎที่เพิ่มจากคอนโซล จาก Dashboard หรือผ่าน POST /api/rewrites/:family อยู่ในเอนจินที่กำลังทำงาน และหายไปเมื่อรีสตาร์ตครั้งถัดไป ซึ่งเหมาะกับการทดลอง แต่เป็นที่แย่มากสำหรับเก็บสิ่งที่คุณต้องพึ่งพา กฎในไฟล์การกำหนดค่าอยู่รอดข้ามการรีสตาร์ต จึงเป็นที่ดีสำหรับเก็บการจำลอง และเป็นสิ่งที่ไม่ควรลืมอย่างยิ่ง: บรรทัด [Map Local] ที่ทิ้งไว้ในการกำหนดค่ายังคงตอบคำขออยู่อีกหลายสัปดาห์ถัดมา และหน้าตาเหมือนเซิร์ฟเวอร์ที่พังทุกประการ

S. Smart Rabbit LLC © All Rights Reserved            updated 2026-09-30 15:11:19

หน้านี้เป็นฉบับแปลจากเวอร์ชันภาษาอังกฤษ หากเนื้อหาไม่ตรงกัน ให้ยึดเวอร์ชันภาษาอังกฤษเป็นหลัก

results matching ""

    No results matching ""