HTTP 508 Loop Detected WebDAV: binding có vòng và PROPFIND
HTTP 508 Loop Detected WebDAV là mã máy chủ WebDAV trả khi nó dừng một thao tác vì gặp vòng lặp vô tận lúc duyệt cây tài nguyên với Depth: infinity. Vòng này sinh ra từ binding, khi một thư mục được gắn vào chính nó hoặc vào thư mục cha. Nó không liên quan tới vòng chuyển hướng URL trên trình duyệt. ProxyVN dựng một máy chủ binding WebDAV mô phỏng theo RFC 5842, chạy requests và httpx thật với PROPFIND các độ sâu khác nhau, rồi so với một vòng redirect 302 để thấy hai loại lỗi khác nhau ở đâu.
HTTP 508 Loop Detected WebDAV: 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à ranh giới.
Bài toán cần giải
Một công cụ đồng bộ kho tài liệu duyệt toàn bộ cây thư mục WebDAV để lập danh sách tệp cần tải. Lệnh PROPFIND Depth: infinity bỗng trả 508. Người vận hành tìm "loop detected" trên mạng, gặp toàn bài về vòng redirect, kiểm cấu hình chuyển hướng, kiểm proxy, không thấy gì. Lý do nằm trong cấu trúc kho: có một binding tạo vòng.
Đầu ra nghiệm thu
Đầu ra là đồ thị binding có vòng của lab, và trace PROPFIND với trạng thái từng tài nguyên trong phản hồi đa trạng thái. Trace phủ bốn tình huống: Depth 1, Depth infinity với client không biết binding, Depth infinity với client có khai DAV: bind, và máy chủ phát hiện vòng giữa lúc đang trả lời. Kèm theo là cách client tự dựng lại đồ thị bằng resource-id.
Ranh giới với bài redirect loop
Bài lỗi ERR_TOO_MANY_REDIRECTS nói về vòng chuyển hướng trên trình duyệt. Bài này không viết lại phần đó. Vòng redirect là chuỗi URL chỉ qua lại nhau qua mã 3xx. Vòng binding là cấu trúc dữ liệu trên máy chủ, nơi cùng một collection có nhiều đường dẫn. Phần cuối bài đặt hai thứ cạnh nhau để phân loại đúng.
Không áp cho mọi lỗi loop
Mã 508 có ngữ cảnh rất hẹp: WebDAV, binding, Depth: infinity. Một số máy chủ hoặc nền tảng hosting trả 508 với nghĩa riêng của họ, ví dụ vượt giới hạn tài nguyên. Bài không bàn những cách dùng đó. Gặp 508 ngoài WebDAV, đọc tài liệu của nhà cung cấp trước.
Giới hạn của lab
Máy chủ trong lab là http.server tự viết ở 127.0.0.1:9761, mô phỏng đồ thị binding bằng một từ điển resource-id. Không có BIND, UNBIND, REBIND, không có khóa, không lưu đĩa. Quy tắc trả 508 hay 208 viết theo RFC 5842. RFC 5842 thuộc loại Experimental, nên máy chủ thật hỗ trợ ở mức khác nhau. Thứ thật trong lab là hai thư viện client và parser XML.
Ai nên đọc
Người viết công cụ đồng bộ hoặc sao lưu kho WebDAV. Người viết crawler cho kho tài liệu nội bộ có cấu trúc thư mục phức tạp. Người gặp 508 và cần biết nó đến từ đâu trước khi đổ cho mạng.
Phân biệt HTTP 508 Loop Detected WebDAV trong binding với vòng redirect URL
Phần này tóm tắt chuẩn, chỉ giữ ý ảnh hưởng tới client.
Binding là gì
RFC 5842 mở rộng WebDAV cho phép một tài nguyên có nhiều binding, tức nhiều tên trong nhiều collection cùng trỏ tới một tài nguyên. Giống liên kết cứng trong hệ tệp. Khi binding trỏ tới collection, cùng một collection có thể xuất hiện ở nhiều đường dẫn.
Vòng binding

Binding tạo vòng
Theo RFC 5842, binding tới collection có thể tạo vòng. Ví dụ collection C có thành viên Bar trỏ lại chính C. Khi đó /Coll/, /Coll/Bar/, /Coll/Bar/Bar/ và vô số đường dẫn khác đều trỏ tới C. RFC yêu cầu máy chủ phải phát hiện vòng khi xử lý request Depth: infinity.
Hai cách xử lý vòng
RFC đưa ra hai lối thoát. Nếu client hiểu binding, PROPFIND vẫn thành công, và máy chủ dùng mã 208 Already Reported cho các binding thứ hai trở đi của cùng một collection. Nếu không, máy chủ trả 508 Loop Detected để báo thao tác đã dừng vì gặp vòng.
Định nghĩa 508
RFC 5842 mục 7.2 nói 508 cho biết máy chủ đã dừng một thao tác vì gặp vòng lặp vô tận khi xử lý request có Depth: infinity. Mã này cho biết toàn bộ thao tác thất bại. Client không nhận được danh sách dở dang nào có thể tin, trừ trường hợp máy chủ đang truyền phản hồi thì mới phát hiện, nói ở phần sau.
Định nghĩa 208
RFC 5842 mục 7.1 nói 208 dùng bên trong propstat của phản hồi đa trạng thái, để tránh liệt kê lặp lại thành viên của nhiều binding tới cùng collection. Với mỗi collection trong phạm vi request, chỉ một binding được báo với 200, các binding khác dùng 208, và không có phản hồi nào cho con cháu của chúng.
Client phải khai báo
RFC nói máy chủ không nên dùng 208 nếu client chưa báo hỗ trợ binding qua header DAV có giá trị bind. Lý do là tương thích ngược: client cũ không hiểu 208. Thay vào đó, máy chủ nên trả 508 khi gặp vòng. Một header quyết định client nhận danh sách đầy đủ hay nhận lỗi.
508 ở hai vị trí
RFC cho phép 508 xuất hiện ở hai chỗ. Là mã cấp cao nhất, nếu máy chủ phát hiện vòng trước khi bắt đầu trả lời. Hoặc nằm giữa phản hồi đa trạng thái, nếu máy chủ phát hiện khi đang truyền dữ liệu. Client vì vậy phải đọc mã từng phần tử, không chỉ mã HTTP.
Vì sao máy chủ phải dừng
Không có phát hiện vòng, PROPFIND Depth infinity trên cây có vòng sinh ra phản hồi dài vô hạn. Máy chủ tốn CPU, bộ nhớ, băng thông cho tới khi hết tài nguyên hoặc client ngắt. Mã 508 là cách cắt sớm, báo rõ lý do, thay vì để cả hai phía treo.
COPY và MOVE cũng bị ảnh hưởng
RFC 5842 có ví dụ COPY với Depth infinity trên cây có vòng, và MOVE có thể tạo ra vòng mới. Các thao tác ghi này nguy hiểm hơn PROPFIND, vì sao chép vô hạn có thể làm đầy kho. Bài chỉ lab PROPFIND, nhưng nguyên tắc đọc mã và khai bind áp cho cả thao tác ghi.
Bảng phân biệt
Bảng dưới đặt các tín hiệu liên quan tới vòng cạnh nhau.
Tín hiệu |
Nơi xuất hiện |
Nguyên nhân |
Client nhận gì |
|---|---|---|---|
508 cấp cao nhất |
Mã HTTP |
Vòng binding, client không khai bind |
Không có danh sách |
508 trong 207 |
Propstat của một phần tử |
Vòng phát hiện khi đang truyền |
Danh sách dở tới chỗ vòng |
208 trong 207 |
Propstat của binding lặp |
Vòng binding, client có khai bind |
Danh sách đầy đủ, không lặp |
TooManyRedirects |
Ngoại lệ client |
Chuỗi 3xx chỉ qua lại |
Không có tài nguyên |
RFC thuộc loại thử nghiệm
RFC 5842 có trạng thái Experimental. Nhiều máy chủ WebDAV phổ biến không hỗ trợ binding, và không bao giờ trả 508 hay 208. Kho dùng symlink trên hệ tệp có thể gặp vòng tương tự, cách máy chủ xử lý tùy phần mềm. Lab mô phỏng đúng RFC để có điểm so sánh rõ ràng.
Vẽ quan hệ binding HTTP 508 Loop Detected WebDAV có chu trình
Phần này dựng đồ thị của lab, chạy thật trên Python 3.12.10.
Năm tài nguyên
Lab có năm tài nguyên với resource-id dạng urn:uuid. C là collection Loop Demo, có ba thành viên Foo, Bar, Sub. R là tệp Bird Inventory. S là collection Sub có Note và Back. N là tệp Note. T là collection Tree không có vòng, dùng làm đối chứng.
RES = {
"urn:uuid:C": ("Loop Demo", {"Foo": "urn:uuid:R", "Bar": "urn:uuid:C", "Sub": "urn:uuid:S"}),
"urn:uuid:R": ("Bird Inventory", None),
"urn:uuid:S": ("Sub", {"Note": "urn:uuid:N", "Back": "urn:uuid:C"}),
"urn:uuid:N": ("Note", None),
"urn:uuid:T": ("Tree", {"a.csv": "urn:uuid:N"}),
}
ROOTS = {"/Coll/": "urn:uuid:C", "/Tree/": "urn:uuid:T"}
Tên Loop Demo và Bird Inventory lấy theo ví dụ trong RFC 5842.
Vì sao không dùng máy chủ thật
Các máy chủ WebDAV phổ biến hiếm khi bật binding, và cài một máy chủ hỗ trợ RFC 5842 đầy đủ nằm ngoài phạm vi máy lab. Mô phỏng cho phép dựng đúng đồ thị trong ví dụ của RFC và kiểm từng nhánh 508, 208. Đổi lại, kết quả chỉ nói client xử lý đúng tín hiệu, không nói máy chủ cụ thể nào hành xử như vậy.
Hai vòng trong đồ thị
Bar trỏ thẳng lại C, tạo vòng độ dài một. Back trong S trỏ về C, tạo vòng qua Sub. Note có hai binding: /Coll/Sub/Note và /Tree/a.csv, nhưng đó là tệp nên không tạo vòng. Đồ thị nhỏ nhưng có đủ ba loại quan hệ: vòng trực tiếp, vòng gián tiếp, tài nguyên chung không vòng.
Phân giải đường dẫn
Máy chủ phân giải đường dẫn bằng cách đi từ gốc qua từng tên binding. /Coll/Bar/Bar/Sub/ phân giải được, trỏ tới S. Đây là lý do số đường dẫn hợp lệ là vô hạn dù chỉ có năm tài nguyên.
def resolve(path):
root = "/" + path.strip("/").split("/")[0] + "/"
rid = ROOTS[root]
for seg in [x for x in path[len(root):].split("/") if x]:
rid = RES[rid][1][seg]
return rid
Bản đầu của lab quên hàm này và chỉ tra gốc, nên request tới /Coll/Bar/ làm máy chủ lỗi. Lab sửa rồi mới chạy các ca bên dưới.
OPTIONS khai bind
RFC 5842 mục 8.1 yêu cầu máy chủ hỗ trợ binding trả tên lớp tuân thủ bind trong header DAV của OPTIONS. Lab trả DAV: 1, bind. Client kiểm header này trước để biết máy chủ có thể dùng 208 hay không.
== OPTIONS: 1, bind
Resource-id là chìa khóa
RFC 5842 định nghĩa DAV:resource-id là thuộc tính bắt buộc, giá trị là URI ổn định của tài nguyên, không đổi khi đổi tên hay dời chỗ. Hai đường dẫn có cùng resource-id là cùng một tài nguyên. Client dựng lại đồ thị dựa trên thuộc tính này, không dựa vào đường dẫn.
Cây ảo so với cây thật
Đường dẫn trong WebDAV là cây ảo nhìn từ client. Tài nguyên thật là đồ thị trên máy chủ. Với kho không có binding, hai thứ trùng nhau. Với kho có binding, cây ảo có thể vô hạn trong khi đồ thị chỉ có vài nút. Công cụ đồng bộ phải làm việc với đồ thị, không với cây ảo.
Ai tạo ra binding có vòng
Binding có vòng thường do một thao tác BIND hoặc MOVE của người dùng, có khi vô tình. RFC 5842 cho phép máy chủ từ chối tạo vòng ngay từ đầu bằng precondition cycle-allowed. Máy chủ chọn cho phép vòng thì phải xử lý Depth infinity đúng cách như lab mô phỏng.
Vẽ đồ thị trên giấy trước
Trước khi viết mã duyệt, vẽ đồ thị với nút là resource-id, cạnh là tên binding. Hai cạnh Bar và Back cùng đi về C hiện ra ngay trên hình. Cách này giúp viết ca kiểm tra có chủ đích, thay vì chạy thử rồi đoán.
Dựng request cần duyệt tập tài nguyên trong lab HTTP 508 Loop Detected WebDAV
Phần này gửi PROPFIND với các độ sâu và header khác nhau.
Thân request PROPFIND
Lab xin hai thuộc tính displayname và resource-id, giống ví dụ trong RFC 5842 mục 7.1.1.
BODY = (b'<?xml version="1.0" encoding="utf-8"?><D:propfind xmlns:D="DAV:">'
b'<D:prop><D:displayname/><D:resource-id/></D:prop></D:propfind>')
def pf(sess, path, depth, dav=None):
h = {"Depth": str(depth), "Content-Type": 'application/xml; charset="utf-8"'}
if dav:
h["DAV"] = dav
return sess.request("PROPFIND", B + path, data=BODY, headers=h)
requests và httpx đều gửi được method tùy ý như PROPFIND qua hàm request chung.
Header DAV trong request
Header DAV trong request khác header DAV trong phản hồi. Phía máy chủ, nó nói máy chủ tuân thủ lớp nào. Phía client, theo RFC 5842 mục 8.2, nó nói client hiểu các yêu cầu của lớp bind, trong đó có mã 208. Client chỉ nên gửi DAV: bind khi đã có mã xử lý 208 như lab.
Năm ca
Lab chạy năm ca: /Coll/ Depth 1, /Coll/ Depth infinity không khai bind, /Coll/ Depth infinity có DAV: bind, /Coll/ Depth infinity ở chế độ phát hiện giữa chừng, /Tree/ Depth infinity làm đối chứng.
Depth 1 không bị ảnh hưởng

Depth 1 vẫn trả 207
PROPFIND Depth 1 trên /Coll/ trả 207 với bốn phần tử: chính /Coll/, Foo, Bar, Sub, đều 200. Bar có resource-id giống /Coll/. Máy chủ không duyệt sâu nên không cần kiểm vòng. RFC chỉ yêu cầu phát hiện vòng cho Depth: infinity.
PROPFIND /Coll/ Depth=1 DAV=None -> 207 Multi-Status
('/Coll/', '200 OK', 'urn:uuid:C')
('/Coll/Foo', '200 OK', 'urn:uuid:R')
('/Coll/Bar/', '200 OK', 'urn:uuid:C')
('/Coll/Sub/', '200 OK', 'urn:uuid:S')
Một client tinh ý đã thấy được vòng ở đây, vì /Coll/Bar/ và /Coll/ trùng resource-id.
Đọc multistatus bằng lxml
Phản hồi 207 là XML theo namespace DAV. Lab đọc từng phần tử response, lấy href, mã trong status và href bên trong resource-id. Ba trường này đủ để dựng bảng trạng thái và đồ thị. Thuộc tính khác như displayname chỉ để người đọc dễ nhận ra tài nguyên.
def table(r):
if r.status_code != 207:
return []
t = etree.fromstring(r.content); rows = []
for x in t.findall("D:response", NS):
rows.append((x.findtext("D:href", namespaces=NS),
x.findtext(".//D:status", namespaces=NS).split(" ", 1)[1],
x.findtext(".//D:resource-id/D:href", namespaces=NS)))
return rows
Hàm này trả rỗng với mã khác 207, nên ca 508 cấp cao nhất cho bảng rỗng.
Đối chứng không vòng
PROPFIND Depth infinity trên /Tree/ trả 207 với /Tree/ và /Tree/a.csv, đều 200. a.csv có resource-id urn:uuid:N, giống /Coll/Sub/Note, nhưng là tệp nên không gây vòng. Đối chứng này cho thấy máy chủ không trả 508 chỉ vì có tài nguyên chung.
Không cần proxy cho lab
Máy chủ lab chạy trên máy cục bộ. Hành vi 508 và 208 thuộc về máy chủ và header DAV của client. Proxy chuyển tiếp HTTP không thay đổi Depth hay DAV, nên không ảnh hưởng kết quả. Lab không đi qua proxy ở phần này.
Dung lượng phản hồi Depth infinity
Với kho lớn không có vòng, phản hồi Depth infinity có thể rất dài, hàng chục nghìn phần tử. Client cần parser chịu được kích thước này, hoặc dùng parser dạng luồng. Lab dùng etree.fromstring vì phản hồi nhỏ. Với kho thật, cân nhắc iterparse của lxml để không giữ cả cây XML trong bộ nhớ.
Depth infinity có thể bị tắt
Nhiều máy chủ WebDAV tắt Depth: infinity cho PROPFIND để tránh tải nặng, trả 403 với precondition propfind-finite-depth theo RFC 4918. Khi đó client phải duyệt từng tầng bằng Depth 1, và việc phát hiện vòng chuyển hẳn sang phía client. Phần sau của bài có mã cho trường hợp này.
Đọc HTTP 508 Loop Detected WebDAV và trạng thái tài nguyên trong response
Phần này đọc kết quả của ba ca Depth infinity trên /Coll/.
Client không khai bind nhận 508
PROPFIND Depth infinity không có header DAV trả 508 Loop Detected, thân rỗng. Máy chủ duyệt trước, gặp vòng ở Bar, từ chối toàn bộ. Client không nhận phần tử nào, kể cả Foo không liên quan tới vòng.
PROPFIND /Coll/ Depth=infinity DAV=None -> 508 Loop Detected
httpx 0.28.1 cho cùng kết quả, reason_phrase Loop Detected, is_server_error True. Hai thư viện biết tên mã nhưng không làm gì thêm.
508 không có thân lỗi
Phản hồi 508 cấp cao nhất của lab không có thân. RFC không yêu cầu thân lỗi cho 508. Client không nên chờ thông tin về vị trí vòng trong phản hồi này. Muốn biết vòng nằm ở đâu, gửi lại với DAV: bind, hoặc duyệt Depth 1 có ghi resource-id.
Client khai bind nhận 207 với 208

208 thay cho 508
Thêm header DAV: bind, cùng request trả 207 với sáu phần tử. /Coll/Bar/ và /Coll/Sub/Back/ mang trạng thái 208 Already Reported, không có phần tử con. Mọi tài nguyên khác 200.
PROPFIND /Coll/ Depth=infinity DAV=bind -> 207 Multi-Status
('/Coll/', '200 OK', 'urn:uuid:C')
('/Coll/Foo', '200 OK', 'urn:uuid:R')
('/Coll/Bar/', '208 Already Reported', 'urn:uuid:C')
('/Coll/Sub/', '200 OK', 'urn:uuid:S')
('/Coll/Sub/Note', '200 OK', 'urn:uuid:N')
('/Coll/Sub/Back/', '208 Already Reported', 'urn:uuid:C')
Đây là danh sách đầy đủ và hữu hạn của cây có vòng.
Phát hiện giữa chừng
Chế độ stream của lab mô phỏng máy chủ đã bắt đầu truyền rồi mới gặp vòng. Kết quả là 207 với ba phần tử: /Coll/ 200, /Coll/Foo 200, /Coll/Bar/ 508 Loop Detected, rồi dừng. Sub và Note không xuất hiện.
PROPFIND /Coll/?stream Depth=infinity DAV=None -> 207 Multi-Status
('/Coll/', '200 OK', 'urn:uuid:C')
('/Coll/Foo', '200 OK', 'urn:uuid:R')
('/Coll/Bar/', '508 Loop Detected', 'urn:uuid:C')
Mã HTTP 207 trông như thành công. Chỉ đọc từng propstat mới thấy danh sách bị cắt.
Phần tử nào đáng tin trong ca stream
Trong ca stream, hai phần tử đầu mang 200 là dữ liệu đúng. Client có thể dùng chúng, nhưng phải đánh dấu lượt duyệt là chưa đủ và không xóa gì dựa trên lượt này. Muốn đủ cây, gửi lại với DAV: bind hoặc chuyển sang duyệt Depth 1 có ghi resource-id.
Bẫy của mã 207
Client chỉ kiểm mã HTTP sẽ nhận 207 ở ca stream và tin rằng đã có đủ cây. Thực tế thiếu Sub và Note. Với công cụ đồng bộ, hậu quả là xóa nhầm các tệp phía bản sao vì tưởng chúng không còn trên kho. Đọc mọi propstat là bắt buộc.
Thứ tự phần tử có ý nghĩa
RFC nói với mỗi collection, chỉ một binding được báo với 200, các binding khác với 208. Binding nào được chọn để báo 200 phụ thuộc thứ tự duyệt của máy chủ. Client không nên giả định đường dẫn 200 luôn là đường dẫn ngắn nhất. Lab duyệt theo chiều sâu nên /Coll/ được báo trước.
Dựng lại đồ thị từ 208
Với phản hồi có 208, client ghép mỗi phần tử 208 với phần tử 200 đầu tiên cùng resource-id. Lab in ra hai cạnh vòng.
canh vong: /Coll/Bar/ -> da bao cao tai /Coll/ (urn:uuid:C)
canh vong: /Coll/Sub/Back/ -> da bao cao tai /Coll/ (urn:uuid:C)
Đây là đồ thị binding đầy đủ: năm nút, hai cạnh vòng, đúng như đồ thị đã vẽ.
Mã HTTP 207 chỉ nói phản hồi có nhiều trạng thái. Nó không nói trạng thái nào cũng tốt.
Lưu cả phản hồi thô
Khi gặp 508 hoặc 208, lưu nguyên thân phản hồi XML. Nếu kho đổi cấu trúc, so bản cũ với bản mới cho thấy binding nào mới xuất hiện. Phản hồi thô cũng là bằng chứng gửi kèm ticket cho quản trị kho, để họ biết chính xác đường dẫn nào tạo vòng.
Bảng trạng thái từng ca
Bảng dưới gom mã HTTP và số phần tử theo trạng thái ở từng ca.
Ca |
Mã HTTP |
200 |
208 |
508 trong 207 |
Cây đầy đủ? |
|---|---|---|---|---|---|
Depth 1 |
207 |
4 |
0 |
0 |
Một tầng |
infinity, không DAV |
508 |
0 |
0 |
0 |
Không có gì |
infinity, DAV: bind |
207 |
4 |
2 |
0 |
Có |
infinity, stream |
207 |
2 |
0 |
1 |
Bị cắt |
/Tree/ infinity |
207 |
2 |
0 |
0 |
Có |
So HTTP 508 Loop Detected WebDAV với redirect loop thông thường để phân loại đúng lỗi
Phần này đặt vòng binding cạnh vòng redirect, và viết bộ duyệt phía client.
Client tự duyệt không đọc resource-id
Khi máy chủ không cho Depth infinity, client duyệt bằng Depth 1 từng tầng. Lab viết một bộ duyệt ngây thơ: lấy mọi collection con, đưa vào hàng đợi, không xét resource-id, trần 40 request. Kết quả: dùng hết 40 request, đường dẫn dài nhất là /Coll/Bar/Bar/Sub/Back/Sub/Back/, hàng đợi còn 26 phần tử.
so request: 40 | URL dai nhat: /Coll/Bar/Bar/Sub/Back/Sub/Back/ | hang doi con: 26
Không có trần, bộ duyệt này chạy mãi. Đây là vòng lặp vô tận mà 508 được sinh ra để chặn ở phía máy chủ.
Client đọc resource-id
Bộ duyệt thứ hai ghi resource-id đã gặp. Collection nào đã duyệt thì bỏ qua và ghi là cạnh vòng. Kết quả: 4 request, vòng phát hiện tại /Coll/Bar/ và /Coll/Sub/Back/.
seen_rid, queue, cyc = set(), ["/Coll/"], []
while queue:
p = queue.pop(0)
rows = table(pf(s, p, 1))
self_rid = next(rid for href, st, rid in rows if href == p)
if self_rid in seen_rid:
cyc.append(p); continue
seen_rid.add(self_rid)
for href, st, rid in rows:
if href != p and href.endswith("/"):
queue.append(href)
Hai bộ duyệt khác nhau đúng một tập hợp, nhưng một cái dừng sau 4 request, một cái không bao giờ dừng.
Trần request luôn cần có
Kể cả bộ duyệt có resource-id cũng nên có trần số request. Kho có thể không trả resource-id cho mọi tài nguyên, hoặc trả giá trị sai. Trần giúp lỗi dữ liệu không biến thành vòng lặp vô tận phía client. Khi chạm trần, dừng và báo, đừng âm thầm bỏ phần còn lại.
Khi kho không có resource-id
Không có resource-id, client có thể dựa vào thuộc tính khác mà kho cung cấp, như đường dẫn vật lý hoặc ID nội bộ. Nếu không có gì, giới hạn độ sâu duyệt theo cấu trúc dự kiến của kho, ví dụ không quá năm tầng. Cách này không phát hiện vòng, chỉ chặn hậu quả.
Vòng redirect trông thế nào
Lab dựng một máy chủ thứ hai ở cổng 9762, /a chuyển 302 sang /b, /b chuyển lại /a. requests 2.34.2 ném TooManyRedirects sau 30 lần chuyển, máy chủ nhận 31 request. httpx 0.28.1 với follow_redirects ném TooManyRedirects, máy chủ nhận 21 request.
requests: TooManyRedirects - Exceeded 30 redirects. | request toi server: 31
httpx: TooManyRedirects - Exceeded maximum allowed redirects. | request toi server: 21
Không có mã 508 nào. Lỗi nằm ở client, do giới hạn số lần chuyển hướng.
Vòng redirect do proxy
Vòng redirect có lúc sinh ra khi proxy hoặc CDN và origin không thống nhất về HTTPS hay tên miền. Proxy chuyển HTTP sang HTTPS, origin lại chuyển HTTPS về HTTP. Bài redirect đã nói các ca này. Điểm cần nhớ ở đây: dạng vòng này cho chuỗi 3xx, không bao giờ cho 508.
Hai loại vòng, hai nơi sửa
Bảng dưới gom khác biệt giữa vòng binding và vòng redirect.
Đặc điểm |
Vòng binding WebDAV |
Vòng redirect |
|---|---|---|
Tín hiệu |
508, hoặc 208 trong 207 |
TooManyRedirects ở client |
Ai phát hiện |
Máy chủ, hoặc client đọc resource-id |
Client đếm số lần chuyển |
Nguyên nhân |
Binding trỏ lại collection cha |
Cấu hình chuyển hướng sai, cookie, proxy |
Sửa ở đâu |
Cấu trúc kho, hoặc client khai bind |
Quy tắc chuyển hướng phía server |
Số request |
Một request bị từ chối |
21 hoặc 31 request lặp |
Đếm request trong log
Ở ca redirect, máy chủ nhận 31 và 21 request cho một lần gọi. Ở ca 508, máy chủ nhận đúng một request. Số request trong log phía máy chủ là cách nhanh nhất để biết đang gặp loại vòng nào khi chỉ có log truy cập trong tay.
Proxy không gây ra 508
Proxy chuyển tiếp không tạo binding. 508 phản ánh cấu trúc dữ liệu trên máy chủ WebDAV. Vòng redirect thì đôi khi có liên quan tới proxy, ví dụ proxy ép HTTPS trong khi origin ép HTTP. Đó là thêm một lý do để không gộp hai loại lỗi.
Đầu ra nghiệm thu của HTTP 508 Loop Detected WebDAV: đồ thị binding và trace PROPFIND
Phần này gom tiêu chí và giới hạn.
Danh sách kiểm tra

Danh sách nghiệm thu
Client đạt khi mọi dòng dưới đây đúng.
- Kiểm OPTIONS để biết máy chủ có lớp bind không.
- Khai DAV: bind khi client hiểu 208.
- Xin resource-id trong mọi PROPFIND.
- Đọc trạng thái từng propstat, không chỉ mã HTTP.
- Phần tử 208 được ghép với phần tử 200 cùng resource-id.
- 508 trong 207 được coi là danh sách bị cắt.
- Duyệt Depth 1 có tập resource-id đã gặp và có trần request.
Kiểm trong pipeline đồng bộ
Trước bước xóa ở bản sao, pipeline kiểm ba điều: lượt duyệt không có 508 ở bất kỳ propstat nào, mọi 208 đã ghép được với một phần tử 200, và số collection duyệt được không chạm trần. Thiếu một điều, bỏ qua bước xóa của lượt đó.
Những gì lab chưa chạy
Chưa thử máy chủ WebDAV thật có binding. Chưa thử BIND, UNBIND, REBIND, COPY và MOVE với Depth infinity trên cây có vòng. Chưa thử symlink hệ tệp trên các máy chủ phổ biến. Chưa thử propfind-finite-depth. Máy chủ lab chỉ là mô phỏng theo RFC 5842.
Ghi kết quả vào sổ lượt duyệt
Mỗi lượt duyệt lưu số collection, số tệp, số 208, số 508, số request và có chạm trần hay không. So các lượt với nhau, số 208 tăng đột ngột là dấu hiệu ai đó vừa tạo binding mới. Sổ này giúp phát hiện thay đổi cấu trúc kho trước khi nó làm hỏng đồng bộ.
Chạy lại với kho thật
Gửi OPTIONS tới kho, xem header DAV có bind không. Nếu không có, khả năng gặp 508 theo nghĩa RFC 5842 là thấp. Gửi PROPFIND Depth 1 với resource-id, xem kho có trả thuộc tính này không. Không có resource-id, bộ duyệt phía client phải dựa vào thứ khác như đường dẫn thật của tệp nếu kho cung cấp.
Proxy ở đâu trong luồng
Proxy nằm giữa công cụ đồng bộ và kho, không đổi Depth hay DAV. Khi kho chỉ cho phép kết nối từ IP tại Việt Nam, anh em có thể mua proxy tại trang chủ ProxyVN. Proxy không sửa được vòng binding. 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.
HTTP 508 Loop Detected WebDAV: câu hỏi thường gặp
Các câu dưới đây gom những điểm hay bị hỏi.
508 có phải vòng redirect không?
Không. 508 là vòng binding trong WebDAV khi duyệt Depth infinity. Vòng redirect cho TooManyRedirects ở client.
Làm sao nhận danh sách thay vì 508?
Khai header DAV: bind nếu client hiểu 208. Lab nhận 207 với hai phần tử 208 thay vì 508.
207 có nghĩa là đã đủ cây?
Không. Lab có ca 207 chứa 508 ở một phần tử, danh sách bị cắt.
Depth 1 có gặp 508 không?
Không trong lab. RFC chỉ yêu cầu phát hiện vòng với Depth infinity.
Client tự duyệt có bị lặp vô tận không?
Có, nếu không ghi resource-id. Lab dùng hết 40 request mà chưa dừng.
Đổi proxy có sửa được 508 không?
Không. 508 phản ánh cấu trúc kho. Sửa binding hoặc khai bind ở client.
RFC 5842 có phổ biến không?
Không nhiều. RFC này thuộc loại Experimental, nhiều máy chủ không hỗ trợ binding. Kiểm OPTIONS trước.
HTTP 508 Loop Detected WebDAV và những điều cần nhớ
HTTP 508 Loop Detected WebDAV là máy chủ báo cây tài nguyên có vòng binding khi duyệt Depth infinity, không phải vòng redirect. Khai DAV: bind, xin resource-id, đọc từng propstat và tự ghi resource-id khi duyệt tay. Proxy.vn - Nhà cung cấp dịch vụ proxy chất lượng hàng đầu Việt Nam khuyên anh em vẽ đồ thị binding trước khi xử lý HTTP 508 Loop Detected WebDAV.