Skip to content

Conversation

@Cloxl
Copy link
Owner

@Cloxl Cloxl commented Dec 5, 2025

Fixes #70

Summary by Sourcery

添加具备时间戳感知能力的签名与请求头生成功能,引入 Trace ID 工具方法,并补充推荐使用模式的文档。

New Features:

  • 允许在为 GET 和 POST 辅助方法以及核心签名流程生成请求签名时,显式提供时间戳。
  • 暴露辅助方法,用于生成 x-b3-traceidx-xray-traceidx-t 请求头的值,并为 GET 和 POST 请求构建完整的已签名请求头。
  • 在加密配置中引入基于随机数的 B3 与 Xray Trace ID 生成器,并支持可配置的限制。

Enhancements:

  • 扩展加密负载构建器,以支持外部提供的时间戳,同时在未提供时间戳时保持现有行为不变。
  • 更新文档,将请求头签名辅助方法作为推荐的集成路径,并展示统一用法和传统用法两种使用模式。
  • 收紧随机字节生成逻辑,使其遵循加密配置中可配置的最大字节数限制。

Tests:

  • 添加单元测试,覆盖时间戳参数处理、Trace ID 生成、统一时间戳的使用、x-t 请求头计算,以及新的请求头签名便捷方法。
Original summary in English

Summary by Sourcery

Add timestamp-aware signing and header generation capabilities, along with trace ID utilities, and document the recommended usage patterns.

New Features:

  • Allow providing explicit timestamps when generating request signatures for GET and POST helpers and the core signing flow.
  • Expose helper methods to generate x-b3-traceid, x-xray-traceid, and x-t header values and to build complete signed request headers for GET and POST requests.
  • Introduce random-based generators for B3 and Xray trace IDs with configurable limits in the crypto configuration.

Enhancements:

  • Extend the crypto payload builder to support externally supplied timestamps while preserving existing behavior when none is provided.
  • Update documentation to promote header-signing helpers as the recommended integration path and show both unified and traditional usage patterns.
  • Tighten random byte generation to respect a configurable max-byte limit in the crypto config.

Tests:

  • Add unit tests covering timestamp parameter handling, trace ID generation, unified timestamp usage, x-t header computation, and the new header-signing convenience methods.

Cloxl added 6 commits December 5, 2025 10:37
…ence methods

- fix: distinguish params/payload parameters in sign_headers method
  - params: for GET requests only
  - payload: for POST requests only
- feat: add sign_headers_get convenience method for GET requests
- feat: add sign_headers_post convenience method for POST requests
- docs: update docstrings with clear parameter usage examples
- fix: use params for GET and payload for POST in sign_headers tests
- test: add test_sign_headers_get for GET convenience method
- test: add test_sign_headers_post for POST convenience method
- test: verify timestamp parameter support in convenience methods
- feat: promote sign_headers_get/post as recommended approach
- feat: move build_url and build_json_body to recommended section
- refactor: move sign_headers unified method to traditional methods section
- refactor: collapse traditional single-field generation methods into details
- improve: clearer documentation structure for better user experience
@sourcery-ai
Copy link

sourcery-ai bot commented Dec 5, 2025

Reviewer's Guide

为 Xhshow 客户端添加统一的时间戳支持和 header/trace-id 生成辅助函数,扩展加密载荷以接受可选时间戳,引入基于随机数的 Trace ID 工具,并为新的时间戳和请求头 API 编写文档和测试。

带时间戳和 Trace ID 的统一请求头签名顺序图

sequenceDiagram
    actor Developer
    participant Xhshow
    participant CryptoProcessor
    participant RandomGenerator

    Developer->>Xhshow: sign_headers(method, uri, a1_value, xsec_appid, params, payload, timestamp=None)
    activate Xhshow
    Xhshow->>Xhshow: if timestamp is None:
    Xhshow->>Xhshow:   timestamp = time.time()
    Xhshow->>Xhshow: request_data = params or payload

    Xhshow->>Xhshow: sign_xs(method, uri, a1_value, xsec_appid, request_data, timestamp)
    activate Xhshow
    Xhshow->>Xhshow: _build_content_string(method, uri, request_data)
    Xhshow->>Xhshow: _generate_d_value(content_string)
    Xhshow->>CryptoProcessor: build_payload_array(d_value, a1_value, xsec_appid, content_string, timestamp)
    activate CryptoProcessor
    CryptoProcessor-->>Xhshow: payload_array
    deactivate CryptoProcessor
    Xhshow->>CryptoProcessor: bit_ops.xor_transform_array(payload_array)
    activate CryptoProcessor
    CryptoProcessor-->>Xhshow: xor_result
    deactivate CryptoProcessor
    Xhshow->>CryptoProcessor: b64encoder.encode(xor_result)
    activate CryptoProcessor
    CryptoProcessor-->>Xhshow: x3_signature
    deactivate CryptoProcessor
    Xhshow->>Xhshow: build x_s from signature_data
    Xhshow-->>Xhshow: x_s
    deactivate Xhshow

    Xhshow->>Xhshow: get_x_t(timestamp)
    Xhshow->>Xhshow: x_t = int(timestamp * 1000)

    Xhshow->>RandomGenerator: generate_b3_trace_id()
    activate RandomGenerator
    RandomGenerator-->>Xhshow: x_b3_traceid
    deactivate RandomGenerator

    Xhshow->>RandomGenerator: generate_xray_trace_id(timestamp_ms=int(timestamp * 1000))
    activate RandomGenerator
    RandomGenerator-->>Xhshow: x_xray_traceid
    deactivate RandomGenerator

    Xhshow-->>Developer: headers { x-s, x-t, x-b3-traceid, x-xray-traceid }
    deactivate Xhshow
Loading

Xhshow、CryptoProcessor、RandomGenerator 和 CryptoConfig 的更新类图

classDiagram
    class CryptoConfig {
        +int MAX_32BIT
        +int MAX_SIGNED_32BIT
        +int MAX_BYTE
        +str STANDARD_BASE64_ALPHABET
        +str URLSAFE_BASE64_ALPHABET
        +str X3_PREFIX
        +str XYS_PREFIX
        +str HEX_CHARS
        +int XRAY_TRACE_ID_SEQ_MAX
        +int XRAY_TRACE_ID_TIMESTAMP_SHIFT
        +int XRAY_TRACE_ID_PART1_LENGTH
        +int XRAY_TRACE_ID_PART2_LENGTH
        +int B3_TRACE_ID_LENGTH
        +CryptoConfig with_overrides(kwargs)
    }

    class RandomGenerator {
        -CryptoConfig config
        +list~int~ generate_random_bytes(byte_count)
        +int generate_random_byte_in_range(min_val, max_val)
        +int generate_random_int()
        +str generate_b3_trace_id()
        +str generate_xray_trace_id(timestamp, seq)
    }

    class CryptoProcessor {
        +CryptoConfig config
        +RandomGenerator random_gen
        +Any bit_ops
        +Any b64encoder
        +list~int~ build_payload_array(d_value, a1_value, app_identifier, string_param, timestamp)
    }

    class Xhshow {
        +CryptoConfig config
        +CryptoProcessor crypto_processor
        +RandomGenerator random_generator
        +__init__(config)
        +str _build_content_string(method, uri, payload)
        +str _build_signature(d_value, a1_value, xsec_appid, string_param, timestamp)
        +str sign_xs(method, uri, a1_value, xsec_appid, payload, timestamp)
        +str sign_xs_get(uri, a1_value, xsec_appid, params, timestamp)
        +str sign_xs_post(uri, a1_value, xsec_appid, payload, timestamp)
        +bytearray decode_x3(x3_signature)
        +str build_url(base_url, params)
        +str build_json_body(payload)
        +str get_b3_trace_id()
        +str get_xray_trace_id(timestamp, seq)
        +int get_x_t(timestamp)
        +dict~str,str~ sign_headers(method, uri, a1_value, xsec_appid, params, payload, timestamp)
        +dict~str,str~ sign_headers_get(uri, a1_value, xsec_appid, params, timestamp)
        +dict~str,str~ sign_headers_post(uri, a1_value, xsec_appid, payload, timestamp)
    }

    CryptoProcessor --> CryptoConfig : uses
    CryptoProcessor --> RandomGenerator : uses
    RandomGenerator --> CryptoConfig : uses
    Xhshow --> CryptoConfig : holds
    Xhshow --> CryptoProcessor : holds
    Xhshow --> RandomGenerator : holds
Loading

文件级改动

