httpr 0.4.6__tar.gz → 0.4.7__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 (76) hide show
  1. {httpr-0.4.6 → httpr-0.4.7}/Cargo.lock +1 -1
  2. {httpr-0.4.6 → httpr-0.4.7}/Cargo.toml +1 -1
  3. {httpr-0.4.6 → httpr-0.4.7}/PKG-INFO +1 -1
  4. {httpr-0.4.6 → httpr-0.4.7}/pyproject.toml +1 -1
  5. {httpr-0.4.6 → httpr-0.4.7}/src/lib.rs +15 -14
  6. {httpr-0.4.6 → httpr-0.4.7}/tests/unit/test_ssl.py +32 -0
  7. {httpr-0.4.6 → httpr-0.4.7}/.github/actions/set-version/action.yml +0 -0
  8. {httpr-0.4.6 → httpr-0.4.7}/.github/copilot-instructions.md +0 -0
  9. {httpr-0.4.6 → httpr-0.4.7}/.github/workflows/CI.yml +0 -0
  10. {httpr-0.4.6 → httpr-0.4.7}/.github/workflows/CI.yml.backup +0 -0
  11. {httpr-0.4.6 → httpr-0.4.7}/.github/workflows/benchmark.yml +0 -0
  12. {httpr-0.4.6 → httpr-0.4.7}/.github/workflows/copilot-setup-steps.yml +0 -0
  13. {httpr-0.4.6 → httpr-0.4.7}/.github/workflows/mkdocs.yml +0 -0
  14. {httpr-0.4.6 → httpr-0.4.7}/.github/workflows/set_version.py +0 -0
  15. {httpr-0.4.6 → httpr-0.4.7}/.gitignore +0 -0
  16. {httpr-0.4.6 → httpr-0.4.7}/.pre-commit-config.yaml +0 -0
  17. {httpr-0.4.6 → httpr-0.4.7}/CLAUDE.md +0 -0
  18. {httpr-0.4.6 → httpr-0.4.7}/LICENSE +0 -0
  19. {httpr-0.4.6 → httpr-0.4.7}/README.md +0 -0
  20. {httpr-0.4.6 → httpr-0.4.7}/Taskfile.yaml +0 -0
  21. {httpr-0.4.6 → httpr-0.4.7}/benchmark/README.md +0 -0
  22. {httpr-0.4.6 → httpr-0.4.7}/benchmark/__init__.py +0 -0
  23. {httpr-0.4.6 → httpr-0.4.7}/benchmark/benchmark.py +0 -0
  24. {httpr-0.4.6 → httpr-0.4.7}/benchmark/benchmark_cbor.py +0 -0
  25. {httpr-0.4.6 → httpr-0.4.7}/benchmark/generate_image.py +0 -0
  26. {httpr-0.4.6 → httpr-0.4.7}/benchmark/server.py +0 -0
  27. {httpr-0.4.6 → httpr-0.4.7}/benchmark.jpg +0 -0
  28. {httpr-0.4.6 → httpr-0.4.7}/docs/advanced/cookies.md +0 -0
  29. {httpr-0.4.6 → httpr-0.4.7}/docs/advanced/index.md +0 -0
  30. {httpr-0.4.6 → httpr-0.4.7}/docs/advanced/proxy.md +0 -0
  31. {httpr-0.4.6 → httpr-0.4.7}/docs/advanced/ssl-tls.md +0 -0
  32. {httpr-0.4.6 → httpr-0.4.7}/docs/api/async-client.md +0 -0
  33. {httpr-0.4.6 → httpr-0.4.7}/docs/api/client.md +0 -0
  34. {httpr-0.4.6 → httpr-0.4.7}/docs/api/functions.md +0 -0
  35. {httpr-0.4.6 → httpr-0.4.7}/docs/api/index.md +0 -0
  36. {httpr-0.4.6 → httpr-0.4.7}/docs/api/response.md +0 -0
  37. {httpr-0.4.6 → httpr-0.4.7}/docs/index.md +0 -0
  38. {httpr-0.4.6 → httpr-0.4.7}/docs/quickstart.md +0 -0
  39. {httpr-0.4.6 → httpr-0.4.7}/docs/tutorial/async.md +0 -0
  40. {httpr-0.4.6 → httpr-0.4.7}/docs/tutorial/authentication.md +0 -0
  41. {httpr-0.4.6 → httpr-0.4.7}/docs/tutorial/index.md +0 -0
  42. {httpr-0.4.6 → httpr-0.4.7}/docs/tutorial/making-requests.md +0 -0
  43. {httpr-0.4.6 → httpr-0.4.7}/docs/tutorial/response-handling.md +0 -0
  44. {httpr-0.4.6 → httpr-0.4.7}/docs/writings/index.md +0 -0
  45. {httpr-0.4.6 → httpr-0.4.7}/docs/writings/posts/2025-02-24-python-http-clients-suck.md +0 -0
  46. {httpr-0.4.6 → httpr-0.4.7}/httpr/__init__.py +0 -0
  47. {httpr-0.4.6 → httpr-0.4.7}/httpr/httpr.pyi +0 -0
  48. {httpr-0.4.6 → httpr-0.4.7}/httpr/py.typed +0 -0
  49. {httpr-0.4.6 → httpr-0.4.7}/httpr.code-workspace +0 -0
  50. {httpr-0.4.6 → httpr-0.4.7}/mkdocs.yml +0 -0
  51. {httpr-0.4.6 → httpr-0.4.7}/scripts/generate_certs.py +0 -0
  52. {httpr-0.4.6 → httpr-0.4.7}/src/exceptions.rs +0 -0
  53. {httpr-0.4.6 → httpr-0.4.7}/src/response.rs +0 -0
  54. {httpr-0.4.6 → httpr-0.4.7}/src/traits.rs +0 -0
  55. {httpr-0.4.6 → httpr-0.4.7}/src/utils.rs +0 -0
  56. {httpr-0.4.6 → httpr-0.4.7}/tests/benchmark/__init__.py +0 -0
  57. {httpr-0.4.6 → httpr-0.4.7}/tests/benchmark/test_performance.py +0 -0
  58. {httpr-0.4.6 → httpr-0.4.7}/tests/conftest.py +0 -0
  59. {httpr-0.4.6 → httpr-0.4.7}/tests/e2e/__init__.py +0 -0
  60. {httpr-0.4.6 → httpr-0.4.7}/tests/e2e/test_async.py +0 -0
  61. {httpr-0.4.6 → httpr-0.4.7}/tests/e2e/test_auth.py +0 -0
  62. {httpr-0.4.6 → httpr-0.4.7}/tests/e2e/test_redirects.py +0 -0
  63. {httpr-0.4.6 → httpr-0.4.7}/tests/e2e/test_ssl.py +0 -0
  64. {httpr-0.4.6 → httpr-0.4.7}/tests/e2e/test_streaming.py +0 -0
  65. {httpr-0.4.6 → httpr-0.4.7}/tests/e2e/test_uploads.py +0 -0
  66. {httpr-0.4.6 → httpr-0.4.7}/tests/unit/__init__.py +0 -0
  67. {httpr-0.4.6 → httpr-0.4.7}/tests/unit/cbor_test_server.py +0 -0
  68. {httpr-0.4.6 → httpr-0.4.7}/tests/unit/httpx_conns.py +0 -0
  69. {httpr-0.4.6 → httpr-0.4.7}/tests/unit/test_asyncclient.py +0 -0
  70. {httpr-0.4.6 → httpr-0.4.7}/tests/unit/test_cbor.py +0 -0
  71. {httpr-0.4.6 → httpr-0.4.7}/tests/unit/test_client.py +0 -0
  72. {httpr-0.4.6 → httpr-0.4.7}/tests/unit/test_defs.py +0 -0
  73. {httpr-0.4.6 → httpr-0.4.7}/tests/unit/test_docs.py +0 -0
  74. {httpr-0.4.6 → httpr-0.4.7}/tests/unit/test_exceptions.py +0 -0
  75. {httpr-0.4.6 → httpr-0.4.7}/tests/unit/test_streaming.py +0 -0
  76. {httpr-0.4.6 → httpr-0.4.7}/uv.lock +0 -0
