Skip to content

Merge User-Agent headers when cloning HTTP client#508

Merged
heyitsaamir merged 7 commits into
mainfrom
feat/merge-user-agent-on-clone
Apr 13, 2026
Merged

Merge User-Agent headers when cloning HTTP client#508
heyitsaamir merged 7 commits into
mainfrom
feat/merge-user-agent-on-clone

Conversation

@heyitsaamir

@heyitsaamir heyitsaamir commented Apr 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Client.clone() now concatenates User-Agent headers instead of overwriting, so each layer's identifier is preserved (e.g. teams.ts[graph]/x.x teams.ts[apps]/x.x)
  • Works across any number of nested clones
  • This is helpful in cases where we want to track what's actually using teams.apps. (Vercel, Hosted Agents etc). It gives them a way to pass in User-Agent and have it be reliably tracked.

Test plan

  • Added tests for two-level merge, parent-only, child-only, and three-level merge
  • Run npx jest src/http/client.spec.ts from packages/common — all 23 tests pass

heyitsaamir and others added 6 commits March 24, 2026 23:48
These two markdown files are all a human needs to write — the e2e testing
skill generates and manages the actual Playwright test code from here.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Instead of overwriting, clone() now concatenates User-Agent headers
so each layer's identifier is preserved across nested clones.

Co-Authored-By: Claude <[email protected]>
lilyydu
lilyydu previously approved these changes Apr 10, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Updates the shared HTTP Client.clone() behavior in @microsoft/teams.common to preserve layered identity by concatenating User-Agent values across nested clones, improving observability of which package/layer is issuing requests.

Changes:

  • Concatenate parent + child User-Agent values during Client.clone() instead of overwriting.
  • Keep existing header spread semantics for non-User-Agent headers.
  • Add Jest coverage for 2-level and 3-level User-Agent merge scenarios and parent/child-only cases.

Reviewed changes

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

File Description
packages/common/src/http/client.ts Implements User-Agent concatenation when cloning clients.
packages/common/src/http/client.spec.ts Adds tests validating merged User-Agent behavior across clone levels.

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

Comment thread packages/common/src/http/client.ts Outdated
Comment thread packages/common/src/http/client.ts Outdated
@heyitsaamir
heyitsaamir merged commit 210c14e into main Apr 13, 2026
7 checks passed
@heyitsaamir
heyitsaamir deleted the feat/merge-user-agent-on-clone branch April 13, 2026 16:33
heyitsaamir added a commit to microsoft/teams.py that referenced this pull request Apr 13, 2026
When `Client.clone()` was called with headers containing a `User-Agent`,
it would overwrite the base client's `User-Agent` rather than merging.
This loses the SDK's default UA token when callers supply their own.

## Changes

- **`packages/common/src/microsoft_teams/common/http/client.py`**
- Added `_merge_headers(base, overrides)` helper: performs a
case-insensitive `User-Agent` merge (space-concatenated, token-based
dedup to avoid substring false positives); all other headers follow
standard last-write-wins semantics
- `Client.clone()` now calls `_merge_headers()` instead of a plain dict
unpack

- **`packages/common/tests/test_client.py`**
- Added 7 tests covering: preservation without overrides, merge when
both sides define UA, non-UA overrides leave UA untouched,
case-insensitive key matching, duplicate-token suppression, substring
false-positive guard, and override-only UA when base has none

## Example

```python
client = Client(ClientOptions(headers={"User-Agent": "teams-py/1.0"}))
clone = client.clone(ClientOptions(headers={"User-Agent": "myapp/2.0"}))

# Before: "myapp/2.0"  (SDK UA lost)
# After:  "teams-py/1.0 myapp/2.0"
print(clone._options.headers["User-Agent"])
```

> [!WARNING]
>
> <details>
> <summary>Firewall rules blocked me from connecting to one or more
addresses (expand for details)</summary>
>
> #### I tried to connect to the following addresses, but was blocked by
firewall rules:
>
> - `releases.astral.sh`
> - Triggering command: `/home/REDACTED/.local/bin/uv uv sync
--all-packages --group dev` (dns block)
>
> If you need me to access, download, or install something from one of
these locations, you can either:
>
> - Configure [Actions setup
steps](https://gh.io/copilot/actions-setup-steps) to set up my
environment, which run before the firewall is enabled
> - Add the appropriate URLs or hosts to the custom allowlist in this
repository's [Copilot coding agent
settings](https://github.com/microsoft/teams.py/settings/copilot/coding_agent)
(admins only)
>
> </details>

<!-- START COPILOT ORIGINAL PROMPT -->



<details>

<summary>Original prompt</summary>

> Implement the change described by PR microsoft/teams.ts#508 ("Merge
User-Agent headers when cloning HTTP client") in the Python SDK
repository @microsoft/teams.py.
> 
> Goal:
> - When the HTTP client is cloned (or a new client is created from an
existing client/session), ensure that `User-Agent` headers are merged
rather than overwritten.
> - Preserve any existing default `User-Agent` set by the library while
also incorporating a user-provided `User-Agent` (or additional UA
tokens) when cloning.
> 
> Requirements:
> 1. Locate the HTTP client implementation and any clone/copy method(s)
in the repository.
> 2. Identify how headers are stored (e.g., requests.Session headers,
httpx.Client headers, custom dict) and how cloning currently works.
> 3. Modify cloning logic so that:
> - If both source and override headers contain `User-Agent`, the
resulting `User-Agent` should include both (e.g., concatenated with a
space, or comma per convention) without duplicating identical values.
>    - If only one side has `User-Agent`, keep it.
>    - Header merging should be case-insensitive for the header key.
> 4. Add/adjust unit tests to cover:
>    - Cloning without overrides preserves original `User-Agent`.
> - Cloning with headers override that includes `User-Agent` results in
merged value.
> - Cloning with headers override without `User-Agent` leaves original
UA intact.
>    - Case-insensitive behavior (`user-agent`, `User-Agent`).
> 5. Update any relevant docs or changelog entry if the repo uses one.
> 
> Reference:
> - PR: microsoft/teams.ts#508
> 
> Notes:
> - Ensure behavior is consistent across sync/async HTTP clients if both
exist.
> - Keep backward compatibility with existing public APIs.
> 
> 
> The following is the prior conversation context from the user's chat
exploration (may be truncated):
> 
> User: Can you implement this in @microsoft/teams.py ?
> 
> Assistant: I’ve queued up the implementation for `microsoft/teams.py`
and I’m now waiting on your confirmation in the prompt to proceed with
making the code changes and opening the PR.
> 
> 


</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

*This pull request was created from Copilot chat.*
>

---------

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: heyitsaamir <[email protected]>
Co-authored-by: heyitsaamir <[email protected]>
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
@heyitsaamir heyitsaamir mentioned this pull request Apr 16, 2026
heyitsaamir added a commit that referenced this pull request Apr 16, 2026
## Summary
- Merges all changes from `main` since v2.0.7 into `release`
- Sets `version.json` to stable `2.0.8`

### What's included
- **feat:** Sovereign cloud support (GCCH, DoD, China) (#500)
- **feat:** Add missing endpoints — Paged Members, Meeting Notifications
& client gaps (#516)
- **feat:** Graceful stream cancellation on 403 (#513)
- **feat:** GitHub issue analysis → Teams notification workflow (#517)
- **fix:** Improve error message when app credentials are missing (#527)
- **fix:** Surface Graph API error body in GraphError (#524)
- **fix:** Resolve missing deps, broken JSON, type errors, and typos in
CLI templates (#521)
- **fix:** Drain entire queue per flush cycle (#520)
- **fix:** Merge User-Agent headers when cloning HTTP client (#508)
- Dependency bumps: hono, axios, vite, @hono/node-server

## Post-merge
1. Trigger the [release
pipeline](https://dev.azure.com/DomoreexpGithub/Github_Pipelines/_build?definitionId=52&_a=summary)
for `release` with **Public** publish type
2. Bump `version.json` on `main` to `2.0.9-preview.{height}`

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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.

3 participants