Change Details Files
在签名生成流程中传递可选时间戳,使调用方可以控制并复用同一次请求时间。
  • 扩展 CryptoProcessor.build_payload_array 以接受可选时间戳,并在未提供时默认使用当前时间。
  • 更新 Xhshow._build_signature 以及 sign_xs / sign_xs_get / sign_xs_post 以接受并向下传递可选的时间戳参数。
  • 调整请求签名校验装饰器,使其支持时间戳感知,同时保留现有的参数校验逻辑。
src/xhshow/core/crypto.py
src/xhshow/client.py
src/xhshow/utils/validators.py
添加用于生成 Trace ID 和完整签名请求头的辅助方法,支持一致地构造 x-s / x-t / x-b3-traceid / x-xray-traceid
  • 在 Xhshow 客户端中实例化一个 RandomGenerator,并暴露 get_b3_trace_idget_xray_trace_id 方法,由其进行具体生成。
  • 添加 get_x_t 用于生成毫秒级时间戳,以及 sign_headers / sign_headers_get / sign_headers_post 用于使用统一的时间戳和 Trace ID 构造完整的请求头字典。
  • 实现 RandomGenerator.generate_b3_trace_idgenerate_xray_trace_id,并添加相应的配置常量,用于十六进制字母表和 X-Ray bit 布局。
src/xhshow/client.py
src/xhshow/utils/random_gen.py
src/xhshow/config/config.py
扩大对时间戳使用、Trace ID 生成以及请求头辅助方法的测试覆盖范围,并更新 README 使用示例以推广新的 API。
  • 新增测试,覆盖 sign_xs_get / sign_xs_post / sign_xs 对时间戳参数的支持、Trace ID 格式及其参数化、签名与 Trace ID 之间统一时间戳的使用、x-t 生成以及所有请求头辅助方法。
  • 更新 README,推荐将 sign_headers_get / sign_headers_post 作为主要用法,展示如何将生成的请求头与已有请求头合并,并将旧的逐字段使用方式移入一个可折叠区域,同时补充统一时间戳示例。
tests/test_crypto.py
README.md

与关联 Issue 的对照评估

Issue Objective Addressed Explanation
#70 在客户端库中添加生成并暴露 x-b3-traceid 参数/请求头的支持。
#70 提供 API(和文档)以生成完整请求头,包括现有签名参数(如 x-s / x-t)以及新的 Trace 相关参数(如 x-b3-traceid 及相关 Trace ID),并可选使用统一时间戳。

Tips and commands

Interacting with Sourcery

  • 触发新评审: 在 pull request 中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的评审评论。
  • 从评审评论生成 GitHub Issue: 在评审评论中回复,请求 Sourcery 根据该评论创建 Issue;也可以直接在评论中回复 @sourcery-ai issue 来创建 Issue。
  • 生成 pull request 标题: 在 pull request 标题中任意位置写上 @sourcery-ai 即可随时生成标题;也可以在 pull request 中评论 @sourcery-ai title 来(重新)生成标题。
  • 生成 pull request 摘要: 在 pull request 正文任意位置写上 @sourcery-ai summary,即可在对应位置生成 PR 摘要;也可以在 pull request 中评论 @sourcery-ai summary 来(重新)生成摘要。
  • 生成审阅者指南: 在 pull request 中评论 @sourcery-ai guide,即可(重新)生成审阅者指南。
  • 解决所有 Sourcery 评论: 在 pull request 中评论 @sourcery-ai resolve,即可将所有 Sourcery 评论标记为已解决。适用于你已经处理完所有评论且不希望再看到它们时。
  • 清除所有 Sourcery 评审: 在 pull request 中评论 @sourcery-ai dismiss,即可清除所有现有 Sourcery 评审。尤其适用于你希望从一次全新的评审开始——别忘了之后再评论 @sourcery-ai review 来触发新的评审!

Customizing Your Experience

访问你的 dashboard 以:

  • 启用或停用评审功能,例如 Sourcery 自动生成的 pull request 摘要、审阅者指南等。
  • 更改评审语言。
  • 添加、删除或编辑自定义评审说明。
  • 调整其他评审相关设置。

Getting Help

Original review guide in English

Reviewer's Guide

Add unified timestamp support and header/trace-id generation helpers to the Xhshow client, extend the crypto payload to accept an optional timestamp, introduce random-based trace ID utilities, and document and test the new timestamp and header APIs.

Sequence diagram for unified header signing with timestamp and trace IDs

