Ngiêm cấm sử đụng PROXY và VPS vào mục đích trái pháp luật bạn sẽ phải chịu toàn bộ trách nhiệm trước pháp luật khi sử dụng dịch vụ của chúng tôi tks!
RFC 9112 message framing qua proxy: parser nhận và từ chối

RFC 9112 message framing qua proxy: bảng parser nhận và từ chối

Chuyên viên Kỹ thuật ProxyVN 04-06-2026 Đang tính... phút đọc

RFC 9112 message framing qua proxy là chuyện xác định đúng chỗ kết thúc của một thông điệp HTTP/1.1, dựa trên Content-Length hoặc Transfer-Encoding, khi thông điệp đi qua client, proxy và origin. Khi ba bên hiểu ranh giới khác nhau, kết quả nhẹ là file tải về bị cắt mà không ai biết, nặng là cache lưu sai nội dung. ProxyVN dựng một origin socket trả 13 response sai framing có kiểm soát, gửi 6 request sai tới hai máy chủ thật, rồi ghi lại từng parser nhận hay từ chối.

RFC 9112 message framing qua proxy: mục tiêu và phạm vi không lặp

Phần này chốt bài toán, đầu ra và giới hạn đạo đức của lab.

Bài toán cần giải

Một crawler lưu tệp CSV từ một nguồn qua proxy. Thỉnh thoảng tệp thiếu vài dòng cuối nhưng job vẫn báo thành công. Đội vận hành nghi proxy cắt dữ liệu. Thật ra nguồn gửi Content-Length lớn hơn số byte thật rồi đóng kết nối, và một công cụ trong chuỗi bỏ qua lỗi đó. Bài này chỉ cách tìm ra mắt xích nào đã bỏ qua.

Đầu ra nghiệm thu

Đầu ra là bộ fixture giao thức chạy trong lab riêng và bảng parser: mỗi fixture một dòng, mỗi client hoặc máy chủ một cột, mỗi ô ghi nhận hay từ chối, số byte body và loại lỗi. Kèm theo là hàm sinh thông điệp đúng và phép kiểm bằng một parser nghiêm.

Ranh giới với nén và Content-Digest

Bài không bàn gzip, Brotli hay cách giải nén body. Bài cũng không bàn Content-Digest, thứ kiểm nội dung có bị sửa không. Framing là câu hỏi đứng trước cả hai: body bắt đầu ở đâu và kết thúc ở đâu. Lỗi 411 Length Required chỉ được nhắc như một hệ quả.

Không hướng dẫn khai thác

Request smuggling là lớp tấn công lợi dụng việc hai máy chủ hiểu framing khác nhau. Bài không đưa ra cách khai thác, không gửi thông điệp mơ hồ tới máy chủ bên ngoài. Mọi fixture chỉ chạy tới máy chủ trên 127.0.0.1 do lab dựng. Mục tiêu là giúp người viết client, proxy và origin từ chối đúng.

Giới hạn của lab

Origin trả fixture là socket tự viết. Thứ thật trong lab là sáu bộ đọc: requests 2.34.2 trên urllib3 2.8.0, httpx 0.28.1 trên h11 0.16.0, http.client của Python 3.12.10, aiohttp 3.14.5, curl 8.13.0 trên Windows và h11 dùng trực tiếp. Hai máy chủ nhận request sai là http.server của thư viện chuẩn và aiohttp.

Ai nên đọc

Người viết crawler cần biết thư viện của mình phản ứng thế nào khi nguồn gửi framing sai. Người vận hành proxy hoặc reverse proxy cần biết khi nào phải trả 502. Người viết máy chủ nhỏ hoặc mock server cần sinh thông điệp đúng.

Đọc Content-Length/Transfer-Encoding để phòng response bị cắt và mơ hồ framing trong RFC 9112 message framing qua proxy

Phần này tóm tắt luật của chuẩn, chỉ giữ ý cần cho phép thử.

Thứ tự ưu tiên

RFC 9112 mục 6 liệt kê tám luật theo thứ tự để xác định độ dài body. Response cho HEAD, mã 1xx, 204 và 304 không có body. Response 2xx cho CONNECT biến kết nối thành tunnel. Sau đó mới tới Transfer-Encoding, Content-Length và đọc tới khi đóng kết nối.

Khi có cả hai header

Luật 3 nói nếu thông điệp có cả Transfer-Encoding và Content-Length, Transfer-Encoding thắng. Chuẩn nói thêm thông điệp như vậy có thể là dấu hiệu của request smuggling hoặc response splitting và nên được xử lý như lỗi. Tầng trung gian chọn chuyển tiếp phải bỏ Content-Length trước.

Such a message might indicate an attempt to perform request smuggling or response splitting and ought to be handled as an error. (RFC 9112, mục 6.3)

Chunked phải là mã hóa cuối

Luật 4 nói nếu chunked là mã hóa cuối cùng, body kết thúc khi khối chunk dài 0 xuất hiện. Với response, nếu có Transfer-Encoding mà chunked không ở cuối, body đọc tới khi máy chủ đóng kết nối. Với request trong trường hợp đó, máy chủ phải trả 400 và đóng kết nối.

Content-Length không hợp lệ

Luật 5 nói Content-Length không hợp lệ là lỗi không thể phục hồi. Ngoại lệ duy nhất là một danh sách các giá trị giống nhau, như 10, 10, khi đó dùng một giá trị. Proxy nhận response như vậy phải đóng kết nối tới máy chủ, bỏ response và trả 502 cho client.

Content-Length hợp lệ nhưng thiếu byte

Luật 6 nói nếu bên gửi đóng kết nối trước khi đủ số byte Content-Length, bên nhận phải coi thông điệp là chưa đầy đủ và đóng kết nối. Đây là luật trực tiếp liên quan tới tệp CSV thiếu dòng trong bài toán đầu bài.

Response không khai độ dài

Luật 8 nói response không có Content-Length và không có Transfer-Encoding thì body là mọi byte nhận được tới khi máy chủ đóng kết nối. Chuẩn nhắc rằng không có cách phân biệt response hoàn tất với response bị đứt giữa chừng, nên máy chủ nên khai độ dài khi có thể.

Dữ liệu thừa sau response

Chuẩn nói nếu còn dữ liệu sau response cuối trên một kết nối, client có thể bỏ đi, nhưng không được xử lý, lưu cache hay chuyển tiếp phần thừa đó như một response riêng. Lý do được nêu là nguy cơ đầu độc cache. Fixture R03 của lab kiểm đúng điểm này.

Request không bao giờ đọc tới khi đóng

Request không có Content-Length và không có Transfer-Encoding thì body dài 0. Request không bao giờ dùng kiểu đọc tới khi đóng kết nối. Máy chủ có thể trả 411 nếu không chấp nhận request có body mà không khai Content-Length.

Bảng tóm tắt luật

Bảng: Bảng tóm tắt luật

Tình huống

Luật RFC 9112

Hành vi đúng

Có cả TE và CL

6.3 luật 3

TE thắng, nên coi là lỗi, proxy bỏ CL

TE có chunked ở cuối

6.3 luật 4

Đọc tới chunk dài 0

Response TE không có chunked cuối

6.3 luật 4

Đọc tới khi đóng

Request TE không có chunked cuối

6.3 luật 4

400 rồi đóng

CL không hợp lệ

6.3 luật 5

Lỗi, proxy trả 502

CL lặp giá trị giống nhau

6.3 luật 5

Dùng một giá trị

Thiếu byte so với CL

6.3 luật 6

Thông điệp chưa đầy đủ

Phân tích RFC 9112 message framing qua proxy theo Content-Length và Transfer-Encoding

Phần này nói vì sao framing sai ở một chặng trở thành vấn đề của cả chuỗi.

HTTP/1.1 dùng chung kết nối

Với kết nối keep-alive, nhiều request và response đi nối tiếp trên một kết nối TCP. Bên nhận biết response thứ nhất kết thúc ở đâu chỉ nhờ framing. Nếu đọc thiếu, phần còn lại bị coi là đầu của response sau. Nếu đọc thừa, response sau bị nuốt vào body của response trước.

Proxy là bên đọc thứ hai

Proxy chuyển tiếp hoặc reverse proxy phải parse thông điệp để biết lúc nào chuyển xong. Origin, proxy và client là ba parser độc lập. Một thông điệp mơ hồ chỉ nguy hiểm khi hai trong ba parser hiểu khác nhau. Vì thế bảng của bài đặt nhiều parser cạnh nhau thay vì thử một.

Tunnel HTTPS không parse

Với HTTPS qua CONNECT, proxy chuyển tiếp chỉ chuyển byte đã mã hóa và không parse HTTP bên trong. Lỗi framing lúc đó chỉ liên quan tới client và origin. Bài này đo trường hợp proxy có đọc thông điệp: proxy HTTP thường, reverse proxy, hoặc proxy giải mã.

HTTP/2 và HTTP/3 khác

HTTP/2 và HTTP/3 có framing nhị phân riêng ở tầng khung, không dựa vào Content-Length để cắt body. Rủi ro xuất hiện lại khi một tầng trung gian chuyển từ HTTP/2 phía client sang HTTP/1.1 phía sau. Lab chỉ đo HTTP/1.1.

Chunked là gì

Chunked chia body thành các khối, mỗi khối mở đầu bằng độ dài viết hệ mười sáu, rồi CRLF, dữ liệu, CRLF. Khối cuối có độ dài 0, có thể theo sau bởi trailer. Fixture R04 là dạng đúng: hai khối 5 byte rồi khối 0.

HTTP/1.1 200 OK
Transfer-Encoding: chunked
 
5
01234
5
56789
0
 

Close-delimited

Response không khai độ dài kết thúc khi máy chủ đóng kết nối. Đây là lối cũ từ HTTP/1.0. Với crawler, đây là kiểu response nguy hiểm nhất vì một lần đứt mạng trông giống hệt một lần tải xong. Fixture R10 thuộc loại này.

Vì sao lỗi im lặng

Thư viện HTTP được viết để dễ dùng, nhiều cái chọn sửa lỗi nhẹ thay vì ném ngoại lệ. Lựa chọn đó tiện cho trình duyệt nhưng nguy hiểm cho pipeline dữ liệu, nơi một tệp bị cắt được lưu như tệp tốt. Bảng của lab cho thấy mỗi thư viện chọn ở đâu.

Hai loại rủi ro

Rủi ro thứ nhất là dữ liệu sai: body bị cắt, thừa hoặc trộn. Rủi ro thứ hai là bảo mật: hai parser bất đồng trên cùng một kết nối dùng chung. Crawler chủ yếu gặp loại thứ nhất. Người vận hành proxy và origin phải lo cả hai.

Tạo thông điệp đúng cùng fixture sai được kiểm soát cho RFC 9112 message framing qua proxy

Phần này mô tả bộ fixture và cách chạy.

Origin socket

Origin là một máy chủ socket trên 127.0.0.1:9841. Nó đọc request tới dòng trống, lấy tên fixture từ đường dẫn, gửi đúng chuỗi byte của fixture, chờ 0,3 giây rồi đóng kết nối. Không có thư viện HTTP nào ở phía origin, nên byte gửi đi là byte viết trong mã.

13 response fixture

Bộ 13 fixture

Bộ 13 fixture

R01 là Content-Length đúng. R02 khai 20 byte nhưng gửi 10. R03 khai 5 byte nhưng gửi 10. R04 là chunked đúng. R05 là chunked thiếu khối 0. R06 có cả Content-Length 4 và chunked. R07 có hai Content-Length 10 và 4.

Phần còn lại của bộ fixture

R08 có Content-Length 10, 10. R09 có Transfer-Encoding gzip không có chunked. R10 không có cả hai header. R11 có độ dài chunk là zz. R12 là chunked đúng có trailer X-Sum. R13 có Content-Length -1. Mọi fixture có body là mười chữ số 0123456789.

FIX = {
 "R02 CL 20, body 10": b"HTTP/1.1 200 OK\r\nContent-Length: 20\r\n\r\n" + BODY,
 "R06 CL va TE": b"HTTP/1.1 200 OK\r\nContent-Length: 4\r\n"
                 b"Transfer-Encoding: chunked\r\n\r\n5\r\n01234\r\n5\r\n56789\r\n0\r\n\r\n",
 "R07 hai CL khac": b"HTTP/1.1 200 OK\r\nContent-Length: 10\r\nContent-Length: 4\r\n\r\n" + BODY,
}

Sáu bộ đọc

Mỗi fixture được đọc bằng requests, httpx, http.client, aiohttp, curl và h11. Năm cái đầu dùng cách gọi thông thường của người viết crawler. h11 được dùng trực tiếp như một parser nghiêm làm thước đo. Mỗi lượt đọc có thời gian chờ 3 giây.

Đọc curl theo mã thoát

curl ghi body ra stdout và in lỗi ra stderr. Lab ghi cả mã thoát, số byte body và dòng lỗi. Điểm này quan trọng vì curl vẫn ghi phần body đã nhận ra stdout khi gặp lỗi. Script chỉ đọc stdout sẽ không biết có lỗi.

6 request fixture

Phần thứ hai gửi request POST sai tới hai máy chủ thật. Q1 là Content-Length đúng. Q2 có cả Content-Length 3 và chunked. Q3 có hai Content-Length. Q4 là chunked đúng. Q5 có Transfer-Encoding: chunked, gzip. Q6 có độ dài chunk zz.

Hai máy chủ nhận

Máy chủ thứ nhất là ThreadingHTTPServer của thư viện chuẩn với một handler đọc body theo Content-Length, giống cách nhiều mock server viết. Máy chủ thứ hai là aiohttp web với parser llhttp. Cả hai chỉ nghe trên 127.0.0.1.

Hai proxy lab

Phần thứ tư dựng hai reverse proxy trước origin fixture. Proxy strict dùng aiohttp client đọc trọn response, gặp lỗi thì trả 502. Proxy lax dùng requests đọc response rồi chuyển tiếp. Client gọi qua hai proxy để thấy lỗi của origin có tới client hay bị che đi.

Không có mạng bên ngoài

Mọi kết nối của lab là tới 127.0.0.1 trên cổng 9841 tới 9845. Không fixture nào được gửi ra ngoài. Đây là điều kiện bắt buộc, không phải chi tiết kỹ thuật.

Ghi cách client/proxy/origin từ chối hoặc đóng kết nối với RFC 9112 message framing qua proxy

Đây là kết quả thật của lab.

Thiếu byte so với Content-Length

Với R02, requests ném ChunkedEncodingError, httpx ném RemoteProtocolError, http.client ném IncompleteRead, aiohttp ném ClientPayloadError, h11 ném RemoteProtocolError. curl thoát mã 18 với dòng end of response with 10 bytes missing, nhưng vẫn ghi 10 byte ra stdout.

Bài học từ curl

Một script dạng curl -s -o file.csv rồi không kiểm mã thoát sẽ lưu tệp 10 byte như tệp tốt. Đây đúng là bệnh của tệp CSV thiếu dòng. curl không sai, nó báo lỗi rõ. Người gọi bỏ qua mã thoát mới là chỗ sai.

Chunked thiếu khối cuối

Với R05, năm thư viện Python đều báo lỗi. curl thoát mã 18 với dòng transfer closed with outstanding read data remaining và vẫn ghi 10 byte. Hành vi giống R02: thông điệp chưa đầy đủ được báo, nhưng phần đã nhận vẫn nằm trong đầu ra của curl.

Content-Length nhỏ hơn body

Với R03, requests, httpx, http.client, curl và h11 đều trả 200 với 5 byte 01234, không báo gì. aiohttp báo lỗi Bad status line với chuỗi 0123456789. aiohttp đã đọc năm byte thừa như đầu của response tiếp theo và từ chối. Năm parser khác bỏ phần thừa.

Có cả TE và CL

Với R06, requests, httpx, http.client, curl và h11 đều theo chunked, trả 10 byte, không cảnh báo. aiohttp từ chối với thông báo Transfer-Encoding can't be present with Content-Length. Cả hai cách đều nằm trong phạm vi chuẩn cho phép, nhưng chỉ aiohttp xử lý như lỗi theo lời khuyên của RFC.

Hai Content-Length khác nhau

R07 là chỗ bất đồng lớn nhất. requests, httpx, aiohttp và h11 từ chối. http.client trả 10 byte. curl trả 4 byte. Cùng một response, hai công cụ phổ biến lưu hai nội dung khác nhau. Đây đúng là tình huống chuẩn gọi là không thể phục hồi.

Các ca còn lại

R08 với 10, 10 được năm bộ đọc chấp nhận như một giá trị, đúng luật 5, còn aiohttp từ chối. R09 với gzip không chunked được requests, http.client, aiohttp và curl đọc tới khi đóng, còn httpx và h11 từ chối vì chỉ hỗ trợ chunked. R13 với -1 được requests và http.client đọc tới khi đóng, bốn bộ còn lại từ chối.

Bảng parser phía client

Bảng parser

Bảng parser

Bảng: Bảng parser phía client

Fixture

requests

httpx

http.client

aiohttp

curl

h11

R02 thiếu byte

lỗi

lỗi

lỗi

lỗi

mã 18, ghi 10 B

lỗi

R03 thừa byte

5 B

5 B

5 B

lỗi

5 B

5 B

R05 thiếu chunk 0

lỗi

lỗi

lỗi

lỗi

mã 18, ghi 10 B

lỗi

R06 TE và CL

10 B

10 B

10 B

lỗi

10 B

10 B

R07 hai CL khác

lỗi

lỗi

10 B

lỗi

4 B

lỗi

R08 CL 10, 10

10 B

10 B

10 B

lỗi

10 B

10 B

R09 TE gzip

10 B

lỗi

10 B

10 B

10 B

lỗi

R11 chunk zz

lỗi

lỗi

lỗi

lỗi

mã 56

lỗi

R13 CL -1

10 B

lỗi

10 B

lỗi

mã 8

lỗi

Request sai tới máy chủ

Với aiohttp, Q2, Q3, Q5 và Q6 đều nhận 400 Bad Request kèm lý do cụ thể, rồi kết nối đóng. Q1 và Q4 nhận 200 với 5 byte hello. Đây là hành vi chuẩn yêu cầu với request mơ hồ: từ chối và đóng.

Máy chủ thư viện chuẩn

Với http.server và handler đọc theo Content-Length, Q2 trả 200 và handler đọc được 3 byte là 5, CRLF, phần đầu của chunk. Q3 trả 200 với Content-Length đầu tiên. Q4, Q5, Q6 trả 200 với body 0 byte vì handler không giải mã chunked. Không lượt nào bị từ chối.

Điều phần này không nói

Kết quả của http.server phản ánh handler mẫu kiểu đơn giản, không phải mọi ứng dụng dựng trên thư viện chuẩn. Kết quả cho thấy một máy chủ nhỏ viết vội có thể chấp nhận request mà máy chủ khác từ chối. Bài dừng ở việc ghi nhận bất đồng, không đi tiếp sang cách lợi dụng nó.

Proxy strict và proxy lax

Proxy strict và lax

Proxy strict và lax

Qua proxy strict, R02, R03, R06, R07 và R13 đều thành 502 cho client. Qua proxy lax, R03 thành 200 với 5 byte, R06 thành 200 với 10 byte, R13 thành 200 với 10 byte, R02 và R07 thành 502. R09 đi qua cả hai với 200. R01 đi qua cả hai bình thường.

Proxy che lỗi

Qua proxy lax, client nhận response có Content-Length đúng với body đã bị proxy hiểu theo cách riêng. Từ phía client, không có dấu hiệu nào cho thấy origin từng gửi framing sai. Proxy đã sửa thông điệp và xóa bằng chứng. Khi chẩn đoán, phải đo ở cả hai phía proxy.

Sửa generator thông điệp và kiểm lại chỉ trong lab riêng với RFC 9112 message framing qua proxy

Phần này áp dụng cho ai viết mock server, máy chủ nhỏ hoặc proxy tự viết.

Một hàm sinh duy nhất

Lỗi framing thường đến từ việc ghép header bằng tay ở nhiều chỗ. Lab gom việc sinh response vào một hàm: hoặc Content-Length tính từ số byte thật, hoặc chunked, không bao giờ cả hai. Độ dài tính trên bytes sau khi mã hóa, không tính trên chuỗi ký tự.

def make_response(status, body, chunked=False, chunk=4, trailers=None):
    head = [f"HTTP/1.1 {status} OK"]
    if chunked:
        head.append("Transfer-Encoding: chunked")
        if trailers:
            head.append("Trailer: " + ", ".join(trailers))
        parts = [b"%x\r\n%s\r\n" % (len(body[i:i + chunk]), body[i:i + chunk])
                 for i in range(0, len(body), chunk)]
        tail = b"0\r\n" + b"".join(f"{k}: {v}\r\n".encode()
                                   for k, v in (trailers or {}).items()) + b"\r\n"
        return ("\r\n".join(head) + "\r\n\r\n").encode() + b"".join(parts) + tail
    head.append(f"Content-Length: {len(body)}")
    return ("\r\n".join(head) + "\r\n\r\n").encode() + body

Bẫy độ dài ký tự

Chuỗi xin chào có 8 ký tự nhưng 9 byte khi mã hóa UTF-8. Máy chủ tính Content-Length bằng len của chuỗi sẽ khai thiếu một byte, đúng dạng R03. Lab sinh response cho chuỗi này và h11 xác nhận body 9 byte, hợp lệ.

Kiểm bằng parser nghiêm

Mỗi thông điệp sinh ra được đưa qua h11 ở vai client. Nếu h11 đọc tới EndOfMessage và body đúng độ dài, thông điệp hợp lệ. Năm thông điệp sinh ra đều qua: Content-Length 10 byte, chunked khối 4 byte, chunked có trailer, body rỗng và chuỗi UTF-8.

Kiểm fixture xấu bằng cùng thước

Cùng hàm kiểm chạy trên các fixture xấu. R02 và R05 dừng ở trạng thái thiếu dữ liệu sau 10 byte. R07 và R11 bị từ chối với RemoteProtocolError. R06 được h11 chấp nhận theo chunked. Như vậy h11 không bắt được ca TE cùng CL, cần một kiểm riêng.

Kiểm thêm cho TE cùng CL

Generator nên có một khẳng định đơn giản: nếu đã có Transfer-Encoding thì không có Content-Length. Kiểm này rẻ, chạy trong test đơn vị, và bắt được lỗi mà parser nghiêm nhất trong lab bỏ qua.

Proxy nên trả 502

Proxy tự viết nên làm như proxy strict của lab: đọc response từ origin bằng parser từ chối lỗi framing, và trả 502 khi parser báo lỗi. Đây là hành vi RFC 9112 luật 5 yêu cầu với Content-Length không hợp lệ. Thêm header hoặc log ghi lý do để người vận hành tìm được nguồn lỗi.

Không gửi thử ra ngoài

Mọi kiểm thử của phần này chạy với origin trong cùng tiến trình hoặc trên 127.0.0.1. Kiểm generator của mình không cần gửi gì tới máy chủ của người khác. Nếu cần thử với máy chủ thật, dựng bản sao máy chủ đó trong lab riêng.

Đầu ra nghiệm thu RFC 9112 message framing qua proxy: fixture giao thức trong lab riêng và bảng parser nhận/từ chối thông điệp sai

Phần này gom đầu ra thành bản ghi có thể lưu.

Tệp đầu ra

Lab sinh result.json gồm phiên bản sáu bộ đọc, kết quả 13 fixture response, 6 fixture request và phần kiểm generator. result_d.json chứa kết quả qua hai proxy. Hai tệp out.txt và out_d.txt là bản in của cùng dữ liệu.

Danh sách kiểm tra

Danh sách nghiệm thu

Danh sách nghiệm thu

  • Fixture chỉ chạy trên 127.0.0.1, không gửi ra ngoài.
  • Mỗi fixture được đọc bằng nhiều parser khác nhau.
  • curl được đánh giá theo mã thoát, không theo stdout.
  • Có fixture thiếu byte, thừa byte, hai header, header lặp và chunk hỏng.
  • Có phép đo qua proxy để thấy lỗi có bị che không.
  • Generator được kiểm bằng parser nghiêm và kiểm riêng cho TE cùng CL.

Những gì lab chưa chạy

Chưa thử Nginx, HAProxy, Squid, Envoy hay proxy thương mại. Chưa thử HTTP/2 chuyển sang HTTP/1.1. Chưa thử keep-alive với nhiều request nối tiếp, nơi R03 có thể làm hỏng response sau. Chưa thử response cho HEAD, 204, 304. Chưa thử khoảng trắng hay ký tự lạ trong tên header.

Câu kết luận được phép viết

Câu được phép viết là: trong lab, với hai Content-Length khác nhau, http.client lấy 10 byte còn curl lấy 4 byte, và proxy lax che lỗi framing của origin. Câu không được phép viết là: một máy chủ hay proxy thương mại cụ thể có lỗ hổng. Lab không thử sản phẩm thương mại nào.

Mẫu báo cáo lỗi cho nguồn dữ liệu

Khi phát hiện nguồn gửi framing sai, báo cáo nên có URL, thời điểm, header Content-Length hoặc Transfer-Encoding nhận được, số byte thật và công cụ đã dùng. Không cần gửi kèm fixture hay phân tích khai thác. Thông tin này đủ để chủ nguồn sửa máy chủ.

Vận hành crawler an toàn với RFC 9112 message framing qua proxy

Phần này chuyển kết quả lab thành quy tắc cho pipeline thu thập.

Luôn kiểm số byte

Sau khi tải, so số byte nhận với Content-Length nếu có. Với requests, len(r.content) và header Content-Length cho phép kiểm nhanh. Khi lệch, đánh dấu bản ghi là chưa đầy đủ thay vì lưu. Phép kiểm này bắt được R03 mà năm bộ đọc bỏ qua.

Kiểm mã thoát của curl

Script dùng curl phải kiểm mã thoát khác 0 và xóa tệp đầu ra khi có lỗi. Thêm tùy chọn --fail để mã HTTP lỗi cũng làm curl thoát với mã khác 0. Mã 18 là dấu hiệu chuẩn của body thiếu.

Không lưu response close-delimited mà không kiểm

Response không khai độ dài như R10 không thể phân biệt với response bị đứt. Với dữ liệu quan trọng, kiểm nội dung bằng cách khác: JSON parse được, CSV có dòng cuối đầy đủ, tệp nén giải nén được. Ghi nhận nguồn nào hay gửi kiểu response này.

