Jeremiah Lowin commited on
Commit
c457a0d
·
1 Parent(s): eea96ed

Ensure behavior-affecting headers are excluded

Browse files
docs/patterns/http-requests.mdx CHANGED
@@ -77,7 +77,7 @@ async def safe_header_info() -> dict:
77
  }
78
  ```
79
 
80
- By default, `get_http_headers()` excludes problematic headers like `content-length`. To include all headers, use `get_http_headers(include_all=True)`.
81
 
82
  ## Important Notes
83
 
 
77
  }
78
  ```
79
 
80
+ By default, `get_http_headers()` excludes problematic headers like `host` and `content-length`. To include all headers, use `get_http_headers(include_all=True)`.
81
 
82
  ## Important Notes
83
 
src/fastmcp/server/dependencies.py CHANGED
@@ -48,11 +48,24 @@ def get_http_headers(include_all: bool = False) -> dict[str, str]:
48
  if include_all:
49
  exclude_headers = set()
50
  else:
51
- exclude_headers = {"content-length"}
52
-
53
- # ensure all lowercase!
54
- # (just in case)
55
- exclude_headers = {h.lower() for h in exclude_headers}
 
 
 
 
 
 
 
 
 
 
 
 
 
56
 
57
  headers = {}
58
 
 
48
  if include_all:
49
  exclude_headers = set()
50
  else:
51
+ exclude_headers = {
52
+ "host",
53
+ "content-length",
54
+ "connection",
55
+ "transfer-encoding",
56
+ "upgrade",
57
+ "te",
58
+ "keep-alive",
59
+ "expect",
60
+ # Proxy-related headers
61
+ "proxy-authenticate",
62
+ "proxy-authorization",
63
+ "proxy-connection",
64
+ }
65
+ # (just in case)
66
+ assert all(h.lower() == h for h in exclude_headers), (
67
+ "Excluded headers must be lowercase"
68
+ )
69
 
70
  headers = {}
71
 
tests/client/test_openapi.py CHANGED
@@ -28,7 +28,8 @@ def fastmcp_server_for_headers() -> FastMCP:
28
  return request.headers
29
 
30
  mcp = FastMCP.from_fastapi(
31
- app, httpx_client_kwargs={"headers": {"X-SERVER": "test-abc"}}
 
32
  )
33
 
34
  return mcp
@@ -169,16 +170,42 @@ class TestClientHeaders:
169
  headers = json.loads(result[0].text)
170
  assert headers["x-test"] == "test-123"
171
 
 
 
 
 
 
 
 
 
 
172
  async def test_client_overrides_server_headers(self, shttp_server: str):
173
  async with Client(
174
  transport=StreamableHttpTransport(
175
- shttp_server, headers={"X-SERVER": "test-client"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  )
177
  ) as client:
178
  result = await client.read_resource("resource://get_headers_headers_get")
179
  assert isinstance(result[0], TextResourceContents)
180
  headers = json.loads(result[0].text)
181
- assert headers["x-server"] == "test-client"
 
182
 
183
  async def test_client_headers_proxy(self, proxy_server: str):
184
  """
@@ -188,4 +215,4 @@ class TestClientHeaders:
188
  result = await client.read_resource("resource://get_headers_headers_get")
189
  assert isinstance(result[0], TextResourceContents)
190
  headers = json.loads(result[0].text)
191
- assert headers["x-server"] == "test-abc"
 
28
  return request.headers
29
 
30
  mcp = FastMCP.from_fastapi(
31
+ app,
32
+ httpx_client_kwargs={"headers": {"x-server-header": "test-abc"}},
33
  )
34
 
35
  return mcp
 
170
  headers = json.loads(result[0].text)
171
  assert headers["x-test"] == "test-123"
172
 
173
+ # async def test_certain_client_headers_are_stripped(self, shttp_server: str):
174
+ # async with Client(
175
+ # transport=StreamableHttpTransport(shttp_server, headers={"host": "1.2.3.4"})
176
+ # ) as client:
177
+ # result = await client.read_resource("resource://get_headers_headers_get")
178
+ # assert isinstance(result[0], TextResourceContents)
179
+ # headers = json.loads(result[0].text)
180
+ # assert "host" not in headers
181
+
182
  async def test_client_overrides_server_headers(self, shttp_server: str):
183
  async with Client(
184
  transport=StreamableHttpTransport(
185
+ shttp_server, headers={"x-server-header": "test-client"}
186
+ )
187
+ ) as client:
188
+ result = await client.read_resource("resource://get_headers_headers_get")
189
+ assert isinstance(result[0], TextResourceContents)
190
+ headers = json.loads(result[0].text)
191
+ assert headers["x-server-header"] == "test-client"
192
+
193
+ async def test_client_with_excluded_header_is_ignored(self, sse_server: str):
194
+ async with Client(
195
+ transport=SSETransport(
196
+ sse_server,
197
+ headers={
198
+ "x-server-header": "test-client",
199
+ "host": "1.2.3.4",
200
+ "not-host": "1.2.3.4",
201
+ },
202
  )
203
  ) as client:
204
  result = await client.read_resource("resource://get_headers_headers_get")
205
  assert isinstance(result[0], TextResourceContents)
206
  headers = json.loads(result[0].text)
207
+ assert headers["not-host"] == "1.2.3.4"
208
+ assert headers["host"] == "fastapi"
209
 
210
  async def test_client_headers_proxy(self, proxy_server: str):
211
  """
 
215
  result = await client.read_resource("resource://get_headers_headers_get")
216
  assert isinstance(result[0], TextResourceContents)
217
  headers = json.loads(result[0].text)
218
+ assert headers["x-server-header"] == "test-abc"