httpp 0.1.0__tar.gz → 0.4.0__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.
- httpp-0.4.0/PKG-INFO +181 -0
- httpp-0.4.0/README.md +156 -0
- httpp-0.4.0/include/httpp/curl.hpp +52 -0
- httpp-0.4.0/include/httpp/curl_compat.h +94 -0
- {httpp-0.1.0 → httpp-0.4.0}/include/httpp.h +4 -2
- {httpp-0.1.0 → httpp-0.4.0}/pyproject.toml +12 -2
- {httpp-0.1.0 → httpp-0.4.0}/src/bindings/python/httpp/__init__.py +2 -2
- {httpp-0.1.0 → httpp-0.4.0}/src/bindings/python/httpp/__main__.py +35 -0
- {httpp-0.1.0 → httpp-0.4.0}/src/bindings/python/httpp_cy.pyx +48 -0
- httpp-0.4.0/src/core/curl.cpp +115 -0
- httpp-0.4.0/src/core/curl_compat.cpp +224 -0
- httpp-0.4.0/tests/cmake/consumer_project/main.cpp +134 -0
- {httpp-0.1.0 → httpp-0.4.0}/tests/cmake/run_consumer_test.cmake +2 -2
- httpp-0.4.0/tests/cpp/test_curl.cpp +111 -0
- httpp-0.4.0/tests/cpp/test_curl_compat.cpp +116 -0
- {httpp-0.1.0 → httpp-0.4.0}/version.py +1 -3
- httpp-0.1.0/PKG-INFO +0 -89
- httpp-0.1.0/README.md +0 -64
- httpp-0.1.0/tests/cmake/consumer_project/main.cpp +0 -8
- {httpp-0.1.0 → httpp-0.4.0}/.github/workflows/cmake.yml +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/.github/workflows/orchestrator.yml +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/.github/workflows/release_template.md +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/.github/workflows/wheels.yml +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/.gitignore +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/.gitmodules +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/CMakeLists.txt +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/LICENSE.txt +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/cmake/DynamicVersion.cmake +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/cmake/FindCython.cmake +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/cmake/Optimize.cmake +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/cmake/Startup.cmake +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/cmake/UseCython.cmake +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/cmake/httppConfig.cmake.in +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/docs/index.rst +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/examples/find_httpp_via_python/CMakeLists.txt +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/examples/find_httpp_via_python/main.cpp +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/include/httpp/client.hpp +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/include/httpp/download.hpp +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/include/httpp/export.hpp +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/include/httpp/progress.hpp +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/include/httpp/server.hpp +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/include/httpp/url.hpp +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/requirements-dev.txt +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/src/bindings/python/CMakeLists.txt +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/src/core/client.cpp +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/src/core/download.cpp +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/src/core/progress.cpp +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/src/core/server.cpp +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/src/core/url.cpp +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/src/third_party/README.md +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/src/third_party/cpp-httplib/httplib.h +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/src/third_party/indicators/indicators.hpp +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/tests/CMakeLists.txt +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/tests/cmake/CMakeLists.txt +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/tests/cmake/consumer_project/CMakeLists.txt +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/tests/cpp/CMakeLists.txt +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/tests/cpp/test_client.cpp +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/tests/cpp/test_download.cpp +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/tests/cpp/test_progress.cpp +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/tests/cpp/test_server.cpp +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/tests/cpp/test_url.cpp +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/tests/python/CMakeLists.txt +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/tests/python/test_httpp_basic.py +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/tests/utest/utest.h +0 -0
- {httpp-0.1.0 → httpp-0.4.0}/tests/utest/utest_main.cxx +0 -0
httpp-0.4.0/PKG-INFO
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: httpp
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: A Cython project with scikit-build
|
|
5
|
+
Author-Email: Mohammad Raziei <mohammadraziei1375@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Project-URL: Homepage, https://github.com/MohammadRaziei/httpp
|
|
17
|
+
Project-URL: Bug Tracker, https://github.com/MohammadRaziei/httpp/issues
|
|
18
|
+
Project-URL: Document, https://MohammadRaziei.github.io/httpp
|
|
19
|
+
Requires-Python: >=3.8
|
|
20
|
+
Provides-Extra: test
|
|
21
|
+
Requires-Dist: pytest>=7.0; extra == "test"
|
|
22
|
+
Requires-Dist: pytest-xdist>=3.0; extra == "test"
|
|
23
|
+
Requires-Dist: pytest-cov>=4.0; extra == "test"
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# httpp
|
|
27
|
+
|
|
28
|
+
<div align="center">
|
|
29
|
+
|
|
30
|
+
[](LICENSE.txt)
|
|
31
|
+
[](https://cmake.org/)
|
|
32
|
+
[](https://en.cppreference.com/w/cpp/17)
|
|
33
|
+
[](https://www.python.org/)
|
|
34
|
+
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
A lightweight HTTP **client + server** library for C++ and Python.
|
|
38
|
+
`pip install httpp` and you get: a Python package, a CLI, a compiled C++
|
|
39
|
+
library with headers and a CMake config — no system dependencies at all
|
|
40
|
+
(no `apt`/`choco`/`brew`, nothing outside what `pip` already gives you).
|
|
41
|
+
|
|
42
|
+
## Why
|
|
43
|
+
|
|
44
|
+
- **Client and server in one library** — most lightweight HTTP libraries
|
|
45
|
+
only do one side.
|
|
46
|
+
- **Zero system dependencies** — every third-party piece is vendored and
|
|
47
|
+
built from source.
|
|
48
|
+
- **One `pip install`, three ways in** — Python API, a CLI, and a real C++
|
|
49
|
+
library (headers + `find_package(httpp)`) all come from the same wheel.
|
|
50
|
+
- **A migration path for existing `libcurl` code** — `httpp/curl_compat.h`
|
|
51
|
+
gives you `curl_easy_*` so you can often switch with just a header swap.
|
|
52
|
+
|
|
53
|
+
## Install
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install httpp
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Quick start
|
|
60
|
+
|
|
61
|
+
### Python
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from httpp import Client, Server, DownloadFile, CurlRequest
|
|
65
|
+
|
|
66
|
+
# client
|
|
67
|
+
res = Client.fetch("http://example.com/")
|
|
68
|
+
print(res.status, res.body)
|
|
69
|
+
|
|
70
|
+
# server — like `python -m http.server`, but embeddable in your own app
|
|
71
|
+
srv = Server()
|
|
72
|
+
srv.serve_directory("/", "./public")
|
|
73
|
+
srv.listen("0.0.0.0", 8000)
|
|
74
|
+
|
|
75
|
+
# download a file, with a tqdm-like progress bar
|
|
76
|
+
DownloadFile("http://example.com/big.zip").output("big.zip").run()
|
|
77
|
+
|
|
78
|
+
# a small curl-flavored request builder
|
|
79
|
+
res = CurlRequest("http://example.com/api").method("POST").data("a=1").run()
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### CLI
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
httpp server . # serve the current directory
|
|
86
|
+
httpp download http://example.com/ # print body to stdout
|
|
87
|
+
httpp download http://example.com/f.zip -o f.zip # save to a file, with a progress bar
|
|
88
|
+
httpp curl -X POST -H "X-Token: abc" -d "a=1" http://example.com/api
|
|
89
|
+
httpp install --user # put headers/lib/cmake config on your system
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### C++
|
|
93
|
+
|
|
94
|
+
```cpp
|
|
95
|
+
#include <httpp.h>
|
|
96
|
+
|
|
97
|
+
auto res = httpp::client::fetch("http://example.com/");
|
|
98
|
+
|
|
99
|
+
httpp::server srv;
|
|
100
|
+
srv.get("/hello", [](const httpp::request&, httpp::response& res) {
|
|
101
|
+
res.status = 200;
|
|
102
|
+
res.body = "world";
|
|
103
|
+
});
|
|
104
|
+
srv.listen("0.0.0.0", 8000);
|
|
105
|
+
|
|
106
|
+
httpp::download_file("http://example.com/f.zip").output("f.zip").run();
|
|
107
|
+
|
|
108
|
+
auto res2 = httpp::curl::request("http://example.com/api")
|
|
109
|
+
.method("POST")
|
|
110
|
+
.header("X-Token", "abc")
|
|
111
|
+
.data("a=1")
|
|
112
|
+
.run();
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Migrating from libcurl
|
|
116
|
+
|
|
117
|
+
```c
|
|
118
|
+
#include <httpp/curl_compat.h> /* was: #include <curl/curl.h> */
|
|
119
|
+
|
|
120
|
+
CURL *curl = curl_easy_init();
|
|
121
|
+
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com/");
|
|
122
|
+
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
|
|
123
|
+
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &out);
|
|
124
|
+
CURLcode rc = curl_easy_perform(curl);
|
|
125
|
+
curl_easy_cleanup(curl);
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Covers the common `curl_easy_*` subset (URL, method, headers, POST data,
|
|
129
|
+
write callback, timeout, follow-redirect, response code) — not a full
|
|
130
|
+
libcurl replacement. See the comment at the top of `curl_compat.h` for the
|
|
131
|
+
exact list.
|
|
132
|
+
|
|
133
|
+
### Using it from CMake, from a plain `pip install`
|
|
134
|
+
|
|
135
|
+
No system-wide install needed — point CMake at the package Python already
|
|
136
|
+
installed:
|
|
137
|
+
|
|
138
|
+
```cmake
|
|
139
|
+
execute_process(
|
|
140
|
+
COMMAND python3 -c "import httpp; print(httpp.get_cmake_dir())"
|
|
141
|
+
OUTPUT_VARIABLE httpp_DIR
|
|
142
|
+
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
143
|
+
)
|
|
144
|
+
find_package(httpp CONFIG REQUIRED)
|
|
145
|
+
target_link_libraries(app PRIVATE httpp::httpp_core)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Or, after `httpp install [--user]`, plain `find_package(httpp)` works with
|
|
149
|
+
no Python involved at all — see [`tests/cmake/consumer_project`](tests/cmake/consumer_project)
|
|
150
|
+
for a full working example.
|
|
151
|
+
|
|
152
|
+
## Build from source
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
git clone --recurse-submodules https://github.com/MohammadRaziei/httpp.git
|
|
156
|
+
cd httpp
|
|
157
|
+
pip install -r requirements-dev.txt
|
|
158
|
+
cmake -B build -DHTTPP_BUILD_TESTS=ON -DHTTPP_BUILD_PYTHON=ON
|
|
159
|
+
cmake --build build -j4
|
|
160
|
+
ctest --test-dir build --output-on-failure # C++, Python, and CMake-integration tests
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Status
|
|
164
|
+
|
|
165
|
+
- [x] `httpp::client` — GET requests; `.fetch(url)` parses a full URL in one call
|
|
166
|
+
- [x] `httpp::server` — route handlers, static directory serving
|
|
167
|
+
- [x] `httpp::curl` — a small curl-flavored fluent request builder (method/headers/data)
|
|
168
|
+
- [x] `httpp::download_file` — fluent download builder with a terminal progress bar
|
|
169
|
+
(sync `.run()` and async `.run_async()`)
|
|
170
|
+
- [x] `httpp::progress` — `bar` (tqdm-like) and `range` (trange-like)
|
|
171
|
+
- [x] `httpp/curl_compat.h` — a `curl_easy_*` C API subset for migrating
|
|
172
|
+
existing libcurl code, built directly on `httpp::curl::request`
|
|
173
|
+
- [x] CLI — `server`, `download`, `curl`, `install`/`uninstall`
|
|
174
|
+
- [x] Python bindings for `Client`, `Server`, `DownloadFile`, `CurlRequest`
|
|
175
|
+
- [ ] HTTPS
|
|
176
|
+
- [ ] Route handlers from Python (directory serving works today; custom
|
|
177
|
+
routes are C++-only for now)
|
|
178
|
+
|
|
179
|
+
## License
|
|
180
|
+
|
|
181
|
+
MIT
|
httpp-0.4.0/README.md
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# httpp
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+
[](LICENSE.txt)
|
|
6
|
+
[](https://cmake.org/)
|
|
7
|
+
[](https://en.cppreference.com/w/cpp/17)
|
|
8
|
+
[](https://www.python.org/)
|
|
9
|
+
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
A lightweight HTTP **client + server** library for C++ and Python.
|
|
13
|
+
`pip install httpp` and you get: a Python package, a CLI, a compiled C++
|
|
14
|
+
library with headers and a CMake config — no system dependencies at all
|
|
15
|
+
(no `apt`/`choco`/`brew`, nothing outside what `pip` already gives you).
|
|
16
|
+
|
|
17
|
+
## Why
|
|
18
|
+
|
|
19
|
+
- **Client and server in one library** — most lightweight HTTP libraries
|
|
20
|
+
only do one side.
|
|
21
|
+
- **Zero system dependencies** — every third-party piece is vendored and
|
|
22
|
+
built from source.
|
|
23
|
+
- **One `pip install`, three ways in** — Python API, a CLI, and a real C++
|
|
24
|
+
library (headers + `find_package(httpp)`) all come from the same wheel.
|
|
25
|
+
- **A migration path for existing `libcurl` code** — `httpp/curl_compat.h`
|
|
26
|
+
gives you `curl_easy_*` so you can often switch with just a header swap.
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install httpp
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quick start
|
|
35
|
+
|
|
36
|
+
### Python
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from httpp import Client, Server, DownloadFile, CurlRequest
|
|
40
|
+
|
|
41
|
+
# client
|
|
42
|
+
res = Client.fetch("http://example.com/")
|
|
43
|
+
print(res.status, res.body)
|
|
44
|
+
|
|
45
|
+
# server — like `python -m http.server`, but embeddable in your own app
|
|
46
|
+
srv = Server()
|
|
47
|
+
srv.serve_directory("/", "./public")
|
|
48
|
+
srv.listen("0.0.0.0", 8000)
|
|
49
|
+
|
|
50
|
+
# download a file, with a tqdm-like progress bar
|
|
51
|
+
DownloadFile("http://example.com/big.zip").output("big.zip").run()
|
|
52
|
+
|
|
53
|
+
# a small curl-flavored request builder
|
|
54
|
+
res = CurlRequest("http://example.com/api").method("POST").data("a=1").run()
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### CLI
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
httpp server . # serve the current directory
|
|
61
|
+
httpp download http://example.com/ # print body to stdout
|
|
62
|
+
httpp download http://example.com/f.zip -o f.zip # save to a file, with a progress bar
|
|
63
|
+
httpp curl -X POST -H "X-Token: abc" -d "a=1" http://example.com/api
|
|
64
|
+
httpp install --user # put headers/lib/cmake config on your system
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### C++
|
|
68
|
+
|
|
69
|
+
```cpp
|
|
70
|
+
#include <httpp.h>
|
|
71
|
+
|
|
72
|
+
auto res = httpp::client::fetch("http://example.com/");
|
|
73
|
+
|
|
74
|
+
httpp::server srv;
|
|
75
|
+
srv.get("/hello", [](const httpp::request&, httpp::response& res) {
|
|
76
|
+
res.status = 200;
|
|
77
|
+
res.body = "world";
|
|
78
|
+
});
|
|
79
|
+
srv.listen("0.0.0.0", 8000);
|
|
80
|
+
|
|
81
|
+
httpp::download_file("http://example.com/f.zip").output("f.zip").run();
|
|
82
|
+
|
|
83
|
+
auto res2 = httpp::curl::request("http://example.com/api")
|
|
84
|
+
.method("POST")
|
|
85
|
+
.header("X-Token", "abc")
|
|
86
|
+
.data("a=1")
|
|
87
|
+
.run();
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Migrating from libcurl
|
|
91
|
+
|
|
92
|
+
```c
|
|
93
|
+
#include <httpp/curl_compat.h> /* was: #include <curl/curl.h> */
|
|
94
|
+
|
|
95
|
+
CURL *curl = curl_easy_init();
|
|
96
|
+
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com/");
|
|
97
|
+
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
|
|
98
|
+
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &out);
|
|
99
|
+
CURLcode rc = curl_easy_perform(curl);
|
|
100
|
+
curl_easy_cleanup(curl);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Covers the common `curl_easy_*` subset (URL, method, headers, POST data,
|
|
104
|
+
write callback, timeout, follow-redirect, response code) — not a full
|
|
105
|
+
libcurl replacement. See the comment at the top of `curl_compat.h` for the
|
|
106
|
+
exact list.
|
|
107
|
+
|
|
108
|
+
### Using it from CMake, from a plain `pip install`
|
|
109
|
+
|
|
110
|
+
No system-wide install needed — point CMake at the package Python already
|
|
111
|
+
installed:
|
|
112
|
+
|
|
113
|
+
```cmake
|
|
114
|
+
execute_process(
|
|
115
|
+
COMMAND python3 -c "import httpp; print(httpp.get_cmake_dir())"
|
|
116
|
+
OUTPUT_VARIABLE httpp_DIR
|
|
117
|
+
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
118
|
+
)
|
|
119
|
+
find_package(httpp CONFIG REQUIRED)
|
|
120
|
+
target_link_libraries(app PRIVATE httpp::httpp_core)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Or, after `httpp install [--user]`, plain `find_package(httpp)` works with
|
|
124
|
+
no Python involved at all — see [`tests/cmake/consumer_project`](tests/cmake/consumer_project)
|
|
125
|
+
for a full working example.
|
|
126
|
+
|
|
127
|
+
## Build from source
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
git clone --recurse-submodules https://github.com/MohammadRaziei/httpp.git
|
|
131
|
+
cd httpp
|
|
132
|
+
pip install -r requirements-dev.txt
|
|
133
|
+
cmake -B build -DHTTPP_BUILD_TESTS=ON -DHTTPP_BUILD_PYTHON=ON
|
|
134
|
+
cmake --build build -j4
|
|
135
|
+
ctest --test-dir build --output-on-failure # C++, Python, and CMake-integration tests
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Status
|
|
139
|
+
|
|
140
|
+
- [x] `httpp::client` — GET requests; `.fetch(url)` parses a full URL in one call
|
|
141
|
+
- [x] `httpp::server` — route handlers, static directory serving
|
|
142
|
+
- [x] `httpp::curl` — a small curl-flavored fluent request builder (method/headers/data)
|
|
143
|
+
- [x] `httpp::download_file` — fluent download builder with a terminal progress bar
|
|
144
|
+
(sync `.run()` and async `.run_async()`)
|
|
145
|
+
- [x] `httpp::progress` — `bar` (tqdm-like) and `range` (trange-like)
|
|
146
|
+
- [x] `httpp/curl_compat.h` — a `curl_easy_*` C API subset for migrating
|
|
147
|
+
existing libcurl code, built directly on `httpp::curl::request`
|
|
148
|
+
- [x] CLI — `server`, `download`, `curl`, `install`/`uninstall`
|
|
149
|
+
- [x] Python bindings for `Client`, `Server`, `DownloadFile`, `CurlRequest`
|
|
150
|
+
- [ ] HTTPS
|
|
151
|
+
- [ ] Route handlers from Python (directory serving works today; custom
|
|
152
|
+
routes are C++-only for now)
|
|
153
|
+
|
|
154
|
+
## License
|
|
155
|
+
|
|
156
|
+
MIT
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "httpp/export.hpp"
|
|
4
|
+
#include "httpp/client.hpp" // reuses httpp::response
|
|
5
|
+
|
|
6
|
+
#include <memory>
|
|
7
|
+
#include <string>
|
|
8
|
+
|
|
9
|
+
namespace httpp::curl {
|
|
10
|
+
|
|
11
|
+
// A small, curl-flavored fluent request builder — the common cases only
|
|
12
|
+
// (-X method, -H headers, -d data), NOT a libcurl API/ABI compatible shim
|
|
13
|
+
// (that's a much bigger, different project; see httpp::client for the
|
|
14
|
+
// plain request API this is built on). Backed by the same vendored
|
|
15
|
+
// cpp-httplib as the rest of httpp, hidden behind this pointer (see
|
|
16
|
+
// src/core/curl.cpp) and never exposed in this public header.
|
|
17
|
+
class HTTPP_API request {
|
|
18
|
+
public:
|
|
19
|
+
explicit request(std::string url);
|
|
20
|
+
~request();
|
|
21
|
+
|
|
22
|
+
request(request&&) noexcept;
|
|
23
|
+
request& operator=(request&&) noexcept;
|
|
24
|
+
request(const request&) = delete;
|
|
25
|
+
request& operator=(const request&) = delete;
|
|
26
|
+
|
|
27
|
+
// -X METHOD (GET/POST/PUT/PATCH/DELETE). If never called, defaults to
|
|
28
|
+
// GET, unless data() was called, in which case it defaults to POST —
|
|
29
|
+
// matching curl's own behavior.
|
|
30
|
+
request& method(std::string m);
|
|
31
|
+
|
|
32
|
+
// -H "Name: value"
|
|
33
|
+
request& header(std::string name, std::string value);
|
|
34
|
+
|
|
35
|
+
// -d "body" (sets Content-Type to application/x-www-form-urlencoded
|
|
36
|
+
// unless content_type() overrides it)
|
|
37
|
+
request& data(std::string body);
|
|
38
|
+
|
|
39
|
+
request& content_type(std::string type);
|
|
40
|
+
|
|
41
|
+
// -m/--max-time (seconds), -L/--location
|
|
42
|
+
request& timeout(long seconds);
|
|
43
|
+
request& follow_redirects(bool enable = true);
|
|
44
|
+
|
|
45
|
+
response run() const;
|
|
46
|
+
|
|
47
|
+
private:
|
|
48
|
+
struct impl;
|
|
49
|
+
std::unique_ptr<impl> impl_;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
} // namespace httpp::curl
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* A small, drop-in-ish subset of libcurl's `curl_easy_*` C API — meant to
|
|
5
|
+
* let existing code migrate to httpp with (ideally) just a header swap:
|
|
6
|
+
*
|
|
7
|
+
* -#include <curl/curl.h>
|
|
8
|
+
* +#include <httpp/curl_compat.h>
|
|
9
|
+
*
|
|
10
|
+
* This is NOT a full libcurl replacement: only the commonly used easy-API
|
|
11
|
+
* subset below is implemented, and every curl_easy_setopt() call is
|
|
12
|
+
* forwarded to (and reuses the exact same code as) httpp::curl::request
|
|
13
|
+
* (see include/httpp/curl.hpp, src/core/curl_compat.cpp) — there is no
|
|
14
|
+
* separate/duplicated HTTP implementation here.
|
|
15
|
+
*
|
|
16
|
+
* Covered: CURLOPT_URL, CURLOPT_CUSTOMREQUEST, CURLOPT_HTTPGET,
|
|
17
|
+
* CURLOPT_POST, CURLOPT_POSTFIELDS(+SIZE), CURLOPT_HTTPHEADER,
|
|
18
|
+
* CURLOPT_WRITEFUNCTION/DATA, CURLOPT_USERAGENT, CURLOPT_TIMEOUT,
|
|
19
|
+
* CURLOPT_FOLLOWLOCATION, curl_slist_*, curl_easy_getinfo
|
|
20
|
+
* (CURLINFO_RESPONSE_CODE), curl_easy_strerror, curl_global_init/cleanup.
|
|
21
|
+
*
|
|
22
|
+
* NOT covered (no such option exists here — curl_easy_setopt() returns
|
|
23
|
+
* CURLE_UNKNOWN_OPTION for anything not listed above): proxies, cookies,
|
|
24
|
+
* TLS/certificate options, auth (basic/digest/bearer), multipart forms,
|
|
25
|
+
* response header callbacks, multi/share handles, and everything else in
|
|
26
|
+
* real libcurl's ~300 CURLOPT_* options.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
#include "httpp/export.hpp"
|
|
30
|
+
|
|
31
|
+
#include <stddef.h>
|
|
32
|
+
|
|
33
|
+
#ifdef __cplusplus
|
|
34
|
+
extern "C" {
|
|
35
|
+
#endif
|
|
36
|
+
|
|
37
|
+
typedef struct CURL CURL;
|
|
38
|
+
|
|
39
|
+
typedef enum {
|
|
40
|
+
CURLE_OK = 0,
|
|
41
|
+
CURLE_UNSUPPORTED_PROTOCOL,
|
|
42
|
+
CURLE_COULDNT_CONNECT,
|
|
43
|
+
CURLE_HTTP_RETURNED_ERROR,
|
|
44
|
+
CURLE_WRITE_ERROR,
|
|
45
|
+
CURLE_OUT_OF_MEMORY,
|
|
46
|
+
CURLE_OPERATION_TIMEDOUT,
|
|
47
|
+
CURLE_BAD_FUNCTION_ARGUMENT,
|
|
48
|
+
CURLE_UNKNOWN_OPTION
|
|
49
|
+
} CURLcode;
|
|
50
|
+
|
|
51
|
+
typedef enum {
|
|
52
|
+
CURLOPT_URL,
|
|
53
|
+
CURLOPT_CUSTOMREQUEST,
|
|
54
|
+
CURLOPT_HTTPGET,
|
|
55
|
+
CURLOPT_POST,
|
|
56
|
+
CURLOPT_POSTFIELDS,
|
|
57
|
+
CURLOPT_POSTFIELDSIZE,
|
|
58
|
+
CURLOPT_HTTPHEADER,
|
|
59
|
+
CURLOPT_WRITEFUNCTION,
|
|
60
|
+
CURLOPT_WRITEDATA,
|
|
61
|
+
CURLOPT_USERAGENT,
|
|
62
|
+
CURLOPT_TIMEOUT,
|
|
63
|
+
CURLOPT_FOLLOWLOCATION,
|
|
64
|
+
CURLOPT_VERBOSE /* accepted, has no effect */
|
|
65
|
+
} CURLoption;
|
|
66
|
+
|
|
67
|
+
typedef enum {
|
|
68
|
+
CURLINFO_RESPONSE_CODE
|
|
69
|
+
} CURLINFO;
|
|
70
|
+
|
|
71
|
+
typedef size_t (*curl_write_callback)(char* ptr, size_t size, size_t nmemb, void* userdata);
|
|
72
|
+
|
|
73
|
+
struct curl_slist {
|
|
74
|
+
char* data;
|
|
75
|
+
struct curl_slist* next;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
HTTPP_API CURL* curl_easy_init(void);
|
|
79
|
+
HTTPP_API void curl_easy_cleanup(CURL* curl);
|
|
80
|
+
HTTPP_API CURLcode curl_easy_setopt(CURL* curl, CURLoption option, ...);
|
|
81
|
+
HTTPP_API CURLcode curl_easy_perform(CURL* curl);
|
|
82
|
+
HTTPP_API CURLcode curl_easy_getinfo(CURL* curl, CURLINFO info, ...);
|
|
83
|
+
HTTPP_API const char* curl_easy_strerror(CURLcode code);
|
|
84
|
+
|
|
85
|
+
HTTPP_API struct curl_slist* curl_slist_append(struct curl_slist* list, const char* value);
|
|
86
|
+
HTTPP_API void curl_slist_free_all(struct curl_slist* list);
|
|
87
|
+
|
|
88
|
+
#define CURL_GLOBAL_DEFAULT 0L
|
|
89
|
+
HTTPP_API CURLcode curl_global_init(long flags);
|
|
90
|
+
HTTPP_API void curl_global_cleanup(void);
|
|
91
|
+
|
|
92
|
+
#ifdef __cplusplus
|
|
93
|
+
}
|
|
94
|
+
#endif
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
*============================================================================*/
|
|
39
39
|
|
|
40
40
|
#define HTTPP_VERSION_MAJOR 0
|
|
41
|
-
#define HTTPP_VERSION_MINOR
|
|
42
|
-
#define HTTPP_VERSION_PATCH
|
|
41
|
+
#define HTTPP_VERSION_MINOR 4
|
|
42
|
+
#define HTTPP_VERSION_PATCH 0
|
|
43
43
|
|
|
44
44
|
/*==============================================================================
|
|
45
45
|
* MARK: - Public API
|
|
@@ -51,5 +51,7 @@
|
|
|
51
51
|
#include "httpp/server.hpp"
|
|
52
52
|
#include "httpp/progress.hpp"
|
|
53
53
|
#include "httpp/download.hpp"
|
|
54
|
+
#include "httpp/curl.hpp"
|
|
55
|
+
#include "httpp/curl_compat.h"
|
|
54
56
|
|
|
55
57
|
#endif /* HTTPP_H */
|
|
@@ -8,7 +8,7 @@ build-backend = "scikit_build_core.build"
|
|
|
8
8
|
|
|
9
9
|
[project]
|
|
10
10
|
name = "httpp"
|
|
11
|
-
|
|
11
|
+
dynamic = ["version"]
|
|
12
12
|
description = "A Cython project with scikit-build"
|
|
13
13
|
authors = [
|
|
14
14
|
{name = "Mohammad Raziei", email = "mohammadraziei1375@gmail.com"},
|
|
@@ -59,6 +59,16 @@ sdist.include = [
|
|
|
59
59
|
"src/third_party/liburlparser/**/*",
|
|
60
60
|
]
|
|
61
61
|
|
|
62
|
+
[tool.scikit-build.metadata.version]
|
|
63
|
+
provider = "scikit_build_core.metadata.regex"
|
|
64
|
+
input = "include/httpp.h"
|
|
65
|
+
regex = '''(?sx)
|
|
66
|
+
\#define \s+ HTTPP_VERSION_MAJOR \s+ (?P<major>\d+) .*?
|
|
67
|
+
\#define \s+ HTTPP_VERSION_MINOR \s+ (?P<minor>\d+) .*?
|
|
68
|
+
\#define \s+ HTTPP_VERSION_PATCH \s+ (?P<patch>\d+) .*?
|
|
69
|
+
'''
|
|
70
|
+
result = "{major}.{minor}.{patch}"
|
|
71
|
+
|
|
62
72
|
|
|
63
73
|
[tool.cibuildwheel]
|
|
64
74
|
skip = "*i686*"
|
|
@@ -85,4 +95,4 @@ target-version = ['py311']
|
|
|
85
95
|
|
|
86
96
|
[tool.isort]
|
|
87
97
|
profile = "black"
|
|
88
|
-
multi_line_output = 3
|
|
98
|
+
multi_line_output = 3
|
|
@@ -17,7 +17,7 @@ if _sys.platform == "win32" and hasattr(_os, "add_dll_directory"):
|
|
|
17
17
|
_os.add_dll_directory(_lib_dir)
|
|
18
18
|
|
|
19
19
|
# Import the compiled Cython module
|
|
20
|
-
from .httpp_cy import Client, Server, download, DownloadFile, DownloadResult
|
|
20
|
+
from .httpp_cy import Client, Server, download, DownloadFile, DownloadResult, CurlRequest
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
def get_include_dir():
|
|
@@ -38,6 +38,6 @@ def get_cmake_dir():
|
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
__all__ = [
|
|
41
|
-
"Client", "Server", "download", "DownloadFile", "DownloadResult",
|
|
41
|
+
"Client", "Server", "download", "DownloadFile", "DownloadResult", "CurlRequest",
|
|
42
42
|
"get_include_dir", "get_lib_dir", "get_cmake_dir",
|
|
43
43
|
]
|
|
@@ -98,6 +98,33 @@ def _cmd_uninstall(args):
|
|
|
98
98
|
return 0
|
|
99
99
|
|
|
100
100
|
|
|
101
|
+
def _cmd_curl(args):
|
|
102
|
+
from httpp import CurlRequest
|
|
103
|
+
|
|
104
|
+
req = CurlRequest(args.url)
|
|
105
|
+
if args.request:
|
|
106
|
+
req.method(args.request)
|
|
107
|
+
for h in args.header or []:
|
|
108
|
+
if ":" not in h:
|
|
109
|
+
print(f"error: malformed header (expected 'Name: value'): {h!r}", file=sys.stderr)
|
|
110
|
+
return 1
|
|
111
|
+
name, value = h.split(":", 1)
|
|
112
|
+
req.header(name.strip(), value.strip())
|
|
113
|
+
if args.data is not None:
|
|
114
|
+
req.data(args.data)
|
|
115
|
+
|
|
116
|
+
res = req.run()
|
|
117
|
+
if args.output:
|
|
118
|
+
with open(args.output, "w", encoding="utf-8") as f:
|
|
119
|
+
f.write(res.body)
|
|
120
|
+
else:
|
|
121
|
+
print(res.body)
|
|
122
|
+
if not res.ok:
|
|
123
|
+
print(f"error: request failed with status {res.status}", file=sys.stderr)
|
|
124
|
+
return 1
|
|
125
|
+
return 0
|
|
126
|
+
|
|
127
|
+
|
|
101
128
|
def build_parser():
|
|
102
129
|
parser = argparse.ArgumentParser(
|
|
103
130
|
prog="httpp", description="httpp: a lightweight HTTP client + server toolkit."
|
|
@@ -116,6 +143,14 @@ def build_parser():
|
|
|
116
143
|
p_download.add_argument("-q", "--quiet", action="store_true", help="With -o, suppress the progress bar.")
|
|
117
144
|
p_download.set_defaults(func=_cmd_download)
|
|
118
145
|
|
|
146
|
+
p_curl = sub.add_parser("curl", help="Make a request (limited curl-like subset: -X/-H/-d).")
|
|
147
|
+
p_curl.add_argument("url")
|
|
148
|
+
p_curl.add_argument("-X", "--request", default=None, help="HTTP method (default: GET, or POST if -d is given).")
|
|
149
|
+
p_curl.add_argument("-H", "--header", action="append", default=None, help="Extra header 'Name: value' (repeatable).")
|
|
150
|
+
p_curl.add_argument("-d", "--data", default=None, help="Request body.")
|
|
151
|
+
p_curl.add_argument("-o", "--output", default=None, help="Write the response body to this file instead of stdout.")
|
|
152
|
+
p_curl.set_defaults(func=_cmd_curl)
|
|
153
|
+
|
|
119
154
|
sdk_parent = argparse.ArgumentParser(add_help=False)
|
|
120
155
|
sdk_parent.add_argument("--prefix", default=None, help="Install location (parent of httpp/). Default: /usr/local.")
|
|
121
156
|
sdk_parent.add_argument("--user", action="store_true", help="Use ~/.local instead of /usr/local.")
|
|
@@ -50,6 +50,16 @@ cdef extern from "httpp/download.hpp" namespace "httpp":
|
|
|
50
50
|
download_result run() except + nogil
|
|
51
51
|
|
|
52
52
|
|
|
53
|
+
cdef extern from "httpp/curl.hpp" namespace "httpp::curl":
|
|
54
|
+
cdef cppclass cpp_curl_request "httpp::curl::request":
|
|
55
|
+
cpp_curl_request(string url) except +
|
|
56
|
+
cpp_curl_request& method(string m) except +
|
|
57
|
+
cpp_curl_request& header(string name, string value) except +
|
|
58
|
+
cpp_curl_request& data(string body) except +
|
|
59
|
+
cpp_curl_request& content_type(string type) except +
|
|
60
|
+
response run() except + nogil
|
|
61
|
+
|
|
62
|
+
|
|
53
63
|
cdef class Response:
|
|
54
64
|
"""A response to an HTTP request made via Client.get()."""
|
|
55
65
|
cdef int _status
|
|
@@ -191,3 +201,41 @@ def download(str url, str dest_path, cbool show_progress=True):
|
|
|
191
201
|
bar unless show_progress=False. Shorthand for
|
|
192
202
|
DownloadFile(url).output(dest_path).run()."""
|
|
193
203
|
return DownloadFile(url).output(dest_path).enable_progress(show_progress).run()
|
|
204
|
+
|
|
205
|
+
cdef class CurlRequest:
|
|
206
|
+
"""A small, curl-flavored fluent request builder — the common cases
|
|
207
|
+
only (-X method, -H headers, -d data). NOT a libcurl-compatible shim;
|
|
208
|
+
see Client for the plain request API this is built on.
|
|
209
|
+
|
|
210
|
+
CurlRequest(url).method("PUT").header("X-Token", "abc").data("body").run()
|
|
211
|
+
"""
|
|
212
|
+
cdef cpp_curl_request* _req
|
|
213
|
+
|
|
214
|
+
def __init__(self, str url):
|
|
215
|
+
self._req = new cpp_curl_request(url.encode("utf-8"))
|
|
216
|
+
|
|
217
|
+
def __dealloc__(self):
|
|
218
|
+
if self._req is not NULL:
|
|
219
|
+
del self._req
|
|
220
|
+
|
|
221
|
+
def method(self, str m):
|
|
222
|
+
self._req.method(m.encode("utf-8"))
|
|
223
|
+
return self
|
|
224
|
+
|
|
225
|
+
def header(self, str name, str value):
|
|
226
|
+
self._req.header(name.encode("utf-8"), value.encode("utf-8"))
|
|
227
|
+
return self
|
|
228
|
+
|
|
229
|
+
def data(self, str body):
|
|
230
|
+
self._req.data(body.encode("utf-8"))
|
|
231
|
+
return self
|
|
232
|
+
|
|
233
|
+
def content_type(self, str type_):
|
|
234
|
+
self._req.content_type(type_.encode("utf-8"))
|
|
235
|
+
return self
|
|
236
|
+
|
|
237
|
+
def run(self):
|
|
238
|
+
cdef response res
|
|
239
|
+
with nogil:
|
|
240
|
+
res = self._req.run()
|
|
241
|
+
return Response(res.status, res.body)
|