httpbinx 1.13.0__tar.gz → 1.13.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. {httpbinx-1.13.0 → httpbinx-1.13.1}/PKG-INFO +1 -1
  2. {httpbinx-1.13.0 → httpbinx-1.13.1}/pyproject.toml +1 -1
  3. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/__init__.py +1 -1
  4. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/main.py +0 -1
  5. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/routers/responseformats.py +23 -3
  6. httpbinx-1.13.1/src/httpbinx/templates/legacy.html +18 -0
  7. httpbinx-1.13.1/tests/test_cors.py +31 -0
  8. {httpbinx-1.13.0 → httpbinx-1.13.1}/tests/test_response_formats.py +23 -4
  9. {httpbinx-1.13.0 → httpbinx-1.13.1}/LICENSE +0 -0
  10. {httpbinx-1.13.0 → httpbinx-1.13.1}/README.md +0 -0
  11. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/cli.py +0 -0
  12. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/constants.py +0 -0
  13. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/examples/__init__.py +0 -0
  14. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/helpers.py +0 -0
  15. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/meta.py +0 -0
  16. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/routers/__init__.py +0 -0
  17. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/routers/anything.py +0 -0
  18. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/routers/auth.py +0 -0
  19. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/routers/cookies.py +0 -0
  20. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/routers/dynamicdata.py +0 -0
  21. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/routers/httpmethods.py +0 -0
  22. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/routers/images.py +0 -0
  23. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/routers/inspection/__init__.py +0 -0
  24. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/routers/inspection/request.py +0 -0
  25. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/routers/inspection/response.py +0 -0
  26. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/routers/redirects.py +0 -0
  27. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/routers/statuscodes.py +0 -0
  28. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/schemas.py +0 -0
  29. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/static/UTF-8-demo.txt +0 -0
  30. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/static/bombs/bomb-1GB.br +0 -0
  31. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/static/bombs/bomb-1GB.gz +0 -0
  32. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/static/favicon.png +0 -0
  33. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/static/images/httbinx_logo.png +0 -0
  34. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/static/images/httpbinx_cover.png +0 -0
  35. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/static/images/jackal.jpg +0 -0
  36. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/static/images/pig_icon.png +0 -0
  37. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/static/images/svg_logo.svg +0 -0
  38. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/static/images/wolf_1.webp +0 -0
  39. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/templates/moby.html +0 -0
  40. {httpbinx-1.13.0 → httpbinx-1.13.1}/src/httpbinx/templates/sample.xml +0 -0
  41. {httpbinx-1.13.0 → httpbinx-1.13.1}/tests/__init__.py +0 -0
  42. {httpbinx-1.13.0 → httpbinx-1.13.1}/tests/conftest.py +0 -0
  43. {httpbinx-1.13.0 → httpbinx-1.13.1}/tests/test_anything.py +0 -0
  44. {httpbinx-1.13.0 → httpbinx-1.13.1}/tests/test_auth.py +0 -0
  45. {httpbinx-1.13.0 → httpbinx-1.13.1}/tests/test_cookies.py +0 -0
  46. {httpbinx-1.13.0 → httpbinx-1.13.1}/tests/test_dynamic_data.py +0 -0
  47. {httpbinx-1.13.0 → httpbinx-1.13.1}/tests/test_http_methods.py +0 -0
  48. {httpbinx-1.13.0 → httpbinx-1.13.1}/tests/test_images.py +0 -0
  49. {httpbinx-1.13.0 → httpbinx-1.13.1}/tests/test_openapi.py +0 -0
  50. {httpbinx-1.13.0 → httpbinx-1.13.1}/tests/test_redirects.py +0 -0
  51. {httpbinx-1.13.0 → httpbinx-1.13.1}/tests/test_request_inspection.py +0 -0
  52. {httpbinx-1.13.0 → httpbinx-1.13.1}/tests/test_response_inspection.py +0 -0
  53. {httpbinx-1.13.0 → httpbinx-1.13.1}/tests/test_schemas.py +0 -0
  54. {httpbinx-1.13.0 → httpbinx-1.13.1}/tests/test_status_codes.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: httpbinx
3
- Version: 1.13.0
3
+ Version: 1.13.1
4
4
  Summary: HTTP Request & Response Service, written in Python + FastAPI.
5
5
  Author-Email: Leo <imleowoo@outlook.com>
6
6
  License: MIT License
@@ -31,7 +31,7 @@ classifiers = [
31
31
  "Programming Language :: Python :: 3.14",
32
32
  "License :: OSI Approved :: MIT License",
33
33
  ]
34
- version = "1.13.0"
34
+ version = "1.13.1"
35
35
 
36
36
  [project.license]
37
37
  file = "LICENSE"
@@ -2,6 +2,6 @@
2
2
 
3
3
  from .main import app
4
4
 
5
- __version__ = '1.13.0'
5
+ __version__ = '1.13.1'
6
6
 
7
7
  app.version = __version__
