1
0
mirror of https://github.com/fafhrd91/actix-net synced 2025-04-04 13:08:04 +02:00

Compare commits

...

376 Commits

Author SHA1 Message Date
dependabot[bot]
7030b26a7c
build(deps): bump socket2 from 0.5.8 to 0.5.9 (#661)
Bumps [socket2](https://github.com/rust-lang/socket2) from 0.5.8 to 0.5.9.
- [Release notes](https://github.com/rust-lang/socket2/releases)
- [Changelog](https://github.com/rust-lang/socket2/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/socket2/commits)

---
updated-dependencies:
- dependency-name: socket2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-03 16:35:18 +00:00
dependabot[bot]
d7f60b88d3
build(deps): bump taiki-e/install-action from 2.49.34 to 2.49.40 (#660)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.49.34 to 2.49.40.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.49.34...v2.49.40)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-03 16:35:15 +00:00
dependabot[bot]
b57ab7e8f0
build(deps): bump taiki-e/install-action from 2.49.32 to 2.49.34 (#659)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.49.32 to 2.49.34.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.49.32...v2.49.34)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-25 00:34:54 +00:00
dependabot[bot]
887975ab11
build(deps): bump taiki-e/install-action from 2.49.19 to 2.49.28 (#657)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.49.19 to 2.49.28.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.49.19...v2.49.28)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-21 06:02:34 +00:00
Rob Ede
9069fd8590
build: fix msrv 2025-03-21 05:38:25 +00:00
dependabot[bot]
8204690568
build(deps): bump syn from 2.0.99 to 2.0.100 (#652)
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.99 to 2.0.100.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.99...2.0.100)

---
updated-dependencies:
- dependency-name: syn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-10 18:34:13 +00:00
dependabot[bot]
bbb3139c45
build(deps): bump taiki-e/install-action from 2.49.11 to 2.49.19 (#653)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.49.11 to 2.49.19.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.49.11...v2.49.19)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-10 18:32:15 +00:00
dependabot[bot]
0915904bdb
build(deps): bump serde from 1.0.218 to 1.0.219 (#654)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.218 to 1.0.219.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.218...v1.0.219)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-10 18:31:58 +00:00
Rob Ede
70f0008f42
chore(actix-service): prepare release 2.0.3 2025-03-09 17:36:45 +00:00
Dylan Anthony
12df4d7027
Remove need for paste (#649)
Co-authored-by: Dylan Anthony <dbanty@users.noreply.github.com>
2025-03-09 16:53:48 +00:00
dependabot[bot]
6f5b81d2a0
build(deps): bump codecov/codecov-action from 5.3.1 to 5.4.0 (#647)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.3.1 to 5.4.0.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v5.3.1...v5.4.0)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-08 18:07:04 +00:00
dependabot[bot]
4cf37171b5
build(deps): bump actions-rust-lang/setup-rust-toolchain (#645)
Bumps [actions-rust-lang/setup-rust-toolchain](https://github.com/actions-rust-lang/setup-rust-toolchain) from 1.10.1 to 1.11.0.
- [Release notes](https://github.com/actions-rust-lang/setup-rust-toolchain/releases)
- [Changelog](https://github.com/actions-rust-lang/setup-rust-toolchain/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions-rust-lang/setup-rust-toolchain/compare/v1.10.1...v1.11.0)

---
updated-dependencies:
- dependency-name: actions-rust-lang/setup-rust-toolchain
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-08 18:02:05 +00:00
dependabot[bot]
00f40e1471
build(deps): bump taiki-e/install-action from 2.49.1 to 2.49.11 (#646)
* build(deps): bump taiki-e/install-action from 2.49.1 to 2.49.11

Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.49.1 to 2.49.11.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.49.1...v2.49.11)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* ci: fix msrv

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Rob Ede <robjtede@icloud.com>
2025-03-08 17:52:46 +00:00
Rob Ede
15f0b63492
test: fix panic in tokio stream construction 2025-03-08 17:37:40 +00:00
Rob Ede
9d4b1673aa
chore(actix-server): prepare release 2.5.1 2025-03-08 17:32:46 +00:00
Rob Ede
fc902b2d56
chore: check lockfile in 2025-03-08 17:24:28 +00:00
Rob Ede
f4175a4ad4
chore: fix doc recipe 2025-03-08 17:24:28 +00:00
Rob Ede
323a2e2931
fix(server): fix panic in test server 2025-03-08 17:24:28 +00:00
dependabot[bot]
4b9f7ae46d
build(deps): bump ilammy/setup-nasm from 1.5.1 to 1.5.2 (#643)
Bumps [ilammy/setup-nasm](https://github.com/ilammy/setup-nasm) from 1.5.1 to 1.5.2.
- [Release notes](https://github.com/ilammy/setup-nasm/releases)
- [Commits](https://github.com/ilammy/setup-nasm/compare/v1.5.1...v1.5.2)

---
updated-dependencies:
- dependency-name: ilammy/setup-nasm
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-24 22:22:32 +00:00
dependabot[bot]
0e119fd9b2
build(deps): bump taiki-e/install-action from 2.48.13 to 2.49.1 (#644)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.48.13 to 2.49.1.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.48.13...v2.49.1)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-24 22:22:23 +00:00
Rob Ede
b04b88e81a
ci: fix msrv job 2025-02-24 22:00:44 +00:00
dependabot[bot]
0a8f2baa11
build(deps): bump taiki-e/install-action from 2.48.6 to 2.48.13 (#642)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.48.6 to 2.48.13.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.48.6...v2.48.13)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-17 18:37:23 +00:00
dependabot[bot]
d79d500ffe
build(deps): bump taiki-e/install-action from 2.48.1 to 2.48.6 (#641)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.48.1 to 2.48.6.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.48.1...v2.48.6)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-10 22:39:52 +00:00
dependabot[bot]
7a7e3de430
build(deps): bump taiki-e/cache-cargo-install-action from 2.1.0 to 2.1.1 (#640)
Bumps [taiki-e/cache-cargo-install-action](https://github.com/taiki-e/cache-cargo-install-action) from 2.1.0 to 2.1.1.
- [Release notes](https://github.com/taiki-e/cache-cargo-install-action/releases)
- [Changelog](https://github.com/taiki-e/cache-cargo-install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/cache-cargo-install-action/compare/v2.1.0...v2.1.1)

---
updated-dependencies:
- dependency-name: taiki-e/cache-cargo-install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-10 22:39:50 +00:00
dependabot[bot]
f062ede06f
build(deps): bump codecov/codecov-action from 5.1.2 to 5.3.1 (#637)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.1.2 to 5.3.1.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v5.1.2...v5.3.1)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-03 18:56:29 +00:00
dependabot[bot]
1338276934
build(deps): bump taiki-e/install-action from 2.47.19 to 2.48.1 (#639)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.47.19 to 2.48.1.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.47.19...v2.48.1)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-03 18:26:03 +00:00
dependabot[bot]
4746b4e2fa
build(deps): bump taiki-e/install-action from 2.47.12 to 2.47.19 (#634)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.47.12 to 2.47.19.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.47.12...v2.47.19)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-23 01:32:18 +00:00
dependabot[bot]
0509f0cede
build(deps): bump taiki-e/cache-cargo-install-action from 2.0.1 to 2.1.0 (#635)
Bumps [taiki-e/cache-cargo-install-action](https://github.com/taiki-e/cache-cargo-install-action) from 2.0.1 to 2.1.0.
- [Release notes](https://github.com/taiki-e/cache-cargo-install-action/releases)
- [Changelog](https://github.com/taiki-e/cache-cargo-install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/cache-cargo-install-action/compare/v2.0.1...v2.1.0)

---
updated-dependencies:
- dependency-name: taiki-e/cache-cargo-install-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-23 01:32:07 +00:00
dependabot[bot]
3831e0d7fe
build(deps): bump taiki-e/install-action from 2.47.7 to 2.47.12 (#633)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.47.7 to 2.47.12.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.47.7...v2.47.12)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-13 20:01:14 +00:00
dependabot[bot]
60945d0481
build(deps): bump taiki-e/install-action from 2.47.0 to 2.47.7 (#632)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.47.0 to 2.47.7.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.47.0...v2.47.7)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-08 10:40:55 +00:00
dependabot[bot]
2615a19e28
build(deps): update itertools requirement from 0.13 to 0.14 (#631)
Updates the requirements on [itertools](https://github.com/rust-itertools/itertools) to permit the latest version.
- [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-itertools/itertools/compare/v0.13.0...v0.14.0)

---
updated-dependencies:
- dependency-name: itertools
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-08 10:40:41 +00:00
Rob Ede
1a0f44fff1
chore: allow missing docs on tests 2024-12-29 14:44:11 +00:00
Rob Ede
8d1cd2ec87
chore: allow missing docs on tests 2024-12-29 14:40:37 +00:00
Rob Ede
b87174b5f2
chore: allow missing docs on tests 2024-12-29 14:34:28 +00:00
Rob Ede
8097af6a27
chore: allow missing docs on tests 2024-12-29 14:31:46 +00:00
Rob Ede
ecba6e21da
chore: fmt project 2024-12-29 14:27:58 +00:00
dependabot[bot]
23f797a81d
build(deps): bump codecov/codecov-action from 5.1.1 to 5.1.2 (#629)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.1.1 to 5.1.2.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v5.1.1...v5.1.2)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-29 13:59:48 +00:00
dependabot[bot]
d2a5091451
build(deps): bump taiki-e/install-action from 2.46.4 to 2.47.0 (#630)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.46.4 to 2.47.0.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.46.4...v2.47.0)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-29 13:57:58 +00:00
Rob Ede
e0c09c2aa4
ci: remove public-api-diff job 2024-12-29 13:44:48 +00:00
Rob Ede
8234543066
ci: fix tests 2024-12-29 13:41:41 +00:00
dependabot[bot]
34826c6253
build(deps): bump codecov/codecov-action from 5.0.7 to 5.1.1 (#624)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.0.7 to 5.1.1.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v5.0.7...v5.1.1)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-10 11:48:11 +00:00
dependabot[bot]
42b788d131
build(deps): bump taiki-e/install-action from 2.45.6 to 2.46.4 (#625)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.45.6 to 2.46.4.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.45.6...v2.46.4)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-10 11:48:02 +00:00
dependabot[bot]
9796593b24
build(deps): bump actions-rust-lang/setup-rust-toolchain (#620)
Bumps [actions-rust-lang/setup-rust-toolchain](https://github.com/actions-rust-lang/setup-rust-toolchain) from 1.8.0 to 1.10.1.
- [Release notes](https://github.com/actions-rust-lang/setup-rust-toolchain/releases)
- [Changelog](https://github.com/actions-rust-lang/setup-rust-toolchain/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions-rust-lang/setup-rust-toolchain/compare/v1.8...v1.10.1)

---
updated-dependencies:
- dependency-name: actions-rust-lang/setup-rust-toolchain
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-02 08:31:34 +00:00
dependabot[bot]
c362fc4414
build(deps): bump codecov/codecov-action from 5.0.2 to 5.0.7 (#621)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.0.2 to 5.0.7.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v5.0.2...v5.0.7)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-02 08:31:21 +00:00
dependabot[bot]
52733337e4
build(deps): bump taiki-e/install-action from 2.34.0 to 2.45.6 (#622)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.34.0 to 2.45.6.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.34.0...v2.45.6)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-02 08:31:14 +00:00
Rob Ede
0e36c5f5c4
feat(bytestring): split_at method (#619) 2024-11-24 00:55:18 +00:00
dependabot[bot]
47f0017899
build(deps): bump codecov/codecov-action from 4.6.0 to 5.0.2 (#617)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4.6.0 to 5.0.2.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v4.6.0...v5.0.2)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-18 21:25:34 +00:00
dependabot[bot]
bb4fc31461
build(deps): bump taiki-e/install-action from 2.44.69 to 2.44.71 (#618)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.44.69 to 2.44.71.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.44.69...v2.44.71)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-18 21:25:24 +00:00
dependabot[bot]
01a104eb82
build(deps): bump taiki-e/install-action from 2.44.60 to 2.44.69 (#616)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.44.60 to 2.44.69.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.44.60...v2.44.69)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-14 09:05:08 +00:00
Rob Ede
4ab27bfc4a
ci: move nightly rust versions to vars (#615) 2024-11-14 08:43:47 +00:00
dependabot[bot]
8084cec705
build(deps): bump taiki-e/install-action from 2.44.54 to 2.44.60 (#613)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-04 21:48:44 +00:00
dependabot[bot]
a2517da225
build(deps): bump taiki-e/install-action from 2.44.44 to 2.44.54 (#612)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-29 23:03:00 +00:00
dependabot[bot]
a4b6943ddc
build(deps): bump taiki-e/install-action from 2.44.35 to 2.44.44 (#611)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.44.35 to 2.44.44.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.44.35...v2.44.44)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-23 14:04:33 +00:00
dependabot[bot]
7d24196d5c
build(deps): bump taiki-e/install-action from 2.44.26 to 2.44.35 (#610)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-14 20:55:07 +01:00
dependabot[bot]
582edf5444
build(deps): bump actions-rust-lang/setup-rust-toolchain (#609)
Bumps [actions-rust-lang/setup-rust-toolchain](https://github.com/actions-rust-lang/setup-rust-toolchain) from 1.10.0 to 1.10.1.
- [Release notes](https://github.com/actions-rust-lang/setup-rust-toolchain/releases)
- [Changelog](https://github.com/actions-rust-lang/setup-rust-toolchain/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions-rust-lang/setup-rust-toolchain/compare/v1.10.0...v1.10.1)

---
updated-dependencies:
- dependency-name: actions-rust-lang/setup-rust-toolchain
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-07 18:48:02 +00:00
dependabot[bot]
57485f1a21
build(deps): bump taiki-e/install-action from 2.44.13 to 2.44.26 (#607)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.44.13 to 2.44.26.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.44.13...v2.44.26)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-07 18:47:49 +00:00
dependabot[bot]
e8871d0d06
build(deps): bump codecov/codecov-action from 4.5.0 to 4.6.0 (#608)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4.5.0 to 4.6.0.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v4.5.0...v4.6.0)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-07 18:47:43 +00:00
Rob Ede
83e896a6e5
ci: remove upload doc job 2024-10-01 04:41:12 +01:00
dependabot[bot]
af00dada5c
build(deps): bump taiki-e/install-action from 2.44.7 to 2.44.13 (#605)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.44.7 to 2.44.13.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.44.7...v2.44.13)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-30 23:51:19 +00:00
dependabot[bot]
0681b515de
build(deps): bump actions-rust-lang/setup-rust-toolchain (#602)
Bumps [actions-rust-lang/setup-rust-toolchain](https://github.com/actions-rust-lang/setup-rust-toolchain) from 1.9.0 to 1.10.0.
- [Release notes](https://github.com/actions-rust-lang/setup-rust-toolchain/releases)
- [Changelog](https://github.com/actions-rust-lang/setup-rust-toolchain/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions-rust-lang/setup-rust-toolchain/compare/v1.9.0...v1.10.0)

---
updated-dependencies:
- dependency-name: actions-rust-lang/setup-rust-toolchain
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-30 17:43:14 +00:00
dependabot[bot]
3672137d17
build(deps): bump taiki-e/install-action from 2.42.37 to 2.44.7 (#603)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.42.37 to 2.44.7.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.42.37...v2.44.7)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-30 17:43:02 +00:00
ahbkc
fad1fda194
Fix fist to first (#598)
Co-authored-by: yinqilei <yinqilei_ug@126.com>
2024-09-05 01:55:49 +01:00
dependabot[bot]
cfae737314
build(deps): bump JamesIves/github-pages-deploy-action (#594)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-03 05:15:44 +01:00
dependabot[bot]
4583daa3c2
build(deps): bump taiki-e/install-action from 2.42.18 to 2.42.37 (#595)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-03 05:15:36 +01:00
ahbkc
b1cbacc7f6
fix actix-service/src/ext.rs 'fist' to 'first' (#596)
Co-authored-by: yinqilei <yinqilei_ug@126.com>
2024-09-03 05:15:24 +01:00
dependabot[bot]
77588aba81
build(deps): update rcgen requirement from 0.12 to 0.13 (#590)
* build(deps): update rcgen requirement from 0.12 to 0.13

Updates the requirements on [rcgen](https://github.com/rustls/rcgen) to permit the latest version.
- [Commits](https://github.com/rustls/rcgen/compare/v0.12.0...v0.13.1)

---
updated-dependencies:
- dependency-name: rcgen
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore: fix rcgen tests

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-08-06 23:54:18 +00:00
dependabot[bot]
aad3a48edd
build(deps): bump taiki-e/install-action from 2.42.10 to 2.42.18 (#589)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.42.10 to 2.42.18.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.42.10...v2.42.18)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-05 22:52:43 +00:00
Rob Ede
97e8c571cf
chore(actix-server): prepare release 2.5.0
closes #586
2024-08-04 22:41:04 +01:00
Rob Ede
0d8c7e5085
build: add nix flake 2024-08-04 22:39:19 +01:00
Rob Ede
baf1b6042a
docs(server): update changelog 2024-08-04 22:34:17 +01:00
dependabot[bot]
779fa28bd5
build(deps): bump taiki-e/install-action from 2.42.4 to 2.42.10 (#587)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.42.4 to 2.42.10.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.42.4...v2.42.10)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-29 19:40:21 +00:00
dependabot[bot]
e282811d69
build(deps): update rustversion-msrv requirement from 0.99 to 0.100 (#584)
---
updated-dependencies:
- dependency-name: rustversion-msrv
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-22 19:27:49 +00:00
dependabot[bot]
5c44115978
build(deps): bump taiki-e/install-action from 2.42.0 to 2.42.4 (#585)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.42.0 to 2.42.4.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.42.0...v2.42.4)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-22 18:56:19 +00:00
Rob Ede
ead0e2b200
chore: fix nightly warnings 2024-07-20 18:57:00 +01:00
Rob Ede
ace737fc4c
chore: overspecified deps 2024-07-20 18:00:39 +01:00
dependabot[bot]
c45ae294fb
build(deps): bump taiki-e/install-action from 2.41.11 to 2.42.0 (#583)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.41.11 to 2.42.0.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.41.11...v2.42.0)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-16 02:14:15 +00:00
dependabot[bot]
939377f6ab
build(deps): bump taiki-e/install-action from 2.41.7 to 2.41.11 (#582)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.41.7 to 2.41.11.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.41.7...v2.41.11)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-08 21:40:27 +00:00
dependabot[bot]
20149f957b
build(deps): bump JamesIves/github-pages-deploy-action (#581)
Bumps [JamesIves/github-pages-deploy-action](https://github.com/jamesives/github-pages-deploy-action) from 4.6.1 to 4.6.3.
- [Release notes](https://github.com/jamesives/github-pages-deploy-action/releases)
- [Commits](https://github.com/jamesives/github-pages-deploy-action/compare/v4.6.1...v4.6.3)

---
updated-dependencies:
- dependency-name: JamesIves/github-pages-deploy-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-08 21:40:25 +00:00
dependabot[bot]
544e5d3b40
build(deps): bump taiki-e/install-action from 2.41.3 to 2.41.7 (#579)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.41.3 to 2.41.7.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.41.3...v2.41.7)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-02 01:08:35 +00:00
dependabot[bot]
d482af529c
build(deps): bump taiki-e/install-action from 2.39.1 to 2.41.3 (#578)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.39.1 to 2.41.3.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.39.1...v2.41.3)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-25 16:32:28 +00:00
Rob Ede
0030800b9a
chore: update mio dependency to v1 (#577) 2024-06-20 01:22:33 +01:00
dependabot[bot]
64fa2f8462
build(deps): bump taiki-e/install-action from 2.38.1 to 2.39.1 (#574)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.38.1 to 2.39.1.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.38.1...v2.39.1)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-17 20:52:59 +00:00
dependabot[bot]
b0d1c8d193
build(deps): bump codecov/codecov-action from 4.4.1 to 4.5.0 (#575)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4.4.1 to 4.5.0.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v4.4.1...v4.5.0)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-17 20:52:48 +00:00
Jiri Andras
46cc62c6d8
Fix logging "starting service:..." (#573)
Co-authored-by: Jiří Andras <jiri.andras@braiins.cz>
2024-06-14 17:11:32 +00:00
dependabot[bot]
912daa3d0a
build(deps): bump taiki-e/cache-cargo-install-action from 2.0.0 to 2.0.1 (#570)
Bumps [taiki-e/cache-cargo-install-action](https://github.com/taiki-e/cache-cargo-install-action) from 2.0.0 to 2.0.1.
- [Release notes](https://github.com/taiki-e/cache-cargo-install-action/releases)
- [Changelog](https://github.com/taiki-e/cache-cargo-install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/cache-cargo-install-action/compare/v2.0.0...v2.0.1)

---
updated-dependencies:
- dependency-name: taiki-e/cache-cargo-install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-10 19:36:41 +00:00
dependabot[bot]
aefa810496
build(deps): bump actions-rust-lang/setup-rust-toolchain (#571)
Bumps [actions-rust-lang/setup-rust-toolchain](https://github.com/actions-rust-lang/setup-rust-toolchain) from 1.8.0 to 1.9.0.
- [Release notes](https://github.com/actions-rust-lang/setup-rust-toolchain/releases)
- [Changelog](https://github.com/actions-rust-lang/setup-rust-toolchain/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions-rust-lang/setup-rust-toolchain/compare/v1.8.0...v1.9.0)

---
updated-dependencies:
- dependency-name: actions-rust-lang/setup-rust-toolchain
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-10 19:36:09 +00:00
dependabot[bot]
2c443a7620
build(deps): bump taiki-e/install-action from 2.34.1 to 2.38.1 (#572)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.34.1 to 2.38.1.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.34.1...v2.38.1)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-10 19:35:52 +00:00
Rob Ede
b3b1583115
docs: update changelog 2024-06-09 18:08:53 +01:00
Rob Ede
0d3d1926bc
fix: relax bounds on with_tokio_rt (#569) 2024-06-09 07:12:24 +01:00
Rob Ede
0c26ecf9fa
chore(actix-server): prepare release 2.4.0 2024-06-09 06:12:28 +01:00
Rob Ede
1bdb15ec20
chore(actix-rt): prepare release 2.10.0 2024-06-09 06:12:07 +01:00
dependabot[bot]
a524f15e34
build(deps): update tokio-uring requirement from 0.4 to 0.5 (#568)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-06-07 18:51:27 +01:00
dependabot[bot]
451a44c2e0
build(deps): bump taiki-e/install-action from 2.33.34 to 2.34.1 (#567)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.33.34 to 2.34.1.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.33.34...v2.34.1)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-03 18:25:40 +00:00
Rob Ede
18071d1fc0
ci: disable io-uring tests 2024-05-27 22:57:35 +01:00
dependabot[bot]
786014cc2f
build(deps): bump taiki-e/install-action from 2.33.26 to 2.33.34 (#566)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.33.26 to 2.33.34.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.33.26...v2.33.34)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-27 21:24:55 +00:00
Rob Ede
a7ef438f25
ci: use mold linker on linux jobs 2024-05-27 22:25:35 +01:00
dependabot[bot]
375c352810
--- (#565)
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-20 19:11:09 +00:00
dependabot[bot]
2d1b5468d0
--- (#563)
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-20 19:08:43 +00:00
dependabot[bot]
3696cda155
--- (#562)
updated-dependencies:
- dependency-name: itertools
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-20 19:06:51 +00:00
dependabot[bot]
55e89d1f30
--- (#564)
updated-dependencies:
- dependency-name: JamesIves/github-pages-deploy-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-20 19:05:28 +00:00
Rob Ede
24be36b18d
chore: prepare actix-tls release v3.4.0 2024-05-18 18:14:26 +01:00
Rob Ede
38ae762569
ci: fix msrv just variable 2024-05-18 17:34:18 +01:00
dependabot[bot]
8cf79d3d13
build(deps): bump taiki-e/install-action from 2.33.17 to 2.33.22 (#561)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.33.17 to 2.33.22.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.33.17...v2.33.22)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-13 20:43:50 +00:00
asonix
73451070db
actix-tls: Disable default features for rustls 0.23 via tokio-rustls 0.26 (#560)
* actix-tls: Disable default features for rustls 0.23 via tokio-rustls 0.26

This also fixes a panic in accept-rustls due to both ring and aws-lc-rs
being enabled for that example

* Switch a test from aws-lc-rs to ring since aws-lc-rs is no longer enabled in dev-dependencies

* Switch another test from aws-lc-rs to ring since aws-lc-rs is no longer enabled in dev-dependencies

* Go the other way - use aws_lc_rs instead of ring

---------

Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-05-12 21:09:59 +00:00
Rob Ede
2632c984cc
ci: fix nightly windows builds 2024-05-12 21:53:26 +01:00
Rob Ede
af8e6cd656
ci: read MSRV from manifest (#559) 2024-05-12 21:35:39 +01:00
SleeplessOne1917
db7988609e
feat(actix-tls): support for rustls 0.23 (#554)
* Add feature for using rustls 0.23

* Fix mistake

* Fix use of wrong tokio rustls package

* Fix accept openssl test

* Use rustls 0.23 for the example

* Install nasm in CI step for windows

* Change outdated step name

* Fix CI mistake

* test: install default crypto provider in tests

* docs: update changelog

---------

Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-05-12 18:47:49 +00:00
Rob Ede
1db640f62e
chore: update env logger 2024-05-12 19:10:27 +01:00
Rob Ede
9e7d612121
ci: remove 32-bit windows support 2024-05-12 18:25:02 +01:00
Rob Ede
5edbf9e3dc
chore: switch to rustversion-msrv 2024-05-12 18:24:37 +01:00
Rob Ede
f028a74240
build: fix rustversion spec 2024-05-11 17:41:31 +01:00
Rob Ede
f4139a0878
chore: update MSRV to 1.70 2024-05-11 17:39:34 +01:00
Rob Ede
3147dbe7ca
ci: rely more on just recipes (#558) 2024-05-11 15:10:56 +01:00
dependabot[bot]
95ca8f0318
build(deps): bump taiki-e/install-action from 2.33.12 to 2.33.17 (#555)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.33.12 to 2.33.17.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.33.12...v2.33.17)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-06 21:02:51 +00:00
dependabot[bot]
f947374a73
build(deps): bump codecov/codecov-action from 4.3.0 to 4.3.1 (#556)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4.3.0 to 4.3.1.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v4.3.0...v4.3.1)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-06 21:02:32 +00:00
dependabot[bot]
85191934c8
build(deps): bump codecov/codecov-action from 4.1.0 to 4.3.0 (#548)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4.1.0 to 4.3.0.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v4.1.0...v4.3.0)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-02 03:24:47 +01:00
dependabot[bot]
234a4c9c7f
build(deps): bump JamesIves/github-pages-deploy-action (#549)
Bumps [JamesIves/github-pages-deploy-action](https://github.com/jamesives/github-pages-deploy-action) from 4.5.0 to 4.6.0.
- [Release notes](https://github.com/jamesives/github-pages-deploy-action/releases)
- [Commits](https://github.com/jamesives/github-pages-deploy-action/compare/v4.5.0...v4.6.0)

---
updated-dependencies:
- dependency-name: JamesIves/github-pages-deploy-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-02 02:20:46 +01:00
dependabot[bot]
d5171c2ab3
build(deps): bump taiki-e/cache-cargo-install-action from 1.3.0 to 2.0.0 (#551)
Bumps [taiki-e/cache-cargo-install-action](https://github.com/taiki-e/cache-cargo-install-action) from 1.3.0 to 2.0.0.
- [Release notes](https://github.com/taiki-e/cache-cargo-install-action/releases)
- [Changelog](https://github.com/taiki-e/cache-cargo-install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/cache-cargo-install-action/compare/v1.3.0...v2.0.0)

---
updated-dependencies:
- dependency-name: taiki-e/cache-cargo-install-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-02 02:20:35 +01:00
dependabot[bot]
875218488c
build(deps): bump taiki-e/install-action from 2.28.1 to 2.33.12 (#553)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.28.1 to 2.33.12.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.28.1...v2.33.12)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-02 02:20:21 +01:00
Rob Ede
b826bf8471
ci: relax ahash msrv pin 2024-05-02 01:48:55 +01:00
Rob Ede
10bd847177
ci: fix msrv checks 2024-05-02 01:30:28 +01:00
dependabot[bot]
481cf55414
build(deps): bump codecov/codecov-action from 4.0.2 to 4.1.0 (#538)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4.0.2 to 4.1.0.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v4.0.2...v4.1.0)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-05 17:51:42 +00:00
dependabot[bot]
b4990023c4
build(deps): bump taiki-e/install-action from 2.27.11 to 2.28.1 (#537)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.27.11 to 2.28.1.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.27.11...v2.28.1)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-05 17:49:17 +00:00
dependabot[bot]
eb5cec0064
build(deps): bump codecov/codecov-action from 4.0.1 to 4.0.2 (#535)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-01 18:06:10 +00:00
dependabot[bot]
db925bf8e6
build(deps): bump taiki-e/install-action from 2.26.13 to 2.27.11 (#536)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-01 18:05:48 +00:00
Rob Ede
850f6c0491
chore: allow unused trait in test 2024-02-19 12:32:06 +00:00
Rob Ede
0f71fd5a7a
chore: remove redundant imports 2024-02-19 12:19:08 +00:00
Rob Ede
40b10847df
chore: remove redundant imports 2024-02-19 12:10:47 +00:00
Rob Ede
39bab04800
chore: remove redundant imports 2024-02-19 11:36:15 +00:00
Rob Ede
3cb247874e
ci: workaround ahash msrv 2024-02-19 11:31:41 +00:00
Rob Ede
5792d9f010
docs: fix repo links 2024-02-19 11:29:37 +00:00
dependabot[bot]
bd8bd1020b
build(deps): bump taiki-e/install-action from 2.26.7 to 2.26.13 (#531)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-05 21:10:17 +00:00
dependabot[bot]
21be7d84bd
build(deps): bump codecov/codecov-action from 3.1.6 to 4.0.1 (#532)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-02-05 21:10:06 +00:00
Rob Ede
57fd6ea809
chore(actix-tls): prepare release 3.3.0 (#530) 2024-02-03 20:26:15 +00:00
Rob Ede
9a3f3eef6a
test(tls): fix accept-openssl test 2024-02-03 17:01:47 +00:00
Rob Ede
e427911cdb
feat(tls): rustls-0_22 create feature 2024-02-03 16:39:21 +00:00
Rob Ede
a1ae524512
docs: clean changelog 2024-02-01 06:42:41 +00:00
dependabot[bot]
88833355e4
build(deps): bump taiki-e/install-action from 2.25.2 to 2.26.7 (#527)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-30 22:41:04 +00:00
dependabot[bot]
7737ba5cfb
build(deps): bump codecov/codecov-action from 3.1.4 to 3.1.6 (#529)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-30 22:40:17 +00:00
Rob Ede
fd32a0a97a
ci: workaround half crate msrv 2024-01-30 21:51:12 +00:00
Rob Ede
07e7f82345
chore(actix-codec): prepare release 0.5.2 2024-01-30 21:16:42 +00:00
Rob Ede
1a5d85ec8b
fix(tls): resolve http imports 2024-01-17 04:29:44 +00:00
Rob Ede
bd1467e928
chore(tls): changelog wording 2024-01-17 04:27:52 +00:00
Rob Ede
968ad3b854
chore(actix-tls): prepare release 3.2.0 2024-01-17 04:26:46 +00:00
Rob Ede
079f0f66f0
chore(tls): clippy 2024-01-17 04:23:48 +00:00
dependabot[bot]
d85903b31a
build(deps): bump taiki-e/install-action from 2.24.1 to 2.25.2 (#521)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.24.1 to 2.25.2.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.24.1...v2.25.2)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-15 18:28:26 +00:00
dependabot[bot]
a0675fb0dd
build(deps): bump actions-rust-lang/setup-rust-toolchain (#520)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-15 18:47:31 +00:00
dependabot[bot]
af9ccd17d9
build(deps): bump taiki-e/install-action from 2.22.0 to 2.24.1 (#518)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.22.0 to 2.24.1.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.22.0...v2.24.1)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-08 19:05:18 +00:00
Rob Ede
eb977e9aeb
ci: combine install steps 2023-12-26 04:03:49 +00:00
Rob Ede
d28c7db3b3
ci: use cargo-ci-clean-cache 2023-12-26 03:58:34 +00:00
Rob Ede
c5b2d0cd36
chore(tls): fix feature flagging of DEFAULT_TLS_HANDSHAKE_TIMEOUT 2023-12-16 00:43:02 +00:00
Rob Ede
02ac0bb4f7
feat(tls): add accept::rustls_0_22 module 2023-12-16 00:23:11 +00:00
dependabot[bot]
86b000fe71
build(deps): bump taiki-e/install-action from 2.21.26 to 2.22.0 (#514)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.21.26 to 2.22.0.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.21.26...v2.22.0)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-11 21:10:42 +00:00
Rob Ede
951e46186b
feat: add rustls v0.22 support (#513) 2023-12-06 04:04:39 +00:00
Rob Ede
9edc0b393a
feat(tls): add crate feature for rustls native root certs (#506) 2023-12-06 01:39:13 +00:00
morenol
1945fa0675
feat: added suport to http crate 1.0 (#508)
* chore: use same feature

* test: add test for new http version added

* stylistic review

---------

Co-authored-by: Luis Moreno <morenol@users.noreply.github.com>
Co-authored-by: Rob Ede <robjtede@icloud.com>
2023-12-06 00:18:17 +00:00
dependabot[bot]
923a443950
Bump taiki-e/install-action from 2.21.20 to 2.21.26 (#512)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.21.20 to 2.21.26.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.21.20...v2.21.26)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-04 19:15:55 +00:00
dependabot[bot]
b526197a9a
Bump JamesIves/github-pages-deploy-action from 4.4.3 to 4.5.0 (#511)
Bumps [JamesIves/github-pages-deploy-action](https://github.com/jamesives/github-pages-deploy-action) from 4.4.3 to 4.5.0.
- [Release notes](https://github.com/jamesives/github-pages-deploy-action/releases)
- [Commits](https://github.com/jamesives/github-pages-deploy-action/compare/v4.4.3...v4.5.0)

---
updated-dependencies:
- dependency-name: JamesIves/github-pages-deploy-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-04 19:15:52 +00:00
dependabot[bot]
8fc2253c61
Bump actions-rust-lang/setup-rust-toolchain from 1.5.0 to 1.6.0 (#510)
Bumps [actions-rust-lang/setup-rust-toolchain](https://github.com/actions-rust-lang/setup-rust-toolchain) from 1.5.0 to 1.6.0.
- [Release notes](https://github.com/actions-rust-lang/setup-rust-toolchain/releases)
- [Changelog](https://github.com/actions-rust-lang/setup-rust-toolchain/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions-rust-lang/setup-rust-toolchain/compare/v1.5.0...v1.6.0)

---
updated-dependencies:
- dependency-name: actions-rust-lang/setup-rust-toolchain
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-04 19:15:50 +00:00
dependabot[bot]
4c12b81492
Bump taiki-e/install-action from 2.21.17 to 2.21.20 (#507)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.21.17 to 2.21.20.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.21.17...v2.21.20)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-27 20:16:33 +00:00
dependabot[bot]
ef716a8488
Bump taiki-e/install-action from 2.21.11 to 2.21.17 (#505)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.21.11 to 2.21.17.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.21.11...v2.21.17)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-20 19:13:51 +00:00
Rob Ede
2a4df30c63
chore(rt): remove hyper example 2023-11-20 18:37:11 +00:00
dependabot[bot]
2d9b147cc3
Bump taiki-e/install-action from 2.21.7 to 2.21.11 (#502)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.21.7 to 2.21.11.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.21.7...v2.21.11)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-13 19:29:01 +00:00
Rob Ede
5515a37002
ci: run on merge groups 2023-11-06 23:53:36 +00:00
Rob Ede
4067fbe8f0
chore: fix check-external-types for rt 2023-11-06 22:19:59 +00:00
dependabot[bot]
01f9910e7c
Bump taiki-e/install-action from 2.21.3 to 2.21.7 (#501)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.21.3 to 2.21.7.
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.21.3...v2.21.7)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-06 22:19:09 +00:00
Rob Ede
df12c10a3f
fix check external types 2023-11-06 22:13:45 +00:00
dependabot[bot]
f632ef2ba8
Bump taiki-e/cache-cargo-install-action from 1.2.2 to 1.3.0 (#499)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-30 19:50:39 +00:00
dependabot[bot]
19d03f0454
Bump taiki-e/install-action from 2.20.2 to 2.21.3 (#500)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-30 19:03:12 +00:00
Rob Ede
e9c2a0c318
ci: fix check-external-types recipe 2023-10-30 00:26:39 +00:00
Rob Ede
f967562ac4
chore: specify external types for each crate (#496) 2023-10-30 00:07:28 +00:00
Rob Ede
61b6e01b02
chore(bytestring): prepare release 1.3.1 2023-10-29 23:17:28 +00:00
Rob Ede
392e591820
chore(local-channel): prepare release 0.1.5 2023-10-29 23:16:53 +00:00
Rob Ede
87440e5734
chore(local-channel): prepare release 0.1.5 2023-10-29 23:15:19 +00:00
Rob Ede
665dec456f
chore(local-waker): prepare release 0.1.4 2023-10-29 23:14:14 +00:00
Rob Ede
7d138f0c31
build: fix min version compat (#498) 2023-10-29 21:15:40 +00:00
Rob Ede
3cd5d8b07a
doc: complete ServerBuilder::workers docs 2023-10-29 18:47:38 +00:00
Rob Ede
09548c96b0
doc: fix doc references 2023-10-29 18:45:12 +00:00
Rob Ede
17fd135349
docs: improve docs on ServerBuilder::{bind, workers} 2023-10-29 18:37:29 +00:00
dependabot[bot]
b9b628c47b
Update criterion requirement from 0.4 to 0.5 (#495)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-29 05:08:55 +00:00
dependabot[bot]
580af3dec4
Bump actions/checkout from 3 to 4 (#494)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-29 04:17:06 +00:00
dependabot[bot]
db54639f0f
Bump taiki-e/install-action from 2.18.9 to 2.21.2 (#497)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-29 03:30:20 +00:00
Rob Ede
6d9eb7e162
ci: specify full action versions 2023-10-29 01:55:34 +00:00
Rob Ede
69e50b5e66
add dependabot updater for actions and cargo 2023-10-29 01:35:12 +00:00
Rob Ede
17409cd203
docs: add bytestring readme 2023-09-17 20:33:36 +01:00
Rob Ede
4a7f2c95af
chore: remove dates from changelogs 2023-09-17 20:25:58 +01:00
Rob Ede
c69b8e9ade
ci: move coverage to own workflow 2023-09-17 20:11:05 +01:00
Rob Ede
9f59093adc
chore(local-channel): prepare release 0.1.4 2023-09-17 19:32:01 +01:00
Paolo Barbolini
bfeb4cd9e7
local-channel: drop futures-util by using future::poll_fn from std (#490) 2023-09-17 11:45:55 +00:00
Rob Ede
14272a1762
chore: force secure rustls-webpki 2023-08-29 18:45:11 +01:00
Rob Ede
7e043048a0
chore(actix-tls): prepare release 3.1.1 2023-08-29 18:41:48 +01:00
Rob Ede
45a7dcba90
chore(tls): fix min rustls version 2023-08-29 18:40:55 +01:00
Rob Ede
5029beb866
prepare actix-tls release 3.1.0 2023-08-26 18:05:39 +01:00
Rob Ede
910c181251
prepare actix-server release 2.3.0 2023-08-26 18:04:22 +01:00
Rob Ede
150d2c05d3
prepare actix-rt release 2.9.0 2023-08-26 18:02:48 +01:00
Alik Aslanyan
3b5716c23e
Add getter methods for actix_rt::Runtime and tokio::runtime::Runtime (#484)
Co-authored-by: Alik Aslanyan <inline0@pm.me>
Co-authored-by: Rob Ede <robjtede@icloud.com>
2023-08-26 15:04:08 +00:00
Rob Ede
0bc310a656
Rustls v0.21 support (#480) 2023-08-26 14:59:51 +01:00
Rob Ede
6ce8307060
remove num_cpus dependency (#488) 2023-08-24 00:56:31 +01:00
Rob Ede
9cb8a1fadc
remove lint exception 2023-07-24 03:27:44 +01:00
Rob Ede
9017de439f
ci: fix post-merge tool installs 2023-07-20 00:22:55 +01:00
Rob Ede
3eba5b152e
prepare actix-macros release 0.2.4 2023-07-19 23:52:56 +01:00
Rob Ede
3c4b0c2755
ci: rename post-merge workflow 2023-07-19 23:49:31 +01:00
Rob Ede
462ab6a4f0
ci: try to fix master jobs on windows 2023-07-18 02:06:01 +01:00
Rob Ede
e539f83615
attempt windows CI fix 2023-07-18 01:47:37 +01:00
Martin André
755b231e00
add MPTCP socket protocol (optional) (#466)
Co-authored-by: Rob Ede <robjtede@icloud.com>
2023-07-17 04:10:36 +01:00
Rob Ede
8d5d1dbf6f
bump MSRV to 1.65 (#485) 2023-07-17 03:05:39 +01:00
Rob Ede
177590a7d8
increase bytestring test coverage 2023-04-01 22:45:18 +01:00
Rob Ede
4cbe741230
use secure openssl version 2023-04-01 22:26:08 +01:00
Rob Ede
80320a0325
use secure tokio version range 2023-04-01 22:24:10 +01:00
Rob Ede
6d0dc9628b
install cargo-hack faster 2023-04-01 22:21:57 +01:00
Rob Ede
dbce150993
update syn to v2 (#481
* update syn to v2

* update changelog
2023-04-01 22:21:14 +01:00
Rob Ede
54ec06cd23
update bitflags to v2 2023-04-01 21:57:10 +01:00
Rob Ede
c0693da9ba
update msrv to 1.60 (#482
* update msrv to 1.60

* inherit workspace msrv
2023-04-01 21:39:19 +01:00
Rob Ede
28f36e4e30
update unreleased sections date 2023-04-01 05:24:40 +01:00
Rob Ede
c6ebbcf21b
clippy run on -tls 2023-04-01 05:24:00 +01:00
Rob Ede
c60d2f9ddb
use doc_auto_cfg 2023-04-01 05:18:22 +01:00
Rob Ede
a6bece7b33
prepare bytestring release 1.3.0 2023-03-03 22:41:09 +00:00
Rob Ede
fbb53f54ef
format changelogs with prettier 2023-03-03 22:36:13 +00:00
Rob Ede
9b388a83c9
reinstate actix-macros trybuild tests 2023-02-26 16:26:19 +00:00
Rob Ede
878d3a1c74
impl AsRef<ByteString> 2023-02-26 16:24:48 +00:00
Yuki Okushi
045cf3f3e8
Upgrade GHA workflows to remove deprecation warnings (#477) 2023-01-28 05:36:05 +09:00
Rob Ede
d13461b337
use secure tokio version range
see RUSTSEC-2023-0001

part of actix/actix-web#2962
2023-01-10 08:56:41 +00:00
Juan Aguilar Santillana
dde38bbe06
remove unnecessary Pin in poll_recv calls (#475) 2023-01-02 13:36:46 +00:00
Rob Ede
d973d5974a
fix msrv ci 2022-12-21 23:09:16 +00:00
Rob Ede
d7afd60606
workaround env-logger msrv 2022-12-21 21:17:21 +00:00
Rob Ede
7e47bf4055
prepare actix-server release 2.2.0 2022-12-21 20:37:51 +00:00
Rob Ede
05e7be337e
prepare actix-rt release 2.8.0 2022-12-21 20:37:04 +00:00
Rob Ede
8d964713c9
fix futures-util version range 2022-12-21 20:36:26 +00:00
Riley
fe38312db0
asonix/tokio uring 04 (#473) 2022-12-21 11:45:31 +09:00
Yuki Okushi
2b83f08a40
Use old cargo-hack for 1.59.0 CI (#472)
* Use old cargo-hack for 1.57.0 CI

Signed-off-by: Yuki Okushi <jtitor@2k36.org>

* Upgrade MSRV to 1.59.0

Signed-off-by: Yuki Okushi <jtitor@2k36.org>

Signed-off-by: Yuki Okushi <jtitor@2k36.org>
Co-authored-by: Rob Ede <robjtede@icloud.com>
2022-11-12 14:00:33 +00:00
Rob Ede
8e9401f8e1
prepare bytestring release 1.2.1 2022-11-12 13:19:09 +00:00
VladimirBramstedt
9ede174e81
fix no_std compatibility (#471)
Co-authored-by: Yuki Okushi <jtitor@2k36.org>
2022-11-12 16:00:06 +09:00
Rob Ede
bb36e2a072
prepare bytestring release 1.2.0 2022-11-07 20:22:47 +00:00
Rob Ede
6061a44a22
slice_ref doc tweaks 2022-11-07 20:21:57 +00:00
Wang, Chi
363984ad75
Add ByteString::slice_ref (#470) 2022-11-07 20:16:46 +00:00
Rob Ede
00654aadc5
use direct tokio exports where possible 2022-10-30 20:25:13 +00:00
fakeshadow
428914e65e
remove fakeshadow from author lists (#468) 2022-10-25 16:34:36 +01:00
Rob Ede
df9a9d1a1e
don't install cargo-cache on 1.57 CI 2022-10-25 00:10:39 +01:00
Rob Ede
056d2cd573
workaround ci msrv issue 2022-10-24 23:44:29 +01:00
Rob Ede
68228a6cf2
update dev deps 2022-10-21 03:23:40 +01:00
Rob Ede
d5a9a6a1c5
prepare actix-utils release 3.0.1 2022-10-21 03:15:48 +01:00
Rob Ede
ade71b7bd3
address soundness footgun in poll_fn 2022-10-21 03:14:38 +01:00
Rob Ede
cb83922b29
add macos test note 2022-10-17 04:27:34 +01:00
Rob Ede
25209f5bd8
use impl-more in -tls 2022-10-17 04:14:09 +01:00
Rob Ede
c4a0f37d0c
fix minimal versions for bytestring 2022-07-23 03:06:44 +01:00
Rob Ede
0e649329b9
fix minimal versions 2022-07-23 01:47:59 +01:00
Rob Ede
66756bc448
update all crates msrv to 1.57 (#464) 2022-07-23 00:51:12 +01:00
Iskandarov Lev
126ed4c2e3
normalize logs capital letter (#463)
Co-authored-by: Rob Ede <robjtede@icloud.com>
2022-07-22 20:53:06 +01:00
Yuki Okushi
283974f3e6
Make actix-codec an optional dependency (#459) 2022-06-23 17:27:36 +01:00
Yuki Okushi
bf2aa3902c
Set depth of --feature-powerset on Windows CI (#460) 2022-06-12 02:17:38 +01:00
Rob Ede
71b4e55c92
prepare bytestring release 1.1.0 (#461) 2022-06-11 13:41:11 +01:00
Marcus Griep
eb5fa30ada
feat: impls for Box and String conversions (#458)
Co-authored-by: Rob Ede <robjtede@icloud.com>
2022-06-11 04:22:34 +01:00
Rob Ede
49a034259f
prepare local-waker release 0.1.3 2022-05-03 14:58:09 +01:00
Rob Ede
3337f63b4e
prepare local-channel release 0.1.3 2022-05-03 14:56:40 +01:00
Rob Ede
86ce140249
ensure all crates include license files
closes #456
2022-05-03 14:55:49 +01:00
Guillaume Desmottes
635aebe887
actix-server: fix UNIX signal handling documentation (#455) 2022-05-03 13:37:18 +01:00
Rob Ede
4c1e581a54
add track_caller atrtibute to spawn calls (#454)
* add track_caller attribute to spawn calls

* fix ci
2022-04-25 21:05:48 +01:00
Rob Ede
dc67ba770d
fmt with import grouping 2022-04-10 02:48:53 +01:00
Rob Ede
855e3f96fe
prepare actix-tls release 3.0.4 2022-03-15 19:43:47 +00:00
Rob Ede
737b438f73
prepare actix-codec release 0.5.1 2022-03-15 19:43:06 +00:00
Rob Ede
0cd70b0536
use tracing for logs (#451) 2022-03-15 19:37:08 +00:00
Rob Ede
4b6a581ef3
prepare actix-server release 2.1.1 2022-03-09 01:08:35 +00:00
Rob Ede
3e132d2bc6
update tokio-uring to 0.3 (#448) 2022-03-08 23:42:52 +00:00
Rob Ede
c5d6174cec
add tracing support 2022-03-08 22:13:55 +00:00
Rob Ede
77d4a69b2f
update tokio-uring to 0.3 (#449) 2022-03-08 21:57:02 +00:00
Rob Ede
ae5377fd6e
update readme 2022-03-08 21:12:46 +00:00
Rob Ede
bd9bda0504
fix readme msrv notes 2022-03-02 16:42:53 +00:00
Rob Ede
41ed48219d
bump lower msrv to 1.49 2022-03-02 16:40:11 +00:00
fakeshadow
7804ed12eb
block and wait for accept thread to exit. (#443)
Co-authored-by: Rob Ede <robjtede@icloud.com>
2022-03-02 03:52:12 +00:00
Rob Ede
2a54065fae
parallel master tests using nextest 2022-02-15 14:19:00 +00:00
Rob Ede
217cbd2228
bump tokio-util to 0.7 (#446) 2022-02-15 01:47:27 +00:00
Rob Ede
d229c1e886
fix min ver check 2022-02-12 08:42:13 +00:00
Rob Ede
6792f799a6
add minimal-versions check 2022-02-12 08:37:56 +00:00
Rob Ede
72481313cc
update readme 2022-01-28 22:28:24 +00:00
Rob Ede
59b629c74b
fix deps badge 2022-01-28 22:17:12 +00:00
Rob Ede
7988694242
update msrv info 2022-01-28 22:15:16 +00:00
Rob Ede
b8a7741524
fix bind_addr 2022-01-28 22:13:10 +00:00
Rob Ede
5e290d76f8
prepare actix-tls release 3.0.2 2022-01-28 22:11:21 +00:00
Rob Ede
0edb64575f
update tls changelog 2022-01-28 22:10:24 +00:00
Rob Ede
941f67dec9
s/e/err 2022-01-28 22:10:06 +00:00
Babur
3e624b8376
Made new constructor for the Connection type public (#439) 2022-01-28 22:09:54 +00:00
Ibraheem Ahmed
26446fdbad
Replace derive_more with declarative macros (#438)
Co-authored-by: Rob Ede <robjtede@icloud.com>
2022-01-28 22:09:33 +00:00
Rob Ede
b7b7bd2cbf
add actix-server readme 2022-01-19 16:14:06 +00:00
Rob Ede
637625f9b7
prepare actix-server release 2.0.0 (#437) 2022-01-19 14:57:50 +00:00
Rob Ede
b1d5d85e72
prepare actix-server release 2.0.0-rc.4 2022-01-12 14:40:06 +00:00
Rob Ede
ed2c07b304
prepare actix-rt release 2.6.0 2022-01-12 14:40:06 +00:00
Rob Ede
4fe7fec5ef
update tokio-uring to 0.2.0 (#436) 2022-01-11 23:36:49 +00:00
Rob Ede
4c9ee88ec4
prepare actix-tls release 3.0.1 2022-01-11 22:17:31 +00:00
david-mcgillicuddy-moixa
9ec3cc0fe7
Replace str_split_once to lower actix-tls msrv to 1.50.0 and bump actix-net to 1.50.0 (#434) 2022-01-10 17:00:04 +00:00
Rob Ede
01e0f922de
fix ci 2021-12-31 08:38:37 +00:00
Rob Ede
10d3bb6d0d
only run coverage on master ci 2021-12-31 08:36:13 +00:00
Rob Ede
3ba4eafde5
prepare actix-server release 2.0.0-rc.3 2021-12-31 08:09:28 +00:00
Rob Ede
5faa98f6d2
prepare actix-rt release 2.5.1 2021-12-31 08:09:10 +00:00
Rob Ede
b552d847ed
prepare actix-codec release 0.4.2 2021-12-31 08:08:14 +00:00
Rob Ede
5058e2d14e
bump tokio in local-channel dev deps 2021-12-31 08:06:41 +00:00
Rob Ede
ae9afd4de7
prepare actix-server release 2.0.0-rc.2 2021-12-27 18:33:57 +00:00
Rob Ede
01d2f18f68
simplify test server (#431) 2021-12-27 18:27:54 +00:00
Rob Ede
e92b5aaf31
expose with_tokio_rt (#430) 2021-12-27 16:00:26 +00:00
Rob Ede
459a6d1b02
update readme 2021-12-27 00:57:16 +00:00
Rob Ede
9935883905
add file reader example 2021-12-26 22:32:35 +00:00
Rob Ede
89a4c2ee27
prepare actix-tls release 3.0.0 2021-12-26 21:12:19 +00:00
Rob Ede
a4681831a7
fix changelogs 2021-12-18 03:35:18 +00:00
Rob Ede
5d2da0fdc7
prepare actix-service release 2.0.2 2021-12-18 03:26:59 +00:00
Rob Ede
ef18a8342e
prepare local-waker release 0.1.2 2021-12-18 03:20:49 +00:00
Rob Ede
621deba990
fix changelog bullet points 2021-12-18 02:49:23 +00:00
Rob Ede
6a9f13c8b4
inline simple future functions 2021-12-18 02:18:01 +00:00
Rob Ede
705b31230f
fix impl assertion tests 2021-12-18 02:17:48 +00:00
Rob Ede
eb490a9125
re-export openssl connector builder (#429) 2021-12-10 23:11:24 +00:00
Rob Ede
90f205a465
standardize crate level lints 2021-12-08 06:09:46 +00:00
Rob Ede
3a3d654cea
use "physical" cpu cores as default worker count 2021-12-08 05:42:54 +00:00
Rob Ede
ba901c70df
prepare actix-server release 2.0.0-rc.1 2021-12-05 19:34:36 +00:00
Ali MJ Al-Nasrawy
4e0dd091f5
Server: run after await (#426)
Co-authored-by: Rob Ede <robjtede@icloud.com>
2021-12-05 19:22:47 +00:00
Ali MJ Al-Nasrawy
8c4ec34cd4
Server: hide internal structure (#424) 2021-12-05 16:44:06 +00:00
Ali MJ Al-Nasrawy
62ffe5f389
fix auto-traits for service types (#397) 2021-12-04 22:30:04 +00:00
Rob Ede
07e3b19461
dedupe tls changelog 2021-12-04 19:51:33 +00:00
Rob Ede
183bcf6ae3
prepare actix-tls release 3.0.0-rc.1 (#423) 2021-11-30 12:34:46 +00:00
Rob Ede
5dc2bfcb01
actix-tls release candidate prep (#422) 2021-11-29 23:53:06 +00:00
Rob Ede
5556afd524
fix bytestring test on older rust versions 2021-11-28 01:19:57 +00:00
Rob Ede
de5908bfe7
tls doc updates 2021-11-28 00:56:15 +00:00
Rob Ede
c63880a292
doc updates 2021-11-28 00:46:29 +00:00
Rob Ede
44e4381879
add some internal server documentation 2021-11-28 00:35:34 +00:00
Rob Ede
18eced7305
add static assertions to bytestring 2021-11-25 03:29:30 +00:00
Rob Ede
a2437eed29
prepare actix-tls release 3.0.0-beta.9 2021-11-22 13:34:54 +00:00
Rob Ede
67b357a175
add TlsError::into_service_error (#420) 2021-11-22 13:33:20 +00:00
Rob Ede
3597af5c45
prepare actix-rt release 2.5.0 2021-11-22 01:15:18 +00:00
Rob Ede
8891c2681e
address unused warning 2021-11-21 23:42:51 +00:00
Rob Ede
233c61ba08
remove dead code 2021-11-21 23:29:25 +00:00
Rob Ede
161f239f12
server: panic earlier if neither runtime detected 2021-11-21 23:29:06 +00:00
fakeshadow
7e7df2f931
add timeout for accepting tls connections (#393)
Co-authored-by: Rob Ede <robjtede@icloud.com>
2021-11-16 00:22:24 +00:00
Luca Bruno
ce8ec15eaa
system: run and return exit code on stop (#411)
Co-authored-by: Rob Ede <robjtede@icloud.com>
2021-11-15 18:49:02 +00:00
Rob Ede
ae28ce5377
update mio to 0.8 2021-11-15 18:48:37 +00:00
Rob Ede
54d1d9e520
prepare actix-tls release 3.0.0-beta.8 2021-11-15 17:55:23 +00:00
Alexander Polakov
0b0cbd5388
actix-tls: allow getting uri from Connect (#415)
Co-authored-by: Rob Ede <robjtede@icloud.com>
2021-11-15 10:39:42 +00:00
Rob Ede
443a328fb4
prepare actix-server release 2.0.0-beta.9 2021-11-15 02:39:55 +00:00
Rob Ede
58a67ade32
improve docs of system_exit 2021-11-15 02:33:13 +00:00
Rob Ede
38caa8f088
Fix server arbiter support (#417) 2021-11-14 19:45:15 +00:00
Rob Ede
ed987eef06
prepare actix-server release 2.0.0-beta.8 2021-11-07 15:46:59 +00:00
fakeshadow
3658929010
fix io-uring feature for actix-server (#414)
Co-authored-by: Rob Ede <robjtede@icloud.com>
2021-11-07 15:43:59 +00:00
fakeshadow
3f49d8ab54
remove usage of mio::net::TcpSocket (#413) 2021-11-07 14:18:23 +00:00
fakeshadow
161d1ee94b
fix accept timeout and worker graceful shutdown (#412) 2021-11-07 13:00:19 +00:00
Rob Ede
81ba7cafaa
fix server non-unix signal impl send (#410) 2021-11-05 02:16:13 +00:00
Rob Ede
f8f51a2240
prepare actix-server release 2.0.0-beta.7 2021-11-05 01:14:28 +00:00
Rob Ede
a2e765ea6e
prepare actix-codec release 0.4.1 2021-11-05 01:05:51 +00:00
Rob Ede
03dae6a4a4
prepare actix-rt release 2.4.0 2021-11-05 00:51:34 +00:00
brockelmore
2080f4c149
Framed::poll_ready flushes when buffer is full (#409) 2021-11-05 00:43:33 +00:00
Rob Ede
b2cef8fcdb
add lines codec (#338) 2021-11-05 00:12:02 +00:00
Rob Ede
15279eaf3d
sync wait for service factories to be ready 2021-11-04 23:26:56 +00:00
Rob Ede
7d98247cb0
fix server worker name 2021-11-04 23:00:43 +00:00
Rob Ede
5b537c7b10
actix-rt-less (#408) 2021-11-04 20:30:43 +00:00
Rob Ede
81d7295486 clippy 2021-11-01 23:41:28 +00:00
Rob Ede
581e599209
rename Server => ServerHandler (#407) 2021-11-01 23:36:51 +00:00
Rob Ede
1c8fcaebbc
tweak server logging 2021-10-22 18:17:26 +01:00
fakeshadow
a1d15f2e08
minimal support of System type with io-uring (#395) 2021-10-21 11:04:51 +01:00
Rob Ede
70ea5322ab
prepare actix-tls 3.0.0-beta.7 release (#401) 2021-10-20 17:12:11 +01:00
Rob Ede
303666278a
prepare actix-tls release 3.0.0-beta.6 2021-10-19 16:51:40 +01:00
Edward Shen
669e868370
Use tokio-rustls 0.23 (#396)
Co-authored-by: Rob Ede <robjtede@icloud.com>
2021-10-19 16:48:23 +01:00
Rob Ede
47f278b17a
fix test macro in presence of other imports named test (#399) 2021-10-19 16:13:13 +01:00
Rob Ede
ca77d8d835
split -server and -tls msrv and raise to 1.52 (#398) 2021-10-19 14:53:42 +01:00
Rob Ede
00775884f8
prepare actix-macros release 0.2.2 2021-10-14 11:08:02 +01:00
Rob Ede
4ff8a2cf68
make runtime macros more IDE friendly (#391) 2021-10-14 10:54:39 +01:00
Rob Ede
5c555a9408
prepare actix-rt release 2.3.0 2021-10-11 22:55:23 +01:00
Rob Ede
ca435b2575
prepare actix-server release 2.0.0-beta.6 2021-10-11 05:14:34 +01:00
Rob Ede
9fa8d7fc5a
avoid dependency on older tokios 2021-10-11 05:12:57 +01:00
Rob Ede
b03fe7c5b6
prepare actix-service release v2.0.1 2021-10-11 04:20:37 +01:00
fakeshadow
6fed1c3e7d
add support for io-uring (#374)
Co-authored-by: Rob Ede <robjtede@icloud.com>
2021-10-11 02:58:11 +01:00
Thales
c3d697df97
server: Don't listen for SIGHUP (#389) 2021-10-04 02:48:10 +01:00
Riley
80a362712f
Fix Service<u8> request documentation (#388) 2021-09-26 01:30:11 +01:00
Ibraheem Ahmed
2b1edb95ea
spawn should allow futures with non-unit outputs (#369)
Co-authored-by: Rob Ede <robjtede@icloud.com>
2021-09-01 21:51:03 +01:00
Rob Ede
4644fa41cf
run doc test in parallel (#387) 2021-09-01 21:30:26 +01:00
Rob Ede
98c37fe47d
clippy 2021-09-01 20:59:54 +01:00
Rob Ede
b9455d2ca9
move router crate 2021-08-06 19:05:29 +01:00
Rob Ede
0183b0f8cc
soft-disallow prefix resources with tail segments (#379) 2021-08-06 18:48:49 +01:00
Ali MJ Al-Nasrawy
b122a1ae1a
ResourceDef::join (#380) 2021-08-06 18:48:30 +01:00
Rob Ede
4303058243
enforce path / separators on dynamic prefixes (#378) 2021-08-06 18:25:21 +01:00
Aravinth Manivannan
48b2e11509
improve malformed path error message (#384) 2021-08-06 18:06:29 +01:00
Ali MJ Al-Nasrawy
5379a46a99
ResourceDef: relax unnecessary bounds (#381) 2021-08-06 17:45:10 +01:00
169 changed files with 11759 additions and 8099 deletions

View File

@ -1,3 +1,15 @@
[alias]
chk = "check --workspace --all-features --tests --examples --bins"
lint = "clippy --workspace --all-features --tests --examples --bins -- -Dclippy::todo"
lint = "clippy --workspace --tests --examples --bins -- -Dclippy::todo"
lint-all = "clippy --workspace --all-features --tests --examples --bins -- -Dclippy::todo"
# just check the library (without dev deps)
ci-check-min = "hack --workspace check --no-default-features"
ci-check-lib = "hack --workspace --feature-powerset --depth=2 --exclude-features=io-uring check"
ci-check-lib-linux = "hack --workspace --feature-powerset --depth=2 check"
# check everything
ci-check = "hack --workspace --feature-powerset --depth=2 --exclude-features=io-uring check --tests --examples"
ci-check-linux = "hack --workspace --feature-powerset --depth=2 check --tests --examples"
# tests avoiding io-uring feature
ci-test = "hack --feature-powerset --depth=2 --exclude-features=io-uring test --lib --tests --no-fail-fast -- --nocapture"

1
.envrc Normal file
View File

@ -0,0 +1 @@
use flake

View File

@ -1,10 +1,12 @@
## PR Type
<!-- What kind of change does this PR make? -->
<!-- Bug Fix / Feature / Refactor / Code Style / Other -->
INSERT_PR_TYPE
## PR Checklist
Check your PR fulfills the following:
<!-- For draft PRs check the boxes as you complete them. -->
@ -14,11 +16,10 @@ Check your PR fulfills the following:
- [ ] A changelog entry has been made for the appropriate packages.
- [ ] Format code with the latest stable rustfmt
## Overview
<!-- Describe the current and new behavior. -->
<!-- Emphasize any breaking changes. -->
<!-- If this PR fixes or closes an issue, reference it here. -->
<!-- Closes #000 -->

10
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
- package-ecosystem: cargo
directory: /
schedule:
interval: weekly

129
.github/workflows/ci-post-merge.yml vendored Normal file
View File

@ -0,0 +1,129 @@
name: CI (post-merge)
on:
push:
branches: [master]
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build_and_test_nightly:
strategy:
fail-fast: false
matrix:
# prettier-ignore
target:
- { name: Linux, os: ubuntu-latest, triple: x86_64-unknown-linux-gnu }
- { name: macOS, os: macos-latest, triple: x86_64-apple-darwin }
- { name: Windows, os: windows-latest, triple: x86_64-pc-windows-msvc }
- { name: Windows (MinGW), os: windows-latest, triple: x86_64-pc-windows-gnu }
version:
- nightly
name: ${{ matrix.target.name }} / ${{ matrix.version }}
runs-on: ${{ matrix.target.os }}
env: {}
steps:
- name: Setup Routing
if: matrix.target.os == 'macos-latest'
run: sudo ifconfig lo0 alias 127.0.0.3
- uses: actions/checkout@v4
- name: Free Disk Space
if: matrix.target.os == 'ubuntu-latest'
run: ./scripts/free-disk-space.sh
- name: Setup mold linker
if: matrix.target.os == 'ubuntu-latest'
uses: rui314/setup-mold@v1
- name: Install nasm
if: matrix.target.os == 'windows-latest'
uses: ilammy/setup-nasm@v1.5.2
- name: Install OpenSSL
if: matrix.target.os == 'windows-latest'
shell: bash
run: |
set -e
choco install openssl --version=1.1.1.2100 -y --no-progress
echo 'OPENSSL_DIR=C:\Program Files\OpenSSL' >> $GITHUB_ENV
echo "RUSTFLAGS=-C target-feature=+crt-static" >> $GITHUB_ENV
- name: Install Rust (${{ matrix.version }})
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
with:
toolchain: ${{ matrix.version }}
- name: Install just, cargo-hack, cargo-nextest, cargo-ci-cache-clean
uses: taiki-e/install-action@v2.49.40
with:
tool: just,cargo-hack,cargo-nextest,cargo-ci-cache-clean
- name: check lib
if: >
matrix.target.os != 'ubuntu-latest'
&& matrix.target.triple != 'x86_64-pc-windows-gnu'
run: cargo ci-check-lib
- name: check lib
if: matrix.target.os == 'ubuntu-latest'
run: cargo ci-check-lib-linux
- name: check lib
if: matrix.target.triple == 'x86_64-pc-windows-gnu'
run: cargo ci-check-min
- name: check full
# TODO: compile OpenSSL and run tests on MinGW
if: >
matrix.target.os != 'ubuntu-latest'
&& matrix.target.triple != 'x86_64-pc-windows-gnu'
run: cargo ci-check
- name: check all
if: matrix.target.os == 'ubuntu-latest'
run: cargo ci-check-linux
- name: tests
run: just test
# TODO: re-instate some io-uring tests PRs
# - name: tests
# if: matrix.target.os == 'ubuntu-latest'
# run: >-
# sudo bash -c "
# ulimit -Sl 512
# && ulimit -Hl 512
# && PATH=$PATH:/usr/share/rust/.cargo/bin
# && RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-rustls-020
# && RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-rustls-021
# && RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-linux
# "
- name: CI cache clean
run: cargo-ci-cache-clean
minimal-versions:
name: minimal versions
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust (nightly)
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
with:
toolchain: nightly
- name: Install cargo-hack & cargo-minimal-versions
uses: taiki-e/install-action@v2.49.40
with:
tool: cargo-hack,cargo-minimal-versions
- name: Check With Minimal Versions
run: cargo minimal-versions check

View File

@ -1,131 +1,133 @@
name: CI
on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches: [master]
pull_request: {}
merge_group: { types: [checks_requested] }
push: { branches: [master] }
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
read_msrv:
name: Read MSRV
uses: actions-rust-lang/msrv/.github/workflows/msrv.yml@v0.1.0
build_and_test:
needs:
- read_msrv
strategy:
fail-fast: false
matrix:
# prettier-ignore
target:
- { name: Linux, os: ubuntu-latest, triple: x86_64-unknown-linux-gnu }
- { name: macOS, os: macos-latest, triple: x86_64-apple-darwin }
- { name: Windows, os: windows-latest, triple: x86_64-pc-windows-msvc }
- { name: Windows (MinGW), os: windows-latest, triple: x86_64-pc-windows-gnu }
- { name: Windows (32-bit), os: windows-latest, triple: i686-pc-windows-msvc }
version:
- 1.46.0 # MSRV
- stable
- nightly
- { name: msrv, version: "${{ needs.read_msrv.outputs.msrv }}" }
- { name: stable, version: stable }
name: ${{ matrix.target.name }} / ${{ matrix.version }}
name: ${{ matrix.target.name }} / ${{ matrix.version.name }}
runs-on: ${{ matrix.target.os }}
env:
VCPKGRS_DYNAMIC: 1
env: {}
steps:
- name: Setup Routing
if: matrix.target.os == 'macos-latest'
run: sudo ifconfig lo0 alias 127.0.0.3
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Free Disk Space
if: matrix.target.os == 'ubuntu-latest'
run: ./scripts/free-disk-space.sh
- name: Setup mold linker
if: matrix.target.os == 'ubuntu-latest'
uses: rui314/setup-mold@v1
- name: Install nasm
if: matrix.target.os == 'windows-latest'
uses: ilammy/setup-nasm@v1.5.2
# install OpenSSL on Windows
- name: Set vcpkg root
if: matrix.target.triple == 'x86_64-pc-windows-msvc' || matrix.target.triple == 'i686-pc-windows-msvc'
run: echo "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append
- name: Install OpenSSL
if: matrix.target.triple == 'x86_64-pc-windows-msvc'
run: vcpkg install openssl:x64-windows
- name: Install OpenSSL
if: matrix.target.triple == 'i686-pc-windows-msvc'
run: vcpkg install openssl:x86-windows
if: matrix.target.os == 'windows-latest'
shell: bash
run: |
set -e
choco install openssl --version=1.1.1.2100 -y --no-progress
echo 'OPENSSL_DIR=C:\Program Files\OpenSSL' >> $GITHUB_ENV
echo "RUSTFLAGS=-C target-feature=+crt-static" >> $GITHUB_ENV
- name: Install ${{ matrix.version }}
uses: actions-rs/toolchain@v1
- name: Install Rust (${{ matrix.version.name }})
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
with:
toolchain: ${{ matrix.version }}-${{ matrix.target.triple }}
profile: minimal
override: true
toolchain: ${{ matrix.version.version }}
# - name: Install MSYS2
# if: matrix.target.triple == 'x86_64-pc-windows-gnu'
# uses: msys2/setup-msys2@v2
# - name: Install MinGW Packages
# if: matrix.target.triple == 'x86_64-pc-windows-gnu'
# run: |
# msys2 -c 'pacman -Sy --noconfirm pacman'
# msys2 -c 'pacman --noconfirm -S base-devel pkg-config'
# - name: Generate Cargo.lock
# uses: actions-rs/cargo@v1
# with:
# command: generate-lockfile
# - name: Cache Dependencies
# uses: Swatinem/rust-cache@v1.2.0
- name: Install cargo-hack
uses: actions-rs/cargo@v1
- name: Install just, cargo-hack, cargo-nextest, cargo-ci-cache-clean
uses: taiki-e/install-action@v2.49.40
with:
command: install
args: cargo-hack
tool: just,cargo-hack,cargo-nextest,cargo-ci-cache-clean
- name: check minimal
uses: actions-rs/cargo@v1
with:
command: hack
args: check --workspace --no-default-features
- name: Generate Cargo.lock
run: cargo generate-lockfile
- name: check minimal + tests
uses: actions-rs/cargo@v1
with:
command: hack
args: check --workspace --no-default-features --tests --examples
- name: workaround MSRV issues
if: matrix.version.name == 'msrv'
run: just downgrade-for-msrv
- name: check default
uses: actions-rs/cargo@v1
with:
command: check
args: --workspace --tests --examples
- name: check lib
if: >
matrix.target.os != 'ubuntu-latest'
&& matrix.target.triple != 'x86_64-pc-windows-gnu'
run: cargo ci-check-lib
- name: check lib
if: matrix.target.os == 'ubuntu-latest'
run: cargo ci-check-lib-linux
- name: check lib
if: matrix.target.triple != 'x86_64-pc-windows-gnu'
run: cargo ci-check-min
- name: check full
# TODO: compile OpenSSL and run tests on MinGW
if: matrix.target.triple != 'x86_64-pc-windows-gnu'
uses: actions-rs/cargo@v1
with:
command: check
args: --workspace --all-features --tests --examples
if: >
matrix.target.os != 'ubuntu-latest'
&& matrix.target.triple != 'x86_64-pc-windows-gnu'
run: cargo ci-check
- name: check all
if: matrix.target.os == 'ubuntu-latest'
run: cargo ci-check-linux
- name: tests
if: matrix.target.triple != 'x86_64-pc-windows-gnu'
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace --all-features --no-fail-fast -- --nocapture
run: just test
- name: Generate coverage file
if: >
matrix.target.os == 'ubuntu-latest'
&& matrix.version == 'stable'
&& github.ref == 'refs/heads/master'
run: |
cargo install cargo-tarpaulin
cargo tarpaulin --out Xml --verbose
- name: Upload to Codecov
if: >
matrix.target.os == 'ubuntu-latest'
&& matrix.version == 'stable'
&& github.ref == 'refs/heads/master'
uses: codecov/codecov-action@v1
with:
file: cobertura.xml
- name: CI cache clean
run: cargo-ci-cache-clean
- name: Clear the cargo caches
run: |
cargo install cargo-cache --version 0.6.2 --no-default-features --features ci-autoclean
cargo-cache
docs:
name: Documentation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust (nightly)
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
with:
toolchain: nightly
- name: Install just
uses: taiki-e/install-action@v2.49.40
with:
tool: just
- name: doc tests
run: just test-docs

View File

@ -1,42 +0,0 @@
name: Lint
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
fmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
components: rustfmt
override: true
- name: Rustfmt Check
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
clippy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
components: clippy
override: true
- name: Clippy Check
uses: actions-rs/clippy-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --workspace --all-features --tests --examples --bins -- -Dclippy::todo

39
.github/workflows/coverage.yml vendored Normal file
View File

@ -0,0 +1,39 @@
name: Coverage
on:
push:
branches: [master]
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
coverage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
with:
components: llvm-tools-preview
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@v2.49.40
with:
tool: cargo-llvm-cov
- name: Generate code coverage
run: cargo llvm-cov --workspace --all-features --codecov --output-path codecov.json
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5.4.0
with:
files: codecov.json
fail_ci_if_error: true
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

69
.github/workflows/lint.yml vendored Normal file
View File

@ -0,0 +1,69 @@
name: Lint
on:
pull_request: {}
merge_group: { types: [checks_requested] }
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
fmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
with:
toolchain: nightly
components: rustfmt
- name: Rustfmt Check
run: cargo fmt --all -- --check
clippy:
permissions:
contents: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
with: { components: clippy }
- uses: giraffate/clippy-action@v1.0.1
with:
reporter: "github-pr-check"
github_token: ${{ secrets.GITHUB_TOKEN }}
clippy_flags: --workspace --all-features --tests --examples --bins -- -Dclippy::todo -Aunknown_lints
check-external-types:
if: false # rustdoc mismatch currently
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust (${{ vars.RUST_VERSION_EXTERNAL_TYPES }})
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
with:
toolchain: ${{ vars.RUST_VERSION_EXTERNAL_TYPES }}
- name: Install just
uses: taiki-e/install-action@v2.49.40
with:
tool: just
- name: Install cargo-check-external-types
uses: taiki-e/cache-cargo-install-action@v2.1.1
with:
tool: cargo-check-external-types
- name: check external types
run: just check-external-types-all +${{ vars.RUST_VERSION_EXTERNAL_TYPES }}

View File

@ -1,35 +0,0 @@
name: Upload documentation
on:
push:
branches: [master]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: nightly-x86_64-unknown-linux-gnu
profile: minimal
override: true
- name: Build Docs
uses: actions-rs/cargo@v1
with:
command: doc
args: --workspace --all-features --no-deps
- name: Tweak HTML
run: echo '<meta http-equiv="refresh" content="0;url=actix_server/index.html">' > target/doc/index.html
- name: Deploy to GitHub Pages
uses: JamesIves/github-pages-deploy-action@3.7.1
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH: gh-pages
FOLDER: target/doc

5
.gitignore vendored
View File

@ -1,4 +1,3 @@
Cargo.lock
target/
guide/build/
/gh-pages
@ -13,4 +12,8 @@ guide/build/
# These are backup files generated by rustfmt
**/*.rs.bk
# IDEs
.idea
# direnv
/.direnv

3
.rustfmt.toml Normal file
View File

@ -0,0 +1,3 @@
group_imports = "StdExternalCrate"
imports_granularity = "Crate"
use_field_init_shorthand = true

29
.taplo.toml Normal file
View File

@ -0,0 +1,29 @@
exclude = ["target/*"]
include = ["**/*.toml"]
[formatting]
column_width = 110
[[rule]]
include = ["**/Cargo.toml"]
keys = [
"dependencies",
"*-dependencies",
"workspace.dependencies",
"workspace.*-dependencies",
"target.*.dependencies",
"target.*.*-dependencies",
]
formatting.reorder_keys = true
[[rule]]
include = ["**/Cargo.toml"]
keys = [
"dependencies.*",
"*-dependencies.*",
"workspace.dependencies.*",
"workspace.*-dependencies.*",
"target.*.dependencies",
"target.*.*-dependencies",
]
formatting.reorder_keys = false

View File

@ -8,19 +8,19 @@ In the interest of fostering an open and welcoming environment, we as contributo
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
- The use of sexualized language or imagery and unwelcome sexual attention or advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
@ -39,7 +39,7 @@ Instances of abusive, harassing, or otherwise unacceptable behavior may be repor
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
[@robjtede]: https://github.com/robjtede
[@JohnTitor]: https://github.com/JohnTitor
[@johntitor]: https://github.com/JohnTitor
## Attribution

2941
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
[workspace]
resolver = "2"
members = [
"actix-codec",
"actix-macros",
"actix-router",
"actix-rt",
"actix-server",
"actix-service",
@ -14,10 +14,14 @@ members = [
"local-waker",
]
[workspace.package]
license = "MIT OR Apache-2.0"
edition = "2021"
rust-version = "1.71.1"
[patch.crates-io]
actix-codec = { path = "actix-codec" }
actix-macros = { path = "actix-macros" }
actix-router = { path = "actix-router" }
actix-rt = { path = "actix-rt" }
actix-server = { path = "actix-server" }
actix-service = { path = "actix-service" }
@ -32,3 +36,9 @@ local-waker = { path = "local-waker" }
lto = true
opt-level = 3
codegen-units = 1
[workspace.lints.rust]
rust_2018_idioms = "deny"
nonstandard-style = "deny"
future_incompatible = "deny"
missing_docs = { level = "warn", priority = -1 }

View File

@ -2,29 +2,25 @@
> A collection of lower-level libraries for composable network services.
![Apache 2.0 or MIT licensed](https://img.shields.io/crates/l/actix-server)
[![codecov](https://codecov.io/gh/actix/actix-net/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-net)
[![CI](https://github.com/actix/actix-net/actions/workflows/ci.yml/badge.svg?event=push&style=flat-square)](https://github.com/actix/actix-net/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/actix/actix-net/graph/badge.svg?token=8rKIZKtLLm)](https://codecov.io/gh/actix/actix-net)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)
[![Dependency Status](https://deps.rs/repo/github/actix/actix-net/status.svg)](https://deps.rs/repo/github/actix/actix-net)
## Build statuses
| Platform | Build Status |
| ---------------- | ------------ |
| Linux | [![build status](https://github.com/actix/actix-net/workflows/CI%20%28Linux%29/badge.svg?branch=master&event=push)](https://github.com/actix/actix-net/actions?query=workflow%3A"CI+(Linux)") |
| macOS | [![build status](https://github.com/actix/actix-net/workflows/CI%20%28macOS%29/badge.svg?branch=master&event=push)](https://github.com/actix/actix-net/actions?query=workflow%3A"CI+(macOS)") |
| Windows | [![build status](https://github.com/actix/actix-net/workflows/CI%20%28Windows%29/badge.svg?branch=master&event=push)](https://github.com/actix/actix-net/actions?query=workflow%3A"CI+(Windows)") |
| Windows (MinGW) | [![build status](https://github.com/actix/actix-net/workflows/CI%20%28Windows-mingw%29/badge.svg?branch=master&event=push)](https://github.com/actix/actix-net/actions?query=workflow%3A"CI+(Windows-mingw)") |
## Examples
## Example
See `actix-server/examples` and `actix-tls/examples` for some basic examples.
See example folders for [`actix-server`](./actix-server/examples) and [`actix-tls`](./actix-tls/examples).
### MSRV
This repo's Minimum Supported Rust Version (MSRV) is 1.46.0.
## MSRV
Crates in this repo currently have a Minimum Supported Rust Version (MSRV) of 1.65. As a policy, we permit MSRV increases in non-breaking releases.
## License
This project is licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0))
* MIT license ([LICENSE-MIT](LICENSE-MIT) or [http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT))
The crates in repo are licensed under either of:
- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0))
- MIT license ([LICENSE-MIT](LICENSE-MIT) or [http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT))
at your option.

View File

@ -1,63 +1,85 @@
# Changes
## Unreleased - 2021-xx-xx
## Unreleased
- Minimum supported Rust version (MSRV) is now 1.71.
## 0.4.0 - 2021-04-20
* No significant changes since v0.4.0-beta.1.
## 0.5.2
- Minimum supported Rust version (MSRV) is now 1.65.
## 0.4.0-beta.1 - 2020-12-28
* Replace `pin-project` with `pin-project-lite`. [#237]
* Upgrade `tokio` dependency to `1`. [#237]
* Upgrade `tokio-util` dependency to `0.6`. [#237]
* Upgrade `bytes` dependency to `1`. [#237]
## 0.5.1
[#237]: https://github.com/actix/actix-net/pull/237
- Logs emitted now use the `tracing` crate with `log` compatibility.
- Minimum supported Rust version (MSRV) is now 1.49.
## 0.5.0
## 0.3.0 - 2020-08-23
* No changes from beta 2.
- Updated `tokio-util` dependency to `0.7.0`.
## 0.4.2
## 0.3.0-beta.2 - 2020-08-19
* Remove unused type parameter from `Framed::replace_codec`.
- No significant changes since `0.4.1`.
## 0.4.1
## 0.3.0-beta.1 - 2020-08-19
* Use `.advance()` instead of `.split_to()`.
* Upgrade `tokio-util` to `0.3`.
* Improve `BytesCodec::encode()` performance.
* Simplify `BytesCodec::decode()`.
* Rename methods on `Framed` to better describe their use.
* Add method on `Framed` to get a pinned reference to the underlying I/O.
* Add method on `Framed` check emptiness of read buffer.
- Added `LinesCodec`.
- `Framed::poll_ready` flushes when the buffer is full.
## 0.4.0
## 0.2.0 - 2019-12-10
* Use specific futures dependencies.
- No significant changes since v0.4.0-beta.1.
## 0.4.0-beta.1
- Replace `pin-project` with `pin-project-lite`.
- Upgrade `tokio` dependency to `1`.
- Upgrade `tokio-util` dependency to `0.6`.
- Upgrade `bytes` dependency to `1`.
## 0.3.0
- No changes from beta 2.
## 0.3.0-beta.2
- Remove unused type parameter from `Framed::replace_codec`.
## 0.3.0-beta.1
- Use `.advance()` instead of `.split_to()`.
- Upgrade `tokio-util` to `0.3`.
- Improve `BytesCodec::encode()` performance.
- Simplify `BytesCodec::decode()`.
- Rename methods on `Framed` to better describe their use.
- Add method on `Framed` to get a pinned reference to the underlying I/O.
- Add method on `Framed` check emptiness of read buffer.
## 0.2.0
- Use specific futures dependencies.
## 0.2.0-alpha.4
* Fix buffer remaining capacity calculation.
- Fix buffer remaining capacity calculation.
## 0.2.0-alpha.3
* Use tokio 0.2.
* Fix low/high watermark for write/read buffers.
- Use tokio 0.2.
- Fix low/high watermark for write/read buffers.
## 0.2.0-alpha.2
* Migrated to `std::future`.
- Migrated to `std::future`.
## 0.1.2 - 2019-03-27
* Added `Framed::map_io()` method.
## 0.1.2
- Added `Framed::map_io()` method.
## 0.1.1 - 2019-03-06
* Added `FramedParts::with_read_buffer()` method.
## 0.1.1
- Added `FramedParts::with_read_buffer()` method.
## 0.1.0 - 2018-12-09
* Move codec to separate crate.
## 0.1.0
- Move codec to separate crate.

View File

@ -1,24 +1,36 @@
[package]
name = "actix-codec"
version = "0.4.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
version = "0.5.2"
authors = ["Nikolay Kim <fafhrd91@gmail.com>", "Rob Ede <robjtede@icloud.com>"]
description = "Codec utilities for working with framed protocols"
keywords = ["network", "framework", "async", "futures"]
repository = "https://github.com/actix/actix-net"
categories = ["network-programming", "asynchronous"]
license = "MIT OR Apache-2.0"
edition = "2018"
edition.workspace = true
rust-version.workspace = true
[lib]
name = "actix_codec"
path = "src/lib.rs"
[package.metadata.cargo_check_external_types]
allowed_external_types = ["bytes::*", "futures_core::*", "futures_sink::*", "tokio::*", "tokio_util::*"]
[dependencies]
bitflags = "1.2.1"
bitflags = "2"
bytes = "1"
futures-core = { version = "0.3.7", default-features = false }
futures-sink = { version = "0.3.7", default-features = false }
log = "0.4"
memchr = "2.3"
pin-project-lite = "0.2"
tokio = "1"
tokio-util = { version = "0.6", features = ["codec", "io"] }
tokio = "1.23.1"
tokio-util = { version = "0.7", features = ["codec", "io"] }
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
[dev-dependencies]
criterion = { version = "0.5", features = ["html_reports"] }
tokio-test = "0.4.2"
[[bench]]
name = "lines"
harness = false
[lints]
workspace = true

View File

@ -0,0 +1,59 @@
#![allow(missing_docs)]
use bytes::BytesMut;
use criterion::{criterion_group, criterion_main, Criterion};
const INPUT: &[u8] = include_bytes!("./lorem.txt");
fn bench_lines_codec(c: &mut Criterion) {
let mut decode_group = c.benchmark_group("lines decode");
decode_group.bench_function("actix", |b| {
b.iter(|| {
use actix_codec::Decoder as _;
let mut codec = actix_codec::LinesCodec::default();
let mut buf = BytesMut::from(INPUT);
while let Ok(Some(_bytes)) = codec.decode_eof(&mut buf) {}
});
});
decode_group.bench_function("tokio", |b| {
b.iter(|| {
use tokio_util::codec::Decoder as _;
let mut codec = tokio_util::codec::LinesCodec::new();
let mut buf = BytesMut::from(INPUT);
while let Ok(Some(_bytes)) = codec.decode_eof(&mut buf) {}
});
});
decode_group.finish();
let mut encode_group = c.benchmark_group("lines encode");
encode_group.bench_function("actix", |b| {
b.iter(|| {
use actix_codec::Encoder as _;
let mut codec = actix_codec::LinesCodec::default();
let mut buf = BytesMut::new();
codec.encode("123", &mut buf).unwrap();
});
});
encode_group.bench_function("tokio", |b| {
b.iter(|| {
use tokio_util::codec::Encoder as _;
let mut codec = tokio_util::codec::LinesCodec::new();
let mut buf = BytesMut::new();
codec.encode("123", &mut buf).unwrap();
});
});
encode_group.finish();
}
criterion_group!(benches, bench_lines_codec);
criterion_main!(benches);

View File

@ -0,0 +1,5 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit. In tortor quam, pulvinar sit amet vestibulum eget, tincidunt non urna. Sed eu sem in felis malesuada venenatis. Suspendisse volutpat aliquet nisi, in condimentum nibh convallis id. Quisque gravida felis scelerisque ipsum aliquam consequat. Praesent libero odio, malesuada vitae odio quis, aliquam aliquet enim. In fringilla ut turpis nec pharetra. Duis eu posuere metus. Sed a aliquet massa. Mauris non tempus mi, quis mattis libero. Vivamus ornare ex at semper cursus. Vestibulum sed facilisis erat, aliquet mollis est. In interdum, magna iaculis ultricies elementum, mi ante vestibulum mauris, nec viverra turpis lorem quis ante. Proin in auctor erat. Vivamus dictum congue massa, fermentum bibendum leo pretium quis. Integer dapibus sodales ligula, sit amet imperdiet felis suscipit eu. Phasellus non ornare enim.
Nam feugiat neque sit amet hendrerit rhoncus. Nunc suscipit molestie vehicula. Aenean vulputate porttitor augue, sit amet molestie dolor volutpat vitae. Nulla vitae condimentum eros. Aliquam tristique purus at metus lacinia egestas. Cras euismod lorem eu orci lobortis, sed tincidunt nisl laoreet. Ut suscipit fermentum mi, et euismod tortor. Pellentesque vitae tempor quam, sed dignissim mi. Suspendisse luctus lacus vitae ligula blandit vehicula. Quisque interdum iaculis tincidunt. Nunc elementum mi vitae tempor placerat. Suspendisse potenti. Donec blandit laoreet ipsum, quis rhoncus velit vulputate sed.
Aliquam suscipit lectus eros, at maximus dolor efficitur quis. Integer blandit tortor orci, nec mattis nunc eleifend ac. Mauris pharetra vel quam quis lacinia. Duis lobortis condimentum nunc ut facilisis. Praesent arcu nisi, porta sit amet viverra sit amet, pellentesque ut nisi. Nunc gravida tortor eu ligula tempus, in interdum magna pretium. Fusce eu ornare sapien. Nullam pellentesque cursus eros. Nam orci massa, faucibus eget leo eget, elementum vulputate erat. Fusce vehicula augue et dui hendrerit vulputate. Mauris neque lacus, porttitor ut condimentum id, efficitur ac neque. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Donec accumsan, lectus fermentum elementum tristique, ipsum tortor mollis ante, non lacinia nibh ex quis sapien.
Donec pharetra, elit eget rutrum luctus, urna ligula facilisis lorem, sit amet rhoncus ante est eu mi. Vestibulum vestibulum ultricies interdum. Nulla tincidunt ante non hendrerit venenatis. Curabitur vestibulum turpis erat, id efficitur quam venenatis eu. Fusce nulla sem, dapibus vel quam feugiat, ornare fermentum ligula. Praesent tempus tincidunt mauris, non pellentesque felis varius in. Aenean eu arcu ligula. Morbi dapibus maximus nulla a pharetra. Fusce leo metus, luctus ut cursus non, sollicitudin non lectus. Integer pellentesque eleifend erat, vel gravida purus tempus a. Mauris id vestibulum quam. Nunc vitae ullamcorper metus, pharetra placerat enim. Fusce in ultrices nisl. Curabitur justo mauris, dignissim in aliquam sit amet, sollicitudin ut risus. Cras tempor rutrum justo, non tincidunt est maximus at.
Aliquam ac velit tincidunt, ullamcorper velit sit amet, pulvinar nisi. Nullam rhoncus rhoncus egestas. Cras ac luctus nisi. Mauris sit amet risus at magna volutpat ultrices quis ac dui. Aliquam condimentum tellus purus, vel sagittis odio vulputate at. Sed ut finibus tellus. Aliquam tincidunt vehicula diam.

View File

@ -1,11 +1,10 @@
use bytes::{Buf, Bytes, BytesMut};
use std::io;
use bytes::{Buf, Bytes, BytesMut};
use super::{Decoder, Encoder};
/// Bytes codec.
///
/// Reads/Writes chunks of bytes from a stream.
/// Bytes codec. Reads/writes chunks of bytes from a stream.
#[derive(Debug, Copy, Clone)]
pub struct BytesCodec;

View File

@ -1,10 +1,14 @@
use std::pin::Pin;
use std::task::{Context, Poll};
use std::{fmt, io};
use std::{
fmt, io,
pin::Pin,
task::{Context, Poll},
};
use bitflags::bitflags;
use bytes::{Buf, BytesMut};
use futures_core::{ready, Stream};
use futures_sink::Sink;
use pin_project_lite::pin_project;
use crate::{AsyncRead, AsyncWrite, Decoder, Encoder};
@ -13,22 +17,22 @@ const LW: usize = 1024;
/// High-water mark
const HW: usize = 8 * 1024;
bitflags::bitflags! {
bitflags! {
#[derive(Debug, Clone, Copy)]
struct Flags: u8 {
const EOF = 0b0001;
const READABLE = 0b0010;
}
}
pin_project_lite::pin_project! {
/// A unified `Stream` and `Sink` interface to an underlying I/O object, using
/// the `Encoder` and `Decoder` traits to encode and decode frames.
pin_project! {
/// A unified `Stream` and `Sink` interface to an underlying I/O object, using the `Encoder` and
/// `Decoder` traits to encode and decode frames.
///
/// Raw I/O objects work with byte sequences, but higher-level code usually
/// wants to batch these into meaningful chunks, called "frames". This
/// method layers framing on top of an I/O object, by using the `Encoder`/`Decoder`
/// traits to handle encoding and decoding of message frames. Note that
/// the incoming and outgoing frame types may be distinct.
/// Raw I/O objects work with byte sequences, but higher-level code usually wants to batch these
/// into meaningful chunks, called "frames". This method layers framing on top of an I/O object,
/// by using the `Encoder`/`Decoder` traits to handle encoding and decoding of message frames.
/// Note that the incoming and outgoing frame types may be distinct.
pub struct Framed<T, U> {
#[pin]
io: T,
@ -44,10 +48,9 @@ where
T: AsyncRead + AsyncWrite,
U: Decoder,
{
/// This function returns a *single* object that is both `Stream` and
/// `Sink`; grouping this into a single object is often useful for layering
/// things like gzip or TLS, which require both read and write access to the
/// underlying object.
/// This function returns a *single* object that is both `Stream` and `Sink`; grouping this into
/// a single object is often useful for layering things like gzip or TLS, which require both
/// read and write access to the underlying object.
pub fn new(io: T, codec: U) -> Framed<T, U> {
Framed {
io,
@ -70,21 +73,18 @@ impl<T, U> Framed<T, U> {
&mut self.codec
}
/// Returns a reference to the underlying I/O stream wrapped by
/// `Frame`.
/// Returns a reference to the underlying I/O stream wrapped by `Frame`.
///
/// Note that care should be taken to not tamper with the underlying stream
/// of data coming in as it may corrupt the stream of frames otherwise
/// being worked with.
/// Note that care should be taken to not tamper with the underlying stream of data coming in as
/// it may corrupt the stream of frames otherwise being worked with.
pub fn io_ref(&self) -> &T {
&self.io
}
/// Returns a mutable reference to the underlying I/O stream.
///
/// Note that care should be taken to not tamper with the underlying stream
/// of data coming in as it may corrupt the stream of frames otherwise
/// being worked with.
/// Note that care should be taken to not tamper with the underlying stream of data coming in as
/// it may corrupt the stream of frames otherwise being worked with.
pub fn io_mut(&mut self) -> &mut T {
&mut self.io
}
@ -157,7 +157,7 @@ impl<T, U> Framed<T, U> {
}
impl<T, U> Framed<T, U> {
/// Serialize item and Write to the inner buffer
/// Serialize item and write to the inner buffer
pub fn write<I>(mut self: Pin<&mut Self>, item: I) -> Result<(), <U as Encoder<I>>::Error>
where
T: AsyncWrite,
@ -183,30 +183,29 @@ impl<T, U> Framed<T, U> {
U: Decoder,
{
loop {
let mut this = self.as_mut().project();
// Repeatedly call `decode` or `decode_eof` as long as it is
// "readable". Readable is defined as not having returned `None`. If
// the upstream has returned EOF, and the decoder is no longer
// readable, it can be assumed that the decoder will never become
let this = self.as_mut().project();
// Repeatedly call `decode` or `decode_eof` as long as it is "readable". Readable is
// defined as not having returned `None`. If the upstream has returned EOF, and the
// decoder is no longer readable, it can be assumed that the decoder will never become
// readable again, at which point the stream is terminated.
if this.flags.contains(Flags::READABLE) {
if this.flags.contains(Flags::EOF) {
match this.codec.decode_eof(&mut this.read_buf) {
match this.codec.decode_eof(this.read_buf) {
Ok(Some(frame)) => return Poll::Ready(Some(Ok(frame))),
Ok(None) => return Poll::Ready(None),
Err(e) => return Poll::Ready(Some(Err(e))),
Err(err) => return Poll::Ready(Some(Err(err))),
}
}
log::trace!("attempting to decode a frame");
tracing::trace!("attempting to decode a frame");
match this.codec.decode(&mut this.read_buf) {
match this.codec.decode(this.read_buf) {
Ok(Some(frame)) => {
log::trace!("frame decoded from buffer");
tracing::trace!("frame decoded from buffer");
return Poll::Ready(Some(Ok(frame)));
}
Err(e) => return Poll::Ready(Some(Err(e))),
Err(err) => return Poll::Ready(Some(Err(err))),
_ => (), // Need more data
}
@ -215,7 +214,7 @@ impl<T, U> Framed<T, U> {
debug_assert!(!this.flags.contains(Flags::EOF));
// Otherwise, try to read more data and try again. Make sure we've got room
// Otherwise, try to read more data and try again. Make sure we've got room.
let remaining = this.read_buf.capacity() - this.read_buf.len();
if remaining < LW {
this.read_buf.reserve(HW - remaining)
@ -223,7 +222,7 @@ impl<T, U> Framed<T, U> {
let cnt = match tokio_util::io::poll_read_buf(this.io, cx, this.read_buf) {
Poll::Pending => return Poll::Pending,
Poll::Ready(Err(e)) => return Poll::Ready(Some(Err(e.into()))),
Poll::Ready(Err(err)) => return Poll::Ready(Some(Err(err.into()))),
Poll::Ready(Ok(cnt)) => cnt,
};
@ -235,19 +234,16 @@ impl<T, U> Framed<T, U> {
}
/// Flush write buffer to underlying I/O stream.
pub fn flush<I>(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), U::Error>>
pub fn flush<I>(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), U::Error>>
where
T: AsyncWrite,
U: Encoder<I>,
{
let mut this = self.as_mut().project();
log::trace!("flushing framed transport");
tracing::trace!("flushing framed transport");
while !this.write_buf.is_empty() {
log::trace!("writing; remaining={}", this.write_buf.len());
tracing::trace!("writing; remaining={}", this.write_buf.len());
let n = ready!(this.io.as_mut().poll_write(cx, this.write_buf))?;
@ -266,15 +262,12 @@ impl<T, U> Framed<T, U> {
// Try flushing the underlying IO
ready!(this.io.poll_flush(cx))?;
log::trace!("framed transport flushed");
tracing::trace!("framed transport flushed");
Poll::Ready(Ok(()))
}
/// Flush write buffer and shutdown underlying I/O stream.
pub fn close<I>(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), U::Error>>
pub fn close<I>(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), U::Error>>
where
T: AsyncWrite,
U: Encoder<I>,
@ -306,11 +299,11 @@ where
{
type Error = U::Error;
fn poll_ready(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
if self.is_write_ready() {
Poll::Ready(Ok(()))
} else {
Poll::Pending
self.flush(cx)
}
}
@ -341,13 +334,12 @@ where
}
impl<T, U> Framed<T, U> {
/// This function returns a *single* object that is both `Stream` and
/// `Sink`; grouping this into a single object is often useful for layering
/// things like gzip or TLS, which require both read and write access to the
/// underlying object.
/// This function returns a *single* object that is both `Stream` and `Sink`; grouping this into
/// a single object is often useful for layering things like gzip or TLS, which require both
/// read and write access to the underlying object.
///
/// These objects take a stream, a read buffer and a write buffer. These
/// fields can be obtained from an existing `Framed` with the `into_parts` method.
/// These objects take a stream, a read buffer and a write buffer. These fields can be obtained
/// from an existing `Framed` with the `into_parts` method.
pub fn from_parts(parts: FramedParts<T, U>) -> Framed<T, U> {
Framed {
io: parts.io,
@ -358,12 +350,11 @@ impl<T, U> Framed<T, U> {
}
}
/// Consumes the `Frame`, returning its underlying I/O stream, the buffer
/// with unprocessed data, and the codec.
/// Consumes the `Frame`, returning its underlying I/O stream, the buffer with unprocessed data,
/// and the codec.
///
/// Note that care should be taken to not tamper with the underlying stream
/// of data coming in as it may corrupt the stream of frames otherwise
/// being worked with.
/// Note that care should be taken to not tamper with the underlying stream of data coming in as
/// it may corrupt the stream of frames otherwise being worked with.
pub fn into_parts(self) -> FramedParts<T, U> {
FramedParts {
io: self.io,
@ -376,14 +367,15 @@ impl<T, U> Framed<T, U> {
}
/// `FramedParts` contains an export of the data of a Framed transport.
/// It can be used to construct a new `Framed` with a different codec.
/// It contains all current buffers and the inner transport.
///
/// It can be used to construct a new `Framed` with a different codec. It contains all current
/// buffers and the inner transport.
#[derive(Debug)]
pub struct FramedParts<T, U> {
/// The inner transport used to read bytes to and write bytes to
/// The inner transport used to read bytes to and write bytes to.
pub io: T,
/// The codec
/// The codec object.
pub codec: U,
/// The buffer with read but unprocessed data.
@ -396,7 +388,7 @@ pub struct FramedParts<T, U> {
}
impl<T, U> FramedParts<T, U> {
/// Create a new, default, `FramedParts`
/// Creates a new default `FramedParts`.
pub fn new(io: T, codec: U) -> FramedParts<T, U> {
FramedParts {
io,
@ -407,7 +399,7 @@ impl<T, U> FramedParts<T, U> {
}
}
/// Create a new `FramedParts` with read buffer
/// Creates a new `FramedParts` with read buffer.
pub fn with_read_buf(io: T, codec: U, read_buf: BytesMut) -> FramedParts<T, U> {
FramedParts {
io,

View File

@ -1,23 +1,26 @@
//! Codec utilities for working with framed protocols.
//!
//! Contains adapters to go from streams of bytes, [`AsyncRead`] and
//! [`AsyncWrite`], to framed streams implementing [`Sink`] and [`Stream`].
//! Framed streams are also known as `transports`.
//! Contains adapters to go from streams of bytes, [`AsyncRead`] and [`AsyncWrite`], to framed
//! streams implementing [`Sink`] and [`Stream`]. Framed streams are also known as `transports`.
//!
//! [`Sink`]: futures_sink::Sink
//! [`Stream`]: futures_core::Stream
#![deny(rust_2018_idioms, nonstandard_style, future_incompatible)]
#![warn(missing_docs)]
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
pub use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
pub use tokio_util::{
codec::{Decoder, Encoder},
io::poll_read_buf,
};
mod bcodec;
mod framed;
mod lines;
pub use self::bcodec::BytesCodec;
pub use self::framed::{Framed, FramedParts};
pub use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
pub use tokio_util::codec::{Decoder, Encoder};
pub use tokio_util::io::poll_read_buf;
pub use self::{
bcodec::BytesCodec,
framed::{Framed, FramedParts},
lines::LinesCodec,
};

158
actix-codec/src/lines.rs Normal file
View File

@ -0,0 +1,158 @@
use std::io;
use bytes::{Buf, BufMut, Bytes, BytesMut};
use memchr::memchr;
use super::{Decoder, Encoder};
/// Lines codec. Reads/writes line delimited strings.
///
/// Will split input up by LF or CRLF delimiters. Carriage return characters at the end of lines are
/// not preserved.
#[derive(Debug, Copy, Clone, Default)]
#[non_exhaustive]
pub struct LinesCodec;
impl<T: AsRef<str>> Encoder<T> for LinesCodec {
type Error = io::Error;
#[inline]
fn encode(&mut self, item: T, dst: &mut BytesMut) -> Result<(), Self::Error> {
let item = item.as_ref();
dst.reserve(item.len() + 1);
dst.put_slice(item.as_bytes());
dst.put_u8(b'\n');
Ok(())
}
}
impl Decoder for LinesCodec {
type Item = String;
type Error = io::Error;
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
if src.is_empty() {
return Ok(None);
}
let len = match memchr(b'\n', src) {
Some(n) => n,
None => {
return Ok(None);
}
};
// split up to new line char
let mut buf = src.split_to(len);
debug_assert_eq!(len, buf.len());
// remove new line char from source
src.advance(1);
match buf.last() {
// remove carriage returns at the end of buf
Some(b'\r') => buf.truncate(len - 1),
// line is empty
None => return Ok(Some(String::new())),
_ => {}
}
try_into_utf8(buf.freeze())
}
fn decode_eof(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
match self.decode(src)? {
Some(frame) => Ok(Some(frame)),
None if src.is_empty() => Ok(None),
None => {
let buf = match src.last() {
// if last line ends in a CR then take everything up to it
Some(b'\r') => src.split_to(src.len() - 1),
// take all bytes from source
_ => src.split(),
};
if buf.is_empty() {
return Ok(None);
}
try_into_utf8(buf.freeze())
}
}
}
}
// Attempts to convert bytes into a `String`.
fn try_into_utf8(buf: Bytes) -> io::Result<Option<String>> {
String::from_utf8(buf.to_vec())
.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))
.map(Some)
}
#[cfg(test)]
mod tests {
use bytes::BufMut as _;
use super::*;
#[test]
fn lines_decoder() {
let mut codec = LinesCodec::default();
let mut buf = BytesMut::from("\nline 1\nline 2\r\nline 3\n\r\n\r");
assert_eq!("", codec.decode(&mut buf).unwrap().unwrap());
assert_eq!("line 1", codec.decode(&mut buf).unwrap().unwrap());
assert_eq!("line 2", codec.decode(&mut buf).unwrap().unwrap());
assert_eq!("line 3", codec.decode(&mut buf).unwrap().unwrap());
assert_eq!("", codec.decode(&mut buf).unwrap().unwrap());
assert!(codec.decode(&mut buf).unwrap().is_none());
assert!(codec.decode_eof(&mut buf).unwrap().is_none());
buf.put_slice(b"k");
assert!(codec.decode(&mut buf).unwrap().is_none());
assert_eq!("\rk", codec.decode_eof(&mut buf).unwrap().unwrap());
assert!(codec.decode(&mut buf).unwrap().is_none());
assert!(codec.decode_eof(&mut buf).unwrap().is_none());
}
#[test]
fn lines_encoder() {
let mut codec = LinesCodec::default();
let mut buf = BytesMut::new();
codec.encode("", &mut buf).unwrap();
assert_eq!(&buf[..], b"\n");
codec.encode("test", &mut buf).unwrap();
assert_eq!(&buf[..], b"\ntest\n");
codec.encode("a\nb", &mut buf).unwrap();
assert_eq!(&buf[..], b"\ntest\na\nb\n");
}
#[test]
fn lines_encoder_no_overflow() {
let mut codec = LinesCodec::default();
let mut buf = BytesMut::new();
codec.encode("1234567", &mut buf).unwrap();
assert_eq!(&buf[..], b"1234567\n");
let mut buf = BytesMut::new();
codec.encode("12345678", &mut buf).unwrap();
assert_eq!(&buf[..], b"12345678\n");
let mut buf = BytesMut::new();
codec.encode("123456789111213", &mut buf).unwrap();
assert_eq!(&buf[..], b"123456789111213\n");
let mut buf = BytesMut::new();
codec.encode("1234567891112131", &mut buf).unwrap();
assert_eq!(&buf[..], b"1234567891112131\n");
}
}

View File

@ -0,0 +1,224 @@
#![allow(missing_docs)]
use std::{
collections::VecDeque,
io::{self, Write},
pin::Pin,
task::{
Context,
Poll::{self, Pending, Ready},
},
};
use actix_codec::*;
use bytes::{Buf as _, BufMut as _, BytesMut};
use futures_sink::Sink;
use tokio_test::{assert_ready, task};
macro_rules! bilateral {
($($x:expr,)*) => {{
let mut v = VecDeque::new();
v.extend(vec![$($x),*]);
Bilateral { calls: v }
}};
}
macro_rules! assert_ready {
($e:expr) => {{
use core::task::Poll::*;
match $e {
Ready(v) => v,
Pending => panic!("pending"),
}
}};
($e:expr, $($msg:tt),+) => {{
use core::task::Poll::*;
match $e {
Ready(v) => v,
Pending => {
let msg = format_args!($($msg),+);
panic!("pending; {}", msg)
}
}
}};
}
#[derive(Debug)]
pub struct Bilateral {
pub calls: VecDeque<io::Result<Vec<u8>>>,
}
impl Write for Bilateral {
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
match self.calls.pop_front() {
Some(Ok(data)) => {
assert!(src.len() >= data.len());
assert_eq!(&data[..], &src[..data.len()]);
Ok(data.len())
}
Some(Err(err)) => Err(err),
None => panic!("unexpected write; {:?}", src),
}
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl AsyncWrite for Bilateral {
fn poll_write(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<Result<usize, io::Error>> {
match Pin::get_mut(self).write(buf) {
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => Pending,
other => Ready(other),
}
}
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
match Pin::get_mut(self).flush() {
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => Pending,
other => Ready(other),
}
}
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
unimplemented!()
}
}
impl AsyncRead for Bilateral {
fn poll_read(
mut self: Pin<&mut Self>,
_: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<Result<(), std::io::Error>> {
use io::ErrorKind::WouldBlock;
match self.calls.pop_front() {
Some(Ok(data)) => {
debug_assert!(buf.remaining() >= data.len());
buf.put_slice(&data);
Ready(Ok(()))
}
Some(Err(ref err)) if err.kind() == WouldBlock => Pending,
Some(Err(err)) => Ready(Err(err)),
None => Ready(Ok(())),
}
}
}
pub struct U32;
impl Encoder<u32> for U32 {
type Error = io::Error;
fn encode(&mut self, item: u32, dst: &mut BytesMut) -> io::Result<()> {
// Reserve space
dst.reserve(4);
dst.put_u32(item);
Ok(())
}
}
impl Decoder for U32 {
type Item = u32;
type Error = io::Error;
fn decode(&mut self, buf: &mut BytesMut) -> io::Result<Option<u32>> {
if buf.len() < 4 {
return Ok(None);
}
let n = buf.split_to(4).get_u32();
Ok(Some(n))
}
}
#[test]
fn test_write_hits_highwater_mark() {
// see here for what this test is based on:
// https://github.com/tokio-rs/tokio/blob/75c07770bfbfea4e5fd914af819c741ed9c3fc36/tokio-util/tests/framed_write.rs#L69
const ITER: usize = 2 * 1024;
let mut bi = bilateral! {
Err(io::Error::new(io::ErrorKind::WouldBlock, "not ready")),
Ok(b"".to_vec()),
};
for i in 0..=ITER {
let mut b = BytesMut::with_capacity(4);
b.put_u32(i as u32);
// Append to the end
match bi.calls.back_mut().unwrap() {
Ok(ref mut data) => {
// Write in 2kb chunks
if data.len() < ITER {
data.extend_from_slice(&b[..]);
continue;
} // else fall through and create a new buffer
}
_ => unreachable!(),
}
// Push a new new chunk
bi.calls.push_back(Ok(b[..].to_vec()));
}
assert_eq!(bi.calls.len(), 6);
let mut framed = Framed::new(bi, U32);
// Send 8KB. This fills up FramedWrite2 buffer
let mut task = task::spawn(());
task.enter(|cx, _| {
// Send 8KB. This fills up Framed buffer
for i in 0..ITER {
{
#[allow(unused_mut)]
let mut framed = Pin::new(&mut framed);
assert!(assert_ready!(framed.poll_ready(cx)).is_ok());
}
#[allow(unused_mut)]
let mut framed = Pin::new(&mut framed);
// write the buffer
assert!(framed.start_send(i as u32).is_ok());
}
{
#[allow(unused_mut)]
let mut framed = Pin::new(&mut framed);
// Now we poll_ready which forces a flush. The bilateral pops the front message
// and decides to block.
assert!(framed.poll_ready(cx).is_pending());
}
{
#[allow(unused_mut)]
let mut framed = Pin::new(&mut framed);
// We poll again, forcing another flush, which this time succeeds
// The whole 8KB buffer is flushed
assert!(assert_ready!(framed.poll_ready(cx)).is_ok());
}
{
#[allow(unused_mut)]
let mut framed = Pin::new(&mut framed);
// Send more data. This matches the final message expected by the bilateral
assert!(framed.start_send(ITER as u32).is_ok());
}
{
#[allow(unused_mut)]
let mut framed = Pin::new(&mut framed);
// Flush the rest of the buffer
assert!(assert_ready!(framed.poll_flush(cx)).is_ok());
}
// Ensure the mock is empty
assert_eq!(0, Pin::new(&framed).get_ref().io_ref().calls.len());
});
}

View File

@ -1,33 +1,53 @@
# Changes
## Unreleased - 2021-xx-xx
## Unreleased
- Minimum supported Rust version (MSRV) is now 1.71.
## 0.2.1 - 2021-02-02
* Add optional argument `system` to `main` macro which can be used to specify the path to `actix_rt::System` (useful for re-exports). [#363]
## 0.2.4
- Update `syn` dependency to `2`.
- Minimum supported Rust version (MSRV) is now 1.65.
## 0.2.3
- Fix test macro in presence of other imports named "test". [#399]
[#399]: https://github.com/actix/actix-net/pull/399
## 0.2.2
- Improve error recovery potential when macro input is invalid. [#391]
- Allow custom `System`s on test macro. [#391]
[#391]: https://github.com/actix/actix-net/pull/391
## 0.2.1
- Add optional argument `system` to `main` macro which can be used to specify the path to `actix_rt::System` (useful for re-exports). [#363]
[#363]: https://github.com/actix/actix-net/pull/363
## 0.2.0
## 0.2.0 - 2021-02-02
* Update to latest `actix_rt::System::new` signature. [#261]
- Update to latest `actix_rt::System::new` signature. [#261]
[#261]: https://github.com/actix/actix-net/pull/261
## 0.2.0-beta.1
## 0.2.0-beta.1 - 2021-01-09
* Remove `actix-reexport` feature. [#218]
- Remove `actix-reexport` feature. [#218]
[#218]: https://github.com/actix/actix-net/pull/218
## 0.1.3
## 0.1.3 - 2020-12-03
* Add `actix-reexport` feature. [#218]
- Add `actix-reexport` feature. [#218]
[#218]: https://github.com/actix/actix-net/pull/218
## 0.1.2
## 0.1.2 - 2020-05-18
* Forward actix_rt::test arguments to test function [#127]
- Forward actix_rt::test arguments to test function [#127]
[#127]: https://github.com/actix/actix-net/pull/127

View File

@ -1,25 +1,39 @@
[package]
name = "actix-macros"
version = "0.2.1"
version = "0.2.4"
authors = [
"Nikolay Kim <fafhrd91@gmail.com>",
"Ibraheem Ahmed <ibrah1440@gmail.com>",
"Nikolay Kim <fafhrd91@gmail.com>",
"Ibraheem Ahmed <ibrah1440@gmail.com>",
"Rob Ede <robjtede@icloud.com>",
]
description = "Macros for Actix system and runtime"
repository = "https://github.com/actix/actix-net"
categories = ["network-programming", "asynchronous"]
license = "MIT OR Apache-2.0"
edition = "2018"
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[package.metadata.cargo-machete]
ignored = [
"proc_macro2", # specified for minimal versions compat
]
[lib]
proc-macro = true
[dependencies]
quote = "1.0.3"
syn = { version = "^1", features = ["full"] }
quote = "1"
syn = { version = "2", features = ["full"] }
# minimal versions compat
[target.'cfg(any())'.dependencies]
proc-macro2 = "1.0.60"
[dev-dependencies]
actix-rt = "2.0.0"
futures-util = { version = "0.3.7", default-features = false }
actix-rt = "2"
futures-util = { version = "0.3.17", default-features = false }
rustversion-msrv = "0.100"
trybuild = "1"
[lints]
workspace = true

View File

@ -8,12 +8,14 @@
//! # Tests
//! See docs for the [`#[test]`](macro@test) macro.
#![deny(rust_2018_idioms, nonstandard_style)]
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
use proc_macro::TokenStream;
use quote::quote;
use syn::parse::Parser as _;
type AttributeArgs = syn::punctuated::Punctuated<syn::Meta, syn::Token![,]>;
/// Marks async entry-point function to be executed by Actix system.
///
@ -24,12 +26,19 @@ use quote::quote;
/// println!("Hello world");
/// }
/// ```
#[allow(clippy::needless_doctest_main)]
#[proc_macro_attribute]
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
let mut input = syn::parse_macro_input!(item as syn::ItemFn);
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
let mut input = match syn::parse::<syn::ItemFn>(item.clone()) {
Ok(input) => input,
// on parse err, make IDEs happy; see fn docs
Err(err) => return input_and_compile_error(item, err),
};
let parser = AttributeArgs::parse_terminated;
let args = match parser.parse(args.clone()) {
Ok(args) => args,
Err(err) => return input_and_compile_error(args, err),
};
let attrs = &input.attrs;
let vis = &input.vis;
@ -49,11 +58,15 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
for arg in &args {
match arg {
syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
lit: syn::Lit::Str(lit),
syn::Meta::NameValue(syn::MetaNameValue {
path,
value:
syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Str(lit),
..
}),
..
})) => match path
}) => match path
.get_ident()
.map(|i| i.to_string().to_lowercase())
.as_deref()
@ -72,6 +85,7 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
.into();
}
},
_ => {
return syn::Error::new_spanned(arg, "Unknown attribute specified")
.to_compile_error()
@ -101,8 +115,19 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
/// }
/// ```
#[proc_macro_attribute]
pub fn test(_: TokenStream, item: TokenStream) -> TokenStream {
let mut input = syn::parse_macro_input!(item as syn::ItemFn);
pub fn test(args: TokenStream, item: TokenStream) -> TokenStream {
let mut input = match syn::parse::<syn::ItemFn>(item.clone()) {
Ok(input) => input,
// on parse err, make IDEs happy; see fn docs
Err(err) => return input_and_compile_error(item, err),
};
let parser = AttributeArgs::parse_terminated;
let args = match parser.parse(args.clone()) {
Ok(args) => args,
Err(err) => return input_and_compile_error(args, err),
};
let attrs = &input.attrs;
let vis = &input.vis;
let sig = &mut input.sig;
@ -110,7 +135,7 @@ pub fn test(_: TokenStream, item: TokenStream) -> TokenStream {
let mut has_test_attr = false;
for attr in attrs {
if attr.path.is_ident("test") {
if attr.path().is_ident("test") {
has_test_attr = true;
}
}
@ -127,18 +152,68 @@ pub fn test(_: TokenStream, item: TokenStream) -> TokenStream {
sig.asyncness = None;
let missing_test_attr = if has_test_attr {
quote!()
quote! {}
} else {
quote!(#[test])
quote! { #[::core::prelude::v1::test] }
};
let mut system = syn::parse_str::<syn::Path>("::actix_rt::System").unwrap();
for arg in &args {
match arg {
syn::Meta::NameValue(syn::MetaNameValue {
path,
value:
syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Str(lit),
..
}),
..
}) => match path
.get_ident()
.map(|i| i.to_string().to_lowercase())
.as_deref()
{
Some("system") => match lit.parse() {
Ok(path) => system = path,
Err(_) => {
return syn::Error::new_spanned(lit, "Expected path")
.to_compile_error()
.into();
}
},
_ => {
return syn::Error::new_spanned(arg, "Unknown attribute specified")
.to_compile_error()
.into();
}
},
_ => {
return syn::Error::new_spanned(arg, "Unknown attribute specified")
.to_compile_error()
.into();
}
}
}
(quote! {
#missing_test_attr
#(#attrs)*
#vis #sig {
actix_rt::System::new()
.block_on(async { #body })
<#system>::new().block_on(async { #body })
}
})
.into()
}
/// Converts the error to a token stream and appends it to the original input.
///
/// Returning the original input in addition to the error is good for IDEs which can gracefully
/// recover and show more precise errors within the macro body.
///
/// See <https://github.com/rust-analyzer/rust-analyzer/issues/10468> for more info.
fn input_and_compile_error(mut item: TokenStream, err: syn::Error) -> TokenStream {
let compile_err = TokenStream::from(err.to_compile_error());
item.extend(compile_err);
item
}

View File

@ -1,6 +1,10 @@
#![allow(missing_docs)]
#[rustversion_msrv::msrv]
#[test]
fn compile_macros() {
let t = trybuild::TestCases::new();
t.pass("tests/trybuild/main-01-basic.rs");
t.compile_fail("tests/trybuild/main-02-only-async.rs");
t.pass("tests/trybuild/main-03-fn-params.rs");
@ -11,4 +15,7 @@ fn compile_macros() {
t.pass("tests/trybuild/test-01-basic.rs");
t.pass("tests/trybuild/test-02-keep-attrs.rs");
t.compile_fail("tests/trybuild/test-03-only-async.rs");
t.pass("tests/trybuild/test-04-system-path.rs");
t.compile_fail("tests/trybuild/test-05-system-expect-path.rs");
t.compile_fail("tests/trybuild/test-06-unknown-attr.rs");
}

View File

@ -1,14 +1,11 @@
error: the async keyword is missing from the function declaration
--> $DIR/main-02-only-async.rs:2:1
--> tests/trybuild/main-02-only-async.rs:2:1
|
2 | fn main() {
| ^^
error[E0601]: `main` function not found in crate `$CRATE`
--> $DIR/main-02-only-async.rs:1:1
--> tests/trybuild/main-02-only-async.rs:4:2
|
1 | / #[actix_rt::main]
2 | | fn main() {
3 | | futures_util::future::ready(()).await
4 | | }
| |_^ consider adding a `main` function to `$DIR/tests/trybuild/main-02-only-async.rs`
4 | }
| ^ consider adding a `main` function to `$DIR/tests/trybuild/main-02-only-async.rs`

View File

@ -0,0 +1,10 @@
mod system {
pub use actix_rt::System as MySystem;
}
#[actix_rt::test(system = "system::MySystem")]
async fn my_test() {
futures_util::future::ready(()).await
}
fn main() {}

View File

@ -0,0 +1,4 @@
#[actix_rt::test(system = "!@#*&")]
async fn my_test() {}
fn main() {}

View File

@ -0,0 +1,5 @@
error: Expected path
--> $DIR/test-05-system-expect-path.rs:1:27
|
1 | #[actix_rt::test(system = "!@#*&")]
| ^^^^^^^

View File

@ -0,0 +1,7 @@
#[actix_rt::test(foo = "bar")]
async fn my_test_1() {}
#[actix_rt::test(bar::baz)]
async fn my_test_2() {}
fn main() {}

View File

@ -0,0 +1,11 @@
error: Unknown attribute specified
--> $DIR/test-06-unknown-attr.rs:1:18
|
1 | #[actix_rt::test(foo = "bar")]
| ^^^^^^^^^^^
error: Unknown attribute specified
--> $DIR/test-06-unknown-attr.rs:4:18
|
4 | #[actix_rt::test(bar::baz)]
| ^^^^^^^^

View File

@ -1,111 +0,0 @@
# Changes
## Unreleased - 2021-xx-xx
## 0.5.0-beta.1 - 2021-07-20
* Fix a bug in multi-patterns where static patterns are interpreted as regex. [#366]
* Introduce `ResourceDef::pattern_iter` to get an iterator over all patterns in a multi-pattern resource. [#373]
* Fix segment interpolation leaving `Path` in unintended state after matching. [#368]
* Fix `ResourceDef` `PartialEq` implementation. [#373]
* Re-work `IntoPatterns` trait, adding a `Patterns` enum. [#372]
* Implement `IntoPatterns` for `bytestring::ByteString`. [#372]
* Rename `Path::{len => segment_count}` to be more descriptive of it's purpose. [#370]
* Rename `ResourceDef::{resource_path => resource_path_from_iter}`. [#371]
* `ResourceDef::resource_path_from_iter` now takes an `IntoIterator`. [#373]
* Rename `ResourceDef::{resource_path_named => resource_path_from_map}`. [#371]
* Rename `ResourceDef::{is_prefix_match => find_match}`. [#373]
* Rename `ResourceDef::{match_path => capture_match_info}`. [#373]
* Rename `ResourceDef::{match_path_checked => capture_match_info_fn}`. [#373]
* Remove `ResourceDef::name_mut` and introduce `ResourceDef::set_name`. [#373]
* Rename `Router::{*_checked => *_fn}`. [#373]
* Return type of `ResourceDef::name` is now `Option<&str>`. [#373]
* Return type of `ResourceDef::pattern` is now `Option<&str>`. [#373]
[#368]: https://github.com/actix/actix-net/pull/368
[#366]: https://github.com/actix/actix-net/pull/366
[#368]: https://github.com/actix/actix-net/pull/368
[#370]: https://github.com/actix/actix-net/pull/370
[#371]: https://github.com/actix/actix-net/pull/371
[#372]: https://github.com/actix/actix-net/pull/372
[#373]: https://github.com/actix/actix-net/pull/373
## 0.4.0 - 2021-06-06
* When matching path parameters, `%25` is now kept in the percent-encoded form; no longer decoded to `%`. [#357]
* Path tail patterns now match new lines (`\n`) in request URL. [#360]
* Fixed a safety bug where `Path` could return a malformed string after percent decoding. [#359]
* Methods `Path::{add, add_static}` now take `impl Into<Cow<'static, str>>`. [#345]
[#345]: https://github.com/actix/actix-net/pull/345
[#357]: https://github.com/actix/actix-net/pull/357
[#359]: https://github.com/actix/actix-net/pull/359
[#360]: https://github.com/actix/actix-net/pull/360
## 0.3.0 - 2019-12-31
* Version was yanked previously. See https://crates.io/crates/actix-router/0.3.0
## 0.2.7 - 2021-02-06
* Add `Router::recognize_checked` [#247]
[#247]: https://github.com/actix/actix-net/pull/247
## 0.2.6 - 2021-01-09
* Use `bytestring` version range compatible with Bytes v1.0. [#246]
[#246]: https://github.com/actix/actix-net/pull/246
## 0.2.5 - 2020-09-20
* Fix `from_hex()` method
## 0.2.4 - 2019-12-31
* Add `ResourceDef::resource_path_named()` path generation method
## 0.2.3 - 2019-12-25
* Add impl `IntoPattern` for `&String`
## 0.2.2 - 2019-12-25
* Use `IntoPattern` for `RouterBuilder::path()`
## 0.2.1 - 2019-12-25
* Add `IntoPattern` trait
* Add multi-pattern resources
## 0.2.0 - 2019-12-07
* Update http to 0.2
* Update regex to 1.3
* Use bytestring instead of string
## 0.1.5 - 2019-05-15
* Remove debug prints
## 0.1.4 - 2019-05-15
* Fix checked resource match
## 0.1.3 - 2019-04-22
* Added support for `remainder match` (i.e "/path/{tail}*")
## 0.1.2 - 2019-04-07
* Export `Quoter` type
* Allow to reset `Path` instance
## 0.1.1 - 2019-04-03
* Get dynamic segment by name instead of iterator.
## 0.1.0 - 2019-03-09
* Initial release

View File

@ -1,38 +0,0 @@
[package]
name = "actix-router"
version = "0.5.0-beta.1"
authors = [
"Nikolay Kim <fafhrd91@gmail.com>",
"Ali MJ Al-Nasrawy <alimjalnasrawy@gmail.com>",
"Rob Ede <robjtede@icloud.com>",
]
description = "Resource path matching and router"
keywords = ["actix", "router", "routing"]
repository = "https://github.com/actix/actix-net.git"
license = "MIT OR Apache-2.0"
edition = "2018"
[lib]
name = "actix_router"
path = "src/lib.rs"
[features]
default = ["http"]
[dependencies]
bytestring = ">=0.1.5, <2"
firestorm = "0.4"
http = { version = "0.2.3", optional = true }
log = "0.4"
regex = "1.5"
serde = "1"
[dev-dependencies]
criterion = { version = "0.3", features = ["html_reports"] }
firestorm = { version = "0.4", features = ["enable_system_time"] }
http = "0.2.3"
serde = { version = "1", features = ["derive"] }
[[bench]]
name = "router"
harness = false

View File

@ -1,194 +0,0 @@
//! Based on https://github.com/ibraheemdev/matchit/blob/master/benches/bench.rs
use criterion::{black_box, criterion_group, criterion_main, Criterion};
macro_rules! register {
(colon) => {{
register!(finish => ":p1", ":p2", ":p3", ":p4")
}};
(brackets) => {{
register!(finish => "{p1}", "{p2}", "{p3}", "{p4}")
}};
(regex) => {{
register!(finish => "(.*)", "(.*)", "(.*)", "(.*)")
}};
(finish => $p1:literal, $p2:literal, $p3:literal, $p4:literal) => {{
let arr = [
concat!("/authorizations"),
concat!("/authorizations/", $p1),
concat!("/applications/", $p1, "/tokens/", $p2),
concat!("/events"),
concat!("/repos/", $p1, "/", $p2, "/events"),
concat!("/networks/", $p1, "/", $p2, "/events"),
concat!("/orgs/", $p1, "/events"),
concat!("/users/", $p1, "/received_events"),
concat!("/users/", $p1, "/received_events/public"),
concat!("/users/", $p1, "/events"),
concat!("/users/", $p1, "/events/public"),
concat!("/users/", $p1, "/events/orgs/", $p2),
concat!("/feeds"),
concat!("/notifications"),
concat!("/repos/", $p1, "/", $p2, "/notifications"),
concat!("/notifications/threads/", $p1),
concat!("/notifications/threads/", $p1, "/subscription"),
concat!("/repos/", $p1, "/", $p2, "/stargazers"),
concat!("/users/", $p1, "/starred"),
concat!("/user/starred"),
concat!("/user/starred/", $p1, "/", $p2),
concat!("/repos/", $p1, "/", $p2, "/subscribers"),
concat!("/users/", $p1, "/subscriptions"),
concat!("/user/subscriptions"),
concat!("/repos/", $p1, "/", $p2, "/subscription"),
concat!("/user/subscriptions/", $p1, "/", $p2),
concat!("/users/", $p1, "/gists"),
concat!("/gists"),
concat!("/gists/", $p1),
concat!("/gists/", $p1, "/star"),
concat!("/repos/", $p1, "/", $p2, "/git/blobs/", $p3),
concat!("/repos/", $p1, "/", $p2, "/git/commits/", $p3),
concat!("/repos/", $p1, "/", $p2, "/git/refs"),
concat!("/repos/", $p1, "/", $p2, "/git/tags/", $p3),
concat!("/repos/", $p1, "/", $p2, "/git/trees/", $p3),
concat!("/issues"),
concat!("/user/issues"),
concat!("/orgs/", $p1, "/issues"),
concat!("/repos/", $p1, "/", $p2, "/issues"),
concat!("/repos/", $p1, "/", $p2, "/issues/", $p3),
concat!("/repos/", $p1, "/", $p2, "/assignees"),
concat!("/repos/", $p1, "/", $p2, "/assignees/", $p3),
concat!("/repos/", $p1, "/", $p2, "/issues/", $p3, "/comments"),
concat!("/repos/", $p1, "/", $p2, "/issues/", $p3, "/events"),
concat!("/repos/", $p1, "/", $p2, "/labels"),
concat!("/repos/", $p1, "/", $p2, "/labels/", $p3),
concat!("/repos/", $p1, "/", $p2, "/issues/", $p3, "/labels"),
concat!("/repos/", $p1, "/", $p2, "/milestones/", $p3, "/labels"),
concat!("/repos/", $p1, "/", $p2, "/milestones/"),
concat!("/repos/", $p1, "/", $p2, "/milestones/", $p3),
concat!("/emojis"),
concat!("/gitignore/templates"),
concat!("/gitignore/templates/", $p1),
concat!("/meta"),
concat!("/rate_limit"),
concat!("/users/", $p1, "/orgs"),
concat!("/user/orgs"),
concat!("/orgs/", $p1),
concat!("/orgs/", $p1, "/members"),
concat!("/orgs/", $p1, "/members", $p2),
concat!("/orgs/", $p1, "/public_members"),
concat!("/orgs/", $p1, "/public_members/", $p2),
concat!("/orgs/", $p1, "/teams"),
concat!("/teams/", $p1),
concat!("/teams/", $p1, "/members"),
concat!("/teams/", $p1, "/members", $p2),
concat!("/teams/", $p1, "/repos"),
concat!("/teams/", $p1, "/repos/", $p2, "/", $p3),
concat!("/user/teams"),
concat!("/repos/", $p1, "/", $p2, "/pulls"),
concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3),
concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3, "/commits"),
concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3, "/files"),
concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3, "/merge"),
concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3, "/comments"),
concat!("/user/repos"),
concat!("/users/", $p1, "/repos"),
concat!("/orgs/", $p1, "/repos"),
concat!("/repositories"),
concat!("/repos/", $p1, "/", $p2),
concat!("/repos/", $p1, "/", $p2, "/contributors"),
concat!("/repos/", $p1, "/", $p2, "/languages"),
concat!("/repos/", $p1, "/", $p2, "/teams"),
concat!("/repos/", $p1, "/", $p2, "/tags"),
concat!("/repos/", $p1, "/", $p2, "/branches"),
concat!("/repos/", $p1, "/", $p2, "/branches/", $p3),
concat!("/repos/", $p1, "/", $p2, "/collaborators"),
concat!("/repos/", $p1, "/", $p2, "/collaborators/", $p3),
concat!("/repos/", $p1, "/", $p2, "/comments"),
concat!("/repos/", $p1, "/", $p2, "/commits/", $p3, "/comments"),
concat!("/repos/", $p1, "/", $p2, "/commits"),
concat!("/repos/", $p1, "/", $p2, "/commits/", $p3),
concat!("/repos/", $p1, "/", $p2, "/readme"),
concat!("/repos/", $p1, "/", $p2, "/keys"),
concat!("/repos/", $p1, "/", $p2, "/keys", $p3),
concat!("/repos/", $p1, "/", $p2, "/downloads"),
concat!("/repos/", $p1, "/", $p2, "/downloads", $p3),
concat!("/repos/", $p1, "/", $p2, "/forks"),
concat!("/repos/", $p1, "/", $p2, "/hooks"),
concat!("/repos/", $p1, "/", $p2, "/hooks", $p3),
concat!("/repos/", $p1, "/", $p2, "/releases"),
concat!("/repos/", $p1, "/", $p2, "/releases/", $p3),
concat!("/repos/", $p1, "/", $p2, "/releases/", $p3, "/assets"),
concat!("/repos/", $p1, "/", $p2, "/stats/contributors"),
concat!("/repos/", $p1, "/", $p2, "/stats/commit_activity"),
concat!("/repos/", $p1, "/", $p2, "/stats/code_frequency"),
concat!("/repos/", $p1, "/", $p2, "/stats/participation"),
concat!("/repos/", $p1, "/", $p2, "/stats/punch_card"),
concat!("/repos/", $p1, "/", $p2, "/statuses/", $p3),
concat!("/search/repositories"),
concat!("/search/code"),
concat!("/search/issues"),
concat!("/search/users"),
concat!("/legacy/issues/search/", $p1, "/", $p2, "/", $p3, "/", $p4),
concat!("/legacy/repos/search/", $p1),
concat!("/legacy/user/search/", $p1),
concat!("/legacy/user/email/", $p1),
concat!("/users/", $p1),
concat!("/user"),
concat!("/users"),
concat!("/user/emails"),
concat!("/users/", $p1, "/followers"),
concat!("/user/followers"),
concat!("/users/", $p1, "/following"),
concat!("/user/following"),
concat!("/user/following/", $p1),
concat!("/users/", $p1, "/following", $p2),
concat!("/users/", $p1, "/keys"),
concat!("/user/keys"),
concat!("/user/keys/", $p1),
];
std::array::IntoIter::new(arr)
}};
}
fn call() -> impl Iterator<Item = &'static str> {
let arr = [
"/authorizations",
"/user/repos",
"/repos/rust-lang/rust/stargazers",
"/orgs/rust-lang/public_members/nikomatsakis",
"/repos/rust-lang/rust/releases/1.51.0",
];
std::array::IntoIter::new(arr)
}
fn compare_routers(c: &mut Criterion) {
let mut group = c.benchmark_group("Compare Routers");
let mut actix = actix_router::Router::<bool>::build();
for route in register!(brackets) {
actix.path(route, true);
}
let actix = actix.finish();
group.bench_function("actix", |b| {
b.iter(|| {
for route in call() {
let mut path = actix_router::Path::new(route);
black_box(actix.recognize(&mut path).unwrap());
}
});
});
let regex_set = regex::RegexSet::new(register!(regex)).unwrap();
group.bench_function("regex", |b| {
b.iter(|| {
for route in call() {
black_box(regex_set.matches(route));
}
});
});
group.finish();
}
criterion_group!(benches, compare_routers);
criterion_main!(benches);

View File

@ -1,169 +0,0 @@
macro_rules! register {
(brackets) => {{
register!(finish => "{p1}", "{p2}", "{p3}", "{p4}")
}};
(finish => $p1:literal, $p2:literal, $p3:literal, $p4:literal) => {{
let arr = [
concat!("/authorizations"),
concat!("/authorizations/", $p1),
concat!("/applications/", $p1, "/tokens/", $p2),
concat!("/events"),
concat!("/repos/", $p1, "/", $p2, "/events"),
concat!("/networks/", $p1, "/", $p2, "/events"),
concat!("/orgs/", $p1, "/events"),
concat!("/users/", $p1, "/received_events"),
concat!("/users/", $p1, "/received_events/public"),
concat!("/users/", $p1, "/events"),
concat!("/users/", $p1, "/events/public"),
concat!("/users/", $p1, "/events/orgs/", $p2),
concat!("/feeds"),
concat!("/notifications"),
concat!("/repos/", $p1, "/", $p2, "/notifications"),
concat!("/notifications/threads/", $p1),
concat!("/notifications/threads/", $p1, "/subscription"),
concat!("/repos/", $p1, "/", $p2, "/stargazers"),
concat!("/users/", $p1, "/starred"),
concat!("/user/starred"),
concat!("/user/starred/", $p1, "/", $p2),
concat!("/repos/", $p1, "/", $p2, "/subscribers"),
concat!("/users/", $p1, "/subscriptions"),
concat!("/user/subscriptions"),
concat!("/repos/", $p1, "/", $p2, "/subscription"),
concat!("/user/subscriptions/", $p1, "/", $p2),
concat!("/users/", $p1, "/gists"),
concat!("/gists"),
concat!("/gists/", $p1),
concat!("/gists/", $p1, "/star"),
concat!("/repos/", $p1, "/", $p2, "/git/blobs/", $p3),
concat!("/repos/", $p1, "/", $p2, "/git/commits/", $p3),
concat!("/repos/", $p1, "/", $p2, "/git/refs"),
concat!("/repos/", $p1, "/", $p2, "/git/tags/", $p3),
concat!("/repos/", $p1, "/", $p2, "/git/trees/", $p3),
concat!("/issues"),
concat!("/user/issues"),
concat!("/orgs/", $p1, "/issues"),
concat!("/repos/", $p1, "/", $p2, "/issues"),
concat!("/repos/", $p1, "/", $p2, "/issues/", $p3),
concat!("/repos/", $p1, "/", $p2, "/assignees"),
concat!("/repos/", $p1, "/", $p2, "/assignees/", $p3),
concat!("/repos/", $p1, "/", $p2, "/issues/", $p3, "/comments"),
concat!("/repos/", $p1, "/", $p2, "/issues/", $p3, "/events"),
concat!("/repos/", $p1, "/", $p2, "/labels"),
concat!("/repos/", $p1, "/", $p2, "/labels/", $p3),
concat!("/repos/", $p1, "/", $p2, "/issues/", $p3, "/labels"),
concat!("/repos/", $p1, "/", $p2, "/milestones/", $p3, "/labels"),
concat!("/repos/", $p1, "/", $p2, "/milestones/"),
concat!("/repos/", $p1, "/", $p2, "/milestones/", $p3),
concat!("/emojis"),
concat!("/gitignore/templates"),
concat!("/gitignore/templates/", $p1),
concat!("/meta"),
concat!("/rate_limit"),
concat!("/users/", $p1, "/orgs"),
concat!("/user/orgs"),
concat!("/orgs/", $p1),
concat!("/orgs/", $p1, "/members"),
concat!("/orgs/", $p1, "/members", $p2),
concat!("/orgs/", $p1, "/public_members"),
concat!("/orgs/", $p1, "/public_members/", $p2),
concat!("/orgs/", $p1, "/teams"),
concat!("/teams/", $p1),
concat!("/teams/", $p1, "/members"),
concat!("/teams/", $p1, "/members", $p2),
concat!("/teams/", $p1, "/repos"),
concat!("/teams/", $p1, "/repos/", $p2, "/", $p3),
concat!("/user/teams"),
concat!("/repos/", $p1, "/", $p2, "/pulls"),
concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3),
concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3, "/commits"),
concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3, "/files"),
concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3, "/merge"),
concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3, "/comments"),
concat!("/user/repos"),
concat!("/users/", $p1, "/repos"),
concat!("/orgs/", $p1, "/repos"),
concat!("/repositories"),
concat!("/repos/", $p1, "/", $p2),
concat!("/repos/", $p1, "/", $p2, "/contributors"),
concat!("/repos/", $p1, "/", $p2, "/languages"),
concat!("/repos/", $p1, "/", $p2, "/teams"),
concat!("/repos/", $p1, "/", $p2, "/tags"),
concat!("/repos/", $p1, "/", $p2, "/branches"),
concat!("/repos/", $p1, "/", $p2, "/branches/", $p3),
concat!("/repos/", $p1, "/", $p2, "/collaborators"),
concat!("/repos/", $p1, "/", $p2, "/collaborators/", $p3),
concat!("/repos/", $p1, "/", $p2, "/comments"),
concat!("/repos/", $p1, "/", $p2, "/commits/", $p3, "/comments"),
concat!("/repos/", $p1, "/", $p2, "/commits"),
concat!("/repos/", $p1, "/", $p2, "/commits/", $p3),
concat!("/repos/", $p1, "/", $p2, "/readme"),
concat!("/repos/", $p1, "/", $p2, "/keys"),
concat!("/repos/", $p1, "/", $p2, "/keys", $p3),
concat!("/repos/", $p1, "/", $p2, "/downloads"),
concat!("/repos/", $p1, "/", $p2, "/downloads", $p3),
concat!("/repos/", $p1, "/", $p2, "/forks"),
concat!("/repos/", $p1, "/", $p2, "/hooks"),
concat!("/repos/", $p1, "/", $p2, "/hooks", $p3),
concat!("/repos/", $p1, "/", $p2, "/releases"),
concat!("/repos/", $p1, "/", $p2, "/releases/", $p3),
concat!("/repos/", $p1, "/", $p2, "/releases/", $p3, "/assets"),
concat!("/repos/", $p1, "/", $p2, "/stats/contributors"),
concat!("/repos/", $p1, "/", $p2, "/stats/commit_activity"),
concat!("/repos/", $p1, "/", $p2, "/stats/code_frequency"),
concat!("/repos/", $p1, "/", $p2, "/stats/participation"),
concat!("/repos/", $p1, "/", $p2, "/stats/punch_card"),
concat!("/repos/", $p1, "/", $p2, "/statuses/", $p3),
concat!("/search/repositories"),
concat!("/search/code"),
concat!("/search/issues"),
concat!("/search/users"),
concat!("/legacy/issues/search/", $p1, "/", $p2, "/", $p3, "/", $p4),
concat!("/legacy/repos/search/", $p1),
concat!("/legacy/user/search/", $p1),
concat!("/legacy/user/email/", $p1),
concat!("/users/", $p1),
concat!("/user"),
concat!("/users"),
concat!("/user/emails"),
concat!("/users/", $p1, "/followers"),
concat!("/user/followers"),
concat!("/users/", $p1, "/following"),
concat!("/user/following"),
concat!("/user/following/", $p1),
concat!("/users/", $p1, "/following", $p2),
concat!("/users/", $p1, "/keys"),
concat!("/user/keys"),
concat!("/user/keys/", $p1),
];
arr.to_vec()
}};
}
static PATHS: [&str; 5] = [
"/authorizations",
"/user/repos",
"/repos/rust-lang/rust/stargazers",
"/orgs/rust-lang/public_members/nikomatsakis",
"/repos/rust-lang/rust/releases/1.51.0",
];
fn main() {
let mut router = actix_router::Router::<bool>::build();
for route in register!(brackets) {
router.path(route, true);
}
let actix = router.finish();
if firestorm::enabled() {
firestorm::bench("target", || {
for &route in &PATHS {
let mut path = actix_router::Path::new(route);
actix.recognize(&mut path).unwrap();
}
})
.unwrap();
}
}

View File

@ -1,723 +0,0 @@
use serde::de::{self, Deserializer, Error as DeError, Visitor};
use serde::forward_to_deserialize_any;
use crate::path::{Path, PathIter};
use crate::ResourcePath;
macro_rules! unsupported_type {
($trait_fn:ident, $name:expr) => {
fn $trait_fn<V>(self, _: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
Err(de::value::Error::custom(concat!(
"unsupported type: ",
$name
)))
}
};
}
macro_rules! parse_single_value {
($trait_fn:ident, $visit_fn:ident, $tp:tt) => {
fn $trait_fn<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
if self.path.segment_count() != 1 {
Err(de::value::Error::custom(
format!(
"wrong number of parameters: {} expected 1",
self.path.segment_count()
)
.as_str(),
))
} else {
let v = self.path[0].parse().map_err(|_| {
de::value::Error::custom(format!(
"can not parse {:?} to a {}",
&self.path[0], $tp
))
})?;
visitor.$visit_fn(v)
}
}
};
}
pub struct PathDeserializer<'de, T: ResourcePath> {
path: &'de Path<T>,
}
impl<'de, T: ResourcePath + 'de> PathDeserializer<'de, T> {
pub fn new(path: &'de Path<T>) -> Self {
PathDeserializer { path }
}
}
impl<'de, T: ResourcePath + 'de> Deserializer<'de> for PathDeserializer<'de, T> {
type Error = de::value::Error;
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_map(ParamsDeserializer {
params: self.path.iter(),
current: None,
})
}
fn deserialize_struct<V>(
self,
_: &'static str,
_: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_map(visitor)
}
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_unit()
}
fn deserialize_unit_struct<V>(
self,
_: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_unit(visitor)
}
fn deserialize_newtype_struct<V>(
self,
_: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_newtype_struct(self)
}
fn deserialize_tuple<V>(self, len: usize, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
if self.path.segment_count() < len {
Err(de::value::Error::custom(
format!(
"wrong number of parameters: {} expected {}",
self.path.segment_count(),
len
)
.as_str(),
))
} else {
visitor.visit_seq(ParamsSeq {
params: self.path.iter(),
})
}
}
fn deserialize_tuple_struct<V>(
self,
_: &'static str,
len: usize,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
if self.path.segment_count() < len {
Err(de::value::Error::custom(
format!(
"wrong number of parameters: {} expected {}",
self.path.segment_count(),
len
)
.as_str(),
))
} else {
visitor.visit_seq(ParamsSeq {
params: self.path.iter(),
})
}
}
fn deserialize_enum<V>(
self,
_: &'static str,
_: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
if self.path.is_empty() {
Err(de::value::Error::custom("expected at least one parameters"))
} else {
visitor.visit_enum(ValueEnum {
value: &self.path[0],
})
}
}
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
if self.path.segment_count() != 1 {
Err(de::value::Error::custom(
format!(
"wrong number of parameters: {} expected 1",
self.path.segment_count()
)
.as_str(),
))
} else {
visitor.visit_str(&self.path[0])
}
}
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_seq(ParamsSeq {
params: self.path.iter(),
})
}
unsupported_type!(deserialize_any, "'any'");
unsupported_type!(deserialize_bytes, "bytes");
unsupported_type!(deserialize_option, "Option<T>");
unsupported_type!(deserialize_identifier, "identifier");
unsupported_type!(deserialize_ignored_any, "ignored_any");
parse_single_value!(deserialize_bool, visit_bool, "bool");
parse_single_value!(deserialize_i8, visit_i8, "i8");
parse_single_value!(deserialize_i16, visit_i16, "i16");
parse_single_value!(deserialize_i32, visit_i32, "i32");
parse_single_value!(deserialize_i64, visit_i64, "i64");
parse_single_value!(deserialize_u8, visit_u8, "u8");
parse_single_value!(deserialize_u16, visit_u16, "u16");
parse_single_value!(deserialize_u32, visit_u32, "u32");
parse_single_value!(deserialize_u64, visit_u64, "u64");
parse_single_value!(deserialize_f32, visit_f32, "f32");
parse_single_value!(deserialize_f64, visit_f64, "f64");
parse_single_value!(deserialize_string, visit_string, "String");
parse_single_value!(deserialize_byte_buf, visit_string, "String");
parse_single_value!(deserialize_char, visit_char, "char");
}
struct ParamsDeserializer<'de, T: ResourcePath> {
params: PathIter<'de, T>,
current: Option<(&'de str, &'de str)>,
}
impl<'de, T: ResourcePath> de::MapAccess<'de> for ParamsDeserializer<'de, T> {
type Error = de::value::Error;
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
where
K: de::DeserializeSeed<'de>,
{
self.current = self.params.next().map(|ref item| (item.0, item.1));
match self.current {
Some((key, _)) => Ok(Some(seed.deserialize(Key { key })?)),
None => Ok(None),
}
}
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
where
V: de::DeserializeSeed<'de>,
{
if let Some((_, value)) = self.current.take() {
seed.deserialize(Value { value })
} else {
Err(de::value::Error::custom("unexpected item"))
}
}
}
struct Key<'de> {
key: &'de str,
}
impl<'de> Deserializer<'de> for Key<'de> {
type Error = de::value::Error;
fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_str(self.key)
}
fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
Err(de::value::Error::custom("Unexpected"))
}
forward_to_deserialize_any! {
bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes
byte_buf option unit unit_struct newtype_struct seq tuple
tuple_struct map struct enum ignored_any
}
}
macro_rules! parse_value {
($trait_fn:ident, $visit_fn:ident, $tp:tt) => {
fn $trait_fn<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
let v = self.value.parse().map_err(|_| {
de::value::Error::custom(format!("can not parse {:?} to a {}", self.value, $tp))
})?;
visitor.$visit_fn(v)
}
};
}
struct Value<'de> {
value: &'de str,
}
impl<'de> Deserializer<'de> for Value<'de> {
type Error = de::value::Error;
parse_value!(deserialize_bool, visit_bool, "bool");
parse_value!(deserialize_i8, visit_i8, "i8");
parse_value!(deserialize_i16, visit_i16, "i16");
parse_value!(deserialize_i32, visit_i32, "i16");
parse_value!(deserialize_i64, visit_i64, "i64");
parse_value!(deserialize_u8, visit_u8, "u8");
parse_value!(deserialize_u16, visit_u16, "u16");
parse_value!(deserialize_u32, visit_u32, "u32");
parse_value!(deserialize_u64, visit_u64, "u64");
parse_value!(deserialize_f32, visit_f32, "f32");
parse_value!(deserialize_f64, visit_f64, "f64");
parse_value!(deserialize_string, visit_string, "String");
parse_value!(deserialize_byte_buf, visit_string, "String");
parse_value!(deserialize_char, visit_char, "char");
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_unit()
}
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_unit()
}
fn deserialize_unit_struct<V>(
self,
_: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_unit()
}
fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_borrowed_bytes(self.value.as_bytes())
}
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_borrowed_str(self.value)
}
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_some(self)
}
fn deserialize_enum<V>(
self,
_: &'static str,
_: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_enum(ValueEnum { value: self.value })
}
fn deserialize_newtype_struct<V>(
self,
_: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_newtype_struct(self)
}
fn deserialize_tuple<V>(self, _: usize, _: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
Err(de::value::Error::custom("unsupported type: tuple"))
}
fn deserialize_struct<V>(
self,
_: &'static str,
_: &'static [&'static str],
_: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
Err(de::value::Error::custom("unsupported type: struct"))
}
fn deserialize_tuple_struct<V>(
self,
_: &'static str,
_: usize,
_: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
Err(de::value::Error::custom("unsupported type: tuple struct"))
}
unsupported_type!(deserialize_any, "any");
unsupported_type!(deserialize_seq, "seq");
unsupported_type!(deserialize_map, "map");
unsupported_type!(deserialize_identifier, "identifier");
}
struct ParamsSeq<'de, T: ResourcePath> {
params: PathIter<'de, T>,
}
impl<'de, T: ResourcePath> de::SeqAccess<'de> for ParamsSeq<'de, T> {
type Error = de::value::Error;
fn next_element_seed<U>(&mut self, seed: U) -> Result<Option<U::Value>, Self::Error>
where
U: de::DeserializeSeed<'de>,
{
match self.params.next() {
Some(item) => Ok(Some(seed.deserialize(Value { value: item.1 })?)),
None => Ok(None),
}
}
}
struct ValueEnum<'de> {
value: &'de str,
}
impl<'de> de::EnumAccess<'de> for ValueEnum<'de> {
type Error = de::value::Error;
type Variant = UnitVariant;
fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>
where
V: de::DeserializeSeed<'de>,
{
Ok((seed.deserialize(Key { key: self.value })?, UnitVariant))
}
}
struct UnitVariant;
impl<'de> de::VariantAccess<'de> for UnitVariant {
type Error = de::value::Error;
fn unit_variant(self) -> Result<(), Self::Error> {
Ok(())
}
fn newtype_variant_seed<T>(self, _seed: T) -> Result<T::Value, Self::Error>
where
T: de::DeserializeSeed<'de>,
{
Err(de::value::Error::custom("not supported"))
}
fn tuple_variant<V>(self, _len: usize, _visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
Err(de::value::Error::custom("not supported"))
}
fn struct_variant<V>(
self,
_: &'static [&'static str],
_: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
Err(de::value::Error::custom("not supported"))
}
}
#[cfg(test)]
mod tests {
use serde::{de, Deserialize};
use super::*;
use crate::path::Path;
use crate::router::Router;
#[derive(Deserialize)]
struct MyStruct {
key: String,
value: String,
}
#[derive(Deserialize)]
struct Id {
_id: String,
}
#[derive(Debug, Deserialize)]
struct Test1(String, u32);
#[derive(Debug, Deserialize)]
struct Test2 {
key: String,
value: u32,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
enum TestEnum {
Val1,
Val2,
}
#[derive(Debug, Deserialize)]
struct Test3 {
val: TestEnum,
}
#[test]
fn test_request_extract() {
let mut router = Router::<()>::build();
router.path("/{key}/{value}/", ());
let router = router.finish();
let mut path = Path::new("/name/user1/");
assert!(router.recognize(&mut path).is_some());
let s: MyStruct = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();
assert_eq!(s.key, "name");
assert_eq!(s.value, "user1");
let s: (String, String) =
de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();
assert_eq!(s.0, "name");
assert_eq!(s.1, "user1");
let mut router = Router::<()>::build();
router.path("/{key}/{value}/", ());
let router = router.finish();
let mut path = Path::new("/name/32/");
assert!(router.recognize(&mut path).is_some());
let s: Test1 = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();
assert_eq!(s.0, "name");
assert_eq!(s.1, 32);
let s: Test2 = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();
assert_eq!(s.key, "name");
assert_eq!(s.value, 32);
let s: (String, u8) =
de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();
assert_eq!(s.0, "name");
assert_eq!(s.1, 32);
let res: Vec<String> =
de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();
assert_eq!(res[0], "name".to_owned());
assert_eq!(res[1], "32".to_owned());
}
#[test]
fn test_extract_path_single() {
let mut router = Router::<()>::build();
router.path("/{value}/", ());
let router = router.finish();
let mut path = Path::new("/32/");
assert!(router.recognize(&mut path).is_some());
let i: i8 = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();
assert_eq!(i, 32);
}
#[test]
fn test_extract_enum() {
let mut router = Router::<()>::build();
router.path("/{val}/", ());
let router = router.finish();
let mut path = Path::new("/val1/");
assert!(router.recognize(&mut path).is_some());
let i: TestEnum = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();
assert_eq!(i, TestEnum::Val1);
let mut router = Router::<()>::build();
router.path("/{val1}/{val2}/", ());
let router = router.finish();
let mut path = Path::new("/val1/val2/");
assert!(router.recognize(&mut path).is_some());
let i: (TestEnum, TestEnum) =
de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();
assert_eq!(i, (TestEnum::Val1, TestEnum::Val2));
}
#[test]
fn test_extract_enum_value() {
let mut router = Router::<()>::build();
router.path("/{val}/", ());
let router = router.finish();
let mut path = Path::new("/val1/");
assert!(router.recognize(&mut path).is_some());
let i: Test3 = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();
assert_eq!(i.val, TestEnum::Val1);
let mut path = Path::new("/val3/");
assert!(router.recognize(&mut path).is_some());
let i: Result<Test3, de::value::Error> =
de::Deserialize::deserialize(PathDeserializer::new(&path));
assert!(i.is_err());
assert!(format!("{:?}", i).contains("unknown variant"));
}
#[test]
fn test_extract_errors() {
let mut router = Router::<()>::build();
router.path("/{value}/", ());
let router = router.finish();
let mut path = Path::new("/name/");
assert!(router.recognize(&mut path).is_some());
let s: Result<Test1, de::value::Error> =
de::Deserialize::deserialize(PathDeserializer::new(&path));
assert!(s.is_err());
assert!(format!("{:?}", s).contains("wrong number of parameters"));
let s: Result<Test2, de::value::Error> =
de::Deserialize::deserialize(PathDeserializer::new(&path));
assert!(s.is_err());
assert!(format!("{:?}", s).contains("can not parse"));
let s: Result<(String, String), de::value::Error> =
de::Deserialize::deserialize(PathDeserializer::new(&path));
assert!(s.is_err());
assert!(format!("{:?}", s).contains("wrong number of parameters"));
let s: Result<u32, de::value::Error> =
de::Deserialize::deserialize(PathDeserializer::new(&path));
assert!(s.is_err());
assert!(format!("{:?}", s).contains("can not parse"));
}
// #[test]
// fn test_extract_path_decode() {
// let mut router = Router::<()>::default();
// router.register_resource(Resource::new(ResourceDef::new("/{value}/")));
// macro_rules! test_single_value {
// ($value:expr, $expected:expr) => {{
// let req = TestRequest::with_uri($value).finish();
// let info = router.recognize(&req, &(), 0);
// let req = req.with_route_info(info);
// assert_eq!(
// *Path::<String>::from_request(&req, &PathConfig::default()).unwrap(),
// $expected
// );
// }};
// }
// test_single_value!("/%25/", "%");
// test_single_value!("/%40%C2%A3%24%25%5E%26%2B%3D/", "@£$%^&+=");
// test_single_value!("/%2B/", "+");
// test_single_value!("/%252B/", "%2B");
// test_single_value!("/%2F/", "/");
// test_single_value!("/%252F/", "%2F");
// test_single_value!(
// "/http%3A%2F%2Flocalhost%3A80%2Ffoo/",
// "http://localhost:80/foo"
// );
// test_single_value!("/%2Fvar%2Flog%2Fsyslog/", "/var/log/syslog");
// test_single_value!(
// "/http%3A%2F%2Flocalhost%3A80%2Ffile%2F%252Fvar%252Flog%252Fsyslog/",
// "http://localhost:80/file/%2Fvar%2Flog%2Fsyslog"
// );
// let req = TestRequest::with_uri("/%25/7/?id=test").finish();
// let mut router = Router::<()>::default();
// router.register_resource(Resource::new(ResourceDef::new("/{key}/{value}/")));
// let info = router.recognize(&req, &(), 0);
// let req = req.with_route_info(info);
// let s = Path::<Test2>::from_request(&req, &PathConfig::default()).unwrap();
// assert_eq!(s.key, "%");
// assert_eq!(s.value, 7);
// let s = Path::<(String, String)>::from_request(&req, &PathConfig::default()).unwrap();
// assert_eq!(s.0, "%");
// assert_eq!(s.1, "7");
// }
// #[test]
// fn test_extract_path_no_decode() {
// let mut router = Router::<()>::default();
// router.register_resource(Resource::new(ResourceDef::new("/{value}/")));
// let req = TestRequest::with_uri("/%25/").finish();
// let info = router.recognize(&req, &(), 0);
// let req = req.with_route_info(info);
// assert_eq!(
// *Path::<String>::from_request(&req, &&PathConfig::default().disable_decoding())
// .unwrap(),
// "%25"
// );
// }
}

View File

@ -1,149 +0,0 @@
//! Resource path matching and router.
#![deny(rust_2018_idioms, nonstandard_style)]
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
mod de;
mod path;
mod resource;
mod router;
pub use self::de::PathDeserializer;
pub use self::path::Path;
pub use self::resource::ResourceDef;
pub use self::router::{ResourceInfo, Router, RouterBuilder};
// TODO: this trait is necessary, document it
// see impl Resource for ServiceRequest
pub trait Resource<T: ResourcePath> {
fn resource_path(&mut self) -> &mut Path<T>;
}
pub trait ResourcePath {
fn path(&self) -> &str;
}
impl ResourcePath for String {
fn path(&self) -> &str {
self.as_str()
}
}
impl<'a> ResourcePath for &'a str {
fn path(&self) -> &str {
self
}
}
impl ResourcePath for bytestring::ByteString {
fn path(&self) -> &str {
&*self
}
}
/// One or many patterns.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Patterns {
Single(String),
List(Vec<String>),
}
impl Patterns {
pub fn is_empty(&self) -> bool {
match self {
Patterns::Single(_) => false,
Patterns::List(pats) => pats.is_empty(),
}
}
}
/// Helper trait for type that could be converted to one or more path pattern.
pub trait IntoPatterns {
fn patterns(&self) -> Patterns;
}
impl IntoPatterns for String {
fn patterns(&self) -> Patterns {
Patterns::Single(self.clone())
}
}
impl<'a> IntoPatterns for &'a String {
fn patterns(&self) -> Patterns {
Patterns::Single((*self).clone())
}
}
impl<'a> IntoPatterns for &'a str {
fn patterns(&self) -> Patterns {
Patterns::Single((*self).to_owned())
}
}
impl IntoPatterns for bytestring::ByteString {
fn patterns(&self) -> Patterns {
Patterns::Single(self.to_string())
}
}
impl IntoPatterns for Patterns {
fn patterns(&self) -> Patterns {
self.clone()
}
}
impl<T: AsRef<str>> IntoPatterns for Vec<T> {
fn patterns(&self) -> Patterns {
let mut patterns = self.iter().map(|v| v.as_ref().to_owned());
match patterns.size_hint() {
(1, _) => Patterns::Single(patterns.next().unwrap()),
_ => Patterns::List(patterns.collect()),
}
}
}
macro_rules! array_patterns_single (($tp:ty) => {
impl IntoPatterns for [$tp; 1] {
fn patterns(&self) -> Patterns {
Patterns::Single(self[0].to_owned())
}
}
});
macro_rules! array_patterns_multiple (($tp:ty, $str_fn:expr, $($num:tt) +) => {
// for each array length specified in $num
$(
impl IntoPatterns for [$tp; $num] {
fn patterns(&self) -> Patterns {
Patterns::List(self.iter().map($str_fn).collect())
}
}
)+
});
array_patterns_single!(&str);
array_patterns_multiple!(&str, |&v| v.to_owned(), 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16);
array_patterns_single!(String);
array_patterns_multiple!(String, |v| v.clone(), 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16);
#[cfg(feature = "http")]
mod url;
#[cfg(feature = "http")]
pub use self::url::{Quoter, Url};
#[cfg(feature = "http")]
mod http_impls {
use http::Uri;
use super::ResourcePath;
impl ResourcePath for Uri {
fn path(&self) -> &str {
self.path()
}
}
}

View File

@ -1,220 +0,0 @@
use std::borrow::Cow;
use std::ops::Index;
use firestorm::profile_method;
use serde::de;
use crate::{de::PathDeserializer, Resource, ResourcePath};
#[derive(Debug, Clone)]
pub(crate) enum PathItem {
Static(Cow<'static, str>),
Segment(u16, u16),
}
impl Default for PathItem {
fn default() -> Self {
Self::Static(Cow::Borrowed(""))
}
}
/// Resource path match information.
///
/// If resource path contains variable patterns, `Path` stores them.
#[derive(Debug, Clone, Default)]
pub struct Path<T> {
path: T,
pub(crate) skip: u16,
pub(crate) segments: Vec<(Cow<'static, str>, PathItem)>,
}
impl<T: ResourcePath> Path<T> {
pub fn new(path: T) -> Path<T> {
Path {
path,
skip: 0,
segments: Vec::new(),
}
}
/// Get reference to inner path instance.
#[inline]
pub fn get_ref(&self) -> &T {
&self.path
}
/// Get mutable reference to inner path instance.
#[inline]
pub fn get_mut(&mut self) -> &mut T {
&mut self.path
}
/// Path.
#[inline]
pub fn path(&self) -> &str {
profile_method!(path);
let skip = self.skip as usize;
let path = self.path.path();
if skip <= path.len() {
&path[skip..]
} else {
""
}
}
/// Set new path.
#[inline]
pub fn set(&mut self, path: T) {
self.skip = 0;
self.path = path;
self.segments.clear();
}
/// Reset state.
#[inline]
pub fn reset(&mut self) {
self.skip = 0;
self.segments.clear();
}
/// Skip first `n` chars in path.
#[inline]
pub fn skip(&mut self, n: u16) {
self.skip += n;
}
pub(crate) fn add(&mut self, name: impl Into<Cow<'static, str>>, value: PathItem) {
profile_method!(add);
match value {
PathItem::Static(s) => self.segments.push((name.into(), PathItem::Static(s))),
PathItem::Segment(begin, end) => self.segments.push((
name.into(),
PathItem::Segment(self.skip + begin, self.skip + end),
)),
}
}
#[doc(hidden)]
pub fn add_static(
&mut self,
name: impl Into<Cow<'static, str>>,
value: impl Into<Cow<'static, str>>,
) {
self.segments
.push((name.into(), PathItem::Static(value.into())));
}
/// Check if there are any matched patterns.
#[inline]
pub fn is_empty(&self) -> bool {
self.segments.is_empty()
}
/// Returns number of interpolated segments.
#[inline]
pub fn segment_count(&self) -> usize {
self.segments.len()
}
/// Get matched parameter by name without type conversion
pub fn get(&self, name: &str) -> Option<&str> {
profile_method!(get);
for (seg_name, val) in self.segments.iter() {
if name == seg_name {
return match val {
PathItem::Static(ref s) => Some(&s),
PathItem::Segment(s, e) => {
Some(&self.path.path()[(*s as usize)..(*e as usize)])
}
};
}
}
None
}
/// Get unprocessed part of the path
pub fn unprocessed(&self) -> &str {
&self.path.path()[(self.skip as usize)..]
}
/// Get matched parameter by name.
///
/// If keyed parameter is not available empty string is used as default value.
pub fn query(&self, key: &str) -> &str {
profile_method!(query);
if let Some(s) = self.get(key) {
s
} else {
""
}
}
/// Return iterator to items in parameter container.
pub fn iter(&self) -> PathIter<'_, T> {
PathIter {
idx: 0,
params: self,
}
}
/// Try to deserialize matching parameters to a specified type `U`
pub fn load<'de, U: serde::Deserialize<'de>>(&'de self) -> Result<U, de::value::Error> {
profile_method!(load);
de::Deserialize::deserialize(PathDeserializer::new(self))
}
}
#[derive(Debug)]
pub struct PathIter<'a, T> {
idx: usize,
params: &'a Path<T>,
}
impl<'a, T: ResourcePath> Iterator for PathIter<'a, T> {
type Item = (&'a str, &'a str);
#[inline]
fn next(&mut self) -> Option<(&'a str, &'a str)> {
if self.idx < self.params.segment_count() {
let idx = self.idx;
let res = match self.params.segments[idx].1 {
PathItem::Static(ref s) => &s,
PathItem::Segment(s, e) => &self.params.path.path()[(s as usize)..(e as usize)],
};
self.idx += 1;
return Some((&self.params.segments[idx].0, res));
}
None
}
}
impl<'a, T: ResourcePath> Index<&'a str> for Path<T> {
type Output = str;
fn index(&self, name: &'a str) -> &str {
self.get(name)
.expect("Value for parameter is not available")
}
}
impl<T: ResourcePath> Index<usize> for Path<T> {
type Output = str;
fn index(&self, idx: usize) -> &str {
match self.segments[idx].1 {
PathItem::Static(ref s) => &s,
PathItem::Segment(s, e) => &self.path.path()[(s as usize)..(e as usize)],
}
}
}
impl<T: ResourcePath> Resource<T> for Path<T> {
fn resource_path(&mut self) -> &mut Self {
self
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,281 +0,0 @@
use firestorm::profile_method;
use crate::{IntoPatterns, Resource, ResourceDef, ResourcePath};
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct ResourceId(pub u16);
/// Information about current resource
#[derive(Clone, Debug)]
pub struct ResourceInfo {
resource: ResourceId,
}
/// Resource router.
// T is the resource itself
// U is any other data needed for routing like method guards
pub struct Router<T, U = ()> {
routes: Vec<(ResourceDef, T, Option<U>)>,
}
impl<T, U> Router<T, U> {
pub fn build() -> RouterBuilder<T, U> {
RouterBuilder {
resources: Vec::new(),
}
}
pub fn recognize<R, P>(&self, resource: &mut R) -> Option<(&T, ResourceId)>
where
R: Resource<P>,
P: ResourcePath,
{
profile_method!(recognize);
for item in self.routes.iter() {
if item.0.capture_match_info(resource.resource_path()) {
return Some((&item.1, ResourceId(item.0.id())));
}
}
None
}
pub fn recognize_mut<R, P>(&mut self, resource: &mut R) -> Option<(&mut T, ResourceId)>
where
R: Resource<P>,
P: ResourcePath,
{
profile_method!(recognize_mut);
for item in self.routes.iter_mut() {
if item.0.capture_match_info(resource.resource_path()) {
return Some((&mut item.1, ResourceId(item.0.id())));
}
}
None
}
pub fn recognize_fn<R, P, F>(&self, resource: &mut R, check: F) -> Option<(&T, ResourceId)>
where
F: Fn(&R, &Option<U>) -> bool,
R: Resource<P>,
P: ResourcePath,
{
profile_method!(recognize_checked);
for item in self.routes.iter() {
if item.0.capture_match_info_fn(resource, &check, &item.2) {
return Some((&item.1, ResourceId(item.0.id())));
}
}
None
}
pub fn recognize_mut_fn<R, P, F>(
&mut self,
resource: &mut R,
check: F,
) -> Option<(&mut T, ResourceId)>
where
F: Fn(&R, &Option<U>) -> bool,
R: Resource<P>,
P: ResourcePath,
{
profile_method!(recognize_mut_checked);
for item in self.routes.iter_mut() {
if item.0.capture_match_info_fn(resource, &check, &item.2) {
return Some((&mut item.1, ResourceId(item.0.id())));
}
}
None
}
}
pub struct RouterBuilder<T, U = ()> {
resources: Vec<(ResourceDef, T, Option<U>)>,
}
impl<T, U> RouterBuilder<T, U> {
/// Register resource for specified path.
pub fn path<P: IntoPatterns>(
&mut self,
path: P,
resource: T,
) -> &mut (ResourceDef, T, Option<U>) {
profile_method!(path);
self.resources
.push((ResourceDef::new(path), resource, None));
self.resources.last_mut().unwrap()
}
/// Register resource for specified path prefix.
pub fn prefix(&mut self, prefix: &str, resource: T) -> &mut (ResourceDef, T, Option<U>) {
profile_method!(prefix);
self.resources
.push((ResourceDef::prefix(prefix), resource, None));
self.resources.last_mut().unwrap()
}
/// Register resource for ResourceDef
pub fn rdef(&mut self, rdef: ResourceDef, resource: T) -> &mut (ResourceDef, T, Option<U>) {
profile_method!(rdef);
self.resources.push((rdef, resource, None));
self.resources.last_mut().unwrap()
}
/// Finish configuration and create router instance.
pub fn finish(self) -> Router<T, U> {
Router {
routes: self.resources,
}
}
}
#[cfg(test)]
mod tests {
use crate::path::Path;
use crate::router::{ResourceId, Router};
#[allow(clippy::cognitive_complexity)]
#[test]
fn test_recognizer_1() {
let mut router = Router::<usize>::build();
router.path("/name", 10).0.set_id(0);
router.path("/name/{val}", 11).0.set_id(1);
router.path("/name/{val}/index.html", 12).0.set_id(2);
router.path("/file/{file}.{ext}", 13).0.set_id(3);
router.path("/v{val}/{val2}/index.html", 14).0.set_id(4);
router.path("/v/{tail:.*}", 15).0.set_id(5);
router.path("/test2/{test}.html", 16).0.set_id(6);
router.path("/{test}/index.html", 17).0.set_id(7);
let mut router = router.finish();
let mut path = Path::new("/unknown");
assert!(router.recognize_mut(&mut path).is_none());
let mut path = Path::new("/name");
let (h, info) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 10);
assert_eq!(info, ResourceId(0));
assert!(path.is_empty());
let mut path = Path::new("/name/value");
let (h, info) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 11);
assert_eq!(info, ResourceId(1));
assert_eq!(path.get("val").unwrap(), "value");
assert_eq!(&path["val"], "value");
let mut path = Path::new("/name/value2/index.html");
let (h, info) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 12);
assert_eq!(info, ResourceId(2));
assert_eq!(path.get("val").unwrap(), "value2");
let mut path = Path::new("/file/file.gz");
let (h, info) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 13);
assert_eq!(info, ResourceId(3));
assert_eq!(path.get("file").unwrap(), "file");
assert_eq!(path.get("ext").unwrap(), "gz");
let mut path = Path::new("/vtest/ttt/index.html");
let (h, info) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 14);
assert_eq!(info, ResourceId(4));
assert_eq!(path.get("val").unwrap(), "test");
assert_eq!(path.get("val2").unwrap(), "ttt");
let mut path = Path::new("/v/blah-blah/index.html");
let (h, info) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 15);
assert_eq!(info, ResourceId(5));
assert_eq!(path.get("tail").unwrap(), "blah-blah/index.html");
let mut path = Path::new("/test2/index.html");
let (h, info) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 16);
assert_eq!(info, ResourceId(6));
assert_eq!(path.get("test").unwrap(), "index");
let mut path = Path::new("/bbb/index.html");
let (h, info) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 17);
assert_eq!(info, ResourceId(7));
assert_eq!(path.get("test").unwrap(), "bbb");
}
#[test]
fn test_recognizer_2() {
let mut router = Router::<usize>::build();
router.path("/index.json", 10);
router.path("/{source}.json", 11);
let mut router = router.finish();
let mut path = Path::new("/index.json");
let (h, _) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 10);
let mut path = Path::new("/test.json");
let (h, _) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 11);
}
#[test]
fn test_recognizer_with_prefix() {
let mut router = Router::<usize>::build();
router.path("/name", 10).0.set_id(0);
router.path("/name/{val}", 11).0.set_id(1);
let mut router = router.finish();
let mut path = Path::new("/name");
path.skip(5);
assert!(router.recognize_mut(&mut path).is_none());
let mut path = Path::new("/test/name");
path.skip(5);
let (h, _) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 10);
let mut path = Path::new("/test/name/value");
path.skip(5);
let (h, id) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 11);
assert_eq!(id, ResourceId(1));
assert_eq!(path.get("val").unwrap(), "value");
assert_eq!(&path["val"], "value");
// same patterns
let mut router = Router::<usize>::build();
router.path("/name", 10);
router.path("/name/{val}", 11);
let mut router = router.finish();
let mut path = Path::new("/name");
path.skip(6);
assert!(router.recognize_mut(&mut path).is_none());
let mut path = Path::new("/test2/name");
path.skip(6);
let (h, _) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 10);
let mut path = Path::new("/test2/name-test");
path.skip(6);
assert!(router.recognize_mut(&mut path).is_none());
let mut path = Path::new("/test2/name/ttt");
path.skip(6);
let (h, _) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 11);
assert_eq!(&path["val"], "ttt");
}
}

View File

@ -1,288 +0,0 @@
use crate::ResourcePath;
#[allow(dead_code)]
const GEN_DELIMS: &[u8] = b":/?#[]@";
#[allow(dead_code)]
const SUB_DELIMS_WITHOUT_QS: &[u8] = b"!$'()*,";
#[allow(dead_code)]
const SUB_DELIMS: &[u8] = b"!$'()*,+?=;";
#[allow(dead_code)]
const RESERVED: &[u8] = b":/?#[]@!$'()*,+?=;";
#[allow(dead_code)]
const UNRESERVED: &[u8] = b"abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ
1234567890
-._~";
const ALLOWED: &[u8] = b"abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ
1234567890
-._~
!$'()*,";
const QS: &[u8] = b"+&=;b";
#[inline]
fn bit_at(array: &[u8], ch: u8) -> bool {
array[(ch >> 3) as usize] & (1 << (ch & 7)) != 0
}
#[inline]
fn set_bit(array: &mut [u8], ch: u8) {
array[(ch >> 3) as usize] |= 1 << (ch & 7)
}
thread_local! {
static DEFAULT_QUOTER: Quoter = Quoter::new(b"@:", b"%/+");
}
#[derive(Default, Clone, Debug)]
pub struct Url {
uri: http::Uri,
path: Option<String>,
}
impl Url {
pub fn new(uri: http::Uri) -> Url {
let path = DEFAULT_QUOTER.with(|q| q.requote(uri.path().as_bytes()));
Url { uri, path }
}
pub fn with_quoter(uri: http::Uri, quoter: &Quoter) -> Url {
Url {
path: quoter.requote(uri.path().as_bytes()),
uri,
}
}
pub fn uri(&self) -> &http::Uri {
&self.uri
}
pub fn path(&self) -> &str {
if let Some(ref s) = self.path {
s
} else {
self.uri.path()
}
}
#[inline]
pub fn update(&mut self, uri: &http::Uri) {
self.uri = uri.clone();
self.path = DEFAULT_QUOTER.with(|q| q.requote(uri.path().as_bytes()));
}
#[inline]
pub fn update_with_quoter(&mut self, uri: &http::Uri, quoter: &Quoter) {
self.uri = uri.clone();
self.path = quoter.requote(uri.path().as_bytes());
}
}
impl ResourcePath for Url {
#[inline]
fn path(&self) -> &str {
self.path()
}
}
pub struct Quoter {
safe_table: [u8; 16],
protected_table: [u8; 16],
}
impl Quoter {
pub fn new(safe: &[u8], protected: &[u8]) -> Quoter {
let mut q = Quoter {
safe_table: [0; 16],
protected_table: [0; 16],
};
// prepare safe table
for i in 0..128 {
if ALLOWED.contains(&i) {
set_bit(&mut q.safe_table, i);
}
if QS.contains(&i) {
set_bit(&mut q.safe_table, i);
}
}
for ch in safe {
set_bit(&mut q.safe_table, *ch)
}
// prepare protected table
for ch in protected {
set_bit(&mut q.safe_table, *ch);
set_bit(&mut q.protected_table, *ch);
}
q
}
pub fn requote(&self, val: &[u8]) -> Option<String> {
let mut has_pct = 0;
let mut pct = [b'%', 0, 0];
let mut idx = 0;
let mut cloned: Option<Vec<u8>> = None;
let len = val.len();
while idx < len {
let ch = val[idx];
if has_pct != 0 {
pct[has_pct] = val[idx];
has_pct += 1;
if has_pct == 3 {
has_pct = 0;
let buf = cloned.as_mut().unwrap();
if let Some(ch) = restore_ch(pct[1], pct[2]) {
if ch < 128 {
if bit_at(&self.protected_table, ch) {
buf.extend_from_slice(&pct);
idx += 1;
continue;
}
if bit_at(&self.safe_table, ch) {
buf.push(ch);
idx += 1;
continue;
}
}
buf.push(ch);
} else {
buf.extend_from_slice(&pct[..]);
}
}
} else if ch == b'%' {
has_pct = 1;
if cloned.is_none() {
let mut c = Vec::with_capacity(len);
c.extend_from_slice(&val[..idx]);
cloned = Some(c);
}
} else if let Some(ref mut cloned) = cloned {
cloned.push(ch)
}
idx += 1;
}
cloned.map(|data| String::from_utf8_lossy(&data).into_owned())
}
}
#[inline]
fn from_hex(v: u8) -> Option<u8> {
if (b'0'..=b'9').contains(&v) {
Some(v - 0x30) // ord('0') == 0x30
} else if (b'A'..=b'F').contains(&v) {
Some(v - 0x41 + 10) // ord('A') == 0x41
} else if (b'a'..=b'f').contains(&v) {
Some(v - 0x61 + 10) // ord('a') == 0x61
} else {
None
}
}
#[inline]
fn restore_ch(d1: u8, d2: u8) -> Option<u8> {
from_hex(d1).and_then(|d1| from_hex(d2).map(move |d2| d1 << 4 | d2))
}
#[cfg(test)]
mod tests {
use http::Uri;
use std::convert::TryFrom;
use super::*;
use crate::{Path, ResourceDef};
const PROTECTED: &[u8] = b"%/+";
fn match_url(pattern: &'static str, url: impl AsRef<str>) -> Path<Url> {
let re = ResourceDef::new(pattern);
let uri = Uri::try_from(url.as_ref()).unwrap();
let mut path = Path::new(Url::new(uri));
assert!(re.capture_match_info(&mut path));
path
}
fn percent_encode(data: &[u8]) -> String {
data.iter().map(|c| format!("%{:02X}", c)).collect()
}
#[test]
fn test_parse_url() {
let re = "/user/{id}/test";
let path = match_url(re, "/user/2345/test");
assert_eq!(path.get("id").unwrap(), "2345");
// "%25" should never be decoded into '%' to guarantee the output is a valid
// percent-encoded format
let path = match_url(re, "/user/qwe%25/test");
assert_eq!(path.get("id").unwrap(), "qwe%25");
let path = match_url(re, "/user/qwe%25rty/test");
assert_eq!(path.get("id").unwrap(), "qwe%25rty");
}
#[test]
fn test_protected_chars() {
let encoded = percent_encode(PROTECTED);
let path = match_url("/user/{id}/test", format!("/user/{}/test", encoded));
assert_eq!(path.get("id").unwrap(), &encoded);
}
#[test]
fn test_non_protecteed_ascii() {
let nonprotected_ascii = ('\u{0}'..='\u{7F}')
.filter(|&c| c.is_ascii() && !PROTECTED.contains(&(c as u8)))
.collect::<String>();
let encoded = percent_encode(nonprotected_ascii.as_bytes());
let path = match_url("/user/{id}/test", format!("/user/{}/test", encoded));
assert_eq!(path.get("id").unwrap(), &nonprotected_ascii);
}
#[test]
fn test_valid_utf8_multibyte() {
let test = ('\u{FF00}'..='\u{FFFF}').collect::<String>();
let encoded = percent_encode(test.as_bytes());
let path = match_url("/a/{id}/b", format!("/a/{}/b", &encoded));
assert_eq!(path.get("id").unwrap(), &test);
}
#[test]
fn test_invalid_utf8() {
let invalid_utf8 = percent_encode((0x80..=0xff).collect::<Vec<_>>().as_slice());
let uri = Uri::try_from(format!("/{}", invalid_utf8)).unwrap();
let path = Path::new(Url::new(uri));
// We should always get a valid utf8 string
assert!(String::from_utf8(path.path().as_bytes().to_owned()).is_ok());
}
#[test]
fn test_from_hex() {
let hex = b"0123456789abcdefABCDEF";
for i in 0..256 {
let c = i as u8;
if hex.contains(&c) {
assert!(from_hex(c).is_some())
} else {
assert!(from_hex(c).is_none())
}
}
let expected = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 10, 11, 12, 13, 14, 15,
];
for i in 0..hex.len() {
assert_eq!(from_hex(hex[i]).unwrap(), expected[i]);
}
}
}

View File

@ -1,158 +1,174 @@
# Changes
## Unreleased - 2021-xx-xx
## Unreleased
- Minimum supported Rust version (MSRV) is now 1.71.
## 2.2.0 - 2021-03-29
* **BREAKING** `ActixStream::{poll_read_ready, poll_write_ready}` methods now return
`Ready` object in ok variant. [#293]
* Breakage is acceptable since `ActixStream` was not intended to be public.
## 2.10.0
[#293]: https://github.com/actix/actix-net/pull/293
- Relax `F`'s bound (`Fn => FnOnce`) on `{Arbiter, System}::with_tokio_rt()` functions.
- Update `tokio-uring` dependency to `0.5`.
- Minimum supported Rust version (MSRV) is now 1.70.
## 2.9.0
## 2.1.0 - 2021-02-24
* Add `ActixStream` extension trait to include readiness methods. [#276]
* Re-export `tokio::net::TcpSocket` in `net` module [#282]
- Add `actix_rt::System::runtime()` method to retrieve the underlying `actix_rt::Runtime` runtime.
- Add `actix_rt::Runtime::tokio_runtime()` method to retrieve the underlying Tokio runtime.
- Minimum supported Rust version (MSRV) is now 1.65.
[#276]: https://github.com/actix/actix-net/pull/276
[#282]: https://github.com/actix/actix-net/pull/282
## 2.8.0
- Add `#[track_caller]` attribute to `spawn` functions and methods.
- Update `tokio-uring` dependency to `0.4`.
- Minimum supported Rust version (MSRV) is now 1.59.
## 2.0.2 - 2021-02-06
* Add `Arbiter::handle` to get a handle of an owned Arbiter. [#274]
* Add `System::try_current` for situations where actix may or may not be running a System. [#275]
## 2.7.0
[#274]: https://github.com/actix/actix-net/pull/274
[#275]: https://github.com/actix/actix-net/pull/275
- Update `tokio-uring` dependency to `0.3`.
- Minimum supported Rust version (MSRV) is now 1.49.
## 2.6.0
## 2.0.1 - 2021-02-06
* Expose `JoinError` from Tokio. [#271]
- Update `tokio-uring` dependency to `0.2`.
[#271]: https://github.com/actix/actix-net/pull/271
## 2.5.1
- Expose `System::with_tokio_rt` and `Arbiter::with_tokio_rt`.
## 2.0.0 - 2021-02-02
* Remove all Arbiter-local storage methods. [#262]
* Re-export `tokio::pin`. [#262]
## 2.5.0
[#262]: https://github.com/actix/actix-net/pull/262
- Add `System::run_with_code` to allow retrieving the exit code on stop.
## 2.4.0
## 2.0.0-beta.3 - 2021-01-31
* Remove `run_in_tokio`, `attach_to_tokio` and `AsyncSystemRunner`. [#253]
* Return `JoinHandle` from `actix_rt::spawn`. [#253]
* Remove old `Arbiter::spawn`. Implementation is now inlined into `actix_rt::spawn`. [#253]
* Rename `Arbiter::{send => spawn}` and `Arbiter::{exec_fn => spawn_fn}`. [#253]
* Remove `Arbiter::exec`. [#253]
* Remove deprecated `Arbiter::local_join` and `Arbiter::is_running`. [#253]
* `Arbiter::spawn` now accepts !Unpin futures. [#256]
* `System::new` no longer takes arguments. [#257]
* Remove `System::with_current`. [#257]
* Remove `Builder`. [#257]
* Add `System::with_init` as replacement for `Builder::run`. [#257]
* Rename `System::{is_set => is_registered}`. [#257]
* Add `ArbiterHandle` for sending messages to non-current-thread arbiters. [#257].
* `System::arbiter` now returns an `&ArbiterHandle`. [#257]
* `Arbiter::current` now returns an `ArbiterHandle` instead. [#257]
* `Arbiter::join` now takes self by value. [#257]
- Add `Arbiter::try_current` for situations where thread may or may not have Arbiter context.
- Start io-uring with `System::new` when feature is enabled.
[#253]: https://github.com/actix/actix-net/pull/253
[#254]: https://github.com/actix/actix-net/pull/254
[#256]: https://github.com/actix/actix-net/pull/256
[#257]: https://github.com/actix/actix-net/pull/257
## 2.3.0
- The `spawn` method can now resolve with non-unit outputs.
- Add experimental (semver-exempt) `io-uring` feature for enabling async file I/O on linux.
## 2.0.0-beta.2 - 2021-01-09
* Add `task` mod with re-export of `tokio::task::{spawn_blocking, yield_now, JoinHandle}` [#245]
* Add default "macros" feature to allow faster compile times when using `default-features=false`.
## 2.2.0
[#245]: https://github.com/actix/actix-net/pull/245
- **BREAKING** `ActixStream::{poll_read_ready, poll_write_ready}` methods now return `Ready` object in ok variant.
- Breakage is acceptable since `ActixStream` was not intended to be public.
## 2.1.0
## 2.0.0-beta.1 - 2020-12-28
* Add `System::attach_to_tokio` method. [#173]
* Update `tokio` dependency to `1.0`. [#236]
* Rename `time` module `delay_for` to `sleep`, `delay_until` to `sleep_until`, `Delay` to `Sleep`
to stay aligned with Tokio's naming. [#236]
* Remove `'static` lifetime requirement for `Runtime::block_on` and `SystemRunner::block_on`.
* These methods now accept `&self` when calling. [#236]
* Remove `'static` lifetime requirement for `System::run` and `Builder::run`. [#236]
* `Arbiter::spawn` now panics when `System` is not in scope. [#207]
* Fix work load issue by removing `PENDING` thread local. [#207]
- Add `ActixStream` extension trait to include readiness methods.
- Re-export `tokio::net::TcpSocket` in `net` module
[#207]: https://github.com/actix/actix-net/pull/207
[#236]: https://github.com/actix/actix-net/pull/236
## 2.0.2
- Add `Arbiter::handle` to get a handle of an owned Arbiter.
- Add `System::try_current` for situations where actix may or may not be running a System.
## 1.1.1 - 2020-04-30
* Fix memory leak due to [#94] (see [#129] for more detail)
## 2.0.1
[#129]: https://github.com/actix/actix-net/issues/129
- Expose `JoinError` from Tokio.
## 2.0.0
## 1.1.0 - 2020-04-08 (YANKED)
* Expose `System::is_set` to check if current system has ben started [#99]
* Add `Arbiter::is_running` to check if event loop is running [#124]
* Add `Arbiter::local_join` associated function
to get be able to `await` for spawned futures [#94]
- Remove all Arbiter-local storage methods.
- Re-export `tokio::pin`.
[#94]: https://github.com/actix/actix-net/pull/94
[#99]: https://github.com/actix/actix-net/pull/99
[#124]: https://github.com/actix/actix-net/pull/124
## 2.0.0-beta.3
- Remove `run_in_tokio`, `attach_to_tokio` and `AsyncSystemRunner`.
- Return `JoinHandle` from `actix_rt::spawn`.
- Remove old `Arbiter::spawn`. Implementation is now inlined into `actix_rt::spawn`.
- Rename `Arbiter::{send => spawn}` and `Arbiter::{exec_fn => spawn_fn}`.
- Remove `Arbiter::exec`.
- Remove deprecated `Arbiter::local_join` and `Arbiter::is_running`.
- `Arbiter::spawn` now accepts !Unpin futures.
- `System::new` no longer takes arguments.
- Remove `System::with_current`.
- Remove `Builder`.
- Add `System::with_init` as replacement for `Builder::run`.
- Rename `System::{is_set => is_registered}`.
- Add `ArbiterHandle` for sending messages to non-current-thread arbiters.
- `System::arbiter` now returns an `&ArbiterHandle`.
- `Arbiter::current` now returns an `ArbiterHandle` instead.
- `Arbiter::join` now takes self by value.
## 1.0.0 - 2019-12-11
* Update dependencies
## 2.0.0-beta.2
- Add `task` mod with re-export of `tokio::task::{spawn_blocking, yield_now, JoinHandle}`
- Add default "macros" feature to allow faster compile times when using `default-features=false`.
## 1.0.0-alpha.3 - 2019-12-07
* Migrate to tokio 0.2
* Fix compilation on non-unix platforms
## 2.0.0-beta.1
- Add `System::attach_to_tokio` method.
- Update `tokio` dependency to `1.0`.
- Rename `time` module `delay_for` to `sleep`, `delay_until` to `sleep_until`, `Delay` to `Sleep` to stay aligned with Tokio's naming.
- Remove `'static` lifetime requirement for `Runtime::block_on` and `SystemRunner::block_on`.
- These methods now accept `&self` when calling.
- Remove `'static` lifetime requirement for `System::run` and `Builder::run`.
- `Arbiter::spawn` now panics when `System` is not in scope.
- Fix work load issue by removing `PENDING` thread local.
## 1.0.0-alpha.2 - 2019-12-02
* Export `main` and `test` attribute macros
* Export `time` module (re-export of tokio-timer)
* Export `net` module (re-export of tokio-net)
## 1.1.1
- Fix memory leak due to
## 1.0.0-alpha.1 - 2019-11-22
* Migrate to std::future and tokio 0.2
## 1.1.0 _(YANKED)_
- Expose `System::is_set` to check if current system has ben started
- Add `Arbiter::is_running` to check if event loop is running
- Add `Arbiter::local_join` associated function to get be able to `await` for spawned futures
## 0.2.6 - 2019-11-14
* Allow to join arbiter's thread. #60
* Fix arbiter's thread panic message.
## 1.0.0
- Update dependencies
## 0.2.5 - 2019-09-02
* Add arbiter specific storage
## 1.0.0-alpha.3
- Migrate to tokio 0.2
- Fix compilation on non-unix platforms
## 0.2.4 - 2019-07-17
* Avoid a copy of the Future when initializing the Box. #29
## 1.0.0-alpha.2
- Export `main` and `test` attribute macros
- Export `time` module (re-export of tokio-timer)
- Export `net` module (re-export of tokio-net)
## 0.2.3 - 2019-06-22
* Allow to start System using existing CurrentThread Handle #22
## 1.0.0-alpha.1
- Migrate to std::future and tokio 0.2
## 0.2.2 - 2019-03-28
* Moved `blocking` module to `actix-threadpool` crate
## 0.2.6
- Allow to join arbiter's thread. #60
- Fix arbiter's thread panic message.
## 0.2.1 - 2019-03-11
* Added `blocking` module
* Added `Arbiter::exec_fn` - execute fn on the arbiter's thread
* Added `Arbiter::exec` - execute fn on the arbiter's thread and wait result
## 0.2.5
- Add arbiter specific storage
## 0.2.0 - 2019-03-06
* `run` method returns `io::Result<()>`
* Removed `Handle`
## 0.2.4
- Avoid a copy of the Future when initializing the Box. #29
## 0.1.0 - 2018-12-09
* Initial release
## 0.2.3
- Allow to start System using existing CurrentThread Handle #22
## 0.2.2
- Moved `blocking` module to `actix-threadpool` crate
## 0.2.1
- Added `blocking` module
- Added `Arbiter::exec_fn` - execute fn on the arbiter's thread
- Added `Arbiter::exec` - execute fn on the arbiter's thread and wait result
## 0.2.0
- `run` method returns `io::Result<()>`
- Removed `Handle`
## 0.1.0
- Initial release

View File

@ -1,33 +1,36 @@
[package]
name = "actix-rt"
version = "2.2.0"
authors = [
"Nikolay Kim <fafhrd91@gmail.com>",
"Rob Ede <robjtede@icloud.com>",
]
version = "2.10.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>", "Rob Ede <robjtede@icloud.com>"]
description = "Tokio-based single-threaded async runtime for the Actix ecosystem"
keywords = ["async", "futures", "io", "runtime"]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-net"
documentation = "https://docs.rs/actix-rt"
categories = ["network-programming", "asynchronous"]
license = "MIT OR Apache-2.0"
edition = "2018"
edition.workspace = true
rust-version.workspace = true
[lib]
name = "actix_rt"
path = "src/lib.rs"
[package.metadata.cargo_check_external_types]
allowed_external_types = ["actix_macros::*", "tokio::*"]
[features]
default = ["macros"]
macros = ["actix-macros"]
io-uring = ["tokio-uring"]
[dependencies]
actix-macros = { version = "0.2.0", optional = true }
actix-macros = { version = "0.2.3", optional = true }
futures-core = { version = "0.3", default-features = false }
tokio = { version = "1.3", features = ["rt", "net", "parking_lot", "signal", "sync", "time"] }
tokio = { version = "1.23.1", features = ["rt", "net", "parking_lot", "signal", "sync", "time"] }
# runtime for `io-uring` feature
[target.'cfg(target_os = "linux")'.dependencies]
tokio-uring = { version = "0.5", optional = true }
[dev-dependencies]
tokio = { version = "1.2", features = ["full"] }
hyper = { version = "0.14", default-features = false, features = ["server", "tcp", "http1"] }
tokio = { version = "1.23.1", features = ["full"] }
[lints]
workspace = true

View File

@ -3,11 +3,11 @@
> Tokio-based single-threaded async runtime for the Actix ecosystem.
[![crates.io](https://img.shields.io/crates/v/actix-rt?label=latest)](https://crates.io/crates/actix-rt)
[![Documentation](https://docs.rs/actix-rt/badge.svg?version=2.2.0)](https://docs.rs/actix-rt/2.2.0)
[![Documentation](https://docs.rs/actix-rt/badge.svg?version=2.10.0)](https://docs.rs/actix-rt/2.10.0)
[![Version](https://img.shields.io/badge/rustc-1.46+-ab6000.svg)](https://blog.rust-lang.org/2020/03/12/Rust-1.46.html)
![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-rt.svg)
<br />
[![dependency status](https://deps.rs/crate/actix-rt/2.2.0/status.svg)](https://deps.rs/crate/actix-rt/2.2.0)
[![dependency status](https://deps.rs/crate/actix-rt/2.10.0/status.svg)](https://deps.rs/crate/actix-rt/2.10.0)
![Download](https://img.shields.io/crates/d/actix-rt.svg)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/WghFtEH6Hb)

View File

@ -1,28 +0,0 @@
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Request, Response, Server};
use std::convert::Infallible;
use std::net::SocketAddr;
async fn handle(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
Ok(Response::new(Body::from("Hello World")))
}
fn main() {
actix_rt::System::with_tokio_rt(|| {
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
})
.block_on(async {
let make_service =
make_service_fn(|_conn| async { Ok::<_, Infallible>(service_fn(handle)) });
let server =
Server::bind(&SocketAddr::from(([127, 0, 0, 1], 3000))).serve(make_service);
if let Err(e) = server.await {
eprintln!("server error: {}", e);
}
})
}

View File

@ -9,17 +9,14 @@ use std::{
};
use futures_core::ready;
use tokio::{sync::mpsc, task::LocalSet};
use tokio::sync::mpsc;
use crate::{
runtime::{default_tokio_runtime, Runtime},
system::{System, SystemCommand},
};
use crate::system::{System, SystemCommand};
pub(crate) static COUNT: AtomicUsize = AtomicUsize::new(0);
thread_local!(
static HANDLE: RefCell<Option<ArbiterHandle>> = RefCell::new(None);
static HANDLE: RefCell<Option<ArbiterHandle>> = const { RefCell::new(None) };
);
pub(crate) enum ArbiterCommand {
@ -98,20 +95,21 @@ impl Arbiter {
///
/// # Panics
/// Panics if a [System] is not registered on the current thread.
#[cfg(not(all(target_os = "linux", feature = "io-uring")))]
#[allow(clippy::new_without_default)]
pub fn new() -> Arbiter {
Self::with_tokio_rt(|| {
default_tokio_runtime().expect("Cannot create new Arbiter's Runtime.")
crate::runtime::default_tokio_runtime().expect("Cannot create new Arbiter's Runtime.")
})
}
/// Spawn a new Arbiter using the [Tokio Runtime](tokio-runtime) returned from a closure.
///
/// [tokio-runtime]: tokio::runtime::Runtime
#[doc(hidden)]
#[cfg(not(all(target_os = "linux", feature = "io-uring")))]
pub fn with_tokio_rt<F>(runtime_factory: F) -> Arbiter
where
F: Fn() -> tokio::runtime::Runtime + Send + 'static,
F: FnOnce() -> tokio::runtime::Runtime + Send + 'static,
{
let sys = System::current();
let system_id = sys.id();
@ -127,7 +125,7 @@ impl Arbiter {
.spawn({
let tx = tx.clone();
move || {
let rt = Runtime::from(runtime_factory());
let rt = crate::runtime::Runtime::from(runtime_factory());
let hnd = ArbiterHandle::new(tx);
System::set_current(sys);
@ -150,24 +148,72 @@ impl Arbiter {
.send(SystemCommand::DeregisterArbiter(arb_id));
}
})
.unwrap_or_else(|err| {
panic!("Cannot spawn Arbiter's thread: {:?}. {:?}", &name, err)
});
.unwrap_or_else(|err| panic!("Cannot spawn Arbiter's thread: {name:?}: {err:?}"));
ready_rx.recv().unwrap();
Arbiter { tx, thread_handle }
}
/// Sets up an Arbiter runner in a new System using the provided runtime local task set.
pub(crate) fn in_new_system(local: &LocalSet) -> ArbiterHandle {
/// Spawn a new Arbiter thread and start its event loop with `tokio-uring` runtime.
///
/// # Panics
/// Panics if a [System] is not registered on the current thread.
#[cfg(all(target_os = "linux", feature = "io-uring"))]
#[allow(clippy::new_without_default)]
pub fn new() -> Arbiter {
let sys = System::current();
let system_id = sys.id();
let arb_id = COUNT.fetch_add(1, Ordering::Relaxed);
let name = format!("actix-rt|system:{}|arbiter:{}", system_id, arb_id);
let (tx, rx) = mpsc::unbounded_channel();
let (ready_tx, ready_rx) = std::sync::mpsc::channel::<()>();
let thread_handle = thread::Builder::new()
.name(name.clone())
.spawn({
let tx = tx.clone();
move || {
let hnd = ArbiterHandle::new(tx);
System::set_current(sys);
HANDLE.with(|cell| *cell.borrow_mut() = Some(hnd.clone()));
// register arbiter
let _ = System::current()
.tx()
.send(SystemCommand::RegisterArbiter(arb_id, hnd));
ready_tx.send(()).unwrap();
// run arbiter event processing loop
tokio_uring::start(ArbiterRunner { rx });
// deregister arbiter
let _ = System::current()
.tx()
.send(SystemCommand::DeregisterArbiter(arb_id));
}
})
.unwrap_or_else(|err| panic!("Cannot spawn Arbiter's thread: {name:?}: {err:?}"));
ready_rx.recv().unwrap();
Arbiter { tx, thread_handle }
}
/// Sets up an Arbiter runner in a new System using the environment's local set.
pub(crate) fn in_new_system() -> ArbiterHandle {
let (tx, rx) = mpsc::unbounded_channel();
let hnd = ArbiterHandle::new(tx);
HANDLE.with(|cell| *cell.borrow_mut() = Some(hnd.clone()));
local.spawn_local(ArbiterRunner { rx });
crate::spawn(ArbiterRunner { rx });
hnd
}
@ -188,6 +234,15 @@ impl Arbiter {
})
}
/// Try to get current running arbiter handle.
///
/// Returns `None` if no Arbiter has been started.
///
/// Unlike [`current`](Self::current), this never panics.
pub fn try_current() -> Option<ArbiterHandle> {
HANDLE.with(|cell| cell.borrow().clone())
}
/// Stop Arbiter from continuing it's event loop.
///
/// Returns true if stop message was sent successfully and false if the Arbiter has been dropped.
@ -200,6 +255,7 @@ impl Arbiter {
/// If you require a result, include a response channel in the future.
///
/// Returns true if future was sent successfully and false if the Arbiter has died.
#[track_caller]
pub fn spawn<Fut>(&self, future: Fut) -> bool
where
Fut: Future<Output = ()> + Send + 'static,
@ -215,6 +271,7 @@ impl Arbiter {
/// channel in the function.
///
/// Returns true if function was sent successfully and false if the Arbiter has died.
#[track_caller]
pub fn spawn_fn<F>(&self, f: F) -> bool
where
F: FnOnce() + Send + 'static,
@ -241,7 +298,7 @@ impl Future for ArbiterRunner {
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
// process all items currently buffered in channel
loop {
match ready!(Pin::new(&mut self.rx).poll_recv(cx)) {
match ready!(self.rx.poll_recv(cx)) {
// channel closed; no more messages can be received
None => return Poll::Ready(()),

View File

@ -15,7 +15,7 @@
//! blocking task thread-pool using [`task::spawn_blocking`].
//!
//! # Examples
//! ```
//! ```no_run
//! use std::sync::mpsc;
//! use actix_rt::{Arbiter, System};
//!
@ -32,31 +32,43 @@
//! arbiter.stop();
//! arbiter.join().unwrap();
//! ```
//!
//! # `io-uring` Support
//!
//! There is experimental support for using io-uring with this crate by enabling the
//! `io-uring` feature. For now, it is semver exempt.
//!
//! Note that there are currently some unimplemented parts of using `actix-rt` with `io-uring`.
//! In particular, when running a `System`, only `System::block_on` is supported.
#![deny(rust_2018_idioms, nonstandard_style)]
#![allow(clippy::type_complexity)]
#![warn(missing_docs)]
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
use std::future::Future;
#[cfg(all(not(target_os = "linux"), feature = "io-uring"))]
compile_error!("io_uring is a linux only feature.");
use tokio::task::JoinHandle;
use std::future::Future;
// Cannot define a main macro when compiled into test harness.
// Workaround for https://github.com/rust-lang/rust/issues/62127.
#[cfg(all(feature = "macros", not(test)))]
pub use actix_macros::{main, test};
pub use actix_macros::main;
#[cfg(feature = "macros")]
pub use actix_macros::test;
mod arbiter;
mod runtime;
mod system;
pub use self::arbiter::{Arbiter, ArbiterHandle};
pub use self::runtime::Runtime;
pub use self::system::{System, SystemRunner};
pub use tokio::pin;
use tokio::task::JoinHandle;
pub use self::{
arbiter::{Arbiter, ArbiterHandle},
runtime::Runtime,
system::{System, SystemRunner},
};
pub mod signal {
//! Asynchronous signal handling (Tokio re-exports).
@ -78,13 +90,13 @@ pub mod net {
task::{Context, Poll},
};
pub use tokio::io::Ready;
use tokio::io::{AsyncRead, AsyncWrite, Interest};
pub use tokio::net::UdpSocket;
pub use tokio::net::{TcpListener, TcpSocket, TcpStream};
#[cfg(unix)]
pub use tokio::net::{UnixDatagram, UnixListener, UnixStream};
pub use tokio::{
io::Ready,
net::{TcpListener, TcpSocket, TcpStream, UdpSocket},
};
/// Extension trait over async read+write types that can also signal readiness.
#[doc(hidden)]
@ -143,10 +155,9 @@ pub mod net {
pub mod time {
//! Utilities for tracking time (Tokio re-exports).
pub use tokio::time::Instant;
pub use tokio::time::{interval, interval_at, Interval};
pub use tokio::time::{sleep, sleep_until, Sleep};
pub use tokio::time::{timeout, Timeout};
pub use tokio::time::{
interval, interval_at, sleep, sleep_until, timeout, Instant, Interval, Sleep, Timeout,
};
}
pub mod task {
@ -155,14 +166,42 @@ pub mod task {
pub use tokio::task::{spawn_blocking, yield_now, JoinError, JoinHandle};
}
/// Spawns a future on the current thread.
/// Spawns a future on the current thread as a new task.
///
/// If not immediately awaited, the task can be cancelled using [`JoinHandle::abort`].
///
/// The provided future is spawned as a new task; therefore, panics are caught.
///
/// # Panics
/// Panics if Actix system is not running.
///
/// # Examples
/// ```
/// # use std::time::Duration;
/// # actix_rt::Runtime::new().unwrap().block_on(async {
/// // task resolves successfully
/// assert_eq!(actix_rt::spawn(async { 1 }).await.unwrap(), 1);
///
/// // task panics
/// assert!(actix_rt::spawn(async {
/// panic!("panic is caught at task boundary");
/// })
/// .await
/// .unwrap_err()
/// .is_panic());
///
/// // task is cancelled before completion
/// let handle = actix_rt::spawn(actix_rt::time::sleep(Duration::from_secs(100)));
/// handle.abort();
/// assert!(handle.await.unwrap_err().is_cancelled());
/// # });
/// ```
#[track_caller]
#[inline]
pub fn spawn<Fut>(f: Fut) -> JoinHandle<()>
pub fn spawn<Fut>(f: Fut) -> JoinHandle<Fut::Output>
where
Fut: Future<Output = ()> + 'static,
Fut: Future + 'static,
Fut::Output: 'static,
{
tokio::task::spawn_local(f)
}

View File

@ -31,11 +31,6 @@ impl Runtime {
})
}
/// Reference to local task set.
pub(crate) fn local_set(&self) -> &LocalSet {
&self.local
}
/// Offload a future onto the single-threaded runtime.
///
/// The returned join handle can be used to await the future's result.
@ -58,6 +53,7 @@ impl Runtime {
/// # Panics
/// This function panics if the spawn fails. Failure occurs if the executor is currently at
/// capacity and is unable to spawn a new future.
#[track_caller]
pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
where
F: Future + 'static,
@ -65,6 +61,62 @@ impl Runtime {
self.local.spawn_local(future)
}
/// Retrieves a reference to the underlying Tokio runtime associated with this instance.
///
/// The Tokio runtime is responsible for executing asynchronous tasks and managing
/// the event loop for an asynchronous Rust program. This method allows accessing
/// the runtime to interact with its features directly.
///
/// In a typical use case, you might need to share the same runtime between different
/// modules of your project. For example, a module might require a `tokio::runtime::Handle`
/// to spawn tasks on the same runtime, or the runtime itself to configure more complex
/// behaviours.
///
/// # Example
///
/// ```
/// use actix_rt::Runtime;
///
/// mod module_a {
/// pub fn do_something(handle: tokio::runtime::Handle) {
/// handle.spawn(async {
/// // Some asynchronous task here
/// });
/// }
/// }
///
/// mod module_b {
/// pub fn do_something_else(rt: &tokio::runtime::Runtime) {
/// rt.spawn(async {
/// // Another asynchronous task here
/// });
/// }
/// }
///
/// let actix_runtime = actix_rt::Runtime::new().unwrap();
/// let tokio_runtime = actix_runtime.tokio_runtime();
///
/// let handle = tokio_runtime.handle().clone();
///
/// module_a::do_something(handle);
/// module_b::do_something_else(tokio_runtime);
/// ```
///
/// # Returns
///
/// An immutable reference to the `tokio::runtime::Runtime` instance associated with this
/// `Runtime` instance.
///
/// # Note
///
/// While this method provides an immutable reference to the Tokio runtime, which is safe to share across threads,
/// be aware that spawning blocking tasks on the Tokio runtime could potentially impact the execution
/// of the Actix runtime. This is because Tokio is responsible for driving the Actix system,
/// and blocking tasks could delay or deadlock other tasks in run loop.
pub fn tokio_runtime(&self) -> &tokio::runtime::Runtime {
&self.rt
}
/// Runs the provided future, blocking the current thread until the future completes.
///
/// This function can be used to synchronously block the current thread until the provided
@ -78,6 +130,7 @@ impl Runtime {
///
/// The caller is responsible for ensuring that other spawned futures complete execution by
/// calling `block_on` or `run`.
#[track_caller]
pub fn block_on<F>(&self, f: F) -> F::Output
where
F: Future,

View File

@ -11,12 +11,12 @@ use std::{
use futures_core::ready;
use tokio::sync::{mpsc, oneshot};
use crate::{arbiter::ArbiterHandle, runtime::default_tokio_runtime, Arbiter, Runtime};
use crate::{arbiter::ArbiterHandle, Arbiter};
static SYSTEM_COUNT: AtomicUsize = AtomicUsize::new(0);
thread_local!(
static CURRENT: RefCell<Option<System>> = RefCell::new(None);
static CURRENT: RefCell<Option<System>> = const { RefCell::new(None) };
);
/// A manager for a per-thread distributed async runtime.
@ -29,6 +29,7 @@ pub struct System {
arbiter_handle: ArbiterHandle,
}
#[cfg(not(feature = "io-uring"))]
impl System {
/// Create a new system.
///
@ -37,7 +38,7 @@ impl System {
#[allow(clippy::new_ret_no_self)]
pub fn new() -> SystemRunner {
Self::with_tokio_rt(|| {
default_tokio_runtime()
crate::runtime::default_tokio_runtime()
.expect("Default Actix (Tokio) runtime could not be created.")
})
}
@ -45,16 +46,15 @@ impl System {
/// Create a new System using the [Tokio Runtime](tokio-runtime) returned from a closure.
///
/// [tokio-runtime]: tokio::runtime::Runtime
#[doc(hidden)]
pub fn with_tokio_rt<F>(runtime_factory: F) -> SystemRunner
where
F: Fn() -> tokio::runtime::Runtime,
F: FnOnce() -> tokio::runtime::Runtime,
{
let (stop_tx, stop_rx) = oneshot::channel();
let (sys_tx, sys_rx) = mpsc::unbounded_channel();
let rt = Runtime::from(runtime_factory());
let sys_arbiter = Arbiter::in_new_system(rt.local_set());
let rt = crate::runtime::Runtime::from(runtime_factory());
let sys_arbiter = rt.block_on(async { Arbiter::in_new_system() });
let system = System::construct(sys_tx, sys_arbiter.clone());
system
@ -66,13 +66,34 @@ impl System {
let sys_ctrl = SystemController::new(sys_rx, stop_tx);
rt.spawn(sys_ctrl);
SystemRunner {
rt,
stop_rx,
system,
}
SystemRunner { rt, stop_rx }
}
}
#[cfg(feature = "io-uring")]
impl System {
/// Create a new system.
///
/// # Panics
/// Panics if underlying Tokio runtime can not be created.
#[allow(clippy::new_ret_no_self)]
pub fn new() -> SystemRunner {
SystemRunner
}
/// Create a new System using the [Tokio Runtime](tokio-runtime) returned from a closure.
///
/// [tokio-runtime]: tokio::runtime::Runtime
#[doc(hidden)]
pub fn with_tokio_rt<F>(_: F) -> SystemRunner
where
F: FnOnce() -> tokio::runtime::Runtime,
{
unimplemented!("System::with_tokio_rt is not implemented for io-uring feature yet")
}
}
impl System {
/// Constructs new system and registers it on the current thread.
pub(crate) fn construct(
sys_tx: mpsc::UnboundedSender<SystemCommand>,
@ -104,7 +125,7 @@ impl System {
///
/// Returns `None` if no System has been started.
///
/// Contrary to `current`, this never panics.
/// Unlike [`current`](Self::current), this never panics.
pub fn try_current() -> Option<System> {
CURRENT.with(|cell| cell.borrow().clone())
}
@ -150,40 +171,121 @@ impl System {
}
/// Runner that keeps a [System]'s event loop alive until stop message is received.
#[cfg(not(feature = "io-uring"))]
#[must_use = "A SystemRunner does nothing unless `run` is called."]
#[derive(Debug)]
pub struct SystemRunner {
rt: Runtime,
rt: crate::runtime::Runtime,
stop_rx: oneshot::Receiver<i32>,
system: System,
}
#[cfg(not(feature = "io-uring"))]
impl SystemRunner {
/// Starts event loop and will return once [System] is [stopped](System::stop).
pub fn run(self) -> io::Result<()> {
let exit_code = self.run_with_code()?;
match exit_code {
0 => Ok(()),
nonzero => Err(io::Error::new(
io::ErrorKind::Other,
format!("Non-zero exit code: {}", nonzero),
)),
}
}
/// Runs the event loop until [stopped](System::stop_with_code), returning the exit code.
pub fn run_with_code(self) -> io::Result<i32> {
let SystemRunner { rt, stop_rx, .. } = self;
// run loop
match rt.block_on(stop_rx) {
Ok(code) => {
if code != 0 {
Err(io::Error::new(
io::ErrorKind::Other,
format!("Non-zero exit code: {}", code),
))
} else {
Ok(())
}
}
rt.block_on(stop_rx)
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))
}
Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)),
}
/// Retrieves a reference to the underlying [Actix runtime](crate::Runtime) associated with this
/// `SystemRunner` instance.
///
/// The Actix runtime is responsible for managing the event loop for an Actix system and
/// executing asynchronous tasks. This method provides access to the runtime, allowing direct
/// interaction with its features.
///
/// In a typical use case, you might need to share the same runtime between different
/// parts of your project. For example, some components might require a [`Runtime`] to spawn
/// tasks on the same runtime.
///
/// Read more in the documentation for [`Runtime`].
///
/// # Examples
///
/// ```
/// let system_runner = actix_rt::System::new();
/// let actix_runtime = system_runner.runtime();
///
/// // Use the runtime to spawn an async task or perform other operations
/// ```
///
/// # Note
///
/// While this method provides an immutable reference to the Actix runtime, which is safe to
/// share across threads, be aware that spawning blocking tasks on the Actix runtime could
/// potentially impact system performance. This is because the Actix runtime is responsible for
/// driving the system, and blocking tasks could delay other tasks in the run loop.
///
/// [`Runtime`]: crate::Runtime
pub fn runtime(&self) -> &crate::runtime::Runtime {
&self.rt
}
/// Runs the provided future, blocking the current thread until the future completes.
#[track_caller]
#[inline]
pub fn block_on<F: Future>(&self, fut: F) -> F::Output {
self.rt.block_on(fut)
}
}
/// Runner that keeps a [System]'s event loop alive until stop message is received.
#[cfg(feature = "io-uring")]
#[must_use = "A SystemRunner does nothing unless `run` is called."]
#[derive(Debug)]
pub struct SystemRunner;
#[cfg(feature = "io-uring")]
impl SystemRunner {
/// Starts event loop and will return once [System] is [stopped](System::stop).
pub fn run(self) -> io::Result<()> {
unimplemented!("SystemRunner::run is not implemented for io-uring feature yet");
}
/// Runs the event loop until [stopped](System::stop_with_code), returning the exit code.
pub fn run_with_code(self) -> io::Result<i32> {
unimplemented!("SystemRunner::run_with_code is not implemented for io-uring feature yet");
}
/// Runs the provided future, blocking the current thread until the future completes.
#[inline]
pub fn block_on<F: Future>(&self, fut: F) -> F::Output {
self.rt.block_on(fut)
tokio_uring::start(async move {
let (stop_tx, stop_rx) = oneshot::channel();
let (sys_tx, sys_rx) = mpsc::unbounded_channel();
let sys_arbiter = Arbiter::in_new_system();
let system = System::construct(sys_tx, sys_arbiter.clone());
system
.tx()
.send(SystemCommand::RegisterArbiter(usize::MAX, sys_arbiter))
.unwrap();
// init background system arbiter
let sys_ctrl = SystemController::new(sys_rx, stop_tx);
tokio_uring::spawn(sys_ctrl);
let res = fut.await;
drop(stop_rx);
res
})
}
}
@ -222,7 +324,7 @@ impl Future for SystemController {
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
// process all items currently buffered in channel
loop {
match ready!(Pin::new(&mut self.cmd_rx).poll_recv(cx)) {
match ready!(self.cmd_rx.poll_recv(cx)) {
// channel closed; no more messages can be received
None => return Poll::Ready(()),

View File

@ -0,0 +1,17 @@
//! Checks that test macro does not cause problems in the presence of imports named "test" that
//! could be either a module with test items or the "test with runtime" macro itself.
//!
//! Before actix/actix-net#399 was implemented, this macro was running twice. The first run output
//! `#[test]` and it got run again and since it was in scope.
//!
//! Prevented by using the fully-qualified test marker (`#[::core::prelude::v1::test]`).
#![cfg(feature = "macros")]
use actix_rt::time as test;
#[actix_rt::test]
async fn test_naming_conflict() {
use test as time;
time::sleep(std::time::Duration::from_millis(2)).await;
}

View File

@ -1,15 +1,16 @@
#![allow(missing_docs)]
use std::{
sync::{
atomic::{AtomicBool, Ordering},
mpsc::channel,
Arc,
},
thread,
future::Future,
time::{Duration, Instant},
};
use actix_rt::{Arbiter, System};
use tokio::sync::oneshot;
use actix_rt::{task::JoinError, Arbiter, System};
#[cfg(not(feature = "io-uring"))]
use {
std::{sync::mpsc::channel, thread},
tokio::sync::oneshot,
};
#[test]
fn await_for_timer() {
@ -24,6 +25,15 @@ fn await_for_timer() {
);
}
#[cfg(not(feature = "io-uring"))]
#[test]
fn run_with_code() {
let sys = System::new();
System::current().stop_with_code(42);
let exit_code = sys.run_with_code().expect("system stop should not error");
assert_eq!(exit_code, 42);
}
#[test]
fn join_another_arbiter() {
let time = Duration::from_secs(1);
@ -99,13 +109,17 @@ fn wait_for_spawns() {
let handle = rt.spawn(async {
println!("running on the runtime");
// assertion panic is caught at task boundary
assert_eq!(1, 2);
// panic is caught at task boundary
panic!("intentional test panic");
});
assert!(rt.block_on(handle).is_err());
}
// Temporary disabled tests for io-uring feature.
// They should be enabled when possible.
#[cfg(not(feature = "io-uring"))]
#[test]
fn arbiter_spawn_fn_runs() {
let _ = System::new();
@ -122,6 +136,7 @@ fn arbiter_spawn_fn_runs() {
arbiter.join().unwrap();
}
#[cfg(not(feature = "io-uring"))]
#[test]
fn arbiter_handle_spawn_fn_runs() {
let sys = System::new();
@ -144,6 +159,7 @@ fn arbiter_handle_spawn_fn_runs() {
sys.run().unwrap();
}
#[cfg(not(feature = "io-uring"))]
#[test]
fn arbiter_drop_no_panic_fn() {
let _ = System::new();
@ -155,6 +171,7 @@ fn arbiter_drop_no_panic_fn() {
arbiter.join().unwrap();
}
#[cfg(not(feature = "io-uring"))]
#[test]
fn arbiter_drop_no_panic_fut() {
let _ = System::new();
@ -166,18 +183,7 @@ fn arbiter_drop_no_panic_fut() {
arbiter.join().unwrap();
}
#[test]
#[should_panic]
fn no_system_current_panic() {
System::current();
}
#[test]
#[should_panic]
fn no_system_arbiter_new_panic() {
Arbiter::new();
}
#[cfg(not(feature = "io-uring"))]
#[test]
fn system_arbiter_spawn() {
let runner = System::new();
@ -208,6 +214,7 @@ fn system_arbiter_spawn() {
thread.join().unwrap();
}
#[cfg(not(feature = "io-uring"))]
#[test]
fn system_stop_stops_arbiters() {
let sys = System::new();
@ -220,8 +227,8 @@ fn system_stop_stops_arbiters() {
System::current().stop();
sys.run().unwrap();
// account for slightly slow thread de-spawns (only observed on windows)
thread::sleep(Duration::from_millis(100));
// account for slightly slow thread de-spawns
thread::sleep(Duration::from_millis(500));
// arbiter should be dead and return false
assert!(!Arbiter::current().spawn_fn(|| {}));
@ -230,6 +237,7 @@ fn system_stop_stops_arbiters() {
arb.join().unwrap();
}
#[cfg(not(feature = "io-uring"))]
#[test]
fn new_system_with_tokio() {
let (tx, rx) = channel();
@ -262,8 +270,14 @@ fn new_system_with_tokio() {
assert_eq!(rx.recv().unwrap(), 42);
}
#[cfg(not(feature = "io-uring"))]
#[test]
fn new_arbiter_with_tokio() {
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
let _ = System::new();
let arb = Arbiter::with_tokio_rt(|| {
@ -289,6 +303,18 @@ fn new_arbiter_with_tokio() {
assert!(!counter.load(Ordering::SeqCst));
}
#[test]
#[should_panic]
fn no_system_current_panic() {
System::current();
}
#[test]
#[should_panic]
fn no_system_arbiter_new_panic() {
Arbiter::new();
}
#[test]
fn try_current_no_system() {
assert!(System::try_current().is_none())
@ -298,3 +324,55 @@ fn try_current_no_system() {
fn try_current_with_system() {
System::new().block_on(async { assert!(System::try_current().is_some()) });
}
#[allow(clippy::unit_cmp)]
#[test]
fn spawn_local() {
System::new().block_on(async {
// demonstrate that spawn -> R is strictly more capable than spawn -> ()
assert_eq!(actix_rt::spawn(async {}).await.unwrap(), ());
assert_eq!(actix_rt::spawn(async { 1 }).await.unwrap(), 1);
assert!(actix_rt::spawn(async { panic!("") }).await.is_err());
actix_rt::spawn(async { tokio::time::sleep(Duration::from_millis(50)).await })
.await
.unwrap();
fn g<F: Future<Output = Result<(), JoinError>>>(_f: F) {}
g(actix_rt::spawn(async {}));
// g(actix_rt::spawn(async { 1 })); // compile err
fn h<F: Future<Output = Result<R, JoinError>>, R>(_f: F) {}
h(actix_rt::spawn(async {}));
h(actix_rt::spawn(async { 1 }));
})
}
#[cfg(all(target_os = "linux", feature = "io-uring"))]
#[test]
fn tokio_uring_arbiter() {
System::new().block_on(async {
let (tx, rx) = std::sync::mpsc::channel();
Arbiter::new().spawn(async move {
let handle = actix_rt::spawn(async move {
let f = tokio_uring::fs::File::create("test.txt").await.unwrap();
let buf = b"Hello World!";
let (res, _) = f.write_all_at(&buf[..], 0).await;
assert!(res.is_ok());
f.sync_all().await.unwrap();
f.close().await.unwrap();
std::fs::remove_file("test.txt").unwrap();
});
handle.await.unwrap();
tx.send(true).unwrap();
});
assert!(rx.recv().unwrap());
})
}

View File

@ -1,179 +1,232 @@
# Changes
## Unreleased - 2021-xx-xx
* Remove `config` module. `ServiceConfig`, `ServiceRuntime` public types are removed due to this change. [#349]
* Remove `ServerBuilder::configure` [#349]
## Unreleased
[#349]: https://github.com/actix/actix-net/pull/349
## 2.5.1
- Fix panic in test server.
- Minimum supported Rust version (MSRV) is now 1.71.
## 2.0.0-beta.5 - 2021-04-20
* Server shutdown would notify all workers to exit regardless if shutdown is graceful.
This would make all worker shutdown immediately in force shutdown case. [#333]
[#333]: https://github.com/actix/actix-net/pull/333
## 2.5.0
- Update `mio` dependency to `1`.
## 2.0.0-beta.4 - 2021-04-01
* Prevent panic when `shutdown_timeout` is very large. [f9262db]
## 2.4.0
[f9262db]: https://github.com/actix/actix-net/commit/f9262db
- Update `tokio-uring` dependency to `0.5`.
- Minimum supported Rust version (MSRV) is now 1.70.
## 2.3.0
## 2.0.0-beta.3 - 2021-02-06
* Hidden `ServerBuilder::start` method has been removed. Use `ServerBuilder::run`. [#246]
* Add retry for EINTR signal (`io::Interrupted`) in `Accept`'s poll loop. [#264]
* Add `ServerBuilder::worker_max_blocking_threads` to customize blocking thread pool size. [#265]
* Update `actix-rt` to `2.0.0`. [#273]
- Add support for MultiPath TCP (MPTCP) with `MpTcp` enum and `ServerBuilder::mptcp()` method.
- Minimum supported Rust version (MSRV) is now 1.65.
[#246]: https://github.com/actix/actix-net/pull/246
[#264]: https://github.com/actix/actix-net/pull/264
[#265]: https://github.com/actix/actix-net/pull/265
[#273]: https://github.com/actix/actix-net/pull/273
## 2.2.0
- Minimum supported Rust version (MSRV) is now 1.59.
- Update `tokio-uring` dependency to `0.4`.
## 2.0.0-beta.2 - 2021-01-03
* Merge `actix-testing` to `actix-server` as `test_server` mod. [#242]
## 2.1.1
[#242]: https://github.com/actix/actix-net/pull/242
- No significant changes since `2.1.0`.
## 2.1.0
## 2.0.0-beta.1 - 2020-12-28
* Added explicit info log message on accept queue pause. [#215]
* Prevent double registration of sockets when back-pressure is resolved. [#223]
* Update `mio` dependency to `0.7.3`. [#239]
* Remove `socket2` dependency. [#239]
* `ServerBuilder::backlog` now accepts `u32` instead of `i32`. [#239]
* Remove `AcceptNotify` type and pass `WakerQueue` to `Worker` to wake up `Accept`'s `Poll`. [#239]
* Convert `mio::net::TcpStream` to `actix_rt::net::TcpStream`(`UnixStream` for uds) using
`FromRawFd` and `IntoRawFd`(`FromRawSocket` and `IntoRawSocket` on windows). [#239]
* Remove `AsyncRead` and `AsyncWrite` trait bound for `socket::FromStream` trait. [#239]
- Update `tokio-uring` dependency to `0.3`.
- Logs emitted now use the `tracing` crate with `log` compatibility.
- Wait for accept thread to stop before sending completion signal.
[#215]: https://github.com/actix/actix-net/pull/215
[#223]: https://github.com/actix/actix-net/pull/223
[#239]: https://github.com/actix/actix-net/pull/239
## 2.0.0
- No significant changes since `2.0.0-rc.4`.
## 1.0.4 - 2020-09-12
* Update actix-codec to 0.3.0.
* Workers must be greater than 0. [#167]
## 2.0.0-rc.4
[#167]: https://github.com/actix/actix-net/pull/167
- Update `tokio-uring` dependency to `0.2`.
## 2.0.0-rc.3
## 1.0.3 - 2020-05-19
* Replace deprecated `net2` crate with `socket2` [#140]
- No significant changes since `2.0.0-rc.2`.
[#140]: https://github.com/actix/actix-net/pull/140
## 2.0.0-rc.2
- Simplify `TestServer`.
## 1.0.2 - 2020-02-26
* Avoid error by calling `reregister()` on Windows [#103]
## 2.0.0-rc.1
[#103]: https://github.com/actix/actix-net/pull/103
- Hide implementation details of `Server`.
- `Server` now runs only after awaiting it.
## 2.0.0-beta.9
## 1.0.1 - 2019-12-29
* Rename `.start()` method to `.run()`
- Restore `Arbiter` support lost in `beta.8`.
## 2.0.0-beta.8
## 1.0.0 - 2019-12-11
* Use actix-net releases
- Fix non-unix signal handler.
## 2.0.0-beta.7
## 1.0.0-alpha.4 - 2019-12-08
* Use actix-service 1.0.0-alpha.4
- Server can be started in regular Tokio runtime.
- Expose new `Server` type whose `Future` impl resolves when server stops.
- Rename `Server` to `ServerHandle`.
- Add `Server::handle` to obtain handle to server.
- Rename `ServerBuilder::{maxconn => max_concurrent_connections}`.
- Deprecate crate-level `new` shortcut for server builder.
- Minimum supported Rust version (MSRV) is now 1.52.
## 2.0.0-beta.6
## 1.0.0-alpha.3 - 2019-12-07
* Migrate to tokio 0.2
* Fix compilation on non-unix platforms
* Better handling server configuration
- Add experimental (semver-exempt) `io-uring` feature for enabling async file I/O on linux.
- Server no long listens to `SIGHUP` signal. Previously, the received was not used but did block subsequent exit signals from working.
- Remove `config` module. `ServiceConfig`, `ServiceRuntime` public types are removed due to this change.
- Remove `ServerBuilder::configure`.
## 2.0.0-beta.5
## 1.0.0-alpha.2 - 2019-12-02
* Simplify server service (remove actix-server-config)
* Allow to wait on `Server` until server stops
- Server shutdown notifies all workers to exit regardless if shutdown is graceful. This causes all workers to shutdown immediately in force shutdown case.
## 2.0.0-beta.4
## 0.8.0-alpha.1 - 2019-11-22
* Migrate to `std::future`
- Prevent panic when `shutdown_timeout` is very large. [f9262db]
## 2.0.0-beta.3
## 0.7.0 - 2019-10-04
* Update `rustls` to 0.16
* Minimum required Rust version upped to 1.37.0
- Hidden `ServerBuilder::start` method has been removed. Use `ServerBuilder::run`.
- Add retry for EINTR signal (`io::Interrupted`) in `Accept`'s poll loop.
- Add `ServerBuilder::worker_max_blocking_threads` to customize blocking thread pool size.
- Update `actix-rt` to `2.0.0`.
## 2.0.0-beta.2
## 0.6.1 - 2019-09-25
* Add UDS listening support to `ServerBuilder`
- Merge `actix-testing` to `actix-server` as `test_server` mod.
## 2.0.0-beta.1
## 0.6.0 - 2019-07-18
* Support Unix domain sockets #3
- Added explicit info log message on accept queue pause.
- Prevent double registration of sockets when back-pressure is resolved.
- Update `mio` dependency to `0.7.3`.
- Remove `socket2` dependency.
- `ServerBuilder::backlog` now accepts `u32` instead of `i32`.
- Remove `AcceptNotify` type and pass `WakerQueue` to `Worker` to wake up `Accept`'s `Poll`.
- Convert `mio::net::TcpStream` to `actix_rt::net::TcpStream`(`UnixStream` for uds) using `FromRawFd` and `IntoRawFd`(`FromRawSocket` and `IntoRawSocket` on windows).
- Remove `AsyncRead` and `AsyncWrite` trait bound for `socket::FromStream` trait.
## 1.0.4
## 0.5.1 - 2019-05-18
* ServerBuilder::shutdown_timeout() accepts u64
- Update actix-codec to 0.3.0.
- Workers must be greater than 0.
## 1.0.3
## 0.5.0 - 2019-05-12
* Add `Debug` impl for `SslError`
* Derive debug for `Server` and `ServerCommand`
* Upgrade to actix-service 0.4
- Replace deprecated `net2` crate with `socket2`.
## 1.0.2
## 0.4.3 - 2019-04-16
* Re-export `IoStream` trait
* Depend on `ssl` and `rust-tls` features from actix-server-config
- Avoid error by calling `reregister()` on Windows.
## 1.0.1
## 0.4.2 - 2019-03-30
* Fix SIGINT force shutdown
- Rename `.start()` method to `.run()`
## 1.0.0
## 0.4.1 - 2019-03-14
* `SystemRuntime::on_start()` - allow to run future before server service initialization
- Use actix-net releases
## 1.0.0-alpha.4
## 0.4.0 - 2019-03-12
* Use `ServerConfig` for service factory
* Wrap tcp socket to `Io` type
* Upgrade actix-service
- Use actix-service 1.0.0-alpha.4
## 1.0.0-alpha.3
## 0.3.1 - 2019-03-04
* Add `ServerBuilder::maxconnrate` sets the maximum per-worker number of concurrent connections
* Add helper ssl error `SslError`
* Rename `StreamServiceFactory` to `ServiceFactory`
* Deprecate `StreamServiceFactory`
- Migrate to tokio 0.2
- Fix compilation on non-unix platforms
- Better handling server configuration
## 1.0.0-alpha.2
## 0.3.0 - 2019-03-02
* Use new `NewService` trait
- Simplify server service (remove actix-server-config)
- Allow to wait on `Server` until server stops
## 0.8.0-alpha.1
## 0.2.1 - 2019-02-09
* Drop service response
- Migrate to `std::future`
## 0.7.0
## 0.2.0 - 2019-02-01
* Migrate to actix-service 0.2
* Updated rustls dependency
- Update `rustls` to 0.16
- Minimum required Rust version upped to 1.37.0
## 0.6.1
## 0.1.3 - 2018-12-21
* Fix max concurrent connections handling
- Add UDS listening support to `ServerBuilder`
## 0.6.0
## 0.1.2 - 2018-12-12
* rename ServiceConfig::rt() to ServiceConfig::apply()
* Fix back-pressure for concurrent ssl handshakes
- Support Unix domain sockets #3
## 0.5.1
## 0.1.1 - 2018-12-11
* Fix signal handling on windows
- ServerBuilder::shutdown_timeout() accepts u64
## 0.5.0
## 0.1.0 - 2018-12-09
* Move server to separate crate
- Add `Debug` impl for `SslError`
- Derive debug for `Server` and `ServerCommand`
- Upgrade to actix-service 0.4
## 0.4.3
- Re-export `IoStream` trait
- Depend on `ssl` and `rust-tls` features from actix-server-config
## 0.4.2
- Fix SIGINT force shutdown
## 0.4.1
- `SystemRuntime::on_start()` - allow to run future before server service initialization
## 0.4.0
- Use `ServerConfig` for service factory
- Wrap tcp socket to `Io` type
- Upgrade actix-service
## 0.3.1
- Add `ServerBuilder::maxconnrate` sets the maximum per-worker number of concurrent connections
- Add helper ssl error `SslError`
- Rename `StreamServiceFactory` to `ServiceFactory`
- Deprecate `StreamServiceFactory`
## 0.3.0
- Use new `NewService` trait
## 0.2.1
- Drop service response
## 0.2.0
- Migrate to actix-service 0.2
- Updated rustls dependency
## 0.1.3
- Fix max concurrent connections handling
## 0.1.2
- rename ServiceConfig::rt() to ServiceConfig::apply()
- Fix back-pressure for concurrent ssl handshakes
## 0.1.1
- Fix signal handling on windows
## 0.1.0
- Move server to separate crate

54
actix-server/Cargo.toml Executable file → Normal file
View File

@ -1,40 +1,50 @@
[package]
name = "actix-server"
version = "2.0.0-beta.5"
version = "2.5.1"
authors = [
"Nikolay Kim <fafhrd91@gmail.com>",
"fakeshadow <24548779@qq.com>",
"Nikolay Kim <fafhrd91@gmail.com>",
"Rob Ede <robjtede@icloud.com>",
"Ali MJ Al-Nasrawy <alimjalnasrawy@gmail.com>",
]
description = "General purpose TCP server built for the Actix ecosystem"
keywords = ["network", "framework", "async", "futures"]
repository = "https://github.com/actix/actix-net"
keywords = ["network", "tcp", "server", "framework", "async"]
categories = ["network-programming", "asynchronous"]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-net/tree/master/actix-server"
license = "MIT OR Apache-2.0"
edition = "2018"
edition.workspace = true
rust-version.workspace = true
[lib]
name = "actix_server"
path = "src/lib.rs"
[package.metadata.cargo_check_external_types]
allowed_external_types = ["tokio::*"]
[features]
default = []
io-uring = ["tokio-uring", "actix-rt/io-uring"]
[dependencies]
actix-rt = { version = "2.0.0", default-features = false }
actix-service = "2.0.0"
actix-utils = "3.0.0"
actix-rt = { version = "2.10", default-features = false }
actix-service = "2"
actix-utils = "3"
futures-core = { version = "0.3.17", default-features = false, features = ["alloc"] }
futures-util = { version = "0.3.17", default-features = false, features = ["alloc"] }
mio = { version = "1", features = ["os-poll", "net"] }
socket2 = "0.5"
tokio = { version = "1.23.1", features = ["sync"] }
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
log = "0.4"
mio = { version = "0.7.6", features = ["os-poll", "net"] }
num_cpus = "1.13"
tokio = { version = "1.2", features = ["sync"] }
# runtime for `io-uring` feature
[target.'cfg(target_os = "linux")'.dependencies]
tokio-uring = { version = "0.5", optional = true }
[dev-dependencies]
actix-codec = "0.4.0-beta.1"
actix-rt = "2.0.0"
actix-codec = "0.5"
actix-rt = "2.8"
bytes = "1"
env_logger = "0.8"
futures-util = { version = "0.3.7", default-features = false, features = ["sink"] }
tokio = { version = "1", features = ["io-util"] }
futures-util = { version = "0.3.17", default-features = false, features = ["sink", "async-await-macro"] }
pretty_env_logger = "0.5"
tokio = { version = "1.23.1", features = ["io-util", "rt-multi-thread", "macros", "fs"] }
[lints]
workspace = true

21
actix-server/README.md Normal file
View File

@ -0,0 +1,21 @@
# actix-server
> General purpose TCP server built for the Actix ecosystem.
<!-- prettier-ignore-start -->
[![crates.io](https://img.shields.io/crates/v/actix-server?label=latest)](https://crates.io/crates/actix-server)
[![Documentation](https://docs.rs/actix-server/badge.svg?version=2.5.1)](https://docs.rs/actix-server/2.5.1)
[![Version](https://img.shields.io/badge/rustc-1.52+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html)
![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-server.svg)
<br />
[![Dependency Status](https://deps.rs/crate/actix-server/2.5.1/status.svg)](https://deps.rs/crate/actix-server/2.5.1)
![Download](https://img.shields.io/crates/d/actix-server.svg)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)
<!-- prettier-ignore-end -->
## Resources
- [Library Documentation](https://docs.rs/actix-server)
- [Examples](/actix-server/examples)

View File

@ -0,0 +1,98 @@
//! Simple file-reader TCP server with framed stream.
//!
//! Using the following command:
//!
//! ```sh
//! nc 127.0.0.1 8080
//! ```
//!
//! Follow the prompt and enter a file path, relative or absolute.
#![allow(missing_docs)]
use std::io;
use actix_codec::{Framed, LinesCodec};
use actix_rt::net::TcpStream;
use actix_server::Server;
use actix_service::{fn_service, ServiceFactoryExt as _};
use futures_util::{SinkExt as _, StreamExt as _};
use tokio::{fs::File, io::AsyncReadExt as _};
async fn run() -> io::Result<()> {
pretty_env_logger::formatted_timed_builder()
.parse_env(pretty_env_logger::env_logger::Env::default().default_filter_or("info"));
let addr = ("127.0.0.1", 8080);
tracing::info!("starting server on port: {}", &addr.0);
// Bind socket address and start worker(s). By default, the server uses the number of physical
// CPU cores as the worker count. For this reason, the closure passed to bind needs to return
// a service *factory*; so it can be created once per worker.
Server::build()
.bind("file-reader", addr, move || {
fn_service(move |stream: TcpStream| async move {
// set up codec to use with I/O resource
let mut framed = Framed::new(stream, LinesCodec::default());
loop {
// prompt for file name
framed.send("Type file name to return:").await?;
// wait for next line
match framed.next().await {
Some(Ok(line)) => {
match File::open(&line).await {
Ok(mut file) => {
tracing::info!("reading file: {}", &line);
// read file into String buffer
let mut buf = String::new();
file.read_to_string(&mut buf).await?;
// send String into framed object
framed.send(buf).await?;
// break out of loop and
break;
}
Err(err) => {
tracing::error!("{}", err);
framed
.send("File not found or not readable. Try again.")
.await?;
continue;
}
};
}
// not being able to read a line from the stream is unrecoverable
Some(Err(err)) => return Err(err),
// This EOF won't be hit.
None => continue,
}
}
// close connection after file has been copied to TCP stream
Ok(())
})
.map_err(|err| tracing::error!("service error: {:?}", err))
})?
.workers(2)
.run()
.await
}
#[tokio::main]
async fn main() -> io::Result<()> {
run().await?;
Ok(())
}
// alternatively:
// #[actix_rt::main]
// async fn main() -> io::Result<()> {
// run().await?;
// Ok(())
// }

View File

@ -10,7 +10,7 @@
//! the length of each line it echos and the total size of data sent when the connection is closed.
use std::{
env, io,
io,
sync::{
atomic::{AtomicUsize, Ordering},
Arc,
@ -22,22 +22,20 @@ use actix_server::Server;
use actix_service::{fn_service, ServiceFactoryExt as _};
use bytes::BytesMut;
use futures_util::future::ok;
use log::{error, info};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
#[actix_rt::main]
async fn main() -> io::Result<()> {
env::set_var("RUST_LOG", "info");
env_logger::init();
async fn run() -> io::Result<()> {
pretty_env_logger::formatted_timed_builder()
.parse_env(pretty_env_logger::env_logger::Env::default().default_filter_or("info"));
let count = Arc::new(AtomicUsize::new(0));
let addr = ("127.0.0.1", 8080);
info!("starting server on port: {}", &addr.0);
tracing::info!("starting server on port: {}", &addr.0);
// Bind socket address and start worker(s). By default, the server uses the number of available
// logical CPU cores as the worker count. For this reason, the closure passed to bind needs
// to return a service *factory*; so it can be created once per worker.
// Bind socket address and start worker(s). By default, the server uses the number of physical
// CPU cores as the worker count. For this reason, the closure passed to bind needs to return
// a service *factory*; so it can be created once per worker.
Server::build()
.bind("echo", addr, move || {
let count = Arc::clone(&count);
@ -60,14 +58,14 @@ async fn main() -> io::Result<()> {
// more bytes to process
Ok(bytes_read) => {
info!("[{}] read {} bytes", num, bytes_read);
tracing::info!("[{}] read {} bytes", num, bytes_read);
stream.write_all(&buf[size..]).await.unwrap();
size += bytes_read;
}
// stream error; bail from loop with error
Err(err) => {
error!("Stream Error: {:?}", err);
tracing::error!("stream error: {:?}", err);
return Err(());
}
}
@ -77,14 +75,27 @@ async fn main() -> io::Result<()> {
Ok((buf.freeze(), size))
}
})
.map_err(|err| error!("Service Error: {:?}", err))
.map_err(|err| tracing::error!("service error: {:?}", err))
.and_then(move |(_, size)| {
let num = num2.load(Ordering::SeqCst);
info!("[{}] total bytes read: {}", num, size);
tracing::info!("[{}] total bytes read: {}", num, size);
ok(size)
})
})?
.workers(1)
.workers(2)
.run()
.await
}
#[tokio::main]
async fn main() -> io::Result<()> {
run().await?;
Ok(())
}
// alternatively:
// #[actix_rt::main]
// async fn main() -> io::Result<()> {
// run().await?;
// Ok(())
// }

View File

@ -1,17 +1,18 @@
use std::time::Duration;
use std::{io, thread};
use std::{io, thread, time::Duration};
use actix_rt::{
time::{sleep, Instant},
System,
};
use log::{error, info};
use actix_rt::time::Instant;
use mio::{Interest, Poll, Token as MioToken};
use tracing::{debug, error, info};
use crate::server::Server;
use crate::socket::MioListener;
use crate::waker_queue::{WakerInterest, WakerQueue, WAKER_TOKEN};
use crate::worker::{Conn, WorkerHandleAccept};
use crate::{
availability::Availability,
socket::MioListener,
waker_queue::{WakerInterest, WakerQueue, WAKER_TOKEN},
worker::{Conn, ServerWorker, WorkerHandleAccept, WorkerHandleServer},
ServerBuilder, ServerHandle,
};
const TIMEOUT_DURATION_ON_ERROR: Duration = Duration::from_millis(510);
struct ServerSocketInfo {
token: usize,
@ -20,211 +21,116 @@ struct ServerSocketInfo {
/// Timeout is used to mark the deadline when this socket's listener should be registered again
/// after an error.
timeout: Option<Instant>,
timeout: Option<actix_rt::time::Instant>,
}
/// Accept loop would live with `ServerBuilder`.
///
/// It's tasked with construct `Poll` instance and `WakerQueue` which would be distributed to
/// `Accept` and `Worker`.
///
/// It would also listen to `ServerCommand` and push interests to `WakerQueue`.
pub(crate) struct AcceptLoop {
srv: Option<Server>,
poll: Option<Poll>,
waker: WakerQueue,
}
impl AcceptLoop {
pub fn new(srv: Server) -> Self {
let poll = Poll::new().unwrap_or_else(|e| panic!("Can not create `mio::Poll`: {}", e));
let waker = WakerQueue::new(poll.registry())
.unwrap_or_else(|e| panic!("Can not create `mio::Waker`: {}", e));
Self {
srv: Some(srv),
poll: Some(poll),
waker,
}
}
pub(crate) fn waker_owned(&self) -> WakerQueue {
self.waker.clone()
}
pub fn wake(&self, i: WakerInterest) {
self.waker.wake(i);
}
pub(crate) fn start(
&mut self,
socks: Vec<(usize, MioListener)>,
handles: Vec<WorkerHandleAccept>,
) {
let srv = self.srv.take().expect("Can not re-use AcceptInfo");
let poll = self.poll.take().unwrap();
let waker = self.waker.clone();
Accept::start(poll, waker, socks, srv, handles);
}
}
/// poll instance of the server.
struct Accept {
/// Poll instance of the server.
pub(crate) struct Accept {
poll: Poll,
waker: WakerQueue,
waker_queue: WakerQueue,
handles: Vec<WorkerHandleAccept>,
srv: Server,
srv: ServerHandle,
next: usize,
avail: Availability,
/// use the smallest duration from sockets timeout.
timeout: Option<Duration>,
paused: bool,
}
/// Array of u128 with every bit as marker for a worker handle's availability.
struct Availability([u128; 4]);
impl Default for Availability {
fn default() -> Self {
Self([0; 4])
}
}
impl Availability {
/// Check if any worker handle is available
#[inline(always)]
fn available(&self) -> bool {
self.0.iter().any(|a| *a != 0)
}
/// Check if worker handle is available by index
#[inline(always)]
fn get_available(&self, idx: usize) -> bool {
let (offset, idx) = Self::offset(idx);
self.0[offset] & (1 << idx as u128) != 0
}
/// Set worker handle available state by index.
fn set_available(&mut self, idx: usize, avail: bool) {
let (offset, idx) = Self::offset(idx);
let off = 1 << idx as u128;
if avail {
self.0[offset] |= off;
} else {
self.0[offset] &= !off
}
}
/// Set all worker handle to available state.
/// This would result in a re-check on all workers' availability.
fn set_available_all(&mut self, handles: &[WorkerHandleAccept]) {
handles.iter().for_each(|handle| {
self.set_available(handle.idx(), true);
})
}
/// Get offset and adjusted index of given worker handle index.
fn offset(idx: usize) -> (usize, usize) {
if idx < 128 {
(0, idx)
} else if idx < 128 * 2 {
(1, idx - 128)
} else if idx < 128 * 3 {
(2, idx - 128 * 2)
} else if idx < 128 * 4 {
(3, idx - 128 * 3)
} else {
panic!("Max WorkerHandle count is 512")
}
}
}
/// This function defines errors that are per-connection. Which basically
/// means that if we get this error from `accept()` system call it means
/// next connection might be ready to be accepted.
///
/// All other errors will incur a timeout before next `accept()` is performed.
/// The timeout is useful to handle resource exhaustion errors like ENFILE
/// and EMFILE. Otherwise, could enter into tight loop.
fn connection_error(e: &io::Error) -> bool {
e.kind() == io::ErrorKind::ConnectionRefused
|| e.kind() == io::ErrorKind::ConnectionAborted
|| e.kind() == io::ErrorKind::ConnectionReset
}
impl Accept {
pub(crate) fn start(
poll: Poll,
waker: WakerQueue,
socks: Vec<(usize, MioListener)>,
srv: Server,
handles: Vec<WorkerHandleAccept>,
) {
// Accept runs in its own thread and would want to spawn additional futures to current
// actix system.
let sys = System::current();
thread::Builder::new()
.name("actix-server accept loop".to_owned())
.spawn(move || {
System::set_current(sys);
let (mut accept, mut sockets) =
Accept::new_with_sockets(poll, waker, socks, handles, srv);
sockets: Vec<(usize, MioListener)>,
builder: &ServerBuilder,
) -> io::Result<(WakerQueue, Vec<WorkerHandleServer>, thread::JoinHandle<()>)> {
let handle_server = ServerHandle::new(builder.cmd_tx.clone());
accept.poll_with(&mut sockets);
// construct poll instance and its waker
let poll = Poll::new()?;
let waker_queue = WakerQueue::new(poll.registry())?;
// start workers and collect handles
let (handles_accept, handles_server) = (0..builder.threads)
.map(|idx| {
// clone service factories
let factories = builder
.factories
.iter()
.map(|f| f.clone_factory())
.collect::<Vec<_>>();
// start worker using service factories
ServerWorker::start(idx, factories, waker_queue.clone(), builder.worker_config)
})
.unwrap();
.collect::<io::Result<Vec<_>>>()?
.into_iter()
.unzip();
let (mut accept, mut sockets) = Accept::new_with_sockets(
poll,
waker_queue.clone(),
sockets,
handles_accept,
handle_server,
)?;
let accept_handle = thread::Builder::new()
.name("actix-server acceptor".to_owned())
.spawn(move || accept.poll_with(&mut sockets))
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
Ok((waker_queue, handles_server, accept_handle))
}
fn new_with_sockets(
poll: Poll,
waker: WakerQueue,
socks: Vec<(usize, MioListener)>,
handles: Vec<WorkerHandleAccept>,
srv: Server,
) -> (Accept, Vec<ServerSocketInfo>) {
let sockets = socks
waker_queue: WakerQueue,
sockets: Vec<(usize, MioListener)>,
accept_handles: Vec<WorkerHandleAccept>,
server_handle: ServerHandle,
) -> io::Result<(Accept, Box<[ServerSocketInfo]>)> {
let sockets = sockets
.into_iter()
.map(|(token, mut lst)| {
// Start listening for incoming connections
poll.registry()
.register(&mut lst, MioToken(token), Interest::READABLE)
.unwrap_or_else(|e| panic!("Can not register io: {}", e));
.register(&mut lst, MioToken(token), Interest::READABLE)?;
ServerSocketInfo {
Ok(ServerSocketInfo {
token,
lst,
timeout: None,
}
})
})
.collect();
.collect::<io::Result<_>>()?;
let mut avail = Availability::default();
// Assume all handles are avail at construct time.
avail.set_available_all(&handles);
avail.set_available_all(&accept_handles);
let accept = Accept {
poll,
waker,
handles,
srv,
waker_queue,
handles: accept_handles,
srv: server_handle,
next: 0,
avail,
timeout: None,
paused: false,
};
(accept, sockets)
Ok((accept, sockets))
}
/// blocking wait for readiness events triggered by mio
fn poll_with(&mut self, sockets: &mut [ServerSocketInfo]) {
let mut events = mio::Events::with_capacity(128);
let mut events = mio::Events::with_capacity(256);
loop {
if let Err(e) = self.poll.poll(&mut events, None) {
match e.kind() {
if let Err(err) = self.poll.poll(&mut events, self.timeout) {
match err.kind() {
io::ErrorKind::Interrupted => {}
_ => panic!("Poll error: {}", e),
_ => panic!("Poll error: {}", err),
}
}
@ -234,7 +140,7 @@ impl Accept {
WAKER_TOKEN => {
let exit = self.handle_waker(sockets);
if exit {
info!("Accept is stopped.");
info!("accept thread stopped");
return;
}
}
@ -244,6 +150,9 @@ impl Accept {
}
}
}
// check for timeout and re-register sockets
self.process_timeout(sockets);
}
}
@ -252,11 +161,13 @@ impl Accept {
// a loop that would try to drain the command channel. It's yet unknown
// if it's necessary/good practice to actively drain the waker queue.
loop {
// take guard with every iteration so no new interest can be added
// until the current task is done.
let mut guard = self.waker.guard();
// Take guard with every iteration so no new interests can be added until the current
// task is done. Take care not to take the guard again inside this loop.
let mut guard = self.waker_queue.guard();
#[allow(clippy::significant_drop_in_scrutinee)]
match guard.pop_front() {
// worker notify it becomes available.
// Worker notified it became available.
Some(WakerInterest::WorkerAvailable(idx)) => {
drop(guard);
@ -266,7 +177,8 @@ impl Accept {
self.accept_all(sockets);
}
}
// a new worker thread is made and it's handle would be added to Accept
// A new worker thread has been created so store its handle.
Some(WakerInterest::Worker(handle)) => {
drop(guard);
@ -277,12 +189,7 @@ impl Accept {
self.accept_all(sockets);
}
}
// got timer interest and it's time to try register socket(s) again
Some(WakerInterest::Timer) => {
drop(guard);
self.process_timer(sockets)
}
Some(WakerInterest::Pause) => {
drop(guard);
@ -292,6 +199,7 @@ impl Accept {
self.deregister_all(sockets);
}
}
Some(WakerInterest::Resume) => {
drop(guard);
@ -305,6 +213,7 @@ impl Accept {
self.accept_all(sockets);
}
}
Some(WakerInterest::Stop) => {
if !self.paused {
self.deregister_all(sockets);
@ -312,6 +221,7 @@ impl Accept {
return true;
}
// waker queue is drained
None => {
// Reset the WakerQueue before break so it does not grow infinitely
@ -323,25 +233,44 @@ impl Accept {
}
}
fn process_timer(&self, sockets: &mut [ServerSocketInfo]) {
let now = Instant::now();
sockets
.iter_mut()
// Only sockets that had an associated timeout were deregistered.
.filter(|info| info.timeout.is_some())
.for_each(|info| {
let inst = info.timeout.take().unwrap();
fn process_timeout(&mut self, sockets: &mut [ServerSocketInfo]) {
// always remove old timeouts
if self.timeout.take().is_some() {
let now = Instant::now();
if now < inst {
info.timeout = Some(inst);
} else if !self.paused {
self.register_logged(info);
sockets
.iter_mut()
// Only sockets that had an associated timeout were deregistered.
.filter(|info| info.timeout.is_some())
.for_each(|info| {
let inst = info.timeout.take().unwrap();
if now < inst {
// still timed out; try to set new timeout
info.timeout = Some(inst);
self.set_timeout(inst - now);
} else if !self.paused {
// timeout expired; register socket again
self.register_logged(info);
}
// Drop the timeout if server is paused and socket timeout is expired.
// When server recovers from pause it will register all sockets without
// a timeout value so this socket register will be delayed till then.
});
}
}
/// Update accept timeout with `duration` if it is shorter than current timeout.
fn set_timeout(&mut self, duration: Duration) {
match self.timeout {
Some(ref mut timeout) => {
if *timeout > duration {
*timeout = duration;
}
// Drop the timeout if server is paused and socket timeout is expired.
// When server recovers from pause it will register all sockets without
// a timeout value so this socket register will be delayed till then.
});
}
None => self.timeout = Some(duration),
}
}
#[cfg(not(target_os = "windows"))]
@ -370,16 +299,16 @@ impl Accept {
fn register_logged(&self, info: &mut ServerSocketInfo) {
match self.register(info) {
Ok(_) => info!("Resume accepting connections on {}", info.lst.local_addr()),
Err(e) => error!("Can not register server socket {}", e),
Ok(_) => debug!("resume accepting connections on {}", info.lst.local_addr()),
Err(err) => error!("can not register server socket {}", err),
}
}
fn deregister_logged(&self, info: &mut ServerSocketInfo) {
match self.poll.registry().deregister(&mut info.lst) {
Ok(_) => info!("Paused accepting connections on {}", info.lst.local_addr()),
Err(e) => {
error!("Can not deregister server socket {}", e)
Ok(_) => debug!("paused accepting connections on {}", info.lst.local_addr()),
Err(err) => {
error!("can not deregister server socket {}", err)
}
}
}
@ -387,12 +316,12 @@ impl Accept {
fn deregister_all(&self, sockets: &mut [ServerSocketInfo]) {
// This is a best effort implementation with following limitation:
//
// Every ServerSocketInfo with associate timeout will be skipped and it's timeout
// is removed in the process.
// Every ServerSocketInfo with associated timeout will be skipped and it's timeout is
// removed in the process.
//
// Therefore WakerInterest::Pause followed by WakerInterest::Resume in a very short
// gap (less than 500ms) would cause all timing out ServerSocketInfos be reregistered
// before expected timing.
// Therefore WakerInterest::Pause followed by WakerInterest::Resume in a very short gap
// (less than 500ms) would cause all timing out ServerSocketInfos be re-registered before
// expected timing.
sockets
.iter_mut()
// Take all timeout.
@ -423,7 +352,7 @@ impl Accept {
self.remove_next();
if self.handles.is_empty() {
error!("No workers");
error!("no workers");
// All workers are gone and Conn is nowhere to be sent.
// Treat this situation as Ok and drop Conn.
return Ok(());
@ -469,10 +398,10 @@ impl Accept {
let conn = Conn { io, token };
self.accept_one(conn);
}
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => return,
Err(ref e) if connection_error(e) => continue,
Err(e) => {
error!("Error accepting connection: {}", e);
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => return,
Err(ref err) if connection_error(err) => continue,
Err(err) => {
error!("error accepting connection: {}", err);
// deregister listener temporary
self.deregister_logged(info);
@ -481,13 +410,7 @@ impl Accept {
// the poll would need it mark which socket and when it's
// listener should be registered
info.timeout = Some(Instant::now() + Duration::from_millis(500));
// after the sleep a Timer interest is sent to Accept Poll
let waker = self.waker.clone();
System::current().arbiter().spawn(async move {
sleep(Duration::from_millis(510)).await;
waker.wake(WakerInterest::Timer);
});
self.set_timeout(TIMEOUT_DURATION_ON_ERROR);
return;
}
@ -526,67 +449,14 @@ impl Accept {
}
}
#[cfg(test)]
mod test {
use super::Availability;
fn single(aval: &mut Availability, idx: usize) {
aval.set_available(idx, true);
assert!(aval.available());
aval.set_available(idx, true);
aval.set_available(idx, false);
assert!(!aval.available());
aval.set_available(idx, false);
assert!(!aval.available());
}
fn multi(aval: &mut Availability, mut idx: Vec<usize>) {
idx.iter().for_each(|idx| aval.set_available(*idx, true));
assert!(aval.available());
while let Some(idx) = idx.pop() {
assert!(aval.available());
aval.set_available(idx, false);
}
assert!(!aval.available());
}
#[test]
fn availability() {
let mut aval = Availability::default();
single(&mut aval, 1);
single(&mut aval, 128);
single(&mut aval, 256);
single(&mut aval, 511);
let idx = (0..511).filter(|i| i % 3 == 0 && i % 5 == 0).collect();
multi(&mut aval, idx);
multi(&mut aval, (0..511).collect())
}
#[test]
#[should_panic]
fn overflow() {
let mut aval = Availability::default();
single(&mut aval, 512);
}
#[test]
fn pin_point() {
let mut aval = Availability::default();
aval.set_available(438, true);
aval.set_available(479, true);
assert_eq!(aval.0[3], 1 << (438 - 384) | 1 << (479 - 384));
}
/// This function defines errors that are per-connection; if we get this error from the `accept()`
/// system call it means the next connection might be ready to be accepted.
///
/// All other errors will incur a timeout before next `accept()` call is attempted. The timeout is
/// useful to handle resource exhaustion errors like `ENFILE` and `EMFILE`. Otherwise, it could
/// enter into a temporary spin loop.
fn connection_error(e: &io::Error) -> bool {
e.kind() == io::ErrorKind::ConnectionRefused
|| e.kind() == io::ErrorKind::ConnectionAborted
|| e.kind() == io::ErrorKind::ConnectionReset
}

View File

@ -0,0 +1,121 @@
use crate::worker::WorkerHandleAccept;
/// Array of u128 with every bit as marker for a worker handle's availability.
#[derive(Debug, Default)]
pub(crate) struct Availability([u128; 4]);
impl Availability {
/// Check if any worker handle is available
#[inline(always)]
pub(crate) fn available(&self) -> bool {
self.0.iter().any(|a| *a != 0)
}
/// Check if worker handle is available by index
#[inline(always)]
pub(crate) fn get_available(&self, idx: usize) -> bool {
let (offset, idx) = Self::offset(idx);
self.0[offset] & (1 << idx as u128) != 0
}
/// Set worker handle available state by index.
pub(crate) fn set_available(&mut self, idx: usize, avail: bool) {
let (offset, idx) = Self::offset(idx);
let off = 1 << idx as u128;
if avail {
self.0[offset] |= off;
} else {
self.0[offset] &= !off
}
}
/// Set all worker handle to available state.
/// This would result in a re-check on all workers' availability.
pub(crate) fn set_available_all(&mut self, handles: &[WorkerHandleAccept]) {
handles.iter().for_each(|handle| {
self.set_available(handle.idx(), true);
})
}
/// Get offset and adjusted index of given worker handle index.
pub(crate) fn offset(idx: usize) -> (usize, usize) {
if idx < 128 {
(0, idx)
} else if idx < 128 * 2 {
(1, idx - 128)
} else if idx < 128 * 3 {
(2, idx - 128 * 2)
} else if idx < 128 * 4 {
(3, idx - 128 * 3)
} else {
panic!("Max WorkerHandle count is 512")
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn single(aval: &mut Availability, idx: usize) {
aval.set_available(idx, true);
assert!(aval.available());
aval.set_available(idx, true);
aval.set_available(idx, false);
assert!(!aval.available());
aval.set_available(idx, false);
assert!(!aval.available());
}
fn multi(aval: &mut Availability, mut idx: Vec<usize>) {
idx.iter().for_each(|idx| aval.set_available(*idx, true));
assert!(aval.available());
while let Some(idx) = idx.pop() {
assert!(aval.available());
aval.set_available(idx, false);
}
assert!(!aval.available());
}
#[test]
fn availability() {
let mut aval = Availability::default();
single(&mut aval, 1);
single(&mut aval, 128);
single(&mut aval, 256);
single(&mut aval, 511);
let idx = (0..511).filter(|i| i % 3 == 0 && i % 5 == 0).collect();
multi(&mut aval, idx);
multi(&mut aval, (0..511).collect())
}
#[test]
#[should_panic]
fn overflow() {
let mut aval = Availability::default();
single(&mut aval, 512);
}
#[test]
fn pin_point() {
let mut aval = Availability::default();
aval.set_available(438, true);
aval.set_available(479, true);
assert_eq!(aval.0[3], 1 << (438 - 384) | 1 << (479 - 384));
}
}

View File

@ -1,43 +1,47 @@
use std::{
future::Future,
io, mem,
pin::Pin,
task::{Context, Poll},
time::Duration,
use std::{io, num::NonZeroUsize, time::Duration};
use actix_rt::net::TcpStream;
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
use crate::{
server::ServerCommand,
service::{InternalServiceFactory, ServerServiceFactory, StreamNewService},
socket::{create_mio_tcp_listener, MioListener, MioTcpListener, StdTcpListener, ToSocketAddrs},
worker::ServerWorkerConfig,
Server,
};
use actix_rt::{self as rt, net::TcpStream, time::sleep, System};
use log::{error, info};
use tokio::sync::{
mpsc::{unbounded_channel, UnboundedReceiver},
oneshot,
};
/// Multipath TCP (MPTCP) preference.
///
/// Currently only useful on Linux.
///
#[cfg_attr(target_os = "linux", doc = "Also see [`ServerBuilder::mptcp()`].")]
#[derive(Debug, Clone)]
pub enum MpTcp {
/// MPTCP will not be used when binding sockets.
Disabled,
use crate::accept::AcceptLoop;
use crate::join_all;
use crate::server::{Server, ServerCommand};
use crate::service::{InternalServiceFactory, ServiceFactory, StreamNewService};
use crate::signals::{Signal, Signals};
use crate::socket::{MioListener, StdSocketAddr, StdTcpListener, ToSocketAddrs};
use crate::socket::{MioTcpListener, MioTcpSocket};
use crate::waker_queue::{WakerInterest, WakerQueue};
use crate::worker::{ServerWorker, ServerWorkerConfig, WorkerHandleAccept, WorkerHandleServer};
/// MPTCP will be attempted when binding sockets. If errors occur, regular TCP will be
/// attempted, too.
TcpFallback,
/// Server builder
/// MPTCP will be used when binding sockets (with no fallback).
NoFallback,
}
/// [Server] builder.
pub struct ServerBuilder {
threads: usize,
token: usize,
backlog: u32,
handles: Vec<(usize, WorkerHandleServer)>,
services: Vec<Box<dyn InternalServiceFactory>>,
sockets: Vec<(usize, String, MioListener)>,
accept: AcceptLoop,
exit: bool,
no_signals: bool,
cmd: UnboundedReceiver<ServerCommand>,
server: Server,
notify: Vec<oneshot::Sender<()>>,
worker_config: ServerWorkerConfig,
pub(crate) threads: usize,
pub(crate) token: usize,
pub(crate) backlog: u32,
pub(crate) factories: Vec<Box<dyn InternalServiceFactory>>,
pub(crate) sockets: Vec<(usize, String, MioListener)>,
pub(crate) mptcp: MpTcp,
pub(crate) exit: bool,
pub(crate) listen_os_signals: bool,
pub(crate) cmd_tx: UnboundedSender<ServerCommand>,
pub(crate) cmd_rx: UnboundedReceiver<ServerCommand>,
pub(crate) worker_config: ServerWorkerConfig,
}
impl Default for ServerBuilder {
@ -49,30 +53,36 @@ impl Default for ServerBuilder {
impl ServerBuilder {
/// Create new Server builder instance
pub fn new() -> ServerBuilder {
let (tx, rx) = unbounded_channel();
let server = Server::new(tx);
let (cmd_tx, cmd_rx) = unbounded_channel();
ServerBuilder {
threads: num_cpus::get(),
threads: std::thread::available_parallelism().map_or(2, NonZeroUsize::get),
token: 0,
handles: Vec::new(),
services: Vec::new(),
factories: Vec::new(),
sockets: Vec::new(),
accept: AcceptLoop::new(server.clone()),
backlog: 2048,
mptcp: MpTcp::Disabled,
exit: false,
no_signals: false,
cmd: rx,
notify: Vec::new(),
server,
listen_os_signals: true,
cmd_tx,
cmd_rx,
worker_config: ServerWorkerConfig::default(),
}
}
/// Set number of workers to start.
/// Sets number of workers to start.
///
/// By default server uses number of available logical cpu as workers
/// count. Workers must be greater than 0.
/// See [`bind()`](Self::bind()) for more details on how worker count affects the number of
/// server factory instantiations.
///
/// The default worker count is the determined by [`std::thread::available_parallelism()`]. See
/// its documentation to determine what behavior you should expect when server is run.
///
/// `num` must be greater than 0.
///
/// # Panics
///
/// Panics if `num` is 0.
pub fn workers(mut self, num: usize) -> Self {
assert_ne!(num, 0, "workers must be greater than 0");
self.threads = num;
@ -99,10 +109,9 @@ impl ServerBuilder {
/// Set the maximum number of pending connections.
///
/// This refers to the number of clients that can be waiting to be served.
/// Exceeding this number results in the client getting an error when
/// attempting to connect. It should only affect servers under significant
/// load.
/// This refers to the number of clients that can be waiting to be served. Exceeding this number
/// results in the client getting an error when attempting to connect. It should only affect
/// servers under significant load.
///
/// Generally set in the 64-2048 range. Default value is 2048.
///
@ -112,26 +121,52 @@ impl ServerBuilder {
self
}
/// Sets MultiPath TCP (MPTCP) preference on bound sockets.
///
/// Multipath TCP (MPTCP) builds on top of TCP to improve connection redundancy and performance
/// by sharing a network data stream across multiple underlying TCP sessions. See [mptcp.dev]
/// for more info about MPTCP itself.
///
/// MPTCP is available on Linux kernel version 5.6 and higher. In addition, you'll also need to
/// ensure the kernel option is enabled using `sysctl net.mptcp.enabled=1`.
///
/// This method will have no effect if called after a `bind()`.
///
/// [mptcp.dev]: https://www.mptcp.dev
#[cfg(target_os = "linux")]
pub fn mptcp(mut self, mptcp_enabled: MpTcp) -> Self {
self.mptcp = mptcp_enabled;
self
}
/// Sets the maximum per-worker number of concurrent connections.
///
/// All socket listeners will stop accepting connections when this limit is
/// reached for each worker.
/// All socket listeners will stop accepting connections when this limit is reached for
/// each worker.
///
/// By default max connections is set to a 25k per worker.
pub fn maxconn(mut self, num: usize) -> Self {
pub fn max_concurrent_connections(mut self, num: usize) -> Self {
self.worker_config.max_concurrent_connections(num);
self
}
/// Stop Actix system.
#[doc(hidden)]
#[deprecated(since = "2.0.0", note = "Renamed to `max_concurrent_connections`.")]
pub fn maxconn(self, num: usize) -> Self {
self.max_concurrent_connections(num)
}
/// Sets flag to stop Actix `System` after server shutdown.
///
/// This has no effect when server is running in a Tokio-only runtime.
pub fn system_exit(mut self) -> Self {
self.exit = true;
self
}
/// Disable signal handling.
/// Disables OS signal handling.
pub fn disable_signals(mut self) -> Self {
self.no_signals = true;
self.listen_os_signals = false;
self
}
@ -147,78 +182,62 @@ impl ServerBuilder {
self
}
/// Add new service to the server.
pub fn bind<F, U, N: AsRef<str>>(mut self, name: N, addr: U, factory: F) -> io::Result<Self>
/// Adds new service to the server.
///
/// Note that, if a DNS lookup is required, resolving hostnames is a blocking operation.
///
/// # Worker Count
///
/// The `factory` will be instantiated multiple times in most scenarios. The number of
/// instantiations is number of [`workers`](Self::workers()) × number of sockets resolved by
/// `addrs`.
///
/// For example, if you've manually set [`workers`](Self::workers()) to 2, and use `127.0.0.1`
/// as the bind `addrs`, then `factory` will be instantiated twice. However, using `localhost`
/// as the bind `addrs` can often resolve to both `127.0.0.1` (IPv4) _and_ `::1` (IPv6), causing
/// the `factory` to be instantiated 4 times (2 workers × 2 bind addresses).
///
/// Using a bind address of `0.0.0.0`, which signals to use all interfaces, may also multiple
/// the number of instantiations in a similar way.
///
/// # Errors
///
/// Returns an `io::Error` if:
/// - `addrs` cannot be resolved into one or more socket addresses;
/// - all the resolved socket addresses are already bound.
pub fn bind<F, U, N>(mut self, name: N, addrs: U, factory: F) -> io::Result<Self>
where
F: ServiceFactory<TcpStream>,
F: ServerServiceFactory<TcpStream>,
U: ToSocketAddrs,
N: AsRef<str>,
{
let sockets = bind_addr(addr, self.backlog)?;
let sockets = bind_addr(addrs, self.backlog, &self.mptcp)?;
tracing::trace!("binding server to: {sockets:?}");
for lst in sockets {
let token = self.next_token();
self.services.push(StreamNewService::create(
self.factories.push(StreamNewService::create(
name.as_ref().to_string(),
token,
factory.clone(),
lst.local_addr()?,
));
self.sockets
.push((token, name.as_ref().to_string(), MioListener::Tcp(lst)));
}
Ok(self)
}
/// Add new unix domain service to the server.
#[cfg(unix)]
pub fn bind_uds<F, U, N>(self, name: N, addr: U, factory: F) -> io::Result<Self>
where
F: ServiceFactory<actix_rt::net::UnixStream>,
N: AsRef<str>,
U: AsRef<std::path::Path>,
{
// The path must not exist when we try to bind.
// Try to remove it to avoid bind error.
if let Err(e) = std::fs::remove_file(addr.as_ref()) {
// NotFound is expected and not an issue. Anything else is.
if e.kind() != std::io::ErrorKind::NotFound {
return Err(e);
}
}
let lst = crate::socket::StdUnixListener::bind(addr)?;
self.listen_uds(name, lst, factory)
}
/// Add new unix domain service to the server.
/// Useful when running as a systemd service and
/// a socket FD can be acquired using the systemd crate.
#[cfg(unix)]
pub fn listen_uds<F, N: AsRef<str>>(
mut self,
name: N,
lst: crate::socket::StdUnixListener,
factory: F,
) -> io::Result<Self>
where
F: ServiceFactory<actix_rt::net::UnixStream>,
{
use std::net::{IpAddr, Ipv4Addr};
lst.set_nonblocking(true)?;
let token = self.next_token();
let addr = StdSocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
self.services.push(StreamNewService::create(
name.as_ref().to_string(),
token,
factory,
addr,
));
self.sockets
.push((token, name.as_ref().to_string(), MioListener::from(lst)));
Ok(self)
}
/// Add new service to the server.
/// Adds service to the server using a socket listener already bound.
///
/// # Worker Count
///
/// The `factory` will be instantiated multiple times in most scenarios. The number of
/// instantiations is: number of [`workers`](Self::workers()).
pub fn listen<F, N: AsRef<str>>(
mut self,
name: N,
@ -226,13 +245,13 @@ impl ServerBuilder {
factory: F,
) -> io::Result<Self>
where
F: ServiceFactory<TcpStream>,
F: ServerServiceFactory<TcpStream>,
{
lst.set_nonblocking(true)?;
let addr = lst.local_addr()?;
let token = self.next_token();
self.services.push(StreamNewService::create(
self.factories.push(StreamNewService::create(
name.as_ref().to_string(),
token,
factory,
@ -246,166 +265,12 @@ impl ServerBuilder {
}
/// Starts processing incoming connections and return server controller.
pub fn run(mut self) -> Server {
pub fn run(self) -> Server {
if self.sockets.is_empty() {
panic!("Server should have at least one bound socket");
} else {
info!("Starting {} workers", self.threads);
// start workers
let handles = (0..self.threads)
.map(|idx| {
let (handle_accept, handle_server) =
self.start_worker(idx, self.accept.waker_owned());
self.handles.push((idx, handle_server));
handle_accept
})
.collect();
// start accept thread
for sock in &self.sockets {
info!("Starting \"{}\" service on {}", sock.1, sock.2);
}
self.accept.start(
mem::take(&mut self.sockets)
.into_iter()
.map(|t| (t.0, t.2))
.collect(),
handles,
);
// handle signals
if !self.no_signals {
Signals::start(self.server.clone());
}
// start http server actor
let server = self.server.clone();
rt::spawn(self);
server
}
}
fn start_worker(
&self,
idx: usize,
waker_queue: WakerQueue,
) -> (WorkerHandleAccept, WorkerHandleServer) {
let services = self.services.iter().map(|v| v.clone_factory()).collect();
ServerWorker::start(idx, services, waker_queue, self.worker_config)
}
fn handle_cmd(&mut self, item: ServerCommand) {
match item {
ServerCommand::Pause(tx) => {
self.accept.wake(WakerInterest::Pause);
let _ = tx.send(());
}
ServerCommand::Resume(tx) => {
self.accept.wake(WakerInterest::Resume);
let _ = tx.send(());
}
ServerCommand::Signal(sig) => {
// Signals support
// Handle `SIGINT`, `SIGTERM`, `SIGQUIT` signals and stop actix system
match sig {
Signal::Int => {
info!("SIGINT received, exiting");
self.exit = true;
self.handle_cmd(ServerCommand::Stop {
graceful: false,
completion: None,
})
}
Signal::Term => {
info!("SIGTERM received, stopping");
self.exit = true;
self.handle_cmd(ServerCommand::Stop {
graceful: true,
completion: None,
})
}
Signal::Quit => {
info!("SIGQUIT received, exiting");
self.exit = true;
self.handle_cmd(ServerCommand::Stop {
graceful: false,
completion: None,
})
}
_ => (),
}
}
ServerCommand::Notify(tx) => {
self.notify.push(tx);
}
ServerCommand::Stop {
graceful,
completion,
} => {
let exit = self.exit;
// stop accept thread
self.accept.wake(WakerInterest::Stop);
let notify = std::mem::take(&mut self.notify);
// stop workers
let stop = self
.handles
.iter()
.map(move |worker| worker.1.stop(graceful))
.collect();
rt::spawn(async move {
if graceful {
let _ = join_all(stop).await;
}
if let Some(tx) = completion {
let _ = tx.send(());
}
for tx in notify {
let _ = tx.send(());
}
if exit {
sleep(Duration::from_millis(300)).await;
System::current().stop();
}
});
}
ServerCommand::WorkerFaulted(idx) => {
let mut found = false;
for i in 0..self.handles.len() {
if self.handles[i].0 == idx {
self.handles.swap_remove(i);
found = true;
break;
}
}
if found {
error!("Worker has died {:?}, restarting", idx);
let mut new_idx = self.handles.len();
'found: loop {
for i in 0..self.handles.len() {
if self.handles[i].0 == new_idx {
new_idx += 1;
continue 'found;
}
}
break;
}
let (handle_accept, handle_server) =
self.start_worker(new_idx, self.accept.waker_owned());
self.handles.push((new_idx, handle_server));
self.accept.wake(WakerInterest::Worker(handle_accept));
}
}
tracing::info!("starting {} workers", self.threads);
Server::new(self)
}
}
@ -416,57 +281,98 @@ impl ServerBuilder {
}
}
impl Future for ServerBuilder {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
loop {
match Pin::new(&mut self.cmd).poll_recv(cx) {
Poll::Ready(Some(it)) => self.as_mut().get_mut().handle_cmd(it),
_ => return Poll::Pending,
#[cfg(unix)]
impl ServerBuilder {
/// Adds new service to the server using a UDS (unix domain socket) address.
///
/// # Worker Count
///
/// The `factory` will be instantiated multiple times in most scenarios. The number of
/// instantiations is: number of [`workers`](Self::workers()).
pub fn bind_uds<F, U, N>(self, name: N, addr: U, factory: F) -> io::Result<Self>
where
F: ServerServiceFactory<actix_rt::net::UnixStream>,
N: AsRef<str>,
U: AsRef<std::path::Path>,
{
// The path must not exist when we try to bind.
// Try to remove it to avoid bind error.
if let Err(err) = std::fs::remove_file(addr.as_ref()) {
// NotFound is expected and not an issue. Anything else is.
if err.kind() != std::io::ErrorKind::NotFound {
return Err(err);
}
}
let lst = crate::socket::StdUnixListener::bind(addr)?;
self.listen_uds(name, lst, factory)
}
/// Adds new service to the server using a UDS (unix domain socket) listener already bound.
///
/// Useful when running as a systemd service and a socket FD is acquired externally.
///
/// # Worker Count
///
/// The `factory` will be instantiated multiple times in most scenarios. The number of
/// instantiations is: number of [`workers`](Self::workers()).
pub fn listen_uds<F, N: AsRef<str>>(
mut self,
name: N,
lst: crate::socket::StdUnixListener,
factory: F,
) -> io::Result<Self>
where
F: ServerServiceFactory<actix_rt::net::UnixStream>,
{
use std::net::{IpAddr, Ipv4Addr};
lst.set_nonblocking(true)?;
let token = self.next_token();
let addr = crate::socket::StdSocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
self.factories.push(StreamNewService::create(
name.as_ref().to_string(),
token,
factory,
addr,
));
self.sockets
.push((token, name.as_ref().to_string(), MioListener::from(lst)));
Ok(self)
}
}
pub(super) fn bind_addr<S: ToSocketAddrs>(
addr: S,
backlog: u32,
mptcp: &MpTcp,
) -> io::Result<Vec<MioTcpListener>> {
let mut err = None;
let mut succ = false;
let mut opt_err = None;
let mut success = false;
let mut sockets = Vec::new();
for addr in addr.to_socket_addrs()? {
match create_tcp_listener(addr, backlog) {
match create_mio_tcp_listener(addr, backlog, mptcp) {
Ok(lst) => {
succ = true;
success = true;
sockets.push(lst);
}
Err(e) => err = Some(e),
Err(err) => opt_err = Some(err),
}
}
if !succ {
if let Some(e) = err.take() {
Err(e)
} else {
Err(io::Error::new(
io::ErrorKind::Other,
"Can not bind to address.",
))
}
} else {
if success {
Ok(sockets)
} else if let Some(err) = opt_err.take() {
Err(err)
} else {
Err(io::Error::new(
io::ErrorKind::Other,
"Can not bind to address.",
))
}
}
fn create_tcp_listener(addr: StdSocketAddr, backlog: u32) -> io::Result<MioTcpListener> {
let socket = match addr {
StdSocketAddr::V4(_) => MioTcpSocket::new_v4()?,
StdSocketAddr::V6(_) => MioTcpSocket::new_v6()?,
};
socket.set_reuseaddr(true)?;
socket.bind(addr)?;
socket.listen(backlog)
}

View File

@ -0,0 +1,56 @@
use std::future::Future;
use tokio::sync::{mpsc::UnboundedSender, oneshot};
use crate::server::ServerCommand;
/// Server handle.
#[derive(Debug, Clone)]
pub struct ServerHandle {
cmd_tx: UnboundedSender<ServerCommand>,
}
impl ServerHandle {
pub(crate) fn new(cmd_tx: UnboundedSender<ServerCommand>) -> Self {
ServerHandle { cmd_tx }
}
pub(crate) fn worker_faulted(&self, idx: usize) {
let _ = self.cmd_tx.send(ServerCommand::WorkerFaulted(idx));
}
/// Pause accepting incoming connections.
///
/// May drop socket pending connection. All open connections remain active.
pub fn pause(&self) -> impl Future<Output = ()> {
let (tx, rx) = oneshot::channel();
let _ = self.cmd_tx.send(ServerCommand::Pause(tx));
async {
let _ = rx.await;
}
}
/// Resume accepting incoming connections.
pub fn resume(&self) -> impl Future<Output = ()> {
let (tx, rx) = oneshot::channel();
let _ = self.cmd_tx.send(ServerCommand::Resume(tx));
async {
let _ = rx.await;
}
}
/// Stop incoming connection processing, stop all workers and exit.
pub fn stop(&self, graceful: bool) -> impl Future<Output = ()> {
let (tx, rx) = oneshot::channel();
let _ = self.cmd_tx.send(ServerCommand::Stop {
graceful,
completion: Some(tx),
force_system_stop: false,
});
async {
let _ = rx.await;
}
}
}

View File

@ -0,0 +1,78 @@
use std::{
future::Future,
pin::Pin,
task::{Context, Poll},
};
use futures_core::future::BoxFuture;
// a poor man's join future. joined future is only used when starting/stopping the server.
// pin_project and pinned futures are overkill for this task.
pub(crate) struct JoinAll<T> {
fut: Vec<JoinFuture<T>>,
}
pub(crate) fn join_all<T>(fut: Vec<impl Future<Output = T> + Send + 'static>) -> JoinAll<T> {
let fut = fut
.into_iter()
.map(|f| JoinFuture::Future(Box::pin(f)))
.collect();
JoinAll { fut }
}
enum JoinFuture<T> {
Future(BoxFuture<'static, T>),
Result(Option<T>),
}
impl<T> Unpin for JoinAll<T> {}
impl<T> Future for JoinAll<T> {
type Output = Vec<T>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut ready = true;
let this = self.get_mut();
for fut in this.fut.iter_mut() {
if let JoinFuture::Future(f) = fut {
match f.as_mut().poll(cx) {
Poll::Ready(t) => {
*fut = JoinFuture::Result(Some(t));
}
Poll::Pending => ready = false,
}
}
}
if ready {
let mut res = Vec::new();
for fut in this.fut.iter_mut() {
if let JoinFuture::Result(f) = fut {
res.push(f.take().unwrap());
}
}
Poll::Ready(res)
} else {
Poll::Pending
}
}
}
#[cfg(test)]
mod test {
use actix_utils::future::ready;
use super::*;
#[actix_rt::test]
async fn test_join_all() {
let futs = vec![ready(Ok(1)), ready(Err(3)), ready(Ok(9))];
let mut res = join_all(futs).await.into_iter();
assert_eq!(Ok(1), res.next().unwrap());
assert_eq!(Err(3), res.next().unwrap());
assert_eq!(Ok(9), res.next().unwrap());
}
}

View File

@ -1,11 +1,13 @@
//! General purpose TCP server.
#![deny(rust_2018_idioms, nonstandard_style)]
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
mod accept;
mod availability;
mod builder;
mod handle;
mod join_all;
mod server;
mod service;
mod signals;
@ -14,90 +16,19 @@ mod test_server;
mod waker_queue;
mod worker;
pub use self::builder::ServerBuilder;
pub use self::server::Server;
pub use self::service::ServiceFactory;
pub use self::test_server::TestServer;
#[doc(hidden)]
pub use self::socket::FromStream;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
pub use self::{
builder::{MpTcp, ServerBuilder},
handle::ServerHandle,
server::Server,
service::ServerServiceFactory,
test_server::TestServer,
};
/// Start server building process
#[doc(hidden)]
#[deprecated(since = "2.0.0", note = "Use `Server::build()`.")]
pub fn new() -> ServerBuilder {
ServerBuilder::default()
}
// a poor man's join future. joined future is only used when starting/stopping the server.
// pin_project and pinned futures are overkill for this task.
pub(crate) struct JoinAll<T> {
fut: Vec<JoinFuture<T>>,
}
pub(crate) fn join_all<T>(fut: Vec<impl Future<Output = T> + 'static>) -> JoinAll<T> {
let fut = fut
.into_iter()
.map(|f| JoinFuture::Future(Box::pin(f)))
.collect();
JoinAll { fut }
}
enum JoinFuture<T> {
Future(Pin<Box<dyn Future<Output = T>>>),
Result(Option<T>),
}
impl<T> Unpin for JoinAll<T> {}
impl<T> Future for JoinAll<T> {
type Output = Vec<T>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut ready = true;
let this = self.get_mut();
for fut in this.fut.iter_mut() {
if let JoinFuture::Future(f) = fut {
match f.as_mut().poll(cx) {
Poll::Ready(t) => {
*fut = JoinFuture::Result(Some(t));
}
Poll::Pending => ready = false,
}
}
}
if ready {
let mut res = Vec::new();
for fut in this.fut.iter_mut() {
if let JoinFuture::Result(f) = fut {
res.push(f.take().unwrap());
}
}
Poll::Ready(res)
} else {
Poll::Pending
}
}
}
#[cfg(test)]
mod test {
use super::*;
use actix_utils::future::ready;
#[actix_rt::test]
async fn test_join_all() {
let futs = vec![ready(Ok(1)), ready(Err(3)), ready(Ok(9))];
let mut res = join_all(futs).await.into_iter();
assert_eq!(Ok(1), res.next().unwrap());
assert_eq!(Err(3), res.next().unwrap());
assert_eq!(Ok(9), res.next().unwrap());
}
}

View File

@ -1,112 +1,368 @@
use std::future::Future;
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::{
future::Future,
io, mem,
pin::Pin,
task::{Context, Poll},
thread,
time::Duration,
};
use tokio::sync::mpsc::UnboundedSender;
use tokio::sync::oneshot;
use actix_rt::{time::sleep, System};
use futures_core::{future::BoxFuture, Stream};
use futures_util::stream::StreamExt as _;
use tokio::sync::{mpsc::UnboundedReceiver, oneshot};
use tracing::{error, info};
use crate::builder::ServerBuilder;
use crate::signals::Signal;
use crate::{
accept::Accept,
builder::ServerBuilder,
join_all::join_all,
service::InternalServiceFactory,
signals::{SignalKind, Signals},
waker_queue::{WakerInterest, WakerQueue},
worker::{ServerWorker, ServerWorkerConfig, WorkerHandleServer},
ServerHandle,
};
#[derive(Debug)]
pub(crate) enum ServerCommand {
/// Worker failed to accept connection, indicating a probable panic.
///
/// Contains index of faulted worker.
WorkerFaulted(usize),
/// Pause accepting connections.
///
/// Contains return channel to notify caller of successful state change.
Pause(oneshot::Sender<()>),
/// Resume accepting connections.
///
/// Contains return channel to notify caller of successful state change.
Resume(oneshot::Sender<()>),
Signal(Signal),
/// Whether to try and shut down gracefully
/// Stop accepting connections and begin shutdown procedure.
Stop {
/// True if shut down should be graceful.
graceful: bool,
/// Return channel to notify caller that shutdown is complete.
completion: Option<oneshot::Sender<()>>,
/// Force System exit when true, overriding `ServerBuilder::system_exit()` if it is false.
force_system_stop: bool,
},
/// Notify of server stop
Notify(oneshot::Sender<()>),
}
#[derive(Debug)]
pub struct Server(
UnboundedSender<ServerCommand>,
Option<oneshot::Receiver<()>>,
);
/// General purpose TCP server that runs services receiving Tokio `TcpStream`s.
///
/// Handles creating worker threads, restarting faulted workers, connection accepting, and
/// back-pressure logic.
///
/// Creates a worker per CPU core (or the number specified in [`ServerBuilder::workers`]) and
/// distributes connections with a round-robin strategy.
///
/// The [Server] must be awaited or polled in order to start running. It will resolve when the
/// server has fully shut down.
///
/// # Shutdown Signals
/// On UNIX systems, `SIGTERM` will start a graceful shutdown and `SIGQUIT` or `SIGINT` will start a
/// forced shutdown. On Windows, a Ctrl-C signal will start a forced shutdown.
///
/// A graceful shutdown will wait for all workers to stop first.
///
/// # Examples
/// The following is a TCP echo server. Test using `telnet 127.0.0.1 8080`.
///
/// ```no_run
/// use std::io;
///
/// use actix_rt::net::TcpStream;
/// use actix_server::Server;
/// use actix_service::{fn_service, ServiceFactoryExt as _};
/// use bytes::BytesMut;
/// use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
///
/// #[actix_rt::main]
/// async fn main() -> io::Result<()> {
/// let bind_addr = ("127.0.0.1", 8080);
///
/// Server::build()
/// .bind("echo", bind_addr, move || {
/// fn_service(move |mut stream: TcpStream| {
/// async move {
/// let mut size = 0;
/// let mut buf = BytesMut::new();
///
/// loop {
/// match stream.read_buf(&mut buf).await {
/// // end of stream; bail from loop
/// Ok(0) => break,
///
/// // write bytes back to stream
/// Ok(bytes_read) => {
/// stream.write_all(&buf[size..]).await.unwrap();
/// size += bytes_read;
/// }
///
/// Err(err) => {
/// eprintln!("Stream Error: {:?}", err);
/// return Err(());
/// }
/// }
/// }
///
/// Ok(())
/// }
/// })
/// .map_err(|err| eprintln!("Service Error: {:?}", err))
/// })?
/// .run()
/// .await
/// }
/// ```
#[must_use = "Server does nothing unless you `.await` or poll it"]
pub struct Server {
handle: ServerHandle,
fut: BoxFuture<'static, io::Result<()>>,
}
impl Server {
pub(crate) fn new(tx: UnboundedSender<ServerCommand>) -> Self {
Server(tx, None)
}
/// Start server building process
/// Create server build.
pub fn build() -> ServerBuilder {
ServerBuilder::default()
}
pub(crate) fn signal(&self, sig: Signal) {
let _ = self.0.send(ServerCommand::Signal(sig));
pub(crate) fn new(builder: ServerBuilder) -> Self {
Server {
handle: ServerHandle::new(builder.cmd_tx.clone()),
fut: Box::pin(ServerInner::run(builder)),
}
}
pub(crate) fn worker_faulted(&self, idx: usize) {
let _ = self.0.send(ServerCommand::WorkerFaulted(idx));
}
/// Pause accepting incoming connections
/// Get a `Server` handle that can be used issue commands and change it's state.
///
/// If socket contains some pending connection, they might be dropped.
/// All opened connection remains active.
pub fn pause(&self) -> impl Future<Output = ()> {
let (tx, rx) = oneshot::channel();
let _ = self.0.send(ServerCommand::Pause(tx));
async {
let _ = rx.await;
}
}
/// Resume accepting incoming connections
pub fn resume(&self) -> impl Future<Output = ()> {
let (tx, rx) = oneshot::channel();
let _ = self.0.send(ServerCommand::Resume(tx));
async {
let _ = rx.await;
}
}
/// Stop incoming connection processing, stop all workers and exit.
///
/// If server starts with `spawn()` method, then spawned thread get terminated.
pub fn stop(&self, graceful: bool) -> impl Future<Output = ()> {
let (tx, rx) = oneshot::channel();
let _ = self.0.send(ServerCommand::Stop {
graceful,
completion: Some(tx),
});
async {
let _ = rx.await;
}
}
}
impl Clone for Server {
fn clone(&self) -> Self {
Self(self.0.clone(), None)
/// See [ServerHandle](ServerHandle) for usage.
pub fn handle(&self) -> ServerHandle {
self.handle.clone()
}
}
impl Future for Server {
type Output = io::Result<()>;
#[inline]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
Pin::new(&mut Pin::into_inner(self).fut).poll(cx)
}
}
if this.1.is_none() {
let (tx, rx) = oneshot::channel();
if this.0.send(ServerCommand::Notify(tx)).is_err() {
return Poll::Ready(Ok(()));
pub struct ServerInner {
worker_handles: Vec<WorkerHandleServer>,
accept_handle: Option<thread::JoinHandle<()>>,
worker_config: ServerWorkerConfig,
services: Vec<Box<dyn InternalServiceFactory>>,
waker_queue: WakerQueue,
system_stop: bool,
stopping: bool,
}
impl ServerInner {
async fn run(builder: ServerBuilder) -> io::Result<()> {
let (mut this, mut mux) = Self::run_sync(builder)?;
while let Some(cmd) = mux.next().await {
this.handle_cmd(cmd).await;
if this.stopping {
break;
}
this.1 = Some(rx);
}
match Pin::new(this.1.as_mut().unwrap()).poll(cx) {
Poll::Pending => Poll::Pending,
Poll::Ready(_) => Poll::Ready(Ok(())),
Ok(())
}
fn run_sync(mut builder: ServerBuilder) -> io::Result<(Self, ServerEventMultiplexer)> {
// Give log information on what runtime will be used.
let is_actix = actix_rt::System::try_current().is_some();
let is_tokio = tokio::runtime::Handle::try_current().is_ok();
match (is_actix, is_tokio) {
(true, _) => info!("Actix runtime found; starting in Actix runtime"),
(_, true) => info!("Tokio runtime found; starting in existing Tokio runtime"),
(_, false) => panic!("Actix or Tokio runtime not found; halting"),
}
for (_, name, lst) in &builder.sockets {
info!(
r#"starting service: "{}", workers: {}, listening on: {}"#,
name,
builder.threads,
lst.local_addr()
);
}
let sockets = mem::take(&mut builder.sockets)
.into_iter()
.map(|t| (t.0, t.2))
.collect();
let (waker_queue, worker_handles, accept_handle) = Accept::start(sockets, &builder)?;
let mux = ServerEventMultiplexer {
signal_fut: (builder.listen_os_signals).then(Signals::new),
cmd_rx: builder.cmd_rx,
};
let server = ServerInner {
waker_queue,
accept_handle: Some(accept_handle),
worker_handles,
worker_config: builder.worker_config,
services: builder.factories,
system_stop: builder.exit,
stopping: false,
};
Ok((server, mux))
}
async fn handle_cmd(&mut self, item: ServerCommand) {
match item {
ServerCommand::Pause(tx) => {
self.waker_queue.wake(WakerInterest::Pause);
let _ = tx.send(());
}
ServerCommand::Resume(tx) => {
self.waker_queue.wake(WakerInterest::Resume);
let _ = tx.send(());
}
ServerCommand::Stop {
graceful,
completion,
force_system_stop,
} => {
self.stopping = true;
// Signal accept thread to stop.
// Signal is non-blocking; we wait for thread to stop later.
self.waker_queue.wake(WakerInterest::Stop);
// send stop signal to workers
let workers_stop = self
.worker_handles
.iter()
.map(|worker| worker.stop(graceful))
.collect::<Vec<_>>();
if graceful {
// wait for all workers to shut down
let _ = join_all(workers_stop).await;
}
// wait for accept thread stop
self.accept_handle
.take()
.unwrap()
.join()
.expect("Accept thread must not panic in any case");
if let Some(tx) = completion {
let _ = tx.send(());
}
if self.system_stop || force_system_stop {
sleep(Duration::from_millis(300)).await;
System::try_current().as_ref().map(System::stop);
}
}
ServerCommand::WorkerFaulted(idx) => {
// TODO: maybe just return with warning log if not found ?
assert!(self.worker_handles.iter().any(|wrk| wrk.idx == idx));
error!("worker {} has died; restarting", idx);
let factories = self
.services
.iter()
.map(|service| service.clone_factory())
.collect();
match ServerWorker::start(
idx,
factories,
self.waker_queue.clone(),
self.worker_config,
) {
Ok((handle_accept, handle_server)) => {
*self
.worker_handles
.iter_mut()
.find(|wrk| wrk.idx == idx)
.unwrap() = handle_server;
self.waker_queue.wake(WakerInterest::Worker(handle_accept));
}
Err(err) => error!("can not restart worker {}: {}", idx, err),
};
}
}
}
fn map_signal(signal: SignalKind) -> ServerCommand {
match signal {
SignalKind::Int => {
info!("SIGINT received; starting forced shutdown");
ServerCommand::Stop {
graceful: false,
completion: None,
force_system_stop: true,
}
}
SignalKind::Term => {
info!("SIGTERM received; starting graceful shutdown");
ServerCommand::Stop {
graceful: true,
completion: None,
force_system_stop: true,
}
}
SignalKind::Quit => {
info!("SIGQUIT received; starting forced shutdown");
ServerCommand::Stop {
graceful: false,
completion: None,
force_system_stop: true,
}
}
}
}
}
struct ServerEventMultiplexer {
cmd_rx: UnboundedReceiver<ServerCommand>,
signal_fut: Option<Signals>,
}
impl Stream for ServerEventMultiplexer {
type Item = ServerCommand;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let this = Pin::into_inner(self);
if let Some(signal_fut) = &mut this.signal_fut {
if let Poll::Ready(signal) = Pin::new(signal_fut).poll(cx) {
this.signal_fut = None;
return Poll::Ready(Some(ServerInner::map_signal(signal)));
}
}
this.cmd_rx.poll_recv(cx)
}
}

View File

@ -1,16 +1,21 @@
use std::marker::PhantomData;
use std::net::SocketAddr;
use std::task::{Context, Poll};
use std::{
marker::PhantomData,
net::SocketAddr,
task::{Context, Poll},
};
use actix_service::{Service, ServiceFactory as BaseServiceFactory};
use actix_utils::future::{ready, Ready};
use futures_core::future::LocalBoxFuture;
use log::error;
use tracing::error;
use crate::socket::{FromStream, MioStream};
use crate::worker::WorkerCounterGuard;
use crate::{
socket::{FromStream, MioStream},
worker::WorkerCounterGuard,
};
pub trait ServiceFactory<Stream: FromStream>: Send + Clone + 'static {
#[doc(hidden)]
pub trait ServerServiceFactory<Stream: FromStream>: Send + Clone + 'static {
type Factory: BaseServiceFactory<Stream, Config = ()>;
fn create(&self) -> Self::Factory;
@ -72,15 +77,15 @@ where
});
Ok(())
}
Err(e) => {
error!("Can not convert to an async tcp stream: {}", e);
Err(err) => {
error!("can not convert to an async TCP stream: {err}");
Err(())
}
})
}
}
pub(crate) struct StreamNewService<F: ServiceFactory<Io>, Io: FromStream> {
pub(crate) struct StreamNewService<F: ServerServiceFactory<Io>, Io: FromStream> {
name: String,
inner: F,
token: usize,
@ -90,7 +95,7 @@ pub(crate) struct StreamNewService<F: ServiceFactory<Io>, Io: FromStream> {
impl<F, Io> StreamNewService<F, Io>
where
F: ServiceFactory<Io>,
F: ServerServiceFactory<Io>,
Io: FromStream + Send + 'static,
{
pub(crate) fn create(
@ -111,7 +116,7 @@ where
impl<F, Io> InternalServiceFactory for StreamNewService<F, Io>
where
F: ServiceFactory<Io>,
F: ServerServiceFactory<Io>,
Io: FromStream + Send + 'static,
{
fn name(&self, _: usize) -> &str {
@ -143,7 +148,7 @@ where
}
}
impl<F, T, I> ServiceFactory<I> for F
impl<F, T, I> ServerServiceFactory<I> for F
where
F: Fn() -> T + Send + Clone + 'static,
T: BaseServiceFactory<I, Config = ()>,

View File

@ -1,49 +1,66 @@
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::{
fmt,
future::Future,
pin::Pin,
task::{Context, Poll},
};
use crate::server::Server;
use tracing::trace;
/// Different types of process signals
#[allow(dead_code)]
#[derive(PartialEq, Clone, Copy, Debug)]
pub(crate) enum Signal {
/// SIGHUP
Hup,
/// SIGINT
/// Types of process signals.
// #[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq)]
#[allow(dead_code)] // variants are never constructed on non-unix
pub(crate) enum SignalKind {
/// `SIGINT`
Int,
/// SIGTERM
/// `SIGTERM`
Term,
/// SIGQUIT
/// `SIGQUIT`
Quit,
}
impl fmt::Display for SignalKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
SignalKind::Int => "SIGINT",
SignalKind::Term => "SIGTERM",
SignalKind::Quit => "SIGQUIT",
})
}
}
/// Process signal listener.
pub(crate) struct Signals {
srv: Server,
#[cfg(not(unix))]
signals: futures_core::future::LocalBoxFuture<'static, std::io::Result<()>>,
signals: futures_core::future::BoxFuture<'static, std::io::Result<()>>,
#[cfg(unix)]
signals: Vec<(Signal, actix_rt::signal::unix::Signal)>,
signals: Vec<(SignalKind, actix_rt::signal::unix::Signal)>,
}
impl Signals {
pub(crate) fn start(srv: Server) {
/// Constructs an OS signal listening future.
pub(crate) fn new() -> Self {
trace!("setting up OS signal listener");
#[cfg(not(unix))]
{
actix_rt::spawn(Signals {
srv,
Signals {
signals: Box::pin(actix_rt::signal::ctrl_c()),
});
}
}
#[cfg(unix)]
{
use actix_rt::signal::unix;
let sig_map = [
(unix::SignalKind::interrupt(), Signal::Int),
(unix::SignalKind::hangup(), Signal::Hup),
(unix::SignalKind::terminate(), Signal::Term),
(unix::SignalKind::quit(), Signal::Quit),
(unix::SignalKind::interrupt(), SignalKind::Int),
(unix::SignalKind::terminate(), SignalKind::Term),
(unix::SignalKind::quit(), SignalKind::Quit),
];
let signals = sig_map
@ -52,8 +69,8 @@ impl Signals {
unix::signal(*kind)
.map(|tokio_sig| (*sig, tokio_sig))
.map_err(|e| {
log::error!(
"Can not initialize stream handler for {:?} err: {}",
tracing::error!(
"can not initialize stream handler for {:?} err: {}",
sig,
e
)
@ -62,32 +79,29 @@ impl Signals {
})
.collect::<Vec<_>>();
actix_rt::spawn(Signals { srv, signals });
Signals { signals }
}
}
}
impl Future for Signals {
type Output = ();
type Output = SignalKind;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
#[cfg(not(unix))]
match self.signals.as_mut().poll(cx) {
Poll::Ready(_) => {
self.srv.signal(Signal::Int);
Poll::Ready(())
}
Poll::Pending => Poll::Pending,
{
self.signals.as_mut().poll(cx).map(|_| SignalKind::Int)
}
#[cfg(unix)]
{
for (sig, fut) in self.signals.iter_mut() {
if Pin::new(fut).poll_recv(cx).is_ready() {
let sig = *sig;
self.srv.signal(sig);
return Poll::Ready(());
if fut.poll_recv(cx).is_ready() {
trace!("{} received", sig);
return Poll::Ready(*sig);
}
}
Poll::Pending
}
}

View File

@ -1,18 +1,17 @@
pub(crate) use std::net::{
SocketAddr as StdSocketAddr, TcpListener as StdTcpListener, ToSocketAddrs,
};
pub(crate) use mio::net::{TcpListener as MioTcpListener, TcpSocket as MioTcpSocket};
#[cfg(unix)]
pub(crate) use {
mio::net::UnixListener as MioUnixListener,
std::os::unix::net::UnixListener as StdUnixListener,
};
use std::{fmt, io};
use actix_rt::net::TcpStream;
pub(crate) use mio::net::TcpListener as MioTcpListener;
use mio::{event::Source, Interest, Registry, Token};
#[cfg(unix)]
pub(crate) use {
mio::net::UnixListener as MioUnixListener, std::os::unix::net::UnixListener as StdUnixListener,
};
use crate::builder::MpTcp;
pub(crate) enum MioListener {
Tcp(MioTcpListener),
@ -107,7 +106,7 @@ impl fmt::Debug for MioListener {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
MioListener::Tcp(ref lst) => write!(f, "{:?}", lst),
#[cfg(all(unix))]
#[cfg(unix)]
MioListener::Uds(ref lst) => write!(f, "{:?}", lst),
}
}
@ -127,7 +126,7 @@ pub(crate) enum SocketAddr {
Unknown,
Tcp(StdSocketAddr),
#[cfg(unix)]
Uds(mio::net::SocketAddr),
Uds(std::os::unix::net::SocketAddr),
}
impl fmt::Display for SocketAddr {
@ -159,24 +158,25 @@ pub enum MioStream {
Uds(mio::net::UnixStream),
}
/// helper trait for converting mio stream to tokio stream.
/// Helper trait for converting a Mio stream into a Tokio stream.
pub trait FromStream: Sized {
/// Creates stream from a `mio` stream.
fn from_mio(sock: MioStream) -> io::Result<Self>;
}
#[cfg(windows)]
mod win_impl {
use super::*;
use std::os::windows::io::{FromRawSocket, IntoRawSocket};
// FIXME: This is a workaround and we need an efficient way to convert between mio and tokio stream
use super::*;
// TODO: This is a workaround and we need an efficient way to convert between Mio and Tokio stream
impl FromStream for TcpStream {
fn from_mio(sock: MioStream) -> io::Result<Self> {
match sock {
MioStream::Tcp(mio) => {
let raw = IntoRawSocket::into_raw_socket(mio);
// SAFETY: This is a in place conversion from mio stream to tokio stream.
// SAFETY: This is an in-place conversion from Mio stream to Tokio stream.
TcpStream::from_std(unsafe { FromRawSocket::from_raw_socket(raw) })
}
}
@ -186,19 +186,19 @@ mod win_impl {
#[cfg(unix)]
mod unix_impl {
use super::*;
use std::os::unix::io::{FromRawFd, IntoRawFd};
use actix_rt::net::UnixStream;
// FIXME: This is a workaround and we need an efficient way to convert between mio and tokio stream
use super::*;
// HACK: This is a workaround and we need an efficient way to convert between Mio and Tokio stream
impl FromStream for TcpStream {
fn from_mio(sock: MioStream) -> io::Result<Self> {
match sock {
MioStream::Tcp(mio) => {
let raw = IntoRawFd::into_raw_fd(mio);
// SAFETY: This is a in place conversion from mio stream to tokio stream.
// SAFETY: This is an in-place conversion from Mio stream to Tokio stream.
TcpStream::from_std(unsafe { FromRawFd::from_raw_fd(raw) })
}
MioStream::Uds(_) => {
@ -208,14 +208,14 @@ mod unix_impl {
}
}
// FIXME: This is a workaround and we need an efficient way to convert between mio and tokio stream
// HACK: This is a workaround and we need an efficient way to convert between Mio and Tokio stream
impl FromStream for UnixStream {
fn from_mio(sock: MioStream) -> io::Result<Self> {
match sock {
MioStream::Tcp(_) => panic!("Should not happen, bug in server impl"),
MioStream::Uds(mio) => {
let raw = IntoRawFd::into_raw_fd(mio);
// SAFETY: This is a in place conversion from mio stream to tokio stream.
// SAFETY: This is an in-place conversion from Mio stream to Tokio stream.
UnixStream::from_std(unsafe { FromRawFd::from_raw_fd(raw) })
}
}
@ -223,6 +223,42 @@ mod unix_impl {
}
}
pub(crate) fn create_mio_tcp_listener(
addr: StdSocketAddr,
backlog: u32,
mptcp: &MpTcp,
) -> io::Result<MioTcpListener> {
use socket2::{Domain, Protocol, Socket, Type};
#[cfg(not(target_os = "linux"))]
let protocol = Protocol::TCP;
#[cfg(target_os = "linux")]
let protocol = if matches!(mptcp, MpTcp::Disabled) {
Protocol::TCP
} else {
Protocol::MPTCP
};
let socket = match Socket::new(Domain::for_address(addr), Type::STREAM, Some(protocol)) {
Ok(sock) => sock,
Err(err) if matches!(mptcp, MpTcp::TcpFallback) => {
tracing::warn!("binding socket as MPTCP failed: {err}");
tracing::warn!("falling back to TCP");
Socket::new(Domain::for_address(addr), Type::STREAM, Some(Protocol::TCP))?
}
Err(err) => return Err(err),
};
socket.set_reuse_address(true)?;
socket.set_nonblocking(true)?;
socket.bind(&addr.into())?;
socket.listen(backlog as i32)?;
Ok(MioTcpListener::from_std(StdTcpListener::from(socket)))
}
#[cfg(test)]
mod tests {
use super::*;
@ -234,11 +270,8 @@ mod tests {
assert_eq!(format!("{}", addr), "127.0.0.1:8080");
let addr: StdSocketAddr = "127.0.0.1:0".parse().unwrap();
let socket = MioTcpSocket::new_v4().unwrap();
socket.set_reuseaddr(true).unwrap();
socket.bind(addr).unwrap();
let tcp = socket.listen(128).unwrap();
let lst = MioListener::Tcp(tcp);
let lst = create_mio_tcp_listener(addr, 128, &MpTcp::Disabled).unwrap();
let lst = MioListener::Tcp(lst);
assert!(format!("{:?}", lst).contains("TcpListener"));
assert!(format!("{}", lst).contains("127.0.0.1"));
}

View File

@ -1,24 +1,22 @@
use std::sync::mpsc;
use std::{net, thread};
use std::{io, net, sync::mpsc, thread};
use actix_rt::{net::TcpStream, System};
use crate::{Server, ServerBuilder, ServiceFactory};
use crate::{Server, ServerBuilder, ServerHandle, ServerServiceFactory};
/// The `TestServer` type.
/// A testing server.
///
/// `TestServer` is very simple test server that simplify process of writing
/// integration tests for actix-net applications.
/// `TestServer` is very simple test server that simplify process of writing integration tests for
/// network applications.
///
/// # Examples
///
/// ```
/// use actix_service::fn_service;
/// use actix_server::TestServer;
///
/// #[actix_rt::main]
/// async fn main() {
/// let srv = TestServer::with(|| fn_service(
/// let srv = TestServer::start(|| fn_service(
/// |sock| async move {
/// println!("New connection: {:?}", sock);
/// Ok::<_, ()>(())
@ -30,115 +28,128 @@ use crate::{Server, ServerBuilder, ServiceFactory};
/// ```
pub struct TestServer;
/// Test server runtime
pub struct TestServerRuntime {
/// Test server handle.
pub struct TestServerHandle {
addr: net::SocketAddr,
host: String,
port: u16,
system: System,
server_handle: ServerHandle,
thread_handle: Option<thread::JoinHandle<io::Result<()>>>,
}
impl TestServer {
/// Start new server with server builder
pub fn start<F>(mut factory: F) -> TestServerRuntime
where
F: FnMut(ServerBuilder) -> ServerBuilder + Send + 'static,
{
let (tx, rx) = mpsc::channel();
// run server in separate thread
thread::spawn(move || {
let sys = System::new();
factory(Server::build()).workers(1).disable_signals().run();
tx.send(System::current()).unwrap();
sys.run()
});
let system = rx.recv().unwrap();
TestServerRuntime {
system,
addr: "127.0.0.1:0".parse().unwrap(),
host: "127.0.0.1".to_string(),
port: 0,
}
/// Start new `TestServer` using application factory and default server config.
pub fn start(factory: impl ServerServiceFactory<TcpStream>) -> TestServerHandle {
Self::start_with_builder(Server::build(), factory)
}
/// Start new test server with application factory
pub fn with<F: ServiceFactory<TcpStream>>(factory: F) -> TestServerRuntime {
/// Start new `TestServer` using application factory and server builder.
pub fn start_with_builder(
server_builder: ServerBuilder,
factory: impl ServerServiceFactory<TcpStream>,
) -> TestServerHandle {
let (tx, rx) = mpsc::channel();
// run server in separate thread
thread::spawn(move || {
let sys = System::new();
let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap();
let local_addr = tcp.local_addr().unwrap();
let thread_handle = thread::spawn(move || {
let lst = net::TcpListener::bind("127.0.0.1:0").unwrap();
let local_addr = lst.local_addr().unwrap();
sys.block_on(async {
Server::build()
.listen("test", tcp, factory)
System::new().block_on(async {
let server = server_builder
.listen("test", lst, factory)
.unwrap()
.workers(1)
.disable_signals()
.run();
tx.send((System::current(), local_addr)).unwrap();
});
sys.run()
tx.send((server.handle(), local_addr)).unwrap();
server.await
})
});
let (system, addr) = rx.recv().unwrap();
let (server_handle, addr) = rx.recv().unwrap();
let host = format!("{}", addr.ip());
let port = addr.port();
TestServerRuntime {
TestServerHandle {
addr,
host,
port,
system,
server_handle,
thread_handle: Some(thread_handle),
}
}
/// Get first available unused local address
/// Get first available unused local address.
pub fn unused_addr() -> net::SocketAddr {
use socket2::{Domain, Protocol, Socket, Type};
let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap();
let socket = mio::net::TcpSocket::new_v4().unwrap();
socket.bind(addr).unwrap();
socket.set_reuseaddr(true).unwrap();
let tcp = socket.listen(1024).unwrap();
tcp.local_addr().unwrap()
let domain = Domain::for_address(addr);
let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP)).unwrap();
socket.set_reuse_address(true).unwrap();
socket.set_nonblocking(true).unwrap();
socket.bind(&addr.into()).unwrap();
socket.listen(1024).unwrap();
net::TcpListener::from(socket).local_addr().unwrap()
}
}
impl TestServerRuntime {
/// Test server host
impl TestServerHandle {
/// Test server host.
pub fn host(&self) -> &str {
&self.host
}
/// Test server port
/// Test server port.
pub fn port(&self) -> u16 {
self.port
}
/// Get test server address
/// Get test server address.
pub fn addr(&self) -> net::SocketAddr {
self.addr
}
/// Stop http server
/// Stop server.
fn stop(&mut self) {
self.system.stop();
drop(self.server_handle.stop(false));
self.thread_handle.take().unwrap().join().unwrap().unwrap();
}
/// Connect to server, return tokio TcpStream
pub fn connect(&self) -> std::io::Result<TcpStream> {
TcpStream::from_std(net::TcpStream::connect(self.addr)?)
/// Connect to server, returning a Tokio `TcpStream`.
pub fn connect(&self) -> io::Result<TcpStream> {
let stream = net::TcpStream::connect(self.addr)?;
stream.set_nonblocking(true)?;
TcpStream::from_std(stream)
}
}
impl Drop for TestServerRuntime {
impl Drop for TestServerHandle {
fn drop(&mut self) {
self.stop()
}
}
#[cfg(test)]
mod tests {
use actix_service::fn_service;
use super::*;
#[tokio::test]
async fn connect_in_tokio_runtime() {
let srv = TestServer::start(|| fn_service(|_sock| async move { Ok::<_, ()>(()) }));
assert!(srv.connect().is_ok());
}
#[actix_rt::test]
async fn connect_in_actix_runtime() {
let srv = TestServer::start(|| fn_service(|_sock| async move { Ok::<_, ()>(()) }));
assert!(srv.connect().is_ok());
}
}

View File

@ -78,12 +78,7 @@ pub(crate) enum WakerInterest {
Pause,
Resume,
Stop,
/// `Timer` is an interest sent as a delayed future. When an error happens on accepting
/// connection `Accept` would deregister socket listener temporary and wake up the poll and
/// register them again after the delayed future resolve.
Timer,
/// `Worker` is an interest happen after a worker runs into faulted state(This is determined
/// by if work can be sent to it successfully).`Accept` would be waked up and add the new
/// `WorkerHandleAccept`.
/// `Worker` is an interest that is triggered after a worker faults. This is determined by
/// trying to send work to it. `Accept` would be waked up and add the new `WorkerHandleAccept`.
Worker(WorkerHandleAccept),
}

View File

@ -1,6 +1,7 @@
use std::{
future::Future,
mem,
io, mem,
num::NonZeroUsize,
pin::Pin,
rc::Rc,
sync::{
@ -14,21 +15,22 @@ use std::{
use actix_rt::{
spawn,
time::{sleep, Instant, Sleep},
Arbiter,
Arbiter, ArbiterHandle, System,
};
use futures_core::{future::LocalBoxFuture, ready};
use log::{error, info, trace};
use tokio::sync::{
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
oneshot,
};
use tracing::{error, info, trace};
use crate::join_all;
use crate::service::{BoxedServerService, InternalServiceFactory};
use crate::socket::MioStream;
use crate::waker_queue::{WakerInterest, WakerQueue};
use crate::{
service::{BoxedServerService, InternalServiceFactory},
socket::MioStream,
waker_queue::{WakerInterest, WakerQueue},
};
/// Stop worker message. Returns `true` on successful graceful shutdown.
/// Stop worker message. Returns `true` on successful graceful shutdown
/// and `false` if some connections still alive when shutdown execute.
pub(crate) struct Stop {
graceful: bool,
@ -41,19 +43,20 @@ pub(crate) struct Conn {
pub token: usize,
}
/// Create accept and server worker handles.
fn handle_pair(
idx: usize,
tx1: UnboundedSender<Conn>,
tx2: UnboundedSender<Stop>,
conn_tx: UnboundedSender<Conn>,
stop_tx: UnboundedSender<Stop>,
counter: Counter,
) -> (WorkerHandleAccept, WorkerHandleServer) {
let accept = WorkerHandleAccept {
idx,
tx: tx1,
conn_tx,
counter,
};
let server = WorkerHandleServer { idx, tx: tx2 };
let server = WorkerHandleServer { idx, stop_tx };
(accept, server)
}
@ -149,13 +152,13 @@ impl Drop for WorkerCounterGuard {
}
}
/// Handle to worker that can send connection message to worker and share the
/// availability of worker to other thread.
/// Handle to worker that can send connection message to worker and share the availability of worker
/// to other threads.
///
/// Held by [Accept](crate::accept::Accept).
pub(crate) struct WorkerHandleAccept {
idx: usize,
tx: UnboundedSender<Conn>,
conn_tx: UnboundedSender<Conn>,
counter: Counter,
}
@ -166,8 +169,8 @@ impl WorkerHandleAccept {
}
#[inline(always)]
pub(crate) fn send(&self, msg: Conn) -> Result<(), Conn> {
self.tx.send(msg).map_err(|msg| msg.0)
pub(crate) fn send(&self, conn: Conn) -> Result<(), Conn> {
self.conn_tx.send(conn).map_err(|msg| msg.0)
}
#[inline(always)]
@ -181,14 +184,14 @@ impl WorkerHandleAccept {
/// Held by [ServerBuilder](crate::builder::ServerBuilder).
#[derive(Debug)]
pub(crate) struct WorkerHandleServer {
idx: usize,
tx: UnboundedSender<Stop>,
pub(crate) idx: usize,
stop_tx: UnboundedSender<Stop>,
}
impl WorkerHandleServer {
pub(crate) fn stop(&self, graceful: bool) -> oneshot::Receiver<bool> {
let (tx, rx) = oneshot::channel();
let _ = self.tx.send(Stop { graceful, tx });
let _ = self.stop_tx.send(Stop { graceful, tx });
rx
}
}
@ -199,8 +202,8 @@ impl WorkerHandleServer {
pub(crate) struct ServerWorker {
// UnboundedReceiver<Conn> should always be the first field.
// It must be dropped as soon as ServerWorker dropping.
rx: UnboundedReceiver<Conn>,
rx2: UnboundedReceiver<Stop>,
conn_rx: UnboundedReceiver<Conn>,
stop_rx: UnboundedReceiver<Stop>,
counter: WorkerCounter,
services: Box<[WorkerService]>,
factories: Box<[Box<dyn InternalServiceFactory>]>,
@ -209,7 +212,7 @@ pub(crate) struct ServerWorker {
}
struct WorkerService {
factory: usize,
factory_idx: usize,
status: WorkerServiceStatus,
service: BoxedServerService,
}
@ -221,7 +224,7 @@ impl WorkerService {
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum WorkerServiceStatus {
Available,
Unavailable,
@ -231,8 +234,14 @@ enum WorkerServiceStatus {
Stopped,
}
impl Default for WorkerServiceStatus {
fn default() -> Self {
Self::Unavailable
}
}
/// Config for worker behavior passed down from server builder.
#[derive(Copy, Clone)]
#[derive(Debug, Clone, Copy)]
pub(crate) struct ServerWorkerConfig {
shutdown_timeout: Duration,
max_blocking_threads: usize,
@ -241,8 +250,11 @@ pub(crate) struct ServerWorkerConfig {
impl Default for ServerWorkerConfig {
fn default() -> Self {
// 512 is the default max blocking thread count of tokio runtime.
let max_blocking_threads = std::cmp::max(512 / num_cpus::get(), 1);
let parallelism = std::thread::available_parallelism().map_or(2, NonZeroUsize::get);
// 512 is the default max blocking thread count of a Tokio runtime.
let max_blocking_threads = std::cmp::max(512 / parallelism, 1);
Self {
shutdown_timeout: Duration::from_secs(30),
max_blocking_threads,
@ -271,77 +283,204 @@ impl ServerWorker {
factories: Vec<Box<dyn InternalServiceFactory>>,
waker_queue: WakerQueue,
config: ServerWorkerConfig,
) -> (WorkerHandleAccept, WorkerHandleServer) {
let (tx1, rx) = unbounded_channel();
let (tx2, rx2) = unbounded_channel();
) -> io::Result<(WorkerHandleAccept, WorkerHandleServer)> {
trace!("starting server worker {}", idx);
let (tx1, conn_rx) = unbounded_channel();
let (tx2, stop_rx) = unbounded_channel();
let counter = Counter::new(config.max_concurrent_connections);
let pair = handle_pair(idx, tx1, tx2, counter.clone());
let counter_clone = counter.clone();
// every worker runs in it's own arbiter.
// get actix system context if it is set
let actix_system = System::try_current();
// get tokio runtime handle if it is set
let tokio_handle = tokio::runtime::Handle::try_current().ok();
// service factories initialization channel
let (factory_tx, factory_rx) = std::sync::mpsc::sync_channel::<io::Result<()>>(1);
// outline of following code:
//
// if system exists
// if uring enabled
// start arbiter using uring method
// else
// start arbiter with regular tokio
// else
// if uring enabled
// start uring in spawned thread
// else
// start regular tokio in spawned thread
// every worker runs in it's own thread and tokio runtime.
// use a custom tokio runtime builder to change the settings of runtime.
Arbiter::with_tokio_rt(move || {
tokio::runtime::Builder::new_current_thread()
.enable_all()
.max_blocking_threads(config.max_blocking_threads)
.build()
.unwrap()
})
.spawn(async move {
let fut = factories
.iter()
.enumerate()
.map(|(idx, factory)| {
let fut = factory.create();
async move { fut.await.map(|(t, s)| (idx, t, s)) }
})
.collect::<Vec<_>>();
// a second spawn to run !Send future tasks.
spawn(async move {
let res = join_all(fut)
.await
.into_iter()
.collect::<Result<Vec<_>, _>>();
let services = match res {
Ok(res) => res
.into_iter()
.fold(Vec::new(), |mut services, (factory, token, service)| {
assert_eq!(token, services.len());
services.push(WorkerService {
factory,
service,
status: WorkerServiceStatus::Unavailable,
match (actix_system, tokio_handle) {
(None, None) => {
panic!("No runtime detected. Start a Tokio (or Actix) runtime.");
}
// no actix system
(None, Some(rt_handle)) => {
std::thread::Builder::new()
.name(format!("actix-server worker {}", idx))
.spawn(move || {
let (worker_stopped_tx, worker_stopped_rx) = oneshot::channel();
// local set for running service init futures and worker services
let ls = tokio::task::LocalSet::new();
// init services using existing Tokio runtime (so probably on main thread)
let services = rt_handle.block_on(ls.run_until(async {
let mut services = Vec::new();
for (idx, factory) in factories.iter().enumerate() {
match factory.create().await {
Ok((token, svc)) => services.push((idx, token, svc)),
Err(err) => {
error!("can not start worker: {:?}", err);
return Err(io::Error::new(
io::ErrorKind::Other,
format!("can not start server service {}", idx),
));
}
}
}
Ok(services)
}));
let services = match services {
Ok(services) => {
factory_tx.send(Ok(())).unwrap();
services
}
Err(err) => {
factory_tx.send(Err(err)).unwrap();
return;
}
};
let worker_services = wrap_worker_services(services);
let worker_fut = async move {
// spawn to make sure ServerWorker runs as non boxed future.
spawn(async move {
ServerWorker {
conn_rx,
stop_rx,
services: worker_services.into_boxed_slice(),
counter: WorkerCounter::new(idx, waker_queue, counter),
factories: factories.into_boxed_slice(),
state: WorkerState::default(),
shutdown_timeout: config.shutdown_timeout,
}
.await;
// wake up outermost task waiting for shutdown
worker_stopped_tx.send(()).unwrap();
});
services
})
.into_boxed_slice(),
Err(e) => {
error!("Can not start worker: {:?}", e);
Arbiter::current().stop();
return;
}
worker_stopped_rx.await.unwrap();
};
#[cfg(all(target_os = "linux", feature = "io-uring"))]
{
// TODO: pass max blocking thread config when tokio-uring enable configuration
// on building runtime.
let _ = config.max_blocking_threads;
tokio_uring::start(worker_fut);
}
#[cfg(not(all(target_os = "linux", feature = "io-uring")))]
{
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.max_blocking_threads(config.max_blocking_threads)
.build()
.unwrap();
rt.block_on(ls.run_until(worker_fut));
}
})
.expect("cannot spawn server worker thread");
}
// with actix system
(Some(_sys), _) => {
#[cfg(all(target_os = "linux", feature = "io-uring"))]
let arbiter = {
// TODO: pass max blocking thread config when tokio-uring enable configuration
// on building runtime.
let _ = config.max_blocking_threads;
Arbiter::new()
};
// a third spawn to make sure ServerWorker runs as non boxed future.
spawn(ServerWorker {
rx,
rx2,
services,
counter: WorkerCounter::new(idx, waker_queue, counter_clone),
factories: factories.into_boxed_slice(),
state: Default::default(),
shutdown_timeout: config.shutdown_timeout,
});
});
});
#[cfg(not(all(target_os = "linux", feature = "io-uring")))]
let arbiter = {
Arbiter::with_tokio_rt(move || {
tokio::runtime::Builder::new_current_thread()
.enable_all()
.max_blocking_threads(config.max_blocking_threads)
.build()
.unwrap()
})
};
handle_pair(idx, tx1, tx2, counter)
arbiter.spawn(async move {
// spawn_local to run !Send future tasks.
spawn(async move {
let mut services = Vec::new();
for (idx, factory) in factories.iter().enumerate() {
match factory.create().await {
Ok((token, svc)) => services.push((idx, token, svc)),
Err(err) => {
error!("can not start worker: {:?}", err);
Arbiter::current().stop();
factory_tx
.send(Err(io::Error::new(
io::ErrorKind::Other,
format!("can not start server service {}", idx),
)))
.unwrap();
return;
}
}
}
factory_tx.send(Ok(())).unwrap();
let worker_services = wrap_worker_services(services);
// spawn to make sure ServerWorker runs as non boxed future.
spawn(ServerWorker {
conn_rx,
stop_rx,
services: worker_services.into_boxed_slice(),
counter: WorkerCounter::new(idx, waker_queue, counter),
factories: factories.into_boxed_slice(),
state: Default::default(),
shutdown_timeout: config.shutdown_timeout,
});
});
});
}
};
// wait for service factories initialization
factory_rx.recv().unwrap()?;
Ok(pair)
}
fn restart_service(&mut self, idx: usize, factory_id: usize) {
let factory = &self.factories[factory_id];
trace!("Service {:?} failed, restarting", factory.name(idx));
trace!("service {:?} failed, restarting", factory.name(idx));
self.services[idx].status = WorkerServiceStatus::Restarting;
self.state = WorkerState::Restarting(Restart {
factory_id,
@ -373,8 +512,8 @@ impl ServerWorker {
Poll::Ready(Ok(_)) => {
if srv.status == WorkerServiceStatus::Unavailable {
trace!(
"Service {:?} is available",
self.factories[srv.factory].name(idx)
"service {:?} is available",
self.factories[srv.factory_idx].name(idx)
);
srv.status = WorkerServiceStatus::Available;
}
@ -384,19 +523,19 @@ impl ServerWorker {
if srv.status == WorkerServiceStatus::Available {
trace!(
"Service {:?} is unavailable",
self.factories[srv.factory].name(idx)
"service {:?} is unavailable",
self.factories[srv.factory_idx].name(idx)
);
srv.status = WorkerServiceStatus::Unavailable;
}
}
Poll::Ready(Err(_)) => {
error!(
"Service {:?} readiness check returned error, restarting",
self.factories[srv.factory].name(idx)
"service {:?} readiness check returned error, restarting",
self.factories[srv.factory_idx].name(idx)
);
srv.status = WorkerServiceStatus::Failed;
return Err((idx, srv.factory));
return Err((idx, srv.factory_idx));
}
}
}
@ -419,13 +558,15 @@ struct Restart {
fut: LocalBoxFuture<'static, Result<(usize, BoxedServerService), ()>>,
}
// Shutdown keep states necessary for server shutdown:
// Sleep for interval check the shutdown progress.
// Instant for the start time of shutdown.
// Sender for send back the shutdown outcome(force/grace) to StopCommand caller.
/// State necessary for server shutdown.
struct Shutdown {
// Interval for checking the shutdown progress.
timer: Pin<Box<Sleep>>,
/// Start time of shutdown.
start_from: Instant,
/// Notify caller of the shutdown outcome (graceful/force).
tx: oneshot::Sender<bool>,
}
@ -437,8 +578,7 @@ impl Default for WorkerState {
impl Drop for ServerWorker {
fn drop(&mut self) {
// Stop the Arbiter ServerWorker runs on on drop.
Arbiter::current().stop();
Arbiter::try_current().as_ref().map(ArbiterHandle::stop);
}
}
@ -449,15 +589,14 @@ impl Future for ServerWorker {
let this = self.as_mut().get_mut();
// `StopWorker` message handler
if let Poll::Ready(Some(Stop { graceful, tx })) = Pin::new(&mut this.rx2).poll_recv(cx)
{
if let Poll::Ready(Some(Stop { graceful, tx })) = this.stop_rx.poll_recv(cx) {
let num = this.counter.total();
if num == 0 {
info!("Shutting down worker, 0 connections");
info!("shutting down idle worker");
let _ = tx.send(true);
return Poll::Ready(());
} else if graceful {
info!("Graceful worker shutdown, {} connections", num);
info!("graceful worker shutdown; finishing {} connections", num);
this.shutdown(false);
this.state = WorkerState::Shutdown(Shutdown {
@ -466,7 +605,7 @@ impl Future for ServerWorker {
tx,
});
} else {
info!("Force shutdown worker, {} connections", num);
info!("force shutdown worker, closing {} connections", num);
this.shutdown(true);
let _ = tx.send(false);
@ -486,12 +625,13 @@ impl Future for ServerWorker {
self.poll(cx)
}
},
WorkerState::Restarting(ref mut restart) => {
let factory_id = restart.factory_id;
let token = restart.token;
let (token_new, service) = ready!(restart.fut.as_mut().poll(cx))
.unwrap_or_else(|_| {
let (token_new, service) =
ready!(restart.fut.as_mut().poll(cx)).unwrap_or_else(|_| {
panic!(
"Can not restart {:?} service",
this.factories[factory_id].name(token)
@ -501,7 +641,7 @@ impl Future for ServerWorker {
assert_eq!(token, token_new);
trace!(
"Service {:?} has been restarted",
"service {:?} has been restarted",
this.factories[factory_id].name(token)
);
@ -510,35 +650,47 @@ impl Future for ServerWorker {
self.poll(cx)
}
WorkerState::Shutdown(ref mut shutdown) => {
// Wait for 1 second.
// drop all pending connections in rx channel.
while let Poll::Ready(Some(conn)) = this.conn_rx.poll_recv(cx) {
// WorkerCounterGuard is needed as Accept thread has incremented counter.
// It's guard's job to decrement the counter together with drop of Conn.
let guard = this.counter.guard();
drop((conn, guard));
}
// wait for 1 second
ready!(shutdown.timer.as_mut().poll(cx));
if this.counter.total() == 0 {
// Graceful shutdown.
// graceful shutdown
if let WorkerState::Shutdown(shutdown) = mem::take(&mut this.state) {
let _ = shutdown.tx.send(true);
}
Poll::Ready(())
} else if shutdown.start_from.elapsed() >= this.shutdown_timeout {
// Timeout forceful shutdown.
// timeout forceful shutdown
if let WorkerState::Shutdown(shutdown) = mem::take(&mut this.state) {
let _ = shutdown.tx.send(false);
}
Poll::Ready(())
} else {
// Reset timer and wait for 1 second.
// reset timer and wait for 1 second
let time = Instant::now() + Duration::from_secs(1);
shutdown.timer.as_mut().reset(time);
shutdown.timer.as_mut().poll(cx)
}
}
// actively poll stream and handle worker command
WorkerState::Available => loop {
match this.check_readiness(cx) {
Ok(true) => {}
Ok(false) => {
trace!("Worker is unavailable");
trace!("worker is unavailable");
this.state = WorkerState::Unavailable;
return self.poll(cx);
}
@ -549,10 +701,13 @@ impl Future for ServerWorker {
}
// handle incoming io stream
match ready!(Pin::new(&mut this.rx).poll_recv(cx)) {
match ready!(this.conn_rx.poll_recv(cx)) {
Some(msg) => {
let guard = this.counter.guard();
let _ = this.services[msg.token].service.call((guard, msg.io));
let _ = this.services[msg.token]
.service
.call((guard, msg.io))
.into_inner();
}
None => return Poll::Ready(()),
};
@ -560,3 +715,17 @@ impl Future for ServerWorker {
}
}
}
fn wrap_worker_services(services: Vec<(usize, usize, BoxedServerService)>) -> Vec<WorkerService> {
services
.into_iter()
.fold(Vec::new(), |mut services, (idx, token, service)| {
assert_eq!(token, services.len());
services.push(WorkerService {
factory_idx: idx,
service,
status: WorkerServiceStatus::Unavailable,
});
services
})
}

View File

@ -1,20 +1,21 @@
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{mpsc, Arc};
use std::{net, thread, time::Duration};
#![allow(clippy::let_underscore_future, missing_docs)]
use std::{
net,
sync::{
atomic::{AtomicUsize, Ordering},
mpsc, Arc,
},
thread,
time::Duration,
};
use actix_rt::{net::TcpStream, time::sleep};
use actix_server::Server;
use actix_server::{Server, TestServer};
use actix_service::fn_service;
use actix_utils::future::ok;
use futures_util::future::lazy;
fn unused_addr() -> net::SocketAddr {
let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap();
let socket = mio::net::TcpSocket::new_v4().unwrap();
socket.bind(addr).unwrap();
socket.set_reuseaddr(true).unwrap();
let tcp = socket.listen(32).unwrap();
tcp.local_addr().unwrap()
TestServer::unused_addr()
}
#[test]
@ -23,52 +24,96 @@ fn test_bind() {
let (tx, rx) = mpsc::channel();
let h = thread::spawn(move || {
let sys = actix_rt::System::new();
let srv = sys.block_on(lazy(|_| {
Server::build()
actix_rt::System::new().block_on(async {
let srv = Server::build()
.workers(1)
.disable_signals()
.bind("test", addr, move || fn_service(|_| ok::<_, ()>(())))
.unwrap()
.run()
}));
.shutdown_timeout(3600)
.bind("test", addr, move || {
fn_service(|_| async { Ok::<_, ()>(()) })
})?
.run();
let _ = tx.send((srv, actix_rt::System::current()));
let _ = sys.run();
tx.send(srv.handle()).unwrap();
srv.await
})
});
let (_, sys) = rx.recv().unwrap();
let srv = rx.recv().unwrap();
thread::sleep(Duration::from_millis(500));
assert!(net::TcpStream::connect(addr).is_ok());
sys.stop();
let _ = h.join();
net::TcpStream::connect(addr).unwrap();
let _ = srv.stop(true);
h.join().unwrap().unwrap();
}
#[test]
fn test_listen() {
let addr = unused_addr();
let (tx, rx) = mpsc::channel();
let lst = net::TcpListener::bind(addr).unwrap();
let h = thread::spawn(move || {
let sys = actix_rt::System::new();
let lst = net::TcpListener::bind(addr).unwrap();
sys.block_on(async {
Server::build()
.disable_signals()
actix_rt::System::new().block_on(async {
let srv = Server::build()
.workers(1)
.listen("test", lst, move || fn_service(|_| ok::<_, ()>(())))
.unwrap()
.disable_signals()
.shutdown_timeout(3600)
.listen("test", lst, move || {
fn_service(|_| async { Ok::<_, ()>(()) })
})?
.run();
let _ = tx.send(actix_rt::System::current());
});
let _ = sys.run();
tx.send(srv.handle()).unwrap();
srv.await
})
});
let sys = rx.recv().unwrap();
let srv = rx.recv().unwrap();
thread::sleep(Duration::from_millis(500));
net::TcpStream::connect(addr).unwrap();
let _ = srv.stop(true);
h.join().unwrap().unwrap();
}
#[test]
fn plain_tokio_runtime() {
let addr = unused_addr();
let (tx, rx) = mpsc::channel();
let h = thread::spawn(move || {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
rt.block_on(async {
let srv = Server::build()
.workers(1)
.disable_signals()
.bind("test", addr, move || {
fn_service(|_| async { Ok::<_, ()>(()) })
})?
.run();
tx.send(srv.handle()).unwrap();
srv.await
})
});
let srv = rx.recv().unwrap();
thread::sleep(Duration::from_millis(500));
assert!(net::TcpStream::connect(addr).is_ok());
sys.stop();
let _ = h.join();
let _ = srv.stop(true);
h.join().unwrap().unwrap();
}
#[test]
@ -84,9 +129,8 @@ fn test_start() {
let (tx, rx) = mpsc::channel();
let h = thread::spawn(move || {
let sys = actix_rt::System::new();
let srv = sys.block_on(lazy(|_| {
Server::build()
actix_rt::System::new().block_on(async {
let srv = Server::build()
.backlog(100)
.disable_signals()
.bind("test", addr, move || {
@ -95,13 +139,13 @@ fn test_start() {
f.send(Bytes::from_static(b"test")).await.unwrap();
Ok::<_, ()>(())
})
})
.unwrap()
.run()
}));
})?
.run();
let _ = tx.send((srv, actix_rt::System::current()));
let _ = sys.run();
let _ = tx.send((srv.handle(), actix_rt::System::current()));
srv.await
})
});
let (srv, sys) = rx.recv().unwrap();
@ -134,20 +178,19 @@ fn test_start() {
// stop
let _ = srv.stop(false);
thread::sleep(Duration::from_millis(100));
assert!(net::TcpStream::connect(addr).is_err());
thread::sleep(Duration::from_millis(100));
sys.stop();
let _ = h.join();
h.join().unwrap().unwrap();
thread::sleep(Duration::from_secs(1));
assert!(net::TcpStream::connect(addr).is_err());
}
#[actix_rt::test]
async fn test_max_concurrent_connections() {
// Note:
// A tcp listener would accept connects based on it's backlog setting.
// A TCP listener would accept connects based on it's backlog setting.
//
// The limit test on the other hand is only for concurrent tcp stream limiting a work
// The limit test on the other hand is only for concurrent TCP stream limiting a work
// thread accept.
use tokio::io::AsyncWriteExt;
@ -162,11 +205,11 @@ async fn test_max_concurrent_connections() {
let h = thread::spawn(move || {
actix_rt::System::new().block_on(async {
let server = Server::build()
let srv = Server::build()
// Set a relative higher backlog.
.backlog(12)
// max connection for a worker is 3.
.maxconn(max_conn)
.max_concurrent_connections(max_conn)
.workers(1)
.disable_signals()
.bind("test", addr, move || {
@ -183,9 +226,9 @@ async fn test_max_concurrent_connections() {
})?
.run();
let _ = tx.send((server.clone(), actix_rt::System::current()));
let _ = tx.send((srv.handle(), actix_rt::System::current()));
server.await
srv.await
})
});
@ -209,11 +252,11 @@ async fn test_max_concurrent_connections() {
}
srv.stop(false).await;
sys.stop();
let _ = h.join().unwrap();
h.join().unwrap().unwrap();
}
// TODO: race-y failures detected due to integer underflow when calling Counter::total
#[actix_rt::test]
async fn test_service_restart() {
use std::task::{Context, Poll};
@ -257,7 +300,7 @@ async fn test_service_restart() {
let h = thread::spawn(move || {
let num = num.clone();
actix_rt::System::new().block_on(async {
let server = Server::build()
let srv = Server::build()
.backlog(1)
.disable_signals()
.bind("addr1", addr1, move || {
@ -266,25 +309,23 @@ async fn test_service_restart() {
let num = num.clone();
async move { Ok::<_, ()>(TestService(num)) }
})
})
.unwrap()
})?
.bind("addr2", addr2, move || {
let num2 = num2.clone();
fn_factory(move || {
let num2 = num2.clone();
async move { Ok::<_, ()>(TestService(num2)) }
})
})
.unwrap()
})?
.workers(1)
.run();
let _ = tx.send((server.clone(), actix_rt::System::current()));
server.await
let _ = tx.send(srv.handle());
srv.await
})
});
let (server, sys) = rx.recv().unwrap();
let srv = rx.recv().unwrap();
for _ in 0..5 {
TcpStream::connect(addr1)
@ -306,12 +347,11 @@ async fn test_service_restart() {
assert!(num_clone.load(Ordering::SeqCst) > 5);
assert!(num2_clone.load(Ordering::SeqCst) > 5);
sys.stop();
let _ = server.stop(false);
let _ = h.join().unwrap();
let _ = srv.stop(false);
h.join().unwrap().unwrap();
}
#[ignore]
#[ignore] // non-deterministic on CI
#[actix_rt::test]
async fn worker_restart() {
use actix_service::{Service, ServiceFactory};
@ -378,19 +418,19 @@ async fn worker_restart() {
let h = thread::spawn(move || {
let counter = counter.clone();
actix_rt::System::new().block_on(async {
let server = Server::build()
let srv = Server::build()
.disable_signals()
.bind("addr", addr, move || TestServiceFactory(counter.clone()))
.unwrap()
.bind("addr", addr, move || TestServiceFactory(counter.clone()))?
.workers(2)
.run();
let _ = tx.send((server.clone(), actix_rt::System::current()));
server.await
let _ = tx.send(srv.handle());
srv.await
})
});
let (server, sys) = rx.recv().unwrap();
let srv = rx.recv().unwrap();
sleep(Duration::from_secs(3)).await;
@ -447,7 +487,51 @@ async fn worker_restart() {
assert_eq!("3", id);
stream.shutdown().await.unwrap();
sys.stop();
let _ = server.stop(false);
let _ = h.join().unwrap();
let _ = srv.stop(false);
h.join().unwrap().unwrap();
}
#[test]
fn no_runtime_on_init() {
use std::{thread::sleep, time::Duration};
let addr = unused_addr();
let counter = Arc::new(AtomicUsize::new(0));
let mut srv = Server::build()
.workers(2)
.disable_signals()
.bind("test", addr, {
let counter = counter.clone();
move || {
counter.fetch_add(1, Ordering::SeqCst);
fn_service(|_| async { Ok::<_, ()>(()) })
}
})
.unwrap()
.run();
fn is_send<T: Send>(_: &T) {}
is_send(&srv);
is_send(&srv.handle());
sleep(Duration::from_millis(1_000));
assert_eq!(counter.load(Ordering::SeqCst), 0);
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
rt.block_on(async move {
let _ = futures_util::poll!(&mut srv);
// available after the first poll
sleep(Duration::from_millis(500));
assert_eq!(counter.load(Ordering::SeqCst), 2);
let _ = srv.handle().stop(true);
srv.await
})
.unwrap();
}

View File

@ -0,0 +1,77 @@
#![allow(missing_docs)]
use std::net;
use actix_rt::net::TcpStream;
use actix_server::{Server, TestServer};
use actix_service::fn_service;
use bytes::BytesMut;
use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
macro_rules! await_timeout_ms {
($fut:expr, $limit:expr) => {
::actix_rt::time::timeout(::std::time::Duration::from_millis($limit), $fut)
.await
.unwrap()
.unwrap();
};
}
#[tokio::test]
async fn testing_server_echo() {
let srv = TestServer::start(|| {
fn_service(move |mut stream: TcpStream| async move {
let mut size = 0;
let mut buf = BytesMut::new();
match stream.read_buf(&mut buf).await {
Ok(0) => return Err(()),
Ok(bytes_read) => {
stream.write_all(&buf[size..]).await.unwrap();
size += bytes_read;
}
Err(_) => return Err(()),
}
Ok((buf.freeze(), size))
})
});
let mut conn = srv.connect().unwrap();
await_timeout_ms!(conn.write_all(b"test"), 200);
let mut buf = Vec::new();
await_timeout_ms!(conn.read_to_end(&mut buf), 200);
assert_eq!(&buf, b"test".as_ref());
}
#[tokio::test]
async fn new_with_builder() {
let alt_addr = TestServer::unused_addr();
let srv = TestServer::start_with_builder(
Server::build()
.bind("alt", alt_addr, || {
fn_service(|_| async { Ok::<_, ()>(()) })
})
.unwrap(),
|| {
fn_service(|mut sock: TcpStream| async move {
let mut buf = [0u8; 16];
sock.read_exact(&mut buf).await
})
},
);
// connect to test server
srv.connect().unwrap();
// connect to alt service defined in custom ServerBuilder
let stream = net::TcpStream::connect(alt_addr).unwrap();
stream.set_nonblocking(true).unwrap();
TcpStream::from_std(stream).unwrap();
}

View File

@ -1,280 +1,193 @@
# Changes
## Unreleased - 2021-xx-xx
## Unreleased
## 2.0.3
## 2.0.0 - 2021-04-16
* Removed pipeline and related structs/functions. [#335]
- Minimum supported Rust version (MSRV) is now 1.71.
## 2.0.2
- Service types can now be `Send` and `'static` regardless of request, response, and config types, etc. [#397]
[#397]: https://github.com/actix/actix-net/pull/397
## 2.0.1
- Documentation fix. [#388]
[#388]: https://github.com/actix/actix-net/pull/388
## 2.0.0
- Removed pipeline and related structs/functions. [#335]
[#335]: https://github.com/actix/actix-net/pull/335
## 2.0.0-beta.5
## 2.0.0-beta.5 - 2021-03-15
* Add default `Service` trait impl for `Rc<S: Service>` and `&S: Service`. [#288]
* Add `boxed::rc_service` function for constructing `boxed::RcService` type [#290]
- Add default `Service` trait impl for `Rc<S: Service>` and `&S: Service`. [#288]
- Add `boxed::rc_service` function for constructing `boxed::RcService` type [#290]
[#288]: https://github.com/actix/actix-net/pull/288
[#290]: https://github.com/actix/actix-net/pull/290
## 2.0.0-beta.4
## 2.0.0-beta.4 - 2021-02-04
* `Service::poll_ready` and `Service::call` receive `&self`. [#247]
* `apply_fn` and `apply_fn_factory` now receive `Fn(Req, &Service)` function type. [#247]
* `apply_cfg` and `apply_cfg_factory` now receive `Fn(Req, &Service)` function type. [#247]
* `fn_service` and friends now receive `Fn(Req)` function type. [#247]
- `Service::poll_ready` and `Service::call` receive `&self`. [#247]
- `apply_fn` and `apply_fn_factory` now receive `Fn(Req, &Service)` function type. [#247]
- `apply_cfg` and `apply_cfg_factory` now receive `Fn(Req, &Service)` function type. [#247]
- `fn_service` and friends now receive `Fn(Req)` function type. [#247]
[#247]: https://github.com/actix/actix-net/pull/247
## 2.0.0-beta.3
## 2.0.0-beta.3 - 2021-01-09
* The `forward_ready!` macro converts errors. [#246]
- The `forward_ready!` macro converts errors. [#246]
[#246]: https://github.com/actix/actix-net/pull/246
## 2.0.0-beta.2
## 2.0.0-beta.2 - 2021-01-03
* Remove redundant type parameter from `map_config`.
- Remove redundant type parameter from `map_config`.
## 2.0.0-beta.1
## 2.0.0-beta.1 - 2020-12-28
* `Service`, other traits, and many type signatures now take the the request type as a type
parameter instead of an associated type. [#232]
* Add `always_ready!` and `forward_ready!` macros. [#233]
* Crate is now `no_std`. [#233]
* Migrate pin projections to `pin-project-lite`. [#233]
* Remove `AndThenApplyFn` and Pipeline `and_then_apply_fn`. Use the
`.and_then(apply_fn(...))` construction. [#233]
* Move non-vital methods to `ServiceExt` and `ServiceFactoryExt` extension traits. [#235]
- `Service`, other traits, and many type signatures now take the the request type as a type parameter instead of an associated type. [#232]
- Add `always_ready!` and `forward_ready!` macros. [#233]
- Crate is now `no_std`. [#233]
- Migrate pin projections to `pin-project-lite`. [#233]
- Remove `AndThenApplyFn` and Pipeline `and_then_apply_fn`. Use the `.and_then(apply_fn(...))` construction. [#233]
- Move non-vital methods to `ServiceExt` and `ServiceFactoryExt` extension traits. [#235]
[#232]: https://github.com/actix/actix-net/pull/232
[#233]: https://github.com/actix/actix-net/pull/233
[#235]: https://github.com/actix/actix-net/pull/235
## 1.0.6
## 1.0.6 - 2020-08-09
- Removed unsound custom Cell implementation that allowed obtaining several mutable references to the same data, which is undefined behavior in Rust and could lead to violations of memory safety. External code could obtain several mutable references to the same data through service combinators. Attempts to acquire several mutable references to the same data will instead result in a panic.
### Fixed
## 1.0.5
* Removed unsound custom Cell implementation that allowed obtaining several mutable references to
the same data, which is undefined behavior in Rust and could lead to violations of memory safety. External code could obtain several mutable references to the same data through
service combinators. Attempts to acquire several mutable references to the same data will instead
result in a panic.
- Fixed unsoundness in .and_then()/.then() service combinators.
## [1.0.5] - 2020-01-16
## 1.0.4
### Fixed
- Revert 1.0.3 change
* Fixed unsoundness in .and_then()/.then() service combinators
## 1.0.3
## [1.0.4] - 2020-01-15
- Fixed unsoundness in `AndThenService` impl.
### Fixed
## 1.0.2
* Revert 1.0.3 change
- Add `into_service` helper function.
## [1.0.3] - 2020-01-15
## 1.0.1
### Fixed
- `map_config()` and `unit_config()` now accept `IntoServiceFactory` type.
* Fixed unsoundness in `AndThenService` impl
## 1.0.0
## [1.0.2] - 2020-01-08
- Add Clone impl for Apply service
### Added
## 1.0.0-alpha.4
* Add `into_service` helper function
- Renamed `service_fn` to `fn_service`
- Renamed `factory_fn` to `fn_factory`
- Renamed `factory_fn_cfg` to `fn_factory_with_config`
## 1.0.0-alpha.3
## [1.0.1] - 2019-12-22
- Add missing Clone impls
- Restore `Transform::map_init_err()` combinator
- Restore `Service/Factory::apply_fn()` in form of `Pipeline/Factory::and_then_apply_fn()`
- Optimize service combinators and futures memory layout
### Changed
## 1.0.0-alpha.2
* `map_config()` and `unit_config()` accepts `IntoServiceFactory` type
- Use owned config value for service factory
- Renamed BoxedNewService/BoxedService to BoxServiceFactory/BoxService
## 1.0.0-alpha.1
## [1.0.0] - 2019-12-11
- Migrated to `std::future`
- `NewService` renamed to `ServiceFactory`
- Added `pipeline` and `pipeline_factory` function
### Added
## 0.4.2
* Add Clone impl for Apply service
- Check service readiness for `new_apply_cfg` combinator
## 0.4.1
## [1.0.0-alpha.4] - 2019-12-08
- Add `new_apply_cfg` function
### Changed
## 0.4.0
* Renamed `service_fn` to `fn_service`
- Add `NewService::map_config` and `NewService::unit_config` combinators.
- Use associated type for `NewService` config.
- Change `apply_cfg` function.
- Renamed helper functions.
* Renamed `factory_fn` to `fn_factory`
## 0.3.6
* Renamed `factory_fn_cfg` to `fn_factory_with_config`
- Poll boxed service call result immediately
## 0.3.5
## [1.0.0-alpha.3] - 2019-12-06
- Add `impl<S: Service> Service for Rc<RefCell<S>>`.
### Changed
## 0.3.4
* Add missing Clone impls
- Add `Transform::from_err()` combinator
- Add `apply_fn` helper
- Add `apply_fn_factory` helper
- Add `apply_transform` helper
- Add `apply_cfg` helper
* Restore `Transform::map_init_err()` combinator
## 0.3.3
* Restore `Service/Factory::apply_fn()` in form of `Pipeline/Factory::and_then_apply_fn()`
- Add `ApplyTransform` new service for transform and new service.
- Add `NewService::apply_cfg()` combinator, allows to use nested `NewService` with different config parameter.
- Revert IntoFuture change
* Optimize service combinators and futures memory layout
## 0.3.2
- Change `NewService::Future` and `Transform::Future` to the `IntoFuture` trait.
- Export `AndThenTransform` type
## [1.0.0-alpha.2] - 2019-12-02
## 0.3.1
### Changed
- Simplify Transform trait
* Use owned config value for service factory
## 0.3.0
* Renamed BoxedNewService/BoxedService to BoxServiceFactory/BoxService
- Added boxed NewService and Service.
- Added `Config` parameter to `NewService` trait.
- Added `Config` parameter to `NewTransform` trait.
## 0.2.2
## [1.0.0-alpha.1] - 2019-11-25
- Added `NewService` impl for `Rc<S> where S: NewService`
- Added `NewService` impl for `Arc<S> where S: NewService`
### Changed
## 0.2.1
* Migraded to `std::future`
- Generalize `.apply` combinator with Transform trait
* `NewService` renamed to `ServiceFactory`
* Added `pipeline` and `pipeline_factory` function
## [0.4.2] - 2019-08-27
### Fixed
* Check service readiness for `new_apply_cfg` combinator
## [0.4.1] - 2019-06-06
### Added
* Add `new_apply_cfg` function
## [0.4.0] - 2019-05-12
### Changed
* Use associated type for `NewService` config
* Change `apply_cfg` function
* Renamed helper functions
### Added
* Add `NewService::map_config` and `NewService::unit_config` combinators
## [0.3.6] - 2019-04-07
### Changed
* Poll boxed service call result immediately
## [0.3.5] - 2019-03-29
### Added
* Add `impl<S: Service> Service for Rc<RefCell<S>>`
## [0.3.4] - 2019-03-12
### Added
* Add `Transform::from_err()` combinator
* Add `apply_fn` helper
* Add `apply_fn_factory` helper
* Add `apply_transform` helper
* Add `apply_cfg` helper
## [0.3.3] - 2019-03-09
### Added
* Add `ApplyTransform` new service for transform and new service.
* Add `NewService::apply_cfg()` combinator, allows to use
nested `NewService` with different config parameter.
### Changed
* Revert IntoFuture change
## [0.3.2] - 2019-03-04
### Changed
* Change `NewService::Future` and `Transform::Future` to the `IntoFuture` trait.
* Export `AndThenTransform` type
## [0.3.1] - 2019-03-04
### Changed
* Simplify Transform trait
## [0.3.0] - 2019-03-02
## Added
* Added boxed NewService and Service.
## Changed
* Added `Config` parameter to `NewService` trait.
* Added `Config` parameter to `NewTransform` trait.
## [0.2.2] - 2019-02-19
### Added
* Added `NewService` impl for `Rc<S> where S: NewService`
* Added `NewService` impl for `Arc<S> where S: NewService`
## [0.2.1] - 2019-02-03
### Changed
* Generalize `.apply` combinator with Transform trait
## [0.2.0] - 2019-02-01
### Changed
* Use associated type instead of generic for Service definition.
* Before:
## 0.2.0
- Use associated type instead of generic for Service definition.
- Before:
```rust
impl Service<Request> for Client {
type Response = Response;
// ...
}
```
* After:
- After:
```rust
impl Service for Client {
type Request = Request;
@ -283,51 +196,31 @@
}
```
## 0.1.6
## [0.1.6] - 2019-01-24
- Use `FnMut` instead of `Fn` for .apply() and .map() combinators and `FnService` type
- Change `.apply()` error semantic, new service's error is `From<Self::Error>`
### Changed
## 0.1.5
* Use `FnMut` instead of `Fn` for .apply() and .map() combinators and `FnService` type
- Make `Out::Error` convertible from `T::Error` for apply combinator
* Change `.apply()` error semantic, new service's error is `From<Self::Error>`
## 0.1.4
- Use `FnMut` instead of `Fn` for `FnService`
## [0.1.5] - 2019-01-13
## 0.1.3
### Changed
- Split service combinators to separate trait
* Make `Out::Error` convertable from `T::Error` for apply combinator
## 0.1.2
- Release future early for `.and_then()` and `.then()` combinators
## [0.1.4] - 2019-01-11
## 0.1.1
### Changed
- Added Service impl for `Box<S: Service>`
* Use `FnMut` instead of `Fn` for `FnService`
## 0.1.0
## [0.1.3] - 2018-12-12
### Changed
* Split service combinators to separate trait
## [0.1.2] - 2018-12-12
### Fixed
* Release future early for `.and_then()` and `.then()` combinators
## [0.1.1] - 2018-12-09
### Added
* Added Service impl for Box<S: Service>
## [0.1.0] - 2018-12-09
* Initial import
- Initial import

View File

@ -1,28 +1,23 @@
[package]
name = "actix-service"
version = "2.0.0"
authors = [
"Nikolay Kim <fafhrd91@gmail.com>",
"Rob Ede <robjtede@icloud.com>",
"fakeshadow <24548779@qq.com>",
]
version = "2.0.3"
authors = ["Nikolay Kim <fafhrd91@gmail.com>", "Rob Ede <robjtede@icloud.com>"]
description = "Service trait and combinators for representing asynchronous request/response operations."
keywords = ["network", "framework", "async", "futures", "service"]
categories = ["network-programming", "asynchronous"]
categories = ["network-programming", "asynchronous", "no-std"]
repository = "https://github.com/actix/actix-net"
license = "MIT OR Apache-2.0"
edition = "2018"
[lib]
name = "actix_service"
path = "src/lib.rs"
edition.workspace = true
rust-version.workspace = true
[dependencies]
futures-core = { version = "0.3.7", default-features = false }
paste = "1"
futures-core = { version = "0.3.17", default-features = false }
pin-project-lite = "0.2"
[dev-dependencies]
actix-rt = "2.0.0"
actix-utils = "3.0.0"
futures-util = { version = "0.3.7", default-features = false }
actix-rt = "2"
actix-utils = "3"
futures-util = { version = "0.3.17", default-features = false }
[lints]
workspace = true

View File

@ -3,10 +3,10 @@
> Service trait and combinators for representing asynchronous request/response operations.
[![crates.io](https://img.shields.io/crates/v/actix-service?label=latest)](https://crates.io/crates/actix-service)
[![Documentation](https://docs.rs/actix-service/badge.svg?version=2.0.0)](https://docs.rs/actix-service/2.0.0)
[![Documentation](https://docs.rs/actix-service/badge.svg?version=2.0.3)](https://docs.rs/actix-service/2.0.3)
[![Version](https://img.shields.io/badge/rustc-1.46+-ab6000.svg)](https://blog.rust-lang.org/2020/03/12/Rust-1.46.html)
![License](https://img.shields.io/crates/l/actix-service.svg)
[![Dependency Status](https://deps.rs/crate/actix-service/2.0.0/status.svg)](https://deps.rs/crate/actix-service/2.0.0)
![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-service.svg)
[![Dependency Status](https://deps.rs/crate/actix-service/2.0.3/status.svg)](https://deps.rs/crate/actix-service/2.0.3)
![Download](https://img.shields.io/crates/d/actix-service.svg)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)

View File

@ -0,0 +1,35 @@
#![allow(missing_docs)]
use std::{future::Future, sync::mpsc, time::Duration};
async fn oracle<F, Fut>(f: F) -> (u32, u32)
where
F: FnOnce() -> Fut + Clone + Send + 'static,
Fut: Future<Output = u32> + 'static,
{
let f1 = actix_rt::spawn(f.clone()());
let f2 = actix_rt::spawn(f());
(f1.await.unwrap(), f2.await.unwrap())
}
#[actix_rt::main]
async fn main() {
let (tx, rx) = mpsc::channel();
let (r1, r2) = oracle({
let tx = tx.clone();
|| async move {
tx.send(()).unwrap();
4 * 4
}
})
.await;
assert_eq!(r1, r2);
tx.send(()).unwrap();
rx.recv_timeout(Duration::from_millis(100)).unwrap();
rx.recv_timeout(Duration::from_millis(100)).unwrap();
}

View File

@ -121,12 +121,7 @@ pub struct AndThenServiceFactory<A, B, Req>
where
A: ServiceFactory<Req>,
A::Config: Clone,
B: ServiceFactory<
A::Response,
Config = A::Config,
Error = A::Error,
InitError = A::InitError,
>,
B: ServiceFactory<A::Response, Config = A::Config, Error = A::Error, InitError = A::InitError>,
{
inner: Rc<(A, B)>,
_phantom: PhantomData<Req>,
@ -136,12 +131,7 @@ impl<A, B, Req> AndThenServiceFactory<A, B, Req>
where
A: ServiceFactory<Req>,
A::Config: Clone,
B: ServiceFactory<
A::Response,
Config = A::Config,
Error = A::Error,
InitError = A::InitError,
>,
B: ServiceFactory<A::Response, Config = A::Config, Error = A::Error, InitError = A::InitError>,
{
/// Create new `AndThenFactory` combinator
pub(crate) fn new(a: A, b: B) -> Self {
@ -156,12 +146,7 @@ impl<A, B, Req> ServiceFactory<Req> for AndThenServiceFactory<A, B, Req>
where
A: ServiceFactory<Req>,
A::Config: Clone,
B: ServiceFactory<
A::Response,
Config = A::Config,
Error = A::Error,
InitError = A::InitError,
>,
B: ServiceFactory<A::Response, Config = A::Config, Error = A::Error, InitError = A::InitError>,
{
type Response = B::Response;
type Error = A::Error;
@ -184,12 +169,7 @@ impl<A, B, Req> Clone for AndThenServiceFactory<A, B, Req>
where
A: ServiceFactory<Req>,
A::Config: Clone,
B: ServiceFactory<
A::Response,
Config = A::Config,
Error = A::Error,
InitError = A::InitError,
>,
B: ServiceFactory<A::Response, Config = A::Config, Error = A::Error, InitError = A::InitError>,
{
fn clone(&self) -> Self {
Self {
@ -334,9 +314,8 @@ mod tests {
async fn test_new_service() {
let cnt = Rc::new(Cell::new(0));
let cnt2 = cnt.clone();
let new_srv =
pipeline_factory(fn_factory(move || ready(Ok::<_, ()>(Srv1(cnt2.clone())))))
.and_then(move || ready(Ok(Srv2(cnt.clone()))));
let new_srv = pipeline_factory(fn_factory(move || ready(Ok::<_, ()>(Srv1(cnt2.clone())))))
.and_then(move || ready(Ok(Srv2(cnt.clone()))));
let srv = new_srv.new_service(()).await.unwrap();
let res = srv.call("srv1").await;

View File

@ -51,7 +51,7 @@ where
{
service: S,
wrap_fn: F,
_phantom: PhantomData<(Req, In, Res, Err)>,
_phantom: PhantomData<fn(Req) -> (In, Res, Err)>,
}
impl<S, F, Fut, Req, In, Res, Err> Apply<S, F, Req, In, Res, Err>
@ -106,7 +106,7 @@ where
pub struct ApplyFactory<SF, F, Req, In, Res, Err> {
factory: SF,
wrap_fn: F,
_phantom: PhantomData<(Req, In, Res, Err)>,
_phantom: PhantomData<fn(Req) -> (In, Res, Err)>,
}
impl<SF, F, Fut, Req, In, Res, Err> ApplyFactory<SF, F, Req, In, Res, Err>
@ -140,8 +140,7 @@ where
}
}
impl<SF, F, Fut, Req, In, Res, Err> ServiceFactory<Req>
for ApplyFactory<SF, F, Req, In, Res, Err>
impl<SF, F, Fut, Req, In, Res, Err> ServiceFactory<Req> for ApplyFactory<SF, F, Req, In, Res, Err>
where
SF: ServiceFactory<In, Error = Err>,
F: Fn(Req, &SF::Service) -> Fut + Clone,
@ -171,7 +170,7 @@ pin_project! {
#[pin]
fut: SF::Future,
wrap_fn: Option<F>,
_phantom: PhantomData<(Req, Res)>,
_phantom: PhantomData<fn(Req) -> Res>,
}
}
@ -209,15 +208,13 @@ where
#[cfg(test)]
mod tests {
use core::task::Poll;
use futures_util::future::lazy;
use super::*;
use crate::{
ok,
pipeline::{pipeline, pipeline_factory},
Ready, Service, ServiceFactory,
Ready,
};
#[derive(Clone)]

View File

@ -198,8 +198,7 @@ pin_project! {
}
}
impl<SF, Req, F, Cfg, Fut, S> Future
for ApplyConfigServiceFactoryResponse<SF, Req, F, Cfg, Fut, S>
impl<SF, Req, F, Cfg, Fut, S> Future for ApplyConfigServiceFactoryResponse<SF, Req, F, Cfg, Fut, S>
where
SF: ServiceFactory<Req, Config = ()>,
SF::InitError: From<SF::Error>,

View File

@ -3,36 +3,38 @@
use alloc::{boxed::Box, rc::Rc};
use core::{future::Future, pin::Pin};
use paste::paste;
use crate::{Service, ServiceFactory};
/// A boxed future with no send bound or lifetime parameters.
pub type BoxFuture<T> = Pin<Box<dyn Future<Output = T>>>;
macro_rules! service_object {
($name: ident, $type: tt, $fn_name: ident) => {
paste! {
#[doc = "Type alias for service trait object using `" $type "`."]
pub type $name<Req, Res, Err> = $type<
dyn Service<Req, Response = Res, Error = Err, Future = BoxFuture<Result<Res, Err>>>,
>;
/// Type alias for service trait object using [`Box`].
pub type BoxService<Req, Res, Err> =
Box<dyn Service<Req, Response = Res, Error = Err, Future = BoxFuture<Result<Res, Err>>>>;
#[doc = "Wraps service as a trait object using [`" $name "`]."]
pub fn $fn_name<S, Req>(service: S) -> $name<Req, S::Response, S::Error>
where
S: Service<Req> + 'static,
Req: 'static,
S::Future: 'static,
{
$type::new(ServiceWrapper::new(service))
}
}
};
/// Wraps service as a trait object using [`BoxService`].
pub fn service<S, Req>(service: S) -> BoxService<Req, S::Response, S::Error>
where
S: Service<Req> + 'static,
Req: 'static,
S::Future: 'static,
{
Box::new(ServiceWrapper::new(service))
}
service_object!(BoxService, Box, service);
service_object!(RcService, Rc, rc_service);
/// Type alias for service trait object using [`Rc`].
pub type RcService<Req, Res, Err> =
Rc<dyn Service<Req, Response = Res, Error = Err, Future = BoxFuture<Result<Res, Err>>>>;
/// Wraps service as a trait object using [`RcService`].
pub fn rc_service<S, Req>(service: S) -> RcService<Req, S::Response, S::Error>
where
S: Service<Req> + 'static,
Req: 'static,
S::Future: 'static,
{
Rc::new(ServiceWrapper::new(service))
}
struct ServiceWrapper<S> {
inner: S,
@ -91,8 +93,7 @@ type Inner<C, Req, Res, Err, InitErr> = Box<
>,
>;
impl<C, Req, Res, Err, InitErr> ServiceFactory<Req>
for BoxServiceFactory<C, Req, Res, Err, InitErr>
impl<C, Req, Res, Err, InitErr> ServiceFactory<Req> for BoxServiceFactory<C, Req, Res, Err, InitErr>
where
Req: 'static,
Res: 'static,

View File

@ -44,7 +44,7 @@ pub trait ServiceExt<Req>: Service<Req> {
/// Call another service after call to this one has resolved successfully.
///
/// This function can be used to chain two services together and ensure that the second service
/// isn't called until call to the fist service have finished. Result of the call to the first
/// isn't called until call to the first service have finished. Result of the call to the first
/// service is used as an input parameter for the second service's call.
///
/// Note that this function consumes the receiving service and returns a wrapped version of it.

View File

@ -3,9 +3,7 @@ use core::{future::Future, marker::PhantomData};
use crate::{ok, IntoService, IntoServiceFactory, Ready, Service, ServiceFactory};
/// Create `ServiceFactory` for function that can act as a `Service`
pub fn fn_service<F, Fut, Req, Res, Err, Cfg>(
f: F,
) -> FnServiceFactory<F, Fut, Req, Res, Err, Cfg>
pub fn fn_service<F, Fut, Req, Res, Err, Cfg>(f: F) -> FnServiceFactory<F, Fut, Req, Res, Err, Cfg>
where
F: Fn(Req) -> Fut + Clone,
Fut: Future<Output = Result<Res, Err>>,
@ -48,13 +46,11 @@ where
/// Ok(())
/// }
/// ```
pub fn fn_factory<F, Cfg, Srv, Req, Fut, Err>(
f: F,
) -> FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err>
pub fn fn_factory<F, Cfg, Srv, Req, Fut, Err>(f: F) -> FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err>
where
Srv: Service<Req>,
F: Fn() -> Fut,
Fut: Future<Output = Result<Srv, Err>>,
Srv: Service<Req>,
{
FnServiceNoConfig::new(f)
}
@ -105,7 +101,7 @@ where
Fut: Future<Output = Result<Res, Err>>,
{
f: F,
_t: PhantomData<Req>,
_t: PhantomData<fn(Req)>,
}
impl<F, Fut, Req, Res, Err> FnService<F, Fut, Req, Res, Err>
@ -160,7 +156,7 @@ where
Fut: Future<Output = Result<Res, Err>>,
{
f: F,
_t: PhantomData<(Req, Cfg)>,
_t: PhantomData<fn(Req, Cfg)>,
}
impl<F, Fut, Req, Res, Err, Cfg> FnServiceFactory<F, Fut, Req, Res, Err, Cfg>
@ -237,7 +233,7 @@ where
Srv: Service<Req>,
{
f: F,
_t: PhantomData<(Fut, Cfg, Req, Srv, Err)>,
_t: PhantomData<fn(Cfg, Req) -> (Fut, Srv, Err)>,
}
impl<F, Fut, Cfg, Srv, Req, Err> FnServiceConfig<F, Fut, Cfg, Srv, Req, Err>
@ -265,8 +261,7 @@ where
}
}
impl<F, Fut, Cfg, Srv, Req, Err> ServiceFactory<Req>
for FnServiceConfig<F, Fut, Cfg, Srv, Req, Err>
impl<F, Fut, Cfg, Srv, Req, Err> ServiceFactory<Req> for FnServiceConfig<F, Fut, Cfg, Srv, Req, Err>
where
F: Fn(Cfg) -> Fut,
Fut: Future<Output = Result<Srv, Err>>,
@ -293,7 +288,7 @@ where
Fut: Future<Output = Result<Srv, Err>>,
{
f: F,
_t: PhantomData<(Cfg, Req)>,
_t: PhantomData<fn(Cfg, Req)>,
}
impl<F, Cfg, Srv, Req, Fut, Err> FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err>
@ -356,7 +351,6 @@ mod tests {
use futures_util::future::lazy;
use super::*;
use crate::{ok, Service, ServiceFactory};
#[actix_rt::test]
async fn test_fn_service() {
@ -391,4 +385,40 @@ mod tests {
assert!(res.is_ok());
assert_eq!(res.unwrap(), ("srv", 1));
}
#[actix_rt::test]
async fn test_auto_impl_send() {
use alloc::rc::Rc;
use crate::{map_config, ServiceExt, ServiceFactoryExt};
let srv_1 = fn_service(|_: Rc<u8>| ok::<_, Rc<u8>>(Rc::new(0u8)));
let fac_1 = fn_factory_with_config(|_: Rc<u8>| {
ok::<_, Rc<u8>>(fn_service(|_: Rc<u8>| ok::<_, Rc<u8>>(Rc::new(0u8))))
});
let fac_2 =
fn_factory(|| ok::<_, Rc<u8>>(fn_service(|_: Rc<u8>| ok::<_, Rc<u8>>(Rc::new(0u8)))));
fn is_send<T: Send + Sync + Clone>(_: &T) {}
is_send(&fac_1);
is_send(&map_config(fac_1.clone(), |_: Rc<u8>| Rc::new(0u8)));
is_send(&fac_1.clone().map_err(|_| Rc::new(0u8)));
is_send(&fac_1.clone().map(|_| Rc::new(0u8)));
is_send(&fac_1.clone().map_init_err(|_| Rc::new(0u8)));
// `and_then` is always !Send
// is_send(&fac_1.clone().and_then(fac_1.clone()));
is_send(&fac_1.new_service(Rc::new(0u8)).await.unwrap());
is_send(&fac_2);
is_send(&fac_2.new_service(Rc::new(0u8)).await.unwrap());
is_send(&srv_1);
is_send(&ServiceExt::map(srv_1.clone(), |_| Rc::new(0u8)));
is_send(&ServiceExt::map_err(srv_1.clone(), |_| Rc::new(0u8)));
// `and_then` is always !Send
// is_send(&ServiceExt::and_then(srv_1.clone(), srv_1.clone()));
}
}

View File

@ -1,8 +1,6 @@
//! See [`Service`] docs for information on this crate's foundational trait.
#![no_std]
#![deny(rust_2018_idioms, nonstandard_style, future_incompatible)]
#![warn(missing_docs)]
#![allow(clippy::type_complexity)]
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
@ -33,15 +31,16 @@ mod then;
mod transform;
mod transform_err;
pub use self::apply::{apply_fn, apply_fn_factory};
pub use self::apply_cfg::{apply_cfg, apply_cfg_factory};
pub use self::ext::{ServiceExt, ServiceFactoryExt, TransformExt};
pub use self::fn_service::{fn_factory, fn_factory_with_config, fn_service};
pub use self::map_config::{map_config, unit_config};
pub use self::transform::{apply, ApplyTransform, Transform};
#[allow(unused_imports)]
use self::ready::{err, ok, ready, Ready};
pub use self::{
apply::{apply_fn, apply_fn_factory},
apply_cfg::{apply_cfg, apply_cfg_factory},
ext::{ServiceExt, ServiceFactoryExt, TransformExt},
fn_service::{fn_factory, fn_factory_with_config, fn_service},
map_config::{map_config, unit_config},
transform::{apply, ApplyTransform, Transform},
};
/// An asynchronous operation from `Request` to a `Response`.
///
@ -77,7 +76,7 @@ use self::ready::{err, ok, ready, Ready};
///
/// fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { ... }
///
/// fn call(&self, req: Self::Request) -> Self::Future { ... }
/// fn call(&self, req: u8) -> Self::Future { ... }
/// }
/// ```
///

View File

@ -1,7 +1,7 @@
/// An implementation of [`poll_ready`]() that always signals readiness.
///
/// This should only be used for basic leaf services that have no concept of un-readiness.
/// For wrapper or other serivice types, use [`forward_ready!`] for simple cases or write a bespoke
/// For wrapper or other service types, use [`forward_ready!`] for simple cases or write a bespoke
/// `poll_ready` implementation.
///
/// [`poll_ready`]: crate::Service::poll_ready
@ -25,6 +25,8 @@
/// }
/// }
/// ```
///
/// [`forward_ready!`]: crate::forward_ready
#[macro_export]
macro_rules! always_ready {
() => {

View File

@ -15,7 +15,7 @@ use super::{Service, ServiceFactory};
pub struct Map<A, F, Req, Res> {
service: A,
f: F,
_t: PhantomData<(Req, Res)>,
_t: PhantomData<fn(Req) -> Res>,
}
impl<A, F, Req, Res> Map<A, F, Req, Res> {
@ -97,7 +97,7 @@ where
match this.fut.poll(cx) {
Poll::Ready(Ok(resp)) => Poll::Ready(Ok((this.f)(resp))),
Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
Poll::Ready(Err(err)) => Poll::Ready(Err(err)),
Poll::Pending => Poll::Pending,
}
}
@ -107,7 +107,7 @@ where
pub struct MapServiceFactory<A, F, Req, Res> {
a: A,
f: F,
r: PhantomData<(Res, Req)>,
r: PhantomData<fn(Req) -> Res>,
}
impl<A, F, Req, Res> MapServiceFactory<A, F, Req, Res> {
@ -202,9 +202,7 @@ mod tests {
use futures_util::future::lazy;
use super::*;
use crate::{
ok, IntoServiceFactory, Ready, Service, ServiceExt, ServiceFactory, ServiceFactoryExt,
};
use crate::{ok, IntoServiceFactory, Ready, ServiceExt, ServiceFactoryExt};
struct Srv;

View File

@ -28,7 +28,7 @@ where
pub struct MapConfig<SF, Req, F, Cfg> {
factory: SF,
cfg_mapper: F,
e: PhantomData<(Cfg, Req)>,
e: PhantomData<fn(Cfg, Req)>,
}
impl<SF, Req, F, Cfg> MapConfig<SF, Req, F, Cfg> {
@ -82,7 +82,7 @@ where
/// `unit_config()` config combinator
pub struct UnitConfig<SF, Cfg, Req> {
factory: SF,
_phantom: PhantomData<(Cfg, Req)>,
_phantom: PhantomData<fn(Cfg, Req)>,
}
impl<SF, Cfg, Req> UnitConfig<SF, Cfg, Req>

View File

@ -9,26 +9,25 @@ use pin_project_lite::pin_project;
use super::{Service, ServiceFactory};
/// Service for the `map_err` combinator, changing the type of a service's
/// error.
/// Service for the `map_err` combinator, changing the type of a service's error.
///
/// This is created by the `ServiceExt::map_err` method.
pub struct MapErr<S, Req, F, E> {
service: S,
f: F,
_t: PhantomData<(E, Req)>,
mapper: F,
_t: PhantomData<fn(Req) -> E>,
}
impl<S, Req, F, E> MapErr<S, Req, F, E> {
/// Create new `MapErr` combinator
pub(crate) fn new(service: S, f: F) -> Self
pub(crate) fn new(service: S, mapper: F) -> Self
where
S: Service<Req>,
F: Fn(S::Error) -> E,
{
Self {
service,
f,
mapper,
_t: PhantomData,
}
}
@ -42,7 +41,7 @@ where
fn clone(&self) -> Self {
MapErr {
service: self.service.clone(),
f: self.f.clone(),
mapper: self.mapper.clone(),
_t: PhantomData,
}
}
@ -58,11 +57,11 @@ where
type Future = MapErrFuture<A, Req, F, E>;
fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready(ctx).map_err(&self.f)
self.service.poll_ready(ctx).map_err(&self.mapper)
}
fn call(&self, req: Req) -> Self::Future {
MapErrFuture::new(self.service.call(req), self.f.clone())
MapErrFuture::new(self.service.call(req), self.mapper.clone())
}
}
@ -105,23 +104,23 @@ where
/// service's error.
///
/// This is created by the `NewServiceExt::map_err` method.
pub struct MapErrServiceFactory<A, Req, F, E>
pub struct MapErrServiceFactory<SF, Req, F, E>
where
A: ServiceFactory<Req>,
F: Fn(A::Error) -> E + Clone,
SF: ServiceFactory<Req>,
F: Fn(SF::Error) -> E + Clone,
{
a: A,
a: SF,
f: F,
e: PhantomData<(E, Req)>,
e: PhantomData<fn(Req) -> E>,
}
impl<A, Req, F, E> MapErrServiceFactory<A, Req, F, E>
impl<SF, Req, F, E> MapErrServiceFactory<SF, Req, F, E>
where
A: ServiceFactory<Req>,
F: Fn(A::Error) -> E + Clone,
SF: ServiceFactory<Req>,
F: Fn(SF::Error) -> E + Clone,
{
/// Create new `MapErr` new service instance
pub(crate) fn new(a: A, f: F) -> Self {
pub(crate) fn new(a: SF, f: F) -> Self {
Self {
a,
f,
@ -130,10 +129,10 @@ where
}
}
impl<A, Req, F, E> Clone for MapErrServiceFactory<A, Req, F, E>
impl<SF, Req, F, E> Clone for MapErrServiceFactory<SF, Req, F, E>
where
A: ServiceFactory<Req> + Clone,
F: Fn(A::Error) -> E + Clone,
SF: ServiceFactory<Req> + Clone,
F: Fn(SF::Error) -> E + Clone,
{
fn clone(&self) -> Self {
Self {
@ -144,57 +143,57 @@ where
}
}
impl<A, Req, F, E> ServiceFactory<Req> for MapErrServiceFactory<A, Req, F, E>
impl<SF, Req, F, E> ServiceFactory<Req> for MapErrServiceFactory<SF, Req, F, E>
where
A: ServiceFactory<Req>,
F: Fn(A::Error) -> E + Clone,
SF: ServiceFactory<Req>,
F: Fn(SF::Error) -> E + Clone,
{
type Response = A::Response;
type Response = SF::Response;
type Error = E;
type Config = A::Config;
type Service = MapErr<A::Service, Req, F, E>;
type InitError = A::InitError;
type Future = MapErrServiceFuture<A, Req, F, E>;
type Config = SF::Config;
type Service = MapErr<SF::Service, Req, F, E>;
type InitError = SF::InitError;
type Future = MapErrServiceFuture<SF, Req, F, E>;
fn new_service(&self, cfg: A::Config) -> Self::Future {
fn new_service(&self, cfg: SF::Config) -> Self::Future {
MapErrServiceFuture::new(self.a.new_service(cfg), self.f.clone())
}
}
pin_project! {
pub struct MapErrServiceFuture<A, Req, F, E>
pub struct MapErrServiceFuture<SF, Req, F, E>
where
A: ServiceFactory<Req>,
F: Fn(A::Error) -> E,
SF: ServiceFactory<Req>,
F: Fn(SF::Error) -> E,
{
#[pin]
fut: A::Future,
f: F,
fut: SF::Future,
mapper: F,
}
}
impl<A, Req, F, E> MapErrServiceFuture<A, Req, F, E>
impl<SF, Req, F, E> MapErrServiceFuture<SF, Req, F, E>
where
A: ServiceFactory<Req>,
F: Fn(A::Error) -> E,
SF: ServiceFactory<Req>,
F: Fn(SF::Error) -> E,
{
fn new(fut: A::Future, f: F) -> Self {
MapErrServiceFuture { fut, f }
fn new(fut: SF::Future, mapper: F) -> Self {
MapErrServiceFuture { fut, mapper }
}
}
impl<A, Req, F, E> Future for MapErrServiceFuture<A, Req, F, E>
impl<SF, Req, F, E> Future for MapErrServiceFuture<SF, Req, F, E>
where
A: ServiceFactory<Req>,
F: Fn(A::Error) -> E + Clone,
SF: ServiceFactory<Req>,
F: Fn(SF::Error) -> E + Clone,
{
type Output = Result<MapErr<A::Service, Req, F, E>, A::InitError>;
type Output = Result<MapErr<SF::Service, Req, F, E>, SF::InitError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
if let Poll::Ready(svc) = this.fut.poll(cx)? {
Poll::Ready(Ok(MapErr::new(svc, this.f.clone())))
Poll::Ready(Ok(MapErr::new(svc, this.mapper.clone())))
} else {
Poll::Pending
}
@ -206,10 +205,7 @@ mod tests {
use futures_util::future::lazy;
use super::*;
use crate::{
err, ok, IntoServiceFactory, Ready, Service, ServiceExt, ServiceFactory,
ServiceFactoryExt,
};
use crate::{err, ok, IntoServiceFactory, Ready, ServiceExt, ServiceFactoryExt};
struct Srv;

View File

@ -13,7 +13,7 @@ use super::ServiceFactory;
pub struct MapInitErr<A, F, Req, Err> {
a: A,
f: F,
e: PhantomData<(Req, Err)>,
e: PhantomData<fn(Req) -> Err>,
}
impl<A, F, Req, Err> MapInitErr<A, F, Req, Err>

View File

@ -6,12 +6,14 @@ use core::{
task::{Context, Poll},
};
use crate::and_then::{AndThenService, AndThenServiceFactory};
use crate::map::{Map, MapServiceFactory};
use crate::map_err::{MapErr, MapErrServiceFactory};
use crate::map_init_err::MapInitErr;
use crate::then::{ThenService, ThenServiceFactory};
use crate::{IntoService, IntoServiceFactory, Service, ServiceFactory};
use crate::{
and_then::{AndThenService, AndThenServiceFactory},
map::{Map, MapServiceFactory},
map_err::{MapErr, MapErrServiceFactory},
map_init_err::MapInitErr,
then::{ThenService, ThenServiceFactory},
IntoService, IntoServiceFactory, Service, ServiceFactory,
};
/// Construct new pipeline with one service in pipeline chain.
pub(crate) fn pipeline<I, S, Req>(service: I) -> Pipeline<S, Req>
@ -40,7 +42,7 @@ where
/// Pipeline service - pipeline allows to compose multiple service into one service.
pub(crate) struct Pipeline<S, Req> {
service: S,
_phantom: PhantomData<Req>,
_phantom: PhantomData<fn(Req)>,
}
impl<S, Req> Pipeline<S, Req>
@ -50,7 +52,7 @@ where
/// Call another service after call to this one has resolved successfully.
///
/// This function can be used to chain two services together and ensure that
/// the second service isn't called until call to the fist service have
/// the second service isn't called until call to the first service have
/// finished. Result of the call to the first service is used as an
/// input parameter for the second service's call.
///
@ -162,7 +164,7 @@ impl<S: Service<Req>, Req> Service<Req> for Pipeline<S, Req> {
/// Pipeline factory
pub(crate) struct PipelineFactory<SF, Req> {
factory: SF,
_phantom: PhantomData<Req>,
_phantom: PhantomData<fn(Req)>,
}
impl<SF, Req> PipelineFactory<SF, Req>
@ -252,10 +254,7 @@ where
}
/// Map this service's error to a different error, returning a new service.
pub fn map_err<F, E>(
self,
f: F,
) -> PipelineFactory<MapErrServiceFactory<SF, Req, F, E>, Req>
pub fn map_err<F, E>(self, f: F) -> PipelineFactory<MapErrServiceFactory<SF, Req, F, E>, Req>
where
Self: Sized,
F: Fn(SF::Error) -> E + Clone,

View File

@ -226,9 +226,9 @@ mod tests {
use actix_utils::future::{ready, Ready};
use super::*;
use crate::Service;
// pseudo-doctest for Transform trait
#[allow(unused)]
pub struct TimeoutTransform {
timeout: Duration,
}
@ -250,6 +250,7 @@ mod tests {
}
// pseudo-doctest for Transform trait
#[allow(unused)]
pub struct Timeout<S> {
service: S,
_timeout: Duration,

View File

@ -14,7 +14,7 @@ use super::Transform;
pub struct TransformMapInitErr<T, S, Req, F, E> {
transform: T,
mapper: F,
_phantom: PhantomData<(S, Req, E)>,
_phantom: PhantomData<fn(Req) -> (S, E)>,
}
impl<T, S, F, E, Req> TransformMapInitErr<T, S, Req, F, E> {

View File

@ -1,91 +1,215 @@
# Changes
## Unreleased - 2021-xx-xx
## Unreleased
- Minimum supported Rust version (MSRV) is now 1.71.
## 3.0.0-beta.5 - 2021-03-29
* Changed `connect::ssl::rustls::RustlsConnectorService` to return error when `DNSNameRef`
generation failed instead of panic. [#296]
* Remove `connect::ssl::openssl::OpensslConnectServiceFactory`. [#297]
* Remove `connect::ssl::openssl::OpensslConnectService`. [#297]
* Add `connect::ssl::native_tls` module for native tls support. [#295]
* Rename `accept::{nativetls => native_tls}`. [#295]
* Remove `connect::TcpConnectService` type. service caller expect a `TcpStream` should use
`connect::ConnectService` instead and call `Connection<T, TcpStream>::into_parts`. [#299]
## 3.4.0
- Add `rustls-0_23`, `rustls-0_23-webpki-roots`, and `rustls-0_23-native-roots` crate features.
- Minimum supported Rust version (MSRV) is now 1.70.
## 3.3.0
- Add `rustls-0_22` crate feature which excludes any root certificate methods or re-exports.
## 3.2.0
- Support Rustls v0.22.
- Add `{accept, connect}::rustls_0_22` modules.
- Add `rustls-0_21-native-roots` and `rustls-0_20-native-roots` crate features which utilize the `rustls-native-certs` crate to enable a `native_roots_cert_store()` functions in each rustls-based `connect` module.
- Implement `Host` for `http::Uri` (`http` crate version `1`).
## 3.1.1
- Fix `rustls` v0.21 version requirement.
## 3.1.0
- Support Rustls v0.21.
- Add `{accept, connect}::rustls_0_21` modules.
- Add `{accept, connect}::rustls_0_20` alias for `{accept, connect}::rustls` modules.
- Minimum supported Rust version (MSRV) is now 1.65.
## 3.0.4
- Logs emitted now use the `tracing` crate with `log` compatibility. [#451]
[#451]: https://github.com/actix/actix-net/pull/451
## 3.0.3
- No significant changes since `3.0.2`.
## 3.0.2
- Expose `connect::Connection::new`. [#439]
[#439]: https://github.com/actix/actix-net/pull/439
## 3.0.1
- No significant changes since `3.0.0`.
## 3.0.0
- No significant changes since `3.0.0-rc.2`.
## 3.0.0-rc.2
- Re-export `openssl::SslConnectorBuilder` in `connect::openssl::reexports`. [#429]
[#429]: https://github.com/actix/actix-net/pull/429
## 3.0.0-rc.1
### Added
- Derive `Debug` for `connect::Connection`. [#422]
- Implement `Display` for `accept::TlsError`. [#422]
- Implement `Error` for `accept::TlsError` where both types also implement `Error`. [#422]
- Implement `Default` for `connect::Resolver`. [#422]
- Implement `Error` for `connect::ConnectError`. [#422]
- Implement `Default` for `connect::tcp::{TcpConnector, TcpConnectorService}`. [#423]
- Implement `Default` for `connect::ConnectorService`. [#423]
### Changed
- The crate's default features flags no longer include `uri`. [#422]
- Useful re-exports from underlying TLS crates are exposed in a `reexports` modules in all acceptors and connectors.
- Convert `connect::ResolverService` from enum to struct. [#422]
- Make `ConnectAddrsIter` private. [#422]
- Mark `tcp::{TcpConnector, TcpConnectorService}` structs `#[non_exhaustive]`. [#423]
- Rename `accept::native_tls::{NativeTlsAcceptorService => AcceptorService}`. [#422]
- Rename `connect::{Address => Host}` trait. [#422]
- Rename method `connect::Connection::{host => hostname}`. [#422]
- Rename struct `connect::{Connect => ConnectInfo}`. [#422]
- Rename struct `connect::{ConnectService => ConnectorService}`. [#422]
- Rename struct `connect::{ConnectServiceFactory => Connector}`. [#422]
- Rename TLS acceptor service future types and hide from docs. [#422]
- Unbox some service futures types. [#422]
- Inline modules in `connect::tls` to `connect` module. [#422]
### Removed
- Remove `connect::{new_connector, new_connector_factory, default_connector, default_connector_factory}` methods. [#422]
- Remove `connect::native_tls::Connector::service` method. [#422]
- Remove redundant `connect::Connection::from_parts` method. [#422]
[#422]: https://github.com/actix/actix-net/pull/422
[#423]: https://github.com/actix/actix-net/pull/423
## 3.0.0-beta.9
- Add configurable timeout for accepting TLS connection. [#393]
- Added `TlsError::Timeout` variant. [#393]
- All TLS acceptor services now use `TlsError` for their error types. [#393]
- Added `TlsError::into_service_error`. [#420]
[#393]: https://github.com/actix/actix-net/pull/393
[#420]: https://github.com/actix/actix-net/pull/420
## 3.0.0-beta.8
- Add `Connect::request` for getting a reference to the connection request. [#415]
[#415]: https://github.com/actix/actix-net/pull/415
## 3.0.0-beta.7
- Add `webpki_roots_cert_store()` to get rustls compatible webpki roots cert store. [#401]
- Alias `connect::ssl` to `connect::tls`. [#401]
[#401]: https://github.com/actix/actix-net/pull/401
## 3.0.0-beta.6
- Update `tokio-rustls` to `0.23` which uses `rustls` `0.20`. [#396]
- Removed a re-export of `Session` from `rustls` as it no longer exist. [#396]
- Minimum supported Rust version (MSRV) is now 1.52.
[#396]: https://github.com/actix/actix-net/pull/396
## 3.0.0-beta.5
- Changed `connect::ssl::rustls::RustlsConnectorService` to return error when `DNSNameRef` generation failed instead of panic. [#296]
- Remove `connect::ssl::openssl::OpensslConnectServiceFactory`. [#297]
- Remove `connect::ssl::openssl::OpensslConnectService`. [#297]
- Add `connect::ssl::native_tls` module for native tls support. [#295]
- Rename `accept::{nativetls => native_tls}`. [#295]
- Remove `connect::TcpConnectService` type. Service caller expecting a `TcpStream` should use `connect::ConnectService` instead and call `Connection<T, TcpStream>::into_parts`. [#299]
[#295]: https://github.com/actix/actix-net/pull/295
[#296]: https://github.com/actix/actix-net/pull/296
[#297]: https://github.com/actix/actix-net/pull/297
[#299]: https://github.com/actix/actix-net/pull/299
## 3.0.0-beta.4
## 3.0.0-beta.4 - 2021-02-24
* Rename `accept::openssl::{SslStream => TlsStream}`.
* Add `connect::Connect::set_local_addr` to attach local `IpAddr`. [#282]
* `connector::TcpConnector` service will try to bind to local_addr of `IpAddr` when given. [#282]
- Rename `accept::openssl::{SslStream => TlsStream}`.
- Add `connect::Connect::set_local_addr` to attach local `IpAddr`. [#282]
- `connector::TcpConnector` service will try to bind to local_addr of `IpAddr` when given. [#282]
[#282]: https://github.com/actix/actix-net/pull/282
## 3.0.0-beta.3
## 3.0.0-beta.3 - 2021-02-06
* Remove `trust-dns-proto` and `trust-dns-resolver`. [#248]
* Use `std::net::ToSocketAddrs` as simple and basic default resolver. [#248]
* Add `Resolve` trait for custom DNS resolvers. [#248]
* Add `Resolver::new_custom` function to construct custom resolvers. [#248]
* Export `webpki_roots::TLS_SERVER_ROOTS` in `actix_tls::connect` mod and remove
the export from `actix_tls::accept` [#248]
* Remove `ConnectTakeAddrsIter`. `Connect::take_addrs` now returns `ConnectAddrsIter<'static>`
as owned iterator. [#248]
* Rename `Address::{host => hostname}` to more accurately describe which URL segment is returned.
* Update `actix-rt` to `2.0.0`. [#273]
- Remove `trust-dns-proto` and `trust-dns-resolver`. [#248]
- Use `std::net::ToSocketAddrs` as simple and basic default resolver. [#248]
- Add `Resolve` trait for custom DNS resolvers. [#248]
- Add `Resolver::new_custom` function to construct custom resolvers. [#248]
- Export `webpki_roots::TLS_SERVER_ROOTS` in `actix_tls::connect` mod and remove the export from `actix_tls::accept` [#248]
- Remove `ConnectTakeAddrsIter`. `Connect::take_addrs` now returns `ConnectAddrsIter<'static>` as owned iterator. [#248]
- Rename `Address::{host => hostname}` to more accurately describe which URL segment is returned.
- Update `actix-rt` to `2.0.0`. [#273]
[#248]: https://github.com/actix/actix-net/pull/248
[#273]: https://github.com/actix/actix-net/pull/273
## 3.0.0-beta.2
## 3.0.0-beta.2 - 2021-xx-xx
* Depend on stable trust-dns packages. [#204]
- Depend on stable trust-dns packages. [#204]
[#204]: https://github.com/actix/actix-net/pull/204
## 3.0.0-beta.1
## 3.0.0-beta.1 - 2020-12-29
* Move acceptors under `accept` module. [#238]
* Merge `actix-connect` crate under `connect` module. [#238]
* Add feature flags to enable acceptors and/or connectors individually. [#238]
- Move acceptors under `accept` module. [#238]
- Merge `actix-connect` crate under `connect` module. [#238]
- Add feature flags to enable acceptors and/or connectors individually. [#238]
[#238]: https://github.com/actix/actix-net/pull/238
## 2.0.0
## 2.0.0 - 2020-09-03
* `nativetls::NativeTlsAcceptor` is renamed to `nativetls::Acceptor`.
* Where possible, "SSL" terminology is replaced with "TLS".
* `SslError` is renamed to `TlsError`.
* `TlsError::Ssl` enum variant is renamed to `TlsError::Tls`.
* `max_concurrent_ssl_connect` is renamed to `max_concurrent_tls_connect`.
- `nativetls::NativeTlsAcceptor` is renamed to `nativetls::Acceptor`.
- Where possible, "SSL" terminology is replaced with "TLS".
- `SslError` is renamed to `TlsError`.
- `TlsError::Ssl` enum variant is renamed to `TlsError::Tls`.
- `max_concurrent_ssl_connect` is renamed to `max_concurrent_tls_connect`.
## 2.0.0-alpha.2
## 2.0.0-alpha.2 - 2020-08-17
* Update `rustls` dependency to 0.18
* Update `tokio-rustls` dependency to 0.14
* Update `webpki-roots` dependency to 0.20
- Update `rustls` dependency to 0.18
- Update `tokio-rustls` dependency to 0.14
- Update `webpki-roots` dependency to 0.20
## [2.0.0-alpha.1]
## [2.0.0-alpha.1] - 2020-03-03
* Update `rustls` dependency to 0.17
* Update `tokio-rustls` dependency to 0.13
* Update `webpki-roots` dependency to 0.19
- Update `rustls` dependency to 0.17
- Update `tokio-rustls` dependency to 0.13
- Update `webpki-roots` dependency to 0.19
## [1.0.0]
## [1.0.0] - 2019-12-11
* 1.0.0 release
- 1.0.0 release
## [1.0.0-alpha.3]
## [1.0.0-alpha.3] - 2019-12-07
* Migrate to tokio 0.2
* Enable rustls acceptor service
* Enable native-tls acceptor service
- Migrate to tokio 0.2
- Enable rustls acceptor service
- Enable native-tls acceptor service
## [1.0.0-alpha.1]
## [1.0.0-alpha.1] - 2019-12-02
* Split openssl acceptor from actix-server package
- Split openssl acceptor from actix-server package

135
actix-tls/Cargo.toml Executable file → Normal file
View File

@ -1,25 +1,30 @@
[package]
name = "actix-tls"
version = "3.0.0-beta.5"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
version = "3.4.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>", "Rob Ede <robjtede@icloud.com>"]
description = "TLS acceptor and connector services for Actix ecosystem"
keywords = ["network", "tls", "ssl", "async", "transport"]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-net.git"
documentation = "https://docs.rs/actix-tls"
categories = ["network-programming", "asynchronous"]
license = "MIT OR Apache-2.0"
edition = "2018"
categories = ["network-programming", "asynchronous", "cryptography"]
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[package.metadata.docs.rs]
features = ["openssl", "rustls", "native-tls", "accept", "connect", "uri"]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[lib]
name = "actix_tls"
path = "src/lib.rs"
[package.metadata.cargo_check_external_types]
allowed_external_types = ["actix_service::*", "actix_utils::*", "futures_core::*", "tokio::*"]
[package.metadata.cargo-machete]
ignored = [
"rustls_021", # specified to force version with add_trust_anchors method
"rustls_webpki_0101", # specified to force secure version
]
[features]
default = ["accept", "connect", "uri"]
default = ["accept", "connect"]
# enable acceptor services
accept = []
@ -28,49 +33,103 @@ accept = []
connect = []
# use openssl impls
openssl = ["tls-openssl", "tokio-openssl"]
openssl = ["dep:tls-openssl", "dep:tokio-openssl"]
# use rustls impls
rustls = ["tokio-rustls", "webpki-roots"]
# alias for backwards compat
rustls = ["rustls-0_20"]
# use rustls v0.20 impls
rustls-0_20 = ["rustls-0_20-webpki-roots"]
rustls-0_20-webpki-roots = ["tokio-rustls-023", "webpki-roots-022"]
rustls-0_20-native-roots = ["tokio-rustls-023", "dep:rustls-native-certs-06"]
# use rustls v0.21 impls
rustls-0_21 = ["rustls-0_21-webpki-roots"]
rustls-0_21-webpki-roots = ["tokio-rustls-024", "webpki-roots-025"]
rustls-0_21-native-roots = ["tokio-rustls-024", "dep:rustls-native-certs-06"]
# use rustls v0.22 impls
rustls-0_22 = ["dep:tokio-rustls-025", "dep:rustls-pki-types-1"]
rustls-0_22-webpki-roots = ["rustls-0_22", "dep:webpki-roots-026"]
rustls-0_22-native-roots = ["rustls-0_22", "dep:rustls-native-certs-07"]
# use rustls v0.23 impls
rustls-0_23 = ["dep:tokio-rustls-026", "dep:rustls-pki-types-1"]
rustls-0_23-webpki-roots = ["rustls-0_23", "dep:webpki-roots-026"]
rustls-0_23-native-roots = ["rustls-0_23", "dep:rustls-native-certs-07"]
# use native-tls impls
native-tls = ["tokio-native-tls"]
native-tls = ["dep:tokio-native-tls"]
# support http::Uri as connect address
uri = ["http"]
uri = ["dep:http-0_2", "dep:http-1"]
[dependencies]
actix-codec = "0.4.0-beta.1"
actix-rt = { version = "2.2.0", default-features = false }
actix-service = "2.0.0"
actix-utils = "3.0.0"
derive_more = "0.99.5"
actix-rt = { version = "2.2", default-features = false }
actix-service = "2"
actix-utils = "3"
futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
http = { version = "0.2.3", optional = true }
log = "0.4"
tokio-util = { version = "0.6.3", default-features = false }
impl-more = "0.1"
pin-project-lite = "0.2.7"
tokio = "1.23.1"
tokio-util = "0.7"
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
# uri
http-0_2 = { package = "http", version = "0.2.3", optional = true }
http-1 = { package = "http", version = "1", optional = true }
# openssl
tls-openssl = { package = "openssl", version = "0.10.9", optional = true }
tls-openssl = { package = "openssl", version = "0.10.55", optional = true }
tokio-openssl = { version = "0.6", optional = true }
# rustls
tokio-rustls = { version = "0.22", optional = true }
webpki-roots = { version = "0.21", optional = true }
# rustls PKI types
rustls-pki-types-1 = { package = "rustls-pki-types", version = "1", optional = true }
# rustls v0.20
tokio-rustls-023 = { package = "tokio-rustls", version = "0.23", optional = true }
# rustls v0.21
tokio-rustls-024 = { package = "tokio-rustls", version = "0.24", optional = true }
# rustls v0.22
tokio-rustls-025 = { package = "tokio-rustls", version = "0.25", optional = true }
# rustls v0.23
tokio-rustls-026 = { package = "tokio-rustls", version = "0.26", default-features = false, optional = true }
# webpki-roots used with rustls features
webpki-roots-022 = { package = "webpki-roots", version = "0.22", optional = true }
webpki-roots-025 = { package = "webpki-roots", version = "0.25", optional = true }
webpki-roots-026 = { package = "webpki-roots", version = "0.26", optional = true }
# native root certificates for rustls impls
rustls-native-certs-06 = { package = "rustls-native-certs", version = "0.6", optional = true }
rustls-native-certs-07 = { package = "rustls-native-certs", version = "0.7", optional = true }
# native-tls
tokio-native-tls = { version = "0.3", optional = true }
[target.'cfg(any())'.dependencies]
rustls-021 = { package = "rustls", version = "0.21.6", optional = true } # force version with add_trust_anchors method
rustls-webpki-0101 = { package = "rustls-webpki", version = "0.101.4", optional = true } # force secure version
[dev-dependencies]
actix-rt = "2.2.0"
actix-server = "2.0.0-beta.5"
actix-codec = "0.5"
actix-rt = "2.2"
actix-server = "2"
bytes = "1"
env_logger = "0.8"
futures-util = { version = "0.3.7", default-features = false, features = ["sink"] }
log = "0.4"
trust-dns-resolver = "0.20.0"
futures-util = { version = "0.3.17", default-features = false, features = ["sink"] }
itertools = "0.14"
pretty_env_logger = "0.5"
rcgen = "0.13"
rustls-pemfile = "2"
tokio-rustls-026 = { package = "tokio-rustls", version = "0.26" }
trust-dns-resolver = "0.23"
[[example]]
name = "tcp-rustls"
required-features = ["accept", "rustls"]
name = "accept-rustls"
required-features = ["accept", "rustls-0_23"]
[lints]
workspace = true

21
actix-tls/README.md Normal file
View File

@ -0,0 +1,21 @@
# `actix-tls`
> TLS acceptor and connector services for the Actix ecosystem.
<!-- prettier-ignore-start -->
[![crates.io](https://img.shields.io/crates/v/actix-tls?label=latest)](https://crates.io/crates/actix-tls)
[![Documentation](https://docs.rs/actix-tls/badge.svg?version=3.4.0)](https://docs.rs/actix-tls/3.4.0)
[![Version](https://img.shields.io/badge/rustc-1.52+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html)
![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-tls.svg)
<br />
[![Dependency Status](https://deps.rs/crate/actix-tls/3.4.0/status.svg)](https://deps.rs/crate/actix-tls/3.4.0)
![Download](https://img.shields.io/crates/d/actix-tls.svg)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)
<!-- prettier-ignore-end -->
## Resources
- [Library Documentation](https://docs.rs/actix-tls)
- [Examples](/actix-tls/examples)

View File

@ -1,4 +1,4 @@
//! TLS Acceptor Server
//! No-Op TLS Acceptor Server
//!
//! Using either HTTPie (`http`) or cURL:
//!
@ -15,14 +15,12 @@
//! http --verify=false https://127.0.0.1:8443
//! ```
// this use only exists because of how we have organised the crate
// it is not necessary for your actual code
use tokio_rustls::rustls;
// this `use` is only exists because of how we have organised the crate
// it is not necessary for your actual code; you should import from `rustls` normally
use std::{
env,
fs::File,
io::{self, BufReader},
path::PathBuf,
sync::{
atomic::{AtomicUsize, Ordering},
Arc,
@ -32,28 +30,40 @@ use std::{
use actix_rt::net::TcpStream;
use actix_server::Server;
use actix_service::ServiceFactoryExt as _;
use actix_tls::accept::rustls::{Acceptor as RustlsAcceptor, TlsStream};
use actix_tls::accept::rustls_0_23::{Acceptor as RustlsAcceptor, TlsStream};
use futures_util::future::ok;
use log::info;
use rustls::{
internal::pemfile::certs, internal::pemfile::rsa_private_keys, NoClientAuth, ServerConfig,
};
use itertools::Itertools as _;
use rustls::server::ServerConfig;
use rustls_pemfile::{certs, rsa_private_keys};
use rustls_pki_types_1::PrivateKeyDer;
use tokio_rustls_026::rustls;
use tracing::info;
#[actix_rt::main]
async fn main() -> io::Result<()> {
env::set_var("RUST_LOG", "info");
env_logger::init();
pretty_env_logger::formatted_timed_builder()
.parse_env(pretty_env_logger::env_logger::Env::default().default_filter_or("info"));
let mut tls_config = ServerConfig::new(NoClientAuth::new());
let root_path = env!("CARGO_MANIFEST_DIR")
.parse::<PathBuf>()
.unwrap()
.join("examples");
let cert_path = root_path.clone().join("cert.pem");
let key_path = root_path.clone().join("key.pem");
// Load TLS key and cert files
let cert_file = &mut BufReader::new(File::open("./examples/cert.pem").unwrap());
let key_file = &mut BufReader::new(File::open("./examples/key.pem").unwrap());
let cert_file = &mut BufReader::new(File::open(cert_path).unwrap());
let key_file = &mut BufReader::new(File::open(key_path).unwrap());
let cert_chain = certs(cert_file).unwrap();
let mut keys = rsa_private_keys(key_file).unwrap();
tls_config
.set_single_cert(cert_chain, keys.remove(0))
let cert_chain = certs(cert_file);
let mut keys = rsa_private_keys(key_file);
let tls_config = ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(
cert_chain.try_collect::<_, Vec<_>, _>()?,
PrivateKeyDer::Pkcs1(keys.next().unwrap()?),
)
.unwrap();
let tls_acceptor = RustlsAcceptor::new(tls_config);
@ -61,7 +71,7 @@ async fn main() -> io::Result<()> {
let count = Arc::new(AtomicUsize::new(0));
let addr = ("127.0.0.1", 8443);
info!("starting server on port: {}", &addr.0);
info!("starting server at: {addr:?}");
Server::build()
.bind("tls-example", addr, move || {

View File

@ -1,25 +1,49 @@
//! TLS acceptor services for Actix ecosystem.
//!
//! ## Crate Features
//! * `openssl` - TLS acceptor using the `openssl` crate.
//! * `rustls` - TLS acceptor using the `rustls` crate.
//! * `native-tls` - TLS acceptor using the `native-tls` crate.
//! TLS connection acceptor services.
use std::sync::atomic::{AtomicUsize, Ordering};
use std::{
convert::Infallible,
error::Error,
fmt,
sync::atomic::{AtomicUsize, Ordering},
};
use actix_utils::counter::Counter;
#[cfg(feature = "openssl")]
pub mod openssl;
#[cfg(feature = "rustls")]
pub mod rustls;
#[cfg(feature = "rustls-0_20")]
pub mod rustls_0_20;
#[doc(hidden)]
#[cfg(feature = "rustls-0_20")]
pub use rustls_0_20 as rustls;
#[cfg(feature = "rustls-0_21")]
pub mod rustls_0_21;
#[cfg(feature = "rustls-0_22")]
pub mod rustls_0_22;
#[cfg(feature = "rustls-0_23")]
pub mod rustls_0_23;
#[cfg(feature = "native-tls")]
pub mod native_tls;
pub(crate) static MAX_CONN: AtomicUsize = AtomicUsize::new(256);
#[cfg(any(
feature = "openssl",
feature = "rustls-0_20",
feature = "rustls-0_21",
feature = "rustls-0_22",
feature = "rustls-0_23",
feature = "native-tls",
))]
pub(crate) const DEFAULT_TLS_HANDSHAKE_TIMEOUT: std::time::Duration =
std::time::Duration::from_secs(3);
thread_local! {
static MAX_CONN_COUNTER: Counter = Counter::new(MAX_CONN.load(Ordering::Relaxed));
}
@ -34,9 +58,74 @@ pub fn max_concurrent_tls_connect(num: usize) {
MAX_CONN.store(num, Ordering::Relaxed);
}
/// TLS error combined with service error.
/// TLS handshake error, TLS timeout, or inner service error.
///
/// All TLS acceptors from this crate will return the `SvcErr` type parameter as [`Infallible`],
/// which can be cast to your own service type, inferred or otherwise, using [`into_service_error`].
///
/// [`into_service_error`]: Self::into_service_error
#[derive(Debug)]
pub enum TlsError<E1, E2> {
Tls(E1),
Service(E2),
pub enum TlsError<TlsErr, SvcErr> {
/// TLS handshake has timed-out.
Timeout,
/// Wraps TLS service errors.
Tls(TlsErr),
/// Wraps service errors.
Service(SvcErr),
}
impl<TlsErr> TlsError<TlsErr, Infallible> {
/// Casts the infallible service error type returned from acceptors into caller's type.
///
/// # Examples
/// ```
/// # use std::convert::Infallible;
/// # use actix_tls::accept::TlsError;
/// let a: TlsError<u32, Infallible> = TlsError::Tls(42);
/// let _b: TlsError<u32, u64> = a.into_service_error();
/// ```
pub fn into_service_error<SvcErr>(self) -> TlsError<TlsErr, SvcErr> {
match self {
Self::Timeout => TlsError::Timeout,
Self::Tls(err) => TlsError::Tls(err),
Self::Service(err) => match err {},
}
}
}
impl<TlsErr, SvcErr> fmt::Display for TlsError<TlsErr, SvcErr> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Timeout => f.write_str("TLS handshake has timed-out"),
Self::Tls(_) => f.write_str("TLS handshake error"),
Self::Service(_) => f.write_str("Service error"),
}
}
}
impl<TlsErr, SvcErr> Error for TlsError<TlsErr, SvcErr>
where
TlsErr: Error + 'static,
SvcErr: Error + 'static,
{
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
TlsError::Tls(err) => Some(err),
TlsError::Service(err) => Some(err),
TlsError::Timeout => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tls_service_error_inference() {
let a: TlsError<u32, Infallible> = TlsError::Tls(42);
let _b: TlsError<u32, u64> = a.into_service_error();
}
}

Some files were not shown because too many files have changed in this diff Show More