Skip to content

Commit 2a40c66

Browse files
committed
feat: initial release of https_samp v1.0.0
HTTPS client plugin for SA-MP and Open Multiplayer, written entirely in Rust on top of rust-samp v3. The same 32-bit binary runs on both runtimes — natively as an Open Multiplayer component (auto-discovered, no config entry needed) or via legacy mode. Plugin capabilities - Non-blocking REST client: GET, POST, HEAD, PUT, DELETE, PATCH. - Worker pool (2–8 threads) with bounded job and response queues. - Unified on_tick dispatch — no Pawn timer required. - Body builders: raw, JSON (validated), form-urlencoded, multipart/form-data with file uploads. One-shot staging, mutually exclusive. - Header layers: built-in User-Agent, persistent global table, one-shot temporary table. Response headers exposed to Pawn via https_response_header inside the callback. - One-shot per-request overrides: total timeout, cross-host redirects, Basic and Bearer authentication helpers. - Cancellation by index, honored at enqueue and dispatch. - mTLS via combined PEM (buffer or file, 256 KiB cap). - Opt-in cookie jar with explicit clear. - Transparent decompression (gzip, brotli, deflate, zstd). Security posture - rustls-only TLS, no system proxy, hardcoded connect (7s) and request (12s) timeouts. - Manual redirect handling, max 5 hops. - Anti-downgrade: HTTPS chains never fall back to HTTP. - Cross-host redirects require explicit opt-in; Authorization header stripped on cross-host hops. - Response body cap (4 KiB – 1 MiB, default 64 KiB), pending response queue capped at 1024. Tooling and infrastructure - Rust 2024 edition, stable toolchain only. - Build scripts for Linux (cargo-xwin for the Windows .dll) and Windows (WSL or Docker/cross for the Linux .so). - GitHub Actions: Rust CI, MkDocs site deploy, release artifact upload. - MkDocs Material documentation under docs/, runnable Pawn snippets under examples/, Pawn include generated from a template at build time. Licensed under AGPL-3.0-or-later.
0 parents  commit 2a40c66

43 files changed

Lines changed: 6104 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/docs.yml

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
name: Docs
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
paths:
8+
- 'docs/**'
9+
- 'mkdocs.yml'
10+
- 'docs/requirements.txt'
11+
- '.github/workflows/docs.yml'
12+
pull_request:
13+
paths:
14+
- 'docs/**'
15+
- 'mkdocs.yml'
16+
- 'docs/requirements.txt'
17+
- '.github/workflows/docs.yml'
18+
workflow_dispatch:
19+
20+
permissions:
21+
contents: read
22+
23+
# Only one Pages deployment can run at a time. Cancelling a queued
24+
# deployment in favour of a newer one is fine; cancelling one already
25+
# in flight would orphan a partial deploy, so we let it finish.
26+
concurrency:
27+
group: pages
28+
cancel-in-progress: false
29+
30+
jobs:
31+
build:
32+
name: Build site
33+
runs-on: ubuntu-latest
34+
permissions:
35+
contents: read
36+
steps:
37+
- uses: actions/checkout@v6
38+
39+
- uses: actions/setup-python@v6
40+
with:
41+
python-version: '3.x'
42+
cache: pip
43+
cache-dependency-path: docs/requirements.txt
44+
45+
- name: Install MkDocs and theme
46+
run: pip install -r docs/requirements.txt
47+
48+
- name: Strict build (fails on warnings)
49+
run: mkdocs build --strict
50+
51+
# Upload the generated site so the deploy job (or anyone debugging
52+
# a PR) can grab it. Uses the dedicated Pages artifact format.
53+
- name: Upload Pages artifact
54+
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
55+
uses: actions/upload-pages-artifact@v5
56+
with:
57+
path: site
58+
59+
deploy:
60+
name: Deploy to GitHub Pages
61+
needs: build
62+
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
63+
runs-on: ubuntu-latest
64+
# `actions/deploy-pages` needs `pages: write` to publish and
65+
# `id-token: write` for OIDC verification of the artifact.
66+
permissions:
67+
pages: write
68+
id-token: write
69+
environment:
70+
name: github-pages
71+
url: ${{ steps.deployment.outputs.page_url }}
72+
steps:
73+
- name: Deploy to GitHub Pages
74+
id: deployment
75+
uses: actions/deploy-pages@v5

.github/workflows/release.yml

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
name: Release
2+
3+
on:
4+
release:
5+
types: [published, prereleased]
6+
7+
permissions:
8+
contents: read
9+
10+
env:
11+
CARGO_TERM_COLOR: always
12+
PLUGIN_NAME: https_samp
13+
14+
jobs:
15+
build-release:
16+
runs-on: ubuntu-latest
17+
# Needs write access to attach .so/.dll/.inc to the GitHub release
18+
# and to amend the release body with the auto-generated notes.
19+
permissions:
20+
contents: write
21+
22+
steps:
23+
- uses: actions/checkout@v6
24+
25+
- name: Verify Cargo.toml version matches release tag
26+
run: |
27+
TAG_VERSION="${GITHUB_REF_NAME#v}"
28+
CARGO_VERSION="$(grep -m1 '^version' Cargo.toml | sed 's/.*= *"\(.*\)"/\1/')"
29+
echo "Release tag: ${GITHUB_REF_NAME} (version=${TAG_VERSION})"
30+
echo "Cargo.toml: ${CARGO_VERSION}"
31+
if [ "${TAG_VERSION}" != "${CARGO_VERSION}" ]; then
32+
echo "::error::Release tag (${GITHUB_REF_NAME}) does not match Cargo.toml version (${CARGO_VERSION})."
33+
echo "::error::Bump Cargo.toml to ${TAG_VERSION} (or retag) and try again."
34+
exit 1
35+
fi
36+
37+
- name: Install Rust
38+
uses: dtolnay/rust-toolchain@stable
39+
with:
40+
targets: i686-unknown-linux-gnu,i686-pc-windows-msvc
41+
42+
- name: Install cross-compilation tools
43+
run: |
44+
sudo apt-get update
45+
sudo apt-get install -y gcc-multilib g++-multilib clang llvm
46+
47+
- name: Install cargo-xwin
48+
run: cargo install cargo-xwin --locked
49+
50+
- name: Cache cargo
51+
uses: actions/cache@v5
52+
with:
53+
path: |
54+
~/.cargo/registry
55+
~/.cargo/git
56+
~/.cache/cargo-xwin
57+
target
58+
key: ${{ runner.os }}-cargo-release-${{ hashFiles('**/Cargo.lock') }}
59+
60+
- name: Build Linux (i686, SA-MP + native OMP)
61+
run: cargo build --release --target i686-unknown-linux-gnu
62+
63+
- name: Build Windows (i686 MSVC, SA-MP + native OMP)
64+
run: cargo xwin build --xwin-arch x86 --release --target i686-pc-windows-msvc
65+
66+
- name: Stage release artifacts
67+
run: |
68+
mkdir -p dist
69+
cp "target/i686-unknown-linux-gnu/release/lib${PLUGIN_NAME}.so" "dist/${PLUGIN_NAME}.so"
70+
cp "target/i686-pc-windows-msvc/release/${PLUGIN_NAME}.dll" "dist/${PLUGIN_NAME}.dll"
71+
cp "include/${PLUGIN_NAME}.inc" "dist/${PLUGIN_NAME}.inc"
72+
73+
- name: Generate release notes
74+
run: |
75+
SDK_VERSION="$(grep -oP 'tag\s*=\s*"\Kv[0-9][^"]*' Cargo.toml | head -n1)"
76+
PLUGIN_VERSION="$(grep -m1 '^version' Cargo.toml | sed 's/.*= *"\(.*\)"/\1/')"
77+
78+
# Extract the section for the current version from CHANGELOG.md.
79+
# Format: "## [X.Y.Z] — yyyy/mm/dd" up to the next "## " heading.
80+
CHANGELOG_SECTION=""
81+
if [ -f CHANGELOG.md ]; then
82+
CHANGELOG_SECTION="$(awk -v ver="${PLUGIN_VERSION}" '
83+
$0 ~ "^## \\[" ver "\\]" { capture = 1; next }
84+
capture && /^## / { exit }
85+
capture { print }
86+
' CHANGELOG.md)"
87+
fi
88+
89+
cat > release_body.md <<EOF
90+
## ${PLUGIN_NAME} v${PLUGIN_VERSION}
91+
92+
Built on top of [rust-samp ${SDK_VERSION}](https://github.com/NullSablex/rust-samp/releases/tag/${SDK_VERSION}).
93+
94+
### Artifacts
95+
- \`${PLUGIN_NAME}.so\` — Linux i686 (\`i686-unknown-linux-gnu\`).
96+
- \`${PLUGIN_NAME}.dll\` — Windows i686 (\`i686-pc-windows-msvc\`).
97+
- \`${PLUGIN_NAME}.inc\` — Pawn include, identical file for SA-MP and Open Multiplayer.
98+
99+
### Compatibility
100+
The binaries are **universal**: they run on SA-MP and on Open Multiplayer, on Linux and Windows alike.
101+
102+
- **SA-MP**: register under \`plugins=\` in \`server.cfg\`.
103+
- **Open Multiplayer (native mode, recommended)**: register as a component under \`components\` in \`config.json\`. Loaded via \`ComponentEntryPoint\`, with access to \`ICore\`, \`ITimersComponent\` and the remaining native APIs.
104+
- **Open Multiplayer (legacy mode)**: still supported — register under \`legacy_plugins\` (or the equivalent key in your \`config.json\`) if you prefer the SA-MP compat path over the native component. Same binary, no extra build flags.
105+
EOF
106+
107+
if [ -n "${CHANGELOG_SECTION}" ]; then
108+
{
109+
echo ""
110+
echo "### Changelog"
111+
echo ""
112+
echo "${CHANGELOG_SECTION}"
113+
} >> release_body.md
114+
fi
115+
116+
cat release_body.md
117+
118+
- name: Upload release assets
119+
uses: softprops/action-gh-release@v3
120+
with:
121+
body_path: release_body.md
122+
append_body: true
123+
files: |
124+
dist/${{ env.PLUGIN_NAME }}.so
125+
dist/${{ env.PLUGIN_NAME }}.dll
126+
dist/${{ env.PLUGIN_NAME }}.inc

.github/workflows/rust.yml

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
name: Rust
2+
3+
on:
4+
push:
5+
branches: [ "master" ]
6+
pull_request:
7+
branches: [ "master" ]
8+
9+
env:
10+
CARGO_TERM_COLOR: always
11+
12+
# Default: read-only. Each job opts in to extra scopes when needed.
13+
permissions:
14+
contents: read
15+
16+
jobs:
17+
build:
18+
name: Build, test, clippy
19+
runs-on: ubuntu-latest
20+
permissions:
21+
contents: read
22+
steps:
23+
- uses: actions/checkout@v6
24+
25+
- name: Install Rust (stable + clippy)
26+
uses: dtolnay/rust-toolchain@stable
27+
with:
28+
targets: i686-unknown-linux-gnu
29+
components: clippy
30+
31+
- name: Install cross-compilation tools
32+
run: |
33+
sudo apt-get update
34+
sudo apt-get install -y gcc-multilib g++-multilib
35+
36+
- uses: Swatinem/rust-cache@v2
37+
38+
- name: Build
39+
run: cargo build --target i686-unknown-linux-gnu --verbose
40+
41+
- name: Test
42+
run: cargo test --target i686-unknown-linux-gnu --verbose
43+
44+
- name: Clippy
45+
run: cargo clippy --target i686-unknown-linux-gnu --all-targets -- -D warnings
46+
47+
fmt:
48+
name: Rustfmt
49+
runs-on: ubuntu-latest
50+
permissions:
51+
contents: read
52+
steps:
53+
- uses: actions/checkout@v6
54+
- name: Install Rust (stable + rustfmt)
55+
uses: dtolnay/rust-toolchain@stable
56+
with:
57+
components: rustfmt
58+
- run: cargo fmt --all -- --check
59+
60+
audit:
61+
name: Security audit
62+
runs-on: ubuntu-latest
63+
# rustsec/audit-check opens issues for new advisories and posts
64+
# review comments on PRs that introduce vulnerable dependencies.
65+
permissions:
66+
contents: read
67+
issues: write
68+
pull-requests: write
69+
checks: write
70+
steps:
71+
- uses: actions/checkout@v6
72+
- uses: rustsec/audit-check@v2
73+
with:
74+
token: ${{ secrets.GITHUB_TOKEN }}
75+
76+
coverage:
77+
name: Coverage (llvm-cov)
78+
runs-on: ubuntu-latest
79+
permissions:
80+
contents: read
81+
steps:
82+
- uses: actions/checkout@v6
83+
84+
- name: Install Rust (stable + llvm-tools)
85+
uses: dtolnay/rust-toolchain@stable
86+
with:
87+
targets: i686-unknown-linux-gnu
88+
components: llvm-tools-preview
89+
90+
- name: Install cross-compilation tools
91+
run: |
92+
sudo apt-get update
93+
sudo apt-get install -y gcc-multilib g++-multilib
94+
95+
- uses: Swatinem/rust-cache@v2
96+
97+
- name: Install cargo-llvm-cov
98+
uses: taiki-e/install-action@cargo-llvm-cov
99+
100+
- name: Generate LCOV report
101+
run: cargo llvm-cov --target i686-unknown-linux-gnu --lcov --output-path lcov.info
102+
103+
- name: Upload coverage artifact
104+
uses: actions/upload-artifact@v7
105+
with:
106+
name: coverage-lcov
107+
path: lcov.info
108+
if-no-files-found: error

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/target
2+
/dist

0 commit comments

Comments
 (0)