@@ -492,7 +492,7 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
492
492
 
493
493
  [[package]]
494
494
  name = "httpr"
495
- version = "0.4.6"
495
+ version = "0.4.7"
496
496
  dependencies = [
497
497
  "anyhow",
498
498
  "bytes",
@@ -2,7 +2,7 @@
2
2
  name = "httpr"
3
3
  # Version is set dynamically by CI from git tags (e.g., v1.2.3 -> 1.2.3).
4
4
  # Do not edit manually. See .github/actions/set-version/ for details.
5
- version = "0.4.6"
5
+ version = "0.4.7"
6
6
  edition = "2021"
7
7
  description = "Fast HTTP client for python"
8
8
  authors = ["thomasht86"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: httpr
3
- Version: 0.4.6
3
+ Version: 0.4.7
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: 3
6
6
  Classifier: Programming Language :: Python :: 3 :: Only
@@ -11,7 +11,7 @@ keywords = [ "python", "request",]
11
11
  classifiers = [ "Programming Language :: Rust", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Internet :: WWW/HTTP", "Topic :: Software Development :: Libraries :: Python Modules",]
12
12
  # Version is set dynamically by CI from git tags (e.g., v1.2.3 -> 1.2.3).
13
13
  # Do not edit manually. See .github/actions/set-version/ for details.
14
- version = "0.4.6"
14
+ version = "0.4.7"
15
15
  dependencies = []
16
16
  [[project.authors]]
17
17
  name = "thomasht86"
@@ -204,24 +204,25 @@ impl RClient {
204
204
  client_builder = client_builder.add_root_certificate(cert);
205
205
  }
206
206
  }
207
- // Load client pem identity if provided
208
- // Either from file path (client_pem) or direct data (client_pem_data)
209
- let client_identity_pem = if let Some(pem_data) = &client_pem_data {
210
- Some(pem_data.clone())
211
- } else if let Some(pem_path) = &client_pem {
212
- Some(fs::read(pem_path).map_err(|e| map_anyhow_error(anyhow::Error::new(e)))?)
213
- } else {
214
- None
215
- };
216
-
217
- if let Some(pem_bytes) = client_identity_pem {
218
- let identity = Identity::from_pem(&pem_bytes).map_err(map_reqwest_error)?;
219
- client_builder = client_builder.identity(identity);
220
- }
221
207
  } else {
222
208
  client_builder = client_builder.danger_accept_invalid_certs(true);
223
209
  }
224
210
 
211
+ // Client mTLS identity must be applied regardless of `verify`: disabling
212
+ // server verification doesn't imply disabling client authentication.
213
+ let client_identity_pem = if let Some(pem_data) = &client_pem_data {
214
+ Some(pem_data.clone())
215
+ } else if let Some(pem_path) = &client_pem {
216
+ Some(fs::read(pem_path).map_err(|e| map_anyhow_error(anyhow::Error::new(e)))?)
217
+ } else {
218
+ None
219
+ };
220
+
221
+ if let Some(pem_bytes) = client_identity_pem {
222
+ let identity = Identity::from_pem(&pem_bytes).map_err(map_reqwest_error)?;
223
+ client_builder = client_builder.identity(identity);
224
+ }
225
+
225
226
  // Https_only
226
227
  if let Some(true) = https_only {
227
228
  client_builder = client_builder.https_only(true);
@@ -184,6 +184,38 @@ class TestClientSSL(unittest.TestCase):
184
184
  ca_cert_file=self.client_ca_path,
185
185
  )
186
186
 
187
+ def test_client_cert_sent_when_verify_disabled(self):
188
+ """Regression for issue #65: client cert must still be presented when verify=False.
189
+
190
+ Disabling server verification should not disable client authentication.
191
+ """
192
+ # client_pem (file path) + verify=False
193
+ with Client(client_pem=self.client_cert_path, verify=False) as client:
194
+ response = client.get(f"https://localhost:{self.server_port}")
195
+ self.assertEqual(response.status_code, 200)
196
+ self.assertEqual(response.text, "OK")
197
+
198
+ # client_pem_data (bytes) + verify=False
199
+ with open(self.client_cert_path, "rb") as f:
200
+ cert_data = f.read()
201
+ with Client(client_pem_data=cert_data, verify=False) as client:
202
+ response = client.get(f"https://localhost:{self.server_port}")
203
+ self.assertEqual(response.status_code, 200)
204
+ self.assertEqual(response.text, "OK")
205
+
206
+ def test_async_client_cert_sent_when_verify_disabled(self):
207
+ """Regression for issue #65 on the AsyncClient Python wrapper."""
208
+ with open(self.client_cert_path, "rb") as f:
209
+ cert_data = f.read()
210
+
211
+ async def make_request():
212
+ async with AsyncClient(client_pem_data=cert_data, verify=False) as client:
213
+ return await client.get(f"https://localhost:{self.server_port}")
214
+
215
+ response = asyncio.run(make_request())
216
+ self.assertEqual(response.status_code, 200)
217
+ self.assertEqual(response.text, "OK")
218
+
187
219
  def test_async_client_pem_and_data_are_mutually_exclusive(self):
188
220
  """AsyncClient should also enforce mutual exclusivity."""
189
221
  with open(self.client_cert_path, "rb") as f:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes