feat(xhttp): accept sc_max_concurrent_posts (legacy, ignored)

scMaxConcurrentPosts is a removed Xray knob (grep + GitHub code search
total:0 in current XTLS/Xray-core and sing-box-extended). Current Xray
serializes to one upload POST body in flight at a time, which our sequential
packet-up Write already matches, so the field is accepted for config/link
symmetry but ignored by the client.

- option: V2RayXHTTPOptions.ScMaxConcurrentPosts (json sc_max_concurrent_posts)
- PARAM_MAP: document as legacy/ignore tier with the real concurrency mechanism
  (bounded pipe + WroteRequest serialization, server-side seq reorder)
- url-parsing doc: scMaxConcurrentPosts -> accept-but-ignore
- xhttp_obfs_full.json: include the field so check covers it

Verified: build/gofmt/vet clean, 16 unit tests pass, sing-box check passes
on all 3 xhttp configs incl the field.
This commit is contained in:
Leadaxe
2026-06-30 00:35:31 +03:00
parent 31926aebd4
commit 7527467965
4 changed files with 36 additions and 2 deletions
@@ -39,6 +39,7 @@
| 15 | `noSSEHeader` | `no_sse_header` | ❌ | server-only | Сервер не шлёт `Content-Type: text/event-stream` |
| 16 | `scMaxBufferedPosts` | `sc_max_buffered_posts` | ❌ | server-only | Глубина буфера переупорядочивания upload на сервере |
| 17 | `scStreamUpServerSecs`| `sc_stream_up_server_secs`| ❌ | server-only | Интервал keepalive-padding в ответе stream-up (сервер) |
| 18 | `scMaxConcurrentPosts`| `sc_max_concurrent_posts`| ❌ | legacy/ignore | Legacy-лимит параллельных upload-POST; **удалён из текущего Xray** |
\* `sessionKey` не входил в исходный список NekoBox+ из 16, но это парный к `sessionPlacement`
ключ (так же как `seqKey` парен к `seqPlacement`); без него placement query/header/cookie для session
@@ -321,6 +322,27 @@ document-and-skip без полей в struct) тоже допустима; фи
`…ScStreamUpServerSecs`; `hub.go` `ListenXH`/`ServeHTTP`/`upsertSession`; extended `server.go` зеркало.
`noSSEHeader` — без нормализатора, читается как сырой bool.
### `scMaxConcurrentPosts` — legacy, удалено из upstream (accept-but-ignore)
- **Статус:** **поля НЕТ** в текущих Xray-core и sing-box-extended. Подтверждено: `grep` пусто,
GitHub code search `scMaxConcurrentPosts repo:XTLS/Xray-core` → `total: 0`, нет
`GetNormalizedScMaxConcurrentPosts`. Это knob **старого** релиза Xray, удалённый при редизайне
upload-пути. Встречается в реальных `vless://`-ссылках (в `extra={...}`) как legacy-артефакт клиента,
сгенерировавшего ссылку.
- **Что было:** когда-то ограничивал число параллельных upload-POST в packet-up.
- **Текущий механизм Xray (вместо него):** **не семафор**, а (a) bounded pipe (backpressure) +
`scMaxBufferedPosts` (server reorder window, default 30) и (b) сериализация на
`<-wroteRequest.Wait()` для `DefaultDialerClient` — следующий POST не диспетчится, пока тело текущего
не дописано в провод. Фактически **1 POST-тело в полёте за раз**. Seq инкрементируется per-packet
синхронно в writer-goroutine; переупорядочивание — server-side по seq (`upload_queue.go`, heap).
- **Наш клиент:** `packetConn.Write` шлёт upload-POST **последовательно** (один `sendPacket` за раз) —
это уже соответствует текущему поведению Xray (1 тело за раз). Истинная bounded-concurrency (N в
полёте) была бы **улучшением над upstream**, не совместимостью.
- **Решение:** поле `sc_max_concurrent_posts` принимается в опциях (чтобы legacy-конфиги/ссылки не
падали на парсинге), но **игнорируется**. Тир — `legacy/ignore`.
- **Источник:** Xray `dialer.go` writer-loop (нет concurrency-cap), `upload_queue.go` (heap по `Seq`);
отсутствие символа подтверждено code search.
---
## 6. Что из этого реализуем в sing-box-lx (резюме)
@@ -330,6 +352,8 @@ document-and-skip без полей в struct) тоже допустима; фи
плюс `sc_max_each_post_bytes` / `sc_min_posts_interval_ms` для packet-up.
- **Не реализуем (4 server-only):** `server_max_header_bytes`, `no_sse_header`, `sc_max_buffered_posts`,
`sc_stream_up_server_secs` — accept-but-ignore в опциях.
- **Не реализуем (1 legacy):** `sc_max_concurrent_posts` — удалено из upstream; наш последовательный
upload = текущий Xray; accept-but-ignore.
- **Зависимости:** только `golang.org/x/net/http2/hpack` для tokenish (уже есть). Без вендоринга Xray.
- **Совместимость:** все дефолты сохраняют текущее (рабочее, лайв-проверенное) поведение байт-в-байт.
+2 -2
View File
@@ -105,7 +105,7 @@ JSON-ключи sing-box — **snake_case**. Источник в URL — camelCa
| URL-параметр | Действие |
|--------------|----------|
| `scMaxConcurrentPosts` | **Не маппить.** Клиент sing-box-lx шлёт upload-POST последовательно; поле не поддержано (см. §6). Игнорировать молча. |
| `scMaxConcurrentPosts` | **Accept-but-ignore.** Legacy-поле старого Xray (в текущем Xray/extended его нет — там 1 POST-тело за раз). Клиент sing-box-lx шлёт upload-POST последовательно (= текущий Xray). Можно влить как `sc_max_concurrent_posts` (принято, но не используется) — или опустить (см. §6). |
| `serverMaxHeaderBytes`, `noSSEHeader`, `scMaxBufferedPosts`, `scStreamUpServerSecs` | server-only. Можно влить как `server_max_header_bytes`/`no_sse_header`/`sc_max_buffered_posts`/`sc_stream_up_server_secs` (клиент их принимает, но игнорирует) — или просто опустить. |
| `fragment`, `fm`, `fragment=...` | TLS-фрагментация (Xray-специфика). **Не часть XHTTP.** Маппить в свою TLS-fragment-фичу, если есть; иначе опустить. |
| `flow` | Для XHTTP всегда пустой (vision несовместим). |
@@ -238,7 +238,7 @@ vless://c59eb5ed-…@199.232.244.214:443?type=xhttp&mode=packet-up&security=tls&
## 6. Известные ограничения клиента (что НЕ маппить)
- `scMaxConcurrentPosts` — параллельные upload-POST не реализованы (последовательная отправка). Игнорировать.
- `scMaxConcurrentPosts` — legacy-поле (удалено из текущего Xray-core и sing-box-extended; там upload сериализован в 1 POST-тело за раз). Наш клиент тоже шлёт последовательно = текущий Xray. Поле принимается (`sc_max_concurrent_posts`), но игнорируется.
- `downloadSettings` (асимметричный download-транспорт) — не поддержан; `mode=auto`+reality+downloadSettings
у нас всё равно даст stream-one, не stream-up.
- `spx` (spiderX), Xray browser-dialer — нет аналога.
+1
View File
@@ -52,6 +52,7 @@
"uplink_http_method": "POST",
"sc_max_each_post_bytes": "1000000-1000000",
"sc_min_posts_interval_ms": "30-30",
"sc_max_concurrent_posts": 100,
"server_max_header_bytes": 8192,
"no_sse_header": false,
"sc_max_buffered_posts": 30,
+9
View File
@@ -105,6 +105,15 @@ type V2RayXHTTPOptions struct {
// to "30-30".
ScMinPostsIntervalMs string `json:"sc_min_posts_interval_ms,omitempty"`
// ---- legacy / accepted-but-ignored -------------------------------------
// ScMaxConcurrentPosts is a legacy Xray knob (removed from current Xray-core
// and sing-box-extended) that once capped concurrent upload POSTs. Current
// Xray serializes to one POST body in flight at a time (matching our
// sequential packet-up upload), so this field is accepted for config/link
// symmetry but IGNORED. See SPECS/002 PARAM_MAP.md.
ScMaxConcurrentPosts int `json:"sc_max_concurrent_posts,omitempty"`
// ---- server-only (accepted for config symmetry, IGNORED by the client) --
// ServerMaxHeaderBytes is server-only (http.Server.MaxHeaderBytes). Ignored