sequenceDiagram
    actor Developer
    participant Xhshow
    participant CryptoProcessor
    participant RandomGenerator

    Developer->>Xhshow: sign_headers(method, uri, a1_value, xsec_appid, params, payload, timestamp=None)
    activate Xhshow
    Xhshow->>Xhshow: if timestamp is None:
    Xhshow->>Xhshow:   timestamp = time.time()
    Xhshow->>Xhshow: request_data = params or payload

    Xhshow->>Xhshow: sign_xs(method, uri, a1_value, xsec_appid, request_data, timestamp)
    activate Xhshow
    Xhshow->>Xhshow: _build_content_string(method, uri, request_data)
    Xhshow->>Xhshow: _generate_d_value(content_string)
    Xhshow->>CryptoProcessor: build_payload_array(d_value, a1_value, xsec_appid, content_string, timestamp)
    activate CryptoProcessor
    CryptoProcessor-->>Xhshow: payload_array
    deactivate CryptoProcessor
    Xhshow->>CryptoProcessor: bit_ops.xor_transform_array(payload_array)
    activate CryptoProcessor
    CryptoProcessor-->>Xhshow: xor_result
    deactivate CryptoProcessor
    Xhshow->>CryptoProcessor: b64encoder.encode(xor_result)
    activate CryptoProcessor
    CryptoProcessor-->>Xhshow: x3_signature
    deactivate CryptoProcessor
    Xhshow->>Xhshow: build x_s from signature_data
    Xhshow-->>Xhshow: x_s
    deactivate Xhshow

    Xhshow->>Xhshow: get_x_t(timestamp)
    Xhshow->>Xhshow: x_t = int(timestamp * 1000)

    Xhshow->>RandomGenerator: generate_b3_trace_id()
    activate RandomGenerator
    RandomGenerator-->>Xhshow: x_b3_traceid
    deactivate RandomGenerator

    Xhshow->>RandomGenerator: generate_xray_trace_id(timestamp_ms=int(timestamp * 1000))
    activate RandomGenerator
    RandomGenerator-->>Xhshow: x_xray_traceid
    deactivate RandomGenerator

    Xhshow-->>Developer: headers { x-s, x-t, x-b3-traceid, x-xray-traceid }
    deactivate Xhshow
Loading

Updated class diagram for Xhshow, CryptoProcessor, RandomGenerator, and CryptoConfig

classDiagram
    class CryptoConfig {
        +int MAX_32BIT
        +int MAX_SIGNED_32BIT
        +int MAX_BYTE
        +str STANDARD_BASE64_ALPHABET
        +str URLSAFE_BASE64_ALPHABET
        +str X3_PREFIX
        +str XYS_PREFIX
        +str HEX_CHARS
        +int XRAY_TRACE_ID_SEQ_MAX
        +int XRAY_TRACE_ID_TIMESTAMP_SHIFT
        +int XRAY_TRACE_ID_PART1_LENGTH
        +int XRAY_TRACE_ID_PART2_LENGTH
        +int B3_TRACE_ID_LENGTH
        +CryptoConfig with_overrides(kwargs)
    }

    class RandomGenerator {
        -CryptoConfig config
        +list~int~ generate_random_bytes(byte_count)
        +int generate_random_byte_in_range(min_val, max_val)
        +int generate_random_int()
        +str generate_b3_trace_id()
        +str generate_xray_trace_id(timestamp, seq)
    }

    class CryptoProcessor {
        +CryptoConfig config
        +RandomGenerator random_gen
        +Any bit_ops
        +Any b64encoder
        +list~int~ build_payload_array(d_value, a1_value, app_identifier, string_param, timestamp)
    }

    class Xhshow {
        +CryptoConfig config
        +CryptoProcessor crypto_processor
        +RandomGenerator random_generator
        +__init__(config)
        +str _build_content_string(method, uri, payload)
        +str _build_signature(d_value, a1_value, xsec_appid, string_param, timestamp)
        +str sign_xs(method, uri, a1_value, xsec_appid, payload, timestamp)
        +str sign_xs_get(uri, a1_value, xsec_appid, params, timestamp)
        +str sign_xs_post(uri, a1_value, xsec_appid, payload, timestamp)
        +bytearray decode_x3(x3_signature)
        +str build_url(base_url, params)
        +str build_json_body(payload)
        +str get_b3_trace_id()
        +str get_xray_trace_id(timestamp, seq)
        +int get_x_t(timestamp)
        +dict~str,str~ sign_headers(method, uri, a1_value, xsec_appid, params, payload, timestamp)
        +dict~str,str~ sign_headers_get(uri, a1_value, xsec_appid, params, timestamp)
        +dict~str,str~ sign_headers_post(uri, a1_value, xsec_appid, payload, timestamp)
    }

    CryptoProcessor --> CryptoConfig : uses
    CryptoProcessor --> RandomGenerator : uses
    RandomGenerator --> CryptoConfig : uses
    Xhshow --> CryptoConfig : holds
    Xhshow --> CryptoProcessor : holds
    Xhshow --> RandomGenerator : holds
Loading

File-Level Changes

Change Details Files
Propagate optional timestamp through signature generation so callers can control and reuse a single request time.
  • Extend CryptoProcessor.build_payload_array to accept an optional timestamp and default to current time when not provided.
  • Update Xhshow._build_signature and sign_xs/sign_xs_get/sign_xs_post to accept and forward an optional timestamp argument.
  • Adjust request signature validation decorators to be timestamp-aware while preserving existing parameter validation.
src/xhshow/core/crypto.py
src/xhshow/client.py
src/xhshow/utils/validators.py
Add helpers for generating trace IDs and full signed headers, enabling consistent x-s/x-t/x-b3-traceid/x-xray-traceid construction.
  • Instantiate a RandomGenerator on the Xhshow client and expose get_b3_trace_id and get_xray_trace_id methods that delegate to it.
  • Add get_x_t to generate millisecond timestamps, and sign_headers/sign_headers_get/sign_headers_post to build complete header dictionaries using unified timestamps and trace IDs.
  • Implement RandomGenerator.generate_b3_trace_id and generate_xray_trace_id plus supporting configuration constants for hex alphabets and xray bit layout.
src/xhshow/client.py
src/xhshow/utils/random_gen.py
src/xhshow/config/config.py
Broaden test coverage for timestamp usage, trace ID generation, and header helpers, and update README usage examples to promote the new APIs.
  • Add tests covering timestamp parameter support in sign_xs_get/sign_xs_post/sign_xs, trace ID formats and parameterization, unified timestamp use across signatures and trace IDs, x-t generation, and all header helper methods.
  • Update README to recommend sign_headers_get/sign_headers_post as the primary usage, showing how to merge generated headers with existing ones, and move the older per-field usage into a collapsible section with unified timestamp examples.
tests/test_crypto.py
README.md

Assessment against linked issues

Issue Objective Addressed Explanation
#70 Add support in the client library for generating and exposing the x-b3-traceid parameter/header.
#70 Provide APIs (and documentation) to generate complete request headers including existing signature parameters (e.g., x-s/x-t) and new trace-related parameters (e.g., x-b3-traceid and related trace IDs), optionally using a unified timestamp.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes and they look great!

用于 AI 代理的提示词
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location> `src/xhshow/client.py:377-386` </location>
<code_context>
+        if timestamp is None:
+            timestamp = time.time()
+
+        # Use params for GET, payload for POST
+        request_data = params if method.upper() == "GET" else payload
+
+        x_s = self.sign_xs(method, uri, a1_value, xsec_appid, request_data, timestamp)
</code_context>

<issue_to_address>
**suggestion:**`sign_headers` 中增加对不支持的 HTTP 方法以及不匹配的 params/payload 使用方式的保护。

目前任何非 `"GET"` 的方法在签名时都会被当作 `POST` 处理,而且当同时传入 `params``payload` 时也不会报错。为避免静默产生错误签名,建议显式地对 `"GET"`/`"POST"` 分支处理,对其他方法抛出 `ValueError`,并校验 `GET` 请求只使用 `params``POST` 请求只使用 `payload````suggestion
            >>> headers.keys()
            dict_keys(['x-s', 'x-t', 'x-b3-traceid', 'x-xray-traceid'])
        """
        if timestamp is None:
            timestamp = time.time()

        method_upper = method.upper()
        if method_upper not in ("GET", "POST"):
            raise ValueError(f"Unsupported HTTP method for signing: {method!r}")

        if method_upper == "GET":
            if params is not None and payload is not None:
                raise ValueError(
                    "For GET requests, provide request data via 'params' only; "
                    "'payload' must be None."
                )
            if payload is not None:
                raise ValueError(
                    "GET requests must not use 'payload'; use 'params' instead."
                )
            request_data = params
        else:  # POST
            if params is not None and payload is not None:
                raise ValueError(
                    "For POST requests, provide request data via 'payload' only; "
                    "'params' must be None."
                )
            if params is not None:
                raise ValueError(
                    "POST requests must not use 'params'; use 'payload' instead."
                )
            request_data = payload

        x_s = self.sign_xs(method_upper, uri, a1_value, xsec_appid, request_data, timestamp)
```
</issue_to_address>

### Comment 2
<location> `tests/test_crypto.py:426-435` </location>
<code_context>
+    def test_trace_id_generation(self):
</code_context>

<issue_to_address>
**suggestion (testing):** 为 xray trace ID 的 seq 范围(0 和最大值)添加边界测试。

目前的 `test_trace_id_generation` 只检测了一个自定义的 `seq` 值,而没有覆盖文档中提到的边界值(0 和 `2^23-1`)。请扩展此测试(或新增一个)调用 `get_xray_trace_id(timestamp=custom_ts, seq=0)``get_xray_trace_id(timestamp=custom_ts, seq=8388607)`,并断言它们依然返回 32 个字符长度的十六进制字符串。你也可以断言在相同的 timestamp 下,`seq=0``seq=max` 生成的 ID 的前 16 个字符是不同的,以确认 `seq` 被编码进了 ID 中。
</issue_to_address>

### Comment 3
<location> `tests/test_crypto.py:434-442` </location>
<code_context>
    def test_trace_id_generation(self):
        """测试 Trace ID 生成"""
        import time

        client = Xhshow()

        # Test b3 trace id
        b3_id = client.get_b3_trace_id()
        assert isinstance(b3_id, str)
        assert len(b3_id) == 16
        assert all(c in "0123456789abcdef" for c in b3_id)

        # Test xray trace id with default timestamp
        xray_id1 = client.get_xray_trace_id()
        assert isinstance(xray_id1, str)
        assert len(xray_id1) == 32
        assert all(c in "0123456789abcdef" for c in xray_id1)

        # Test xray trace id with custom timestamp
        custom_ts = int(time.time() * 1000)
        xray_id2 = client.get_xray_trace_id(timestamp=custom_ts)
        assert isinstance(xray_id2, str)
        assert len(xray_id2) == 32

        # Test xray trace id with custom timestamp and seq
        xray_id3 = client.get_xray_trace_id(timestamp=custom_ts, seq=12345)
        assert isinstance(xray_id3, str)
        assert len(xray_id3) == 32

</code_context>

<issue_to_address>
**issue (code-quality):** 将重复代码提取到一个单独的方法中([`extract-duplicate-method`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/extract-duplicate-method/))。
</issue_to_address>

Sourcery 对开源项目是免费的——如果你觉得我们的评审有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进后续评审。
Original comment in English

Hey there - I've reviewed your changes and they look great!

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location> `src/xhshow/client.py:377-386` </location>
<code_context>
+        if timestamp is None:
+            timestamp = time.time()
+
+        # Use params for GET, payload for POST
+        request_data = params if method.upper() == "GET" else payload
+
+        x_s = self.sign_xs(method, uri, a1_value, xsec_appid, request_data, timestamp)
</code_context>

<issue_to_address>
**suggestion:** Guard against unsupported HTTP methods and mismatched params/payload usage in `sign_headers`.

Currently any non-`"GET"` method is treated like `POST` for signing, and there’s no error if both `params` and `payload` are passed. To avoid silent mis-signing, explicitly branch on `"GET"`/`"POST"`, raise `ValueError` for other methods, and validate that only `params` is used with `GET` and only `payload` with `POST`.

```suggestion
            >>> headers.keys()
            dict_keys(['x-s', 'x-t', 'x-b3-traceid', 'x-xray-traceid'])
        """
        if timestamp is None:
            timestamp = time.time()

        method_upper = method.upper()
        if method_upper not in ("GET", "POST"):
            raise ValueError(f"Unsupported HTTP method for signing: {method!r}")

        if method_upper == "GET":
            if params is not None and payload is not None:
                raise ValueError(
                    "For GET requests, provide request data via 'params' only; "
                    "'payload' must be None."
                )
            if payload is not None:
                raise ValueError(
                    "GET requests must not use 'payload'; use 'params' instead."
                )
            request_data = params
        else:  # POST
            if params is not None and payload is not None:
                raise ValueError(
                    "For POST requests, provide request data via 'payload' only; "
                    "'params' must be None."
                )
            if params is not None:
                raise ValueError(
                    "POST requests must not use 'params'; use 'payload' instead."
                )
            request_data = payload

        x_s = self.sign_xs(method_upper, uri, a1_value, xsec_appid, request_data, timestamp)
```
</issue_to_address>

### Comment 2
<location> `tests/test_crypto.py:426-435` </location>
<code_context>
+    def test_trace_id_generation(self):
</code_context>

<issue_to_address>
**suggestion (testing):** Add boundary tests for xray trace ID seq range (0 and max)

The existing `test_trace_id_generation` only checks a custom `seq` value, but not the documented boundaries (0 and `2^23-1`). Please extend this test (or add a new one) to call `get_xray_trace_id(timestamp=custom_ts, seq=0)` and `get_xray_trace_id(timestamp=custom_ts, seq=8388607)` and assert they still return 32-char hex strings. You may also want to assert that the first 16 characters differ between `seq=0` and `seq=max` for the same timestamp to confirm `seq` is encoded into the ID.
</issue_to_address>

### Comment 3
<location> `tests/test_crypto.py:434-442` </location>
<code_context>
    def test_trace_id_generation(self):
        """测试 Trace ID 生成"""
        import time

        client = Xhshow()

        # Test b3 trace id
        b3_id = client.get_b3_trace_id()
        assert isinstance(b3_id, str)
        assert len(b3_id) == 16
        assert all(c in "0123456789abcdef" for c in b3_id)

        # Test xray trace id with default timestamp
        xray_id1 = client.get_xray_trace_id()
        assert isinstance(xray_id1, str)
        assert len(xray_id1) == 32
        assert all(c in "0123456789abcdef" for c in xray_id1)

        # Test xray trace id with custom timestamp
        custom_ts = int(time.time() * 1000)
        xray_id2 = client.get_xray_trace_id(timestamp=custom_ts)
        assert isinstance(xray_id2, str)
        assert len(xray_id2) == 32

        # Test xray trace id with custom timestamp and seq
        xray_id3 = client.get_xray_trace_id(timestamp=custom_ts, seq=12345)
        assert isinstance(xray_id3, str)
        assert len(xray_id3) == 32

</code_context>

<issue_to_address>
**issue (code-quality):** Extract duplicate code into method ([`extract-duplicate-method`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/extract-duplicate-method/))
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Cloxl added 2 commits December 5, 2025 12:11
- validate GET requests only use params, not payload
- validate POST requests only use payload, not params
- raise ValueError for unsupported HTTP methods (only GET/POST allowed)
- add comprehensive test coverage for parameter validation
- improve error messages for better developer experience
@Cloxl Cloxl mentioned this pull request Dec 5, 2025
illusiona and others added 3 commits December 7, 2025 02:07
* Add the cryptodemo library, using a custom encode_to_b64 method instead of the official standard base64 method.

Add some initialization parameters to the configuration file to generate fingerprints.

Perhaps using customer_encoder to replace the encoder file name is better than directly using the encoder name, since encoders are a native Python method.

* Add the cryptodemo library, using a custom encode_to_b64 method instead of the official standard base64 method.

Add some initialization parameters to the configuration file to generate fingerprints.

ADD CRC32_encrypt for gen xs-common

Perhaps using customer_encoder to replace the encoder file name is better than directly using the encoder name, since encoders are a native Python method.

Use case:
cookie = "you cookie dict or string"
xs_common = client.sign_xsc(cookie)

* style: format code to match project standards

* refactor(core): split fingerprint generation into separate modules

* perf(encoder): optimize base64 encoding with cached translation tables

* feat(client): add unified cookie parsing and x-s-common signature support

* test: add comprehensive cookie parsing tests and update crypto tests

* chore(crc32): remove example code comments

* docs: update API documentation for cookie-based authentication

* chore(deps): update project dependencies

---------

Co-authored-by: Cloxl <cloxl@cloxl.com>
@Cloxl Cloxl merged commit 8aa7056 into master Dec 6, 2025
4 checks passed
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.

Feat: xsc、x-b3-traceid

3 participants