Skip to content

Fix Mihomo xHTTP subscription options#509

Open
Rerowros wants to merge 2 commits into
PasarGuard:mainfrom
Rerowros:fix/mihomo-xhttp-options
Open

Fix Mihomo xHTTP subscription options#509
Rerowros wants to merge 2 commits into
PasarGuard:mainfrom
Rerowros:fix/mihomo-xhttp-options

Conversation

@Rerowros
Copy link
Copy Markdown
Contributor

@Rerowros Rerowros commented May 19, 2026

Summary

  • serialize Mihomo xHTTP advanced options into xhttp-opts
  • map xHTTP xmux values to Mihomo reuse-settings
  • serialize xHTTP download-settings with a Mihomo-specific shape instead of raw internal/Xray data
  • preserve xHTTP advanced defaults parsed from core config when host transport overrides are not set
  • extend Xray core config parsing for additional xhttpSettings.extra fields

Fixes #508

Tests

  • uv run ruff check app/core/hosts.py app/core/xray.py app/subscription/clash.py app/subscription/xray.py tests/test_subscription_clash_xhttp.py
  • uv run pytest tests/test_subscription_clash_xhttp.py
  • uv run pytest tests/api/test_core.py::test_xray_auto_detects_fallback_tls_without_manual_fallback_tags
  • uv run python -m py_compile app/core/hosts.py app/core/xray.py app/subscription/clash.py app/subscription/xray.py tests/test_subscription_clash_xhttp.py
  • git diff --check

Summary by CodeRabbit

  • Improvements

    • Fix xHTTP fallback so missing per-host settings inherit inbound defaults and headers are normalized.
    • Extend xHTTP options (padding, session/uplink, xmux reuse, download-settings) and preserve explicit false/zero values.
    • Better host/port/address normalization and refined User‑Agent selection for specific stream modes.
    • Accept both serialized and structured download-settings and avoid unnecessary async processing for raw dicts.
    • Normalize numeric xHTTP fields on input.
  • Tests

    • Added comprehensive xHTTP parsing, serialization, and clash/meta behavior tests.

Review Change Stack

Copilot AI review requested due to automatic review settings May 19, 2026 17:21
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6c26f888-59bb-476b-9189-27dde7bdf442

📥 Commits

Reviewing files that changed from the base of the PR and between 018c3bf and 8adf97e.

📒 Files selected for processing (1)
  • app/models/subscription.py

Walkthrough

Normalizes xHTTP parsing and host defaults, adds Mihomo/Clash Meta xhttp-opts serialization (including download/reuse settings and advanced fields), hardens host/port selection, guards download-settings processing, updates models to accept numeric inputs, and adds comprehensive tests.

Changes

xHTTP Subscription Enhancement

Layer / File(s) Summary
Core xHTTP defaults and XRay mapping
app/core/hosts.py, app/core/xray.py, app/subscription/xray.py
Host-level xHTTP transport now falls back to inbound defaults for no_grpc_header, sc_max_each_post_bytes, sc_min_posts_interval_ms, xmux, download_settings, and http_headers. XRay parsing prefers extra dict values, maps new advanced fields, filters headers to string→string, and exposes _xhttp_download_config handling dict or inbound objects.
Clash Meta / Mihomo xHTTP implementation
app/subscription/clash.py
Adds Mihomo xhttp-opts generation and helpers: _select_host/_select_address/_select_port, _mihomo_download_settings (inbound or xray-dict), XMUX→reuse-settings, _normalize_mihomo_xhttp_opts, expanded padding/session/seq/uplink/sc fields, conditional random UA for stream modes, and explicit network == "xhttp" transport branch.
Download-settings processing guard
app/subscription/share.py
process_inbounds_and_tags now only awaits _prepare_download_settings when download_settings is a SubscriptionInboundData; non-SubscriptionInboundData download settings are passed through unchanged.
XHTTP model validation & normalization
app/models/subscription.py
XHTTPTransportConfig.x_padding_bytes accepts int and is validated with numeric-or-range pattern; a field_validator normalizes integer numeric/range fields to strings (before mode).
xHTTP subscription integration tests
tests/test_subscription_clash_xhttp.py
New tests validate classic vs meta behavior, meta xhttp-opts include advanced fields and flattened download-settings (TLS/ALPN/fingerprint/headers), raw xray-dict download-settings reduction, and correct XRay parsing/fallback semantics.

Sequence Diagram

sequenceDiagram
  participant Client as Xray JSON (subscription)
  participant Parser as XRayConfig._handle_xhttp_settings
  participant Inbound as Parsed SubscriptionInboundData
  participant Hosts as app.core.hosts (host builder)
  participant Clash as ClashConfiguration / Mihomo serializer
  Client->>Parser: xhttpSettings (top-level + extra)
  Parser->>Inbound: map fields, http_headers, download_settings
  Inbound->>Hosts: apply inbound defaults (no_grpc_header, xmux, download_settings)
  Hosts->>Clash: provide normalized transport_config and download_settings
  Clash->>Clash: build xhttp-opts (padding/session/uplink/reuse/download-settings)
  Clash->>Output: serialized Mihomo / Clash Meta host entry
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I nibbled through configs late at night,

Paddings and sessions set just right.
XMUX and downloads neatly aligned,
Mihomo finds hosts it once declined.
Hooray — subscriptions now take flight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fix Mihomo xHTTP subscription options' accurately summarizes the main objective: fixing how xHTTP options are serialized in Mihomo/Clash Meta subscriptions.
Linked Issues check ✅ Passed The PR comprehensively addresses all coding requirements from #508: xHTTP now generates in Clash Meta (not skipped), advanced xhttp-opts fields are serialized, xmux maps to reuse-settings, and download-settings uses Mihomo format.
Out of Scope Changes check ✅ Passed All changes are scoped to xHTTP subscription serialization improvements. Updates to hosts.py, xray.py, clash.py, and share.py directly support the linked objectives without introducing unrelated modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/test_subscription_clash_xhttp.py (1)

133-235: ⚡ Quick win

Add a regression test for inbound-default download_settings fallback.

Current tests don’t cover the path where xHTTP downloadSettings comes from parsed core inbound defaults (without host-level xhttp_settings.download_settings). A small case here would guard the fallback contract end-to-end.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_subscription_clash_xhttp.py` around lines 133 - 235, Add a
regression test that verifies xHTTP downloadSettings provided via an
inbound-level/default location are picked up when host-level
xhttp_settings.download_settings is absent: create a new test (similar to
test_xray_parser_reads_xhttp_extra_advanced_fields) that feeds XRayConfig input
where "downloadSettings" appears only in the inbound's
streamSettings/xhttpSettings (or core inbound defaults) and assert
parsed.inbounds_by_tag[...] produces the expected "download_settings" dict; also
add an integration-like assertion that ClashMetaConfiguration (meta.add and the
resulting meta.data["proxies"][0]["xhttp-opts"]["download-settings"]) uses those
fallback values when serializing. Ensure the test references XRayConfig,
parsed.inbounds_by_tag, ClashMetaConfiguration and meta.add so reviewers can
locate the behavior under test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/core/hosts.py`:
- Around line 264-267: The download_settings field is being set to None unless
both xs and down_settings exist, which discards core-parsed xHTTP defaults;
change the assignment so it uses down_settings when available (xs and
down_settings) otherwise falls back to inbound_config.get("download_settings")
(and only None if neither exists). Update the download_settings expression near
where xmux and http_headers are set (referencing xs, down_settings,
inbound_config, host) to: download_settings = down_settings if xs and
down_settings else inbound_config.get("download_settings").

---

Nitpick comments:
In `@tests/test_subscription_clash_xhttp.py`:
- Around line 133-235: Add a regression test that verifies xHTTP
downloadSettings provided via an inbound-level/default location are picked up
when host-level xhttp_settings.download_settings is absent: create a new test
(similar to test_xray_parser_reads_xhttp_extra_advanced_fields) that feeds
XRayConfig input where "downloadSettings" appears only in the inbound's
streamSettings/xhttpSettings (or core inbound defaults) and assert
parsed.inbounds_by_tag[...] produces the expected "download_settings" dict; also
add an integration-like assertion that ClashMetaConfiguration (meta.add and the
resulting meta.data["proxies"][0]["xhttp-opts"]["download-settings"]) uses those
fallback values when serializing. Ensure the test references XRayConfig,
parsed.inbounds_by_tag, ClashMetaConfiguration and meta.add so reviewers can
locate the behavior under test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a4d78da4-ac40-49c4-b046-d24de3cbdbb5

📥 Commits

Reviewing files that changed from the base of the PR and between 0b0ddaa and eecac35.

📒 Files selected for processing (5)
  • app/core/hosts.py
  • app/core/xray.py
  • app/subscription/clash.py
  • app/subscription/xray.py
  • tests/test_subscription_clash_xhttp.py

Comment thread app/core/hosts.py
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes Mihomo/Clash Meta subscription generation for xHTTP by ensuring xHTTP inbounds are no longer skipped, and by serializing Mihomo-specific xhttp-opts (including advanced fields, XMUX→reuse-settings, and a normalized download-settings shape). It also extends Xray core config parsing to capture additional xhttpSettings.extra fields and adds coverage for these behaviors.

Changes:

  • Enable xHTTP proxy generation for ClashMetaConfiguration while keeping classic Clash skipping behavior.
  • Expand Mihomo xHTTP serialization (xhttp-opts) to include advanced options, XMUX mapping (reuse-settings), and a safer Mihomo-shaped download-settings.
  • Extend Xray core parsing + subscription serialization to carry more xHTTP extra fields and accept dict-shaped download settings; add focused tests.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/test_subscription_clash_xhttp.py Adds tests ensuring xHTTP is generated for Clash Meta and advanced xhttp-opts/download-settings are serialized correctly.
app/subscription/xray.py Allows xHTTP downloadSettings to pass through when already provided as a dict.
app/subscription/clash.py Implements Mihomo-specific xHTTP option serialization, XMUX→reuse-settings mapping, and Clash Meta xHTTP support.
app/core/xray.py Parses additional xHTTP extra fields (headers, noGRPCHeader, sc*, xmux, downloadSettings) into inbound settings.
app/core/hosts.py Adjusts merge behavior for xHTTP advanced defaults (no_grpc_header/sc*) and xmux/http_headers sourcing.
Comments suppressed due to low confidence (1)

app/subscription/clash.py:547

  • On invalid string ports, _select_port returns 0, which will emit an invalid port in generated configs (and is hard to debug). Prefer returning None (so it can be omitted) or raising/propagating an error instead of silently producing port 0.
    def _select_port(port: int | str | list[int] | list[str] | None) -> int | None:
        """Normalize port values from subscription data."""
        if port is None:
            return None
        if isinstance(port, list):
            if not port:
                return None
            port = port[0]
        if isinstance(port, str):
            try:
                return int(port)
            except ValueError:
                return 0
        return port

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread app/core/hosts.py Outdated
Comment thread app/subscription/clash.py
@Rerowros Rerowros force-pushed the fix/mihomo-xhttp-options branch from eecac35 to 2c829b1 Compare May 19, 2026 17:34
@Rerowros Rerowros force-pushed the fix/mihomo-xhttp-options branch from 2c829b1 to 018c3bf Compare May 19, 2026 17:41
@Rerowros
Copy link
Copy Markdown
Contributor Author

Concrete upstream references for the xHTTP field mapping in this PR:

Mihomo client config shape

Mihomo now documents xhttp-opts with the same kebab-case keys that this PR emits:

Relevant Mihomo source at v1.19.25:

Relevant Mihomo PR history:

Xray / XHTTP source field names

PasarGuard stores/parses Xray-style camelCase fields. Xray's SplitHTTPConfig defines those fields here:

Mapping used by this PR

This PR converts Xray/PasarGuard camelCase settings to Mihomo kebab-case settings:

  • xPaddingObfsMode -> x-padding-obfs-mode
  • xPaddingKey -> x-padding-key
  • xPaddingHeader -> x-padding-header
  • xPaddingPlacement -> x-padding-placement
  • xPaddingMethod -> x-padding-method
  • uplinkHTTPMethod -> uplink-http-method
  • sessionPlacement -> session-placement
  • sessionKey -> session-key
  • seqPlacement -> seq-placement
  • seqKey -> seq-key
  • uplinkDataPlacement -> uplink-data-placement
  • uplinkDataKey -> uplink-data-key
  • uplinkChunkSize -> uplink-chunk-size
  • scMaxEachPostBytes -> sc-max-each-post-bytes
  • scMinPostsIntervalMs -> sc-min-posts-interval-ms
  • xmux -> reuse-settings
  • downloadSettings -> download-settings

This is why the PR keeps the existing basic xHTTP support from #347 and only fills in the missing advanced Mihomo fields plus safer normalization for nested download-settings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Mihomo xHTTP subscriptions are skipped and xHTTP advanced fields are not mapped

2 participants