Chọn thư viện theo rủi ro

Lab cho thấy aiohttp từ chối nhiều nhất, http.client chấp nhận nhiều nhất. Không thư viện nào đúng trong mọi ca. Với pipeline dữ liệu, thư viện từ chối nhiều giúp phát hiện nguồn xấu sớm. Với crawler cần tải được càng nhiều càng tốt, cần thêm kiểm số byte ở tầng trên.

Proxy ở đâu trong luồng

Proxy chuyển tiếp HTTP cũng là một parser. Với HTTPS, proxy chỉ tạo tunnel và framing do client và origin tự lo. Khi cần IP tại Việt Nam cho crawler, anh em có thể mua proxy tại trang chủ ProxyVN, rồi đo framing ở cả hai phía như lab. Vai trò của proxy có ở bài Web Scraping proxy là gì, cách ghi chuỗi cấu hình có ở bài định dạng proxy. Lỗi khung ở HTTP/2 có ở bài ERR_HTTP2_PROTOCOL_ERROR.

Ghi log framing

Thêm vào log tải trang ba trường: Content-Length nhận được, có Transfer-Encoding không, số byte thật. Khi một nguồn bắt đầu gửi framing lạ, log cho thấy ngay. Không cần lưu body để chẩn đoán loại lỗi này.

Đưa fixture vào test

Bộ 13 fixture chạy trong vài giây và không cần mạng. Đưa nó vào test của crawler để kiểm hàm tải thật của dự án. Khi ai đó đổi thư viện HTTP hoặc nâng phiên bản, test cho biết hành vi với framing sai có đổi không.

RFC 9112 message framing qua proxy: câu hỏi thường gặp

Các câu dưới đây gom những điểm hay bị hỏi.

Có cả Content-Length và Transfer-Encoding thì đọc theo cái nào?

Theo Transfer-Encoding. RFC 9112 khuyên coi thông điệp đó là lỗi, và proxy chuyển tiếp phải bỏ Content-Length.

Vì sao file tải về bị thiếu mà không báo lỗi?

Thường do công cụ báo lỗi nhưng người gọi không kiểm. Trong lab, curl thoát mã 18 nhưng vẫn ghi phần body đã nhận.

Content-Length nhỏ hơn body thì sao?

Năm trên sáu bộ đọc trong lab lấy đúng số byte khai báo và bỏ phần thừa mà không báo. aiohttp báo lỗi.

Proxy phải làm gì với Content-Length không hợp lệ?

Đóng kết nối tới máy chủ, bỏ response và trả 502 cho client, theo RFC 9112 mục 6.3.

Hai Content-Length giống nhau có sao không?

Chuẩn cho phép dùng một giá trị nếu mọi giá trị giống nhau. Trong lab, aiohttp vẫn từ chối.

HTTPS qua proxy có bị ảnh hưởng không?

Với tunnel CONNECT, proxy không parse thông điệp bên trong. Framing chỉ còn là chuyện của client và origin.

Có nên thử các fixture này với website thật không?

Không. Fixture chỉ dùng trong lab riêng với máy chủ của mình. Gửi thông điệp mơ hồ tới máy chủ người khác là hành vi không được phép.

RFC 9112 message framing qua proxy và những điều cần nhớ

RFC 9112 message framing qua proxy quyết định body kết thúc ở đâu, và mỗi parser trong chuỗi có thể hiểu khác nhau. Lab cho thấy hai Content-Length khác nhau cho ra hai nội dung, curl ghi phần body thiếu dù báo lỗi, và proxy dễ dãi che mất lỗi của origin. Proxy.vn - Nhà cung cấp dịch vụ proxy chất lượng hàng đầu Việt Nam khuyên anh em kiểm số byte và mã thoát khi làm việc với RFC 9112 message framing qua proxy.

TIN TỨC LIÊN QUAN