@@ -18,7 +18,6 @@ app = FastAPI(
18
18
  app.add_middleware(
19
19
  CORSMiddleware, # type: ignore
20
20
  allow_origins=['*'],
21
- allow_credentials=True,
22
21
  allow_methods=['*'],
23
22
  allow_headers=['*'],
24
23
  )
@@ -134,14 +134,34 @@ async def json_endpoint():
134
134
 
135
135
 
136
136
  @router.get(
137
- '/robot.txt',
137
+ '/legacy',
138
+ response_class=HTMLResponse,
139
+ summary='Returns the legacy landing page.',
140
+ response_description='An HTML page listing the available endpoints.',
141
+ )
142
+ async def legacy_page(request: Request, templates: Jinja2Templates = Depends(get_templates)):
143
+ schema = request.app.openapi()
144
+ endpoints = []
145
+ for path, methods in sorted(schema.get('paths', {}).items()):
146
+ for method, operation in methods.items():
147
+ if method.upper() not in {'GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'}:
148
+ continue
149
+ endpoints.append({'method': method.upper(), 'path': path, 'summary': operation.get('summary', '')})
150
+ return templates.TemplateResponse(
151
+ request,
152
+ 'legacy.html',
153
+ context={'request': request, 'endpoints': endpoints},
154
+ )
155
+
156
+
157
+ @router.get(
158
+ '/robots.txt',
138
159
  response_class=PlainTextResponse,
139
160
  summary='Returns some robots.txt rules.',
140
161
  response_description='Robots file',
141
162
  )
142
163
  async def robots_page():
143
- response = PlainTextResponse(content=ROBOT_TXT)
144
- return response
164
+ return PlainTextResponse(content=ROBOT_TXT)
145
165
 
146
166
 
147
167
  @router.get(
@@ -0,0 +1,18 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>httpbinx - HTTP Request &amp; Response Service</title>
6
+ </head>
7
+ <body>
8
+ <h1>httpbinx</h1>
9
+ <p>HTTP Request &amp; Response Service, written in Python + FastAPI.</p>
10
+ <p>See the <a href="/">interactive API documentation</a> for details and examples.</p>
11
+ <h2>Endpoints</h2>
12
+ <ul>
13
+ {% for endpoint in endpoints %}
14
+ <li><a href="{{ endpoint.path }}"><code>{{ endpoint.method }} {{ endpoint.path }}</code></a> {{ endpoint.summary }}</li>
15
+ {% endfor %}
16
+ </ul>
17
+ </body>
18
+ </html>
@@ -0,0 +1,31 @@
1
+ """CORS configuration tests."""
2
+
3
+ ORIGIN = 'https://evil.example.com'
4
+
5
+
6
+ def test_wildcard_origin_is_returned_as_is(client):
7
+ """A wildcard allowlist is echoed literally, not per-request origin."""
8
+ response = client.get('/get', headers={'Origin': ORIGIN})
9
+
10
+ assert response.headers['access-control-allow-origin'] == '*'
11
+
12
+
13
+ def test_credentials_are_not_allowed_for_any_origin(client):
14
+ """Credentials must not be combined with a wildcard origin allowlist."""
15
+ response = client.get('/get', headers={'Origin': ORIGIN})
16
+
17
+ assert 'access-control-allow-credentials' not in response.headers
18
+
19
+
20
+ def test_preflight_does_not_allow_credentials(client):
21
+ """Preflight responses must not advertise credentialed cross-origin access."""
22
+ response = client.options(
23
+ '/get',
24
+ headers={
25
+ 'Origin': ORIGIN,
26
+ 'Access-Control-Request-Method': 'GET',
27
+ },
28
+ )
29
+
30
+ assert response.headers['access-control-allow-origin'] == '*'
31
+ assert 'access-control-allow-credentials' not in response.headers
@@ -83,15 +83,34 @@ class TestJson:
83
83
  assert len(data['slides']) == 2
84
84
 
85
85
 
86
- class TestRobot:
87
- """Tests for the /robot.txt endpoint."""
86
+ class TestLegacy:
87
+ """Tests for the /legacy landing page endpoint."""
88
88
 
89
- def test_content(self, client):
90
- response = client.get('/robot.txt')
89
+ def test_ok_and_html(self, client):
90
+ response = client.get('/legacy')
91
+ assert response.status_code == status.HTTP_200_OK
92
+ assert response.headers['content-type'].startswith('text/html')
93
+
94
+ def test_lists_endpoints(self, client):
95
+ response = client.get('/legacy')
96
+ assert '/get' in response.text
97
+ assert '/robots.txt' in response.text
98
+
99
+
100
+ class TestRobots:
101
+ """Tests for the /robots.txt endpoint."""
102
+
103
+ def test_canonical_route(self, client):
104
+ response = client.get('/robots.txt')
91
105
  assert response.status_code == status.HTTP_200_OK
106
+ assert response.headers['content-type'].startswith('text/plain')
92
107
  assert 'User-agent' in response.text
93
108
  assert 'Disallow' in response.text
94
109
 
110
+ def test_nonstandard_robot_txt_is_gone(self, client):
111
+ response = client.get('/robot.txt')
112
+ assert response.status_code == status.HTTP_404_NOT_FOUND
113
+
95
114
 
96
115
  @pytest.mark.skip(reason='Endpoint uses CWD-relative path to templates (source bug)')
97
116
  def test_xml(client):
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes