1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-06-11 17:59:35 +00:00

Compare commits

...

304 commits

Author SHA1 Message Date
Rob Ede 188206a903
feat: Html responder (#3399) 2024-06-11 00:36:46 +01:00
Rob Ede 0ce488e57a
docs: fix build 2024-06-10 23:54:16 +01:00
Rob Ede 132b84d3b1
docs(multipart): use cargo-rdme 2024-06-10 23:35:26 +01:00
Rob Ede cc5030c542
docs(http-test): use cargo-rdme 2024-06-10 23:31:45 +01:00
Rob Ede cd301a6932
docs: local docs doc everything but only list workspace crates 2024-06-10 23:30:51 +01:00
Rob Ede 4c4c279938
docs(test): intrgrate cargo-rdme 2024-06-10 23:23:38 +01:00
Rob Ede 0fd85bae2a
test: demonstrate panic in multipart forms (#3397) 2024-06-10 21:51:53 +01:00
Rob Ede 9b3de1f1fe
ci: fix doctest coverage 2024-06-10 04:15:58 +01:00
Rob Ede 9553e7afff
ci: fix coverage 2024-06-10 04:08:10 +01:00
Rob Ede d9579cf58a
test: coverage for doctests 2024-06-10 04:05:21 +01:00
Timo Caktu 7a2313cc4b
web: add HttpRequest::full_url() (#3096)
* implemented function which returns full uir

* changes added into the changelog

* added test funtion for full_uri method

* refactor: rename to full_url

---------

Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-06-10 02:49:50 +00:00
Rob Ede 2ee92d778e
ci: external types checking (#3175) 2024-06-10 03:39:06 +01:00
Matt Palmer 59e42c1446
Return 415 rather than 400 on Urlencoded Content-Type mismatch (#3334)
Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-06-10 01:19:35 +00:00
Rob Ede 53086a90a6
build: add coverage recipes to justfile 2024-06-10 01:58:16 +01:00
dependabot[bot] 7f529e35b2
build(deps): bump actions-rust-lang/setup-rust-toolchain from 1.8.0 to 1.9.0 (#3395)
build(deps): bump actions-rust-lang/setup-rust-toolchain

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 00:45:11 +00:00
dependabot[bot] 4908fd7dea
build(deps): bump taiki-e/install-action from 2.34.0 to 2.38.0 (#3396)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.34.0 to 2.38.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.34.0...v2.38.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-06-10 00:44:58 +00:00
Matt Palmer a2b9823d9d
Strip non-address characters from Forwarded for= (#3343)
* Strip non-address characters from Forwarded for=

This is something of a followup to #2528, which asked for port information to not be included in  when it was taken from the local socket.

The  header's  element may optionally contain port information (https://datatracker.ietf.org/doc/html/rfc7239#section-6).
However, as I understand it,  is *supposed* to only contain an IP address, without port (per #2528).

This PR corrects that discrepancy, making it easier to parse the result of this method in application code.

There should not be any compatibility concerns, as anyone parsing the output of  would already need to handle both port and portless cases anyway.

* Update CHANGES.md

---------

Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-06-09 23:40:09 +00:00
Rob Ede da56de4556
chore(actix-test): prepare release 0.1.5 2024-06-10 00:01:17 +01:00
Matt Palmer 758ae1dac1
actix-test: allow the configuration of the TestServer address (#3351)
* actix-test: allow the configuration of the TestServer address

This is useful if you're running (say) Selenium tests against a running TestServer, and the Selenium workers are Docker containers elsewhere in the network.
Not a *particularly* common use case, perhaps, but one that I can attest happens every now and then.

* Update CHANGES.md

* Adjust default listen address to avoid test failures

---------

Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-06-09 19:07:08 +00:00
Rob Ede 37577dcb89
chore(actix-files): prepare release 0.6.6 2024-06-09 19:45:14 +01:00
dependabot[bot] 8b8eb4eae1
build(deps): update tokio-uring requirement from 0.4 to 0.5 (#3385)
* build(deps): update tokio-uring requirement from 0.4 to 0.5

Updates the requirements on [tokio-uring](https://github.com/tokio-rs/tokio-uring) to permit the latest version.
- [Release notes](https://github.com/tokio-rs/tokio-uring/releases)
- [Changelog](https://github.com/tokio-rs/tokio-uring/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tokio-rs/tokio-uring/compare/v0.4.0...v0.5.0)

---
updated-dependencies:
- dependency-name: tokio-uring
  dependency-type: direct:production
...

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

* chore: narrow actix-server requirement

---------

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-06-09 17:37:42 +00:00
Samuel Marks 22593a1532
Re-export http::status::InvalidStatusCode (#3393)
* [actix-http/src/lib.rs] Expose/re-export `http::status::InvalidStatusCode`

* [actix-http/src/error.rs] Re-export `http::status::InvalidStatusCode` ; [actix-http/src/lib.rs] Revert
2024-06-09 05:07:56 +00:00
asonix f7646bcc48
actix-web-actors: take the internal buffer when yielding (#3369)
* actix-web-actors: take the internal buffer when yielding

* actix-web-actors: Add CHANGES entry re: taking buffer
2024-06-09 05:04:42 +00:00
Rob Ede 8018983a68
docs: update changelog for #3393 2024-06-09 06:08:21 +01:00
Rob Ede 266834cf7c
chore: narrow h2 version 2024-06-09 04:51:53 +01:00
Rob Ede 40e1034566
docs: update changelog 2024-06-09 00:38:49 +01:00
Rob Ede a5c78483f9
chore(actix-web): prepare release 4.7.0 2024-06-09 00:22:03 +01:00
Rob Ede 12a0521ef8
chore(actix-multipart): prepare release 0.6.2 2024-06-09 00:20:36 +01:00
Rob Ede b4faf8820c
chore(actix-web-codegen): prepare release 4.3.0 2024-06-09 00:19:09 +01:00
Rob Ede d6f885127d
chore(actix-test): prepare release 0.1.4 2024-06-09 00:16:36 +01:00
Rob Ede ebc43dcf1b
feat: forwards-compatibility for handler visibility inheritance fix (#3391) 2024-06-09 00:10:15 +01:00
Rob Ede 7c4c26d2df
feat: expose Identity middleware (#3390) 2024-06-08 05:26:26 +01:00
Jonathan Lim 3db7891303
Scope macro (#3136)
* add scope proc macro

* Update scope macro code to work with current HttpServiceFactory

* started some test code

* add some unit tests

* code formatting cleanup

* add another test for combining and calling 2 scopes

* format code with formatter

* Update actix-web-codegen/src/lib.rs with comment documentation fix

Co-authored-by: oliver <151407407+kwfn@users.noreply.github.com>

* work in progress. revised procedural macro to change othe macro call

* add tests again. refactor nested code.

* clean up code. fix bugs with route and method attributes with parameters

* clean up for rust fmt

* clean up for rust fmt

* fix out of date comment for scope macro

* sync to master branch by adding test_wrap

* needed to format code

* test: split out scope tests

* test: add negative tests

* chore: move imports back inside (?)

* docs: tweak scope docs

* fix: prevent trailing slashes in scope prefixes

* chore: address clippy lints

---------

Co-authored-by: oliver <151407407+kwfn@users.noreply.github.com>
Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-06-07 22:10:48 +00:00
Rob Ede c366649516
docs: example of CPU core pinning 2024-06-07 16:57:13 +01:00
Sebastian Detert 534cfe1fda
feat: add .customize().add_cookie() (#3215)
* feat: add .customize().add_cookie()

* docs: added cookie hint

* fix: added unwrap to test of add_cookie()

* docs: added changelog entry for .customize().add_cookie()

* chore: make append_header infallible

* docs: update changelog

---------

Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-06-07 15:22:48 +00:00
Rob Ede cff958e518
chore: address clippy lint 2024-06-07 16:10:25 +01:00
Rob Ede b9305ff59d
chore: fmt 2024-06-07 16:05:42 +01:00
Rob Ede 5221c1b194
ci: pin msrv lookup job 2024-06-07 16:05:41 +01:00
asonix 4493aa35d0
actix-http::ws: Remove redundant + 4 byte reservation when masked (#3371)
* actix-http::ws: Remove redundant + 4 byte reservation when masked

* actix-http: Update CHANGES wrt byte fix

* docs: remove changelog entry

---------

Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-06-07 14:41:32 +00:00
Matt Palmer 8b4d23a69a
Allow disabling redirect following in actix-test (#3356)
If you're testing that redirects are being properly generated, then it's
useful to not have the client go off on a wild goose chase of its own.

Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-06-07 14:40:55 +00:00
Raphael C 8fdf358954
Add app_data method to GuardContext (#3341)
* changes: guard

* fix(guard): docs link to app_data

* docs: fix changelog

---------

Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-06-07 14:31:53 +00:00
Dylan Anthony b2d0196f34
Do not require actix-router default features from actix-web-codegen (#3372)
* fix: Do not require actix-router default features from actix-web-codegen

* docs: update changelog

* test: update trybuild stderr

---------

Co-authored-by: Dylan Anthony <dbanty@users.noreply.github.com>
Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-06-07 14:08:13 +00:00
Abedi 85655f731d
From Boxed ResponseError impl added (#3388)
* From Boxed ResponseError impl added

* docs: update changelog

---------

Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-06-07 13:55:29 +00:00
Rob Ede ebd8bb266d
ci: fix cargo-public-api 2024-06-07 14:31:20 +01:00
Rob Ede 5c18569b78
docs: align App:app_data arg name 2024-06-07 14:31:20 +01:00
dependabot[bot] dd84bcb609
build(deps): bump taiki-e/install-action from 2.33.34 to 2.34.0 (#3386)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.33.34 to 2.34.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.33.34...v2.34.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-06-03 01:24:09 +00:00
Rob Ede 3ce97effa2
ci: delete upload doc workflow 2024-05-28 01:21:23 +01:00
dependabot[bot] 26efa64278
build(deps): bump taiki-e/install-action from 2.33.26 to 2.33.34 (#3380)
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 00:58:18 +00:00
dependabot[bot] cc06fd6a5e
build(deps): bump codecov/codecov-action from 4.4.0 to 4.4.1 (#3381)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4.4.0 to 4.4.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.4.0...v4.4.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-27 00:57:59 +00:00
dependabot[bot] 1b214bc5f5
build(deps): bump codecov/codecov-action from 4.3.1 to 4.4.0 (#3374)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4.3.1 to 4.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/v4.3.1...v4.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>
2024-05-20 09:30:48 +00:00
dependabot[bot] d4bcdf28f2
build(deps): bump JamesIves/github-pages-deploy-action from 4.6.0 to 4.6.1 (#3375)
build(deps): bump JamesIves/github-pages-deploy-action

Bumps [JamesIves/github-pages-deploy-action](https://github.com/jamesives/github-pages-deploy-action) from 4.6.0 to 4.6.1.
- [Release notes](https://github.com/jamesives/github-pages-deploy-action/releases)
- [Commits](https://github.com/jamesives/github-pages-deploy-action/compare/v4.6.0...v4.6.1)

---
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 09:30:27 +00:00
dependabot[bot] 4f7b334d80
build(deps): bump taiki-e/install-action from 2.33.22 to 2.33.26 (#3376)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.33.22 to 2.33.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.33.22...v2.33.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-05-20 09:27:44 +00:00
Rob Ede fdff3775a8
ci: use mold linker (#3370) 2024-05-19 20:24:33 +01:00
Rob Ede b342b8fc82
chore(actix-router): prepare release 0.5.3 2024-05-19 12:09:46 +01:00
Rob Ede 804a344565
ci: limit cargo hack concurrency 2024-05-19 12:06:20 +01:00
Rob Ede acb740584c
fix: correct aws rustls v0.23 feature gating 2024-05-19 11:55:12 +01:00
Rob Ede 9a437fe835
chore(awc): prepare release 3.5.0 2024-05-19 10:16:16 +01:00
Rob Ede 59115bca49
chore(actix-web): prepare release 4.6.0 2024-05-19 10:15:48 +01:00
Rob Ede fe7268487a
chore(actix-http): prepare release 3.7.0 2024-05-19 10:14:30 +01:00
Rob Ede e8262da138
chore: update rcgen to 0.13 2024-05-19 10:12:32 +01:00
Rob Ede 18e02b83d5
docs: fix middleware docs warning 2024-05-18 20:35:12 +01:00
asonix 2e63ff5928
actix-web: Add rustls 0.23 (#3363)
* Fix type confusion in some scenarios

When the feature for rustls 0.22 is enabled, and rustls 0.23 is also
present in a project, there suddently exist multiple paths for errors
when building middleware chains due to the use of two consecutive `?`
operators without specifying the intermediate error type.

This commit addresses the issue by removing the first `?`, so that the
first error type will always be known, and the second `?` always has a
well defined implementation.

* Add CHANGES entry about type confusion

* actix-http: add rustls 0.23 support

* actix-http: update ws example, tests for rustls 0.23

* actix-http: add rustls 0.23 to changelog

* Update comments to mention 0.23 instead of 0.22

* awc: add rustls 0.23 support

This also fixes certificate lookup when native-roots is enabled for rustls 0.22.

* awc: update changelog for rustls 0.23

* awc: Add base rustls-0_23 feature without roots to better enable custom config

* actix-test: add rustls-0.23

* actix-test: add rustls 0.23 to changelog

* awc: update changelog with rustls 0.23 tweaks

* actix-web: add rustls 0.23

* Add rustls-0_23 to CI

* Update tls_rustls.rs

* review nits

* review nits part 2

* fix doc test

---------

Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-05-18 19:05:58 +00:00
Raphael C 48d7adb7bf
Documentation for actix multipart (#3344)
example for actix-multipart readme & crate docs

Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-05-18 19:02:00 +00:00
Matt Palmer 0a2788d662
actix-test: re-export types from awc (#3349)
This allows us to pass these types around in functions, without having
to add `awc` as a direct (dev-)dependency.

Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-05-18 18:57:35 +00:00
asonix 2d035c066e
actix-http: Add rustls 0.23 (#3361)
Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-05-18 19:22:53 +01:00
dependabot[bot] fff45b28f4
build(deps): update brotli requirement from 3.3.3 to 6.0.0 (#3353)
* build(deps): update brotli requirement from 3.3.3 to 6.0.0

Updates the requirements on [brotli](https://github.com/dropbox/rust-brotli) to permit the latest version.
- [Release notes](https://github.com/dropbox/rust-brotli/releases)
- [Commits](https://github.com/dropbox/rust-brotli/compare/3.3.3...6.0.0)

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

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

* docs: update changelogs

---------

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-05-14 08:58:05 +00:00
dependabot[bot] c20603fc83
build(deps): bump taiki-e/install-action from 2.33.16 to 2.33.22 (#3364)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.33.16 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.16...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-14 05:50:32 +00:00
asonix 33c47c0ba9
Fix type confusion in some scenarios (#3348)
* Fix type confusion in some scenarios

When the feature for rustls 0.22 is enabled, and rustls 0.23 is also
present in a project, there suddently exist multiple paths for errors
when building middleware chains due to the use of two consecutive `?`
operators without specifying the intermediate error type.

This commit addresses the issue by removing the first `?`, so that the
first error type will always be known, and the second `?` always has a
well defined implementation.

* Add CHANGES entry about type confusion

---------

Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-05-14 05:45:35 +00:00
Rob Ede 3c9a930bd1
ci: rely more on justfiles (#3365) 2024-05-14 06:30:58 +01:00
asonix 44f502e050
awc: gate TlsConnectorService behind any feature that uses it (#3350) 2024-05-14 05:57:58 +01:00
Rob Ede c1a6388614
refactor: address clippy warnings 2024-05-06 06:03:44 +01:00
dependabot[bot] bb65628de5
build(deps): bump taiki-e/install-action from 2.33.12 to 2.33.16 (#3355)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-06 03:28:35 +01:00
dependabot[bot] e4b9d17355
build(deps): bump codecov/codecov-action from 4.3.0 to 4.3.1 (#3354)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-06 03:27:31 +01:00
dependabot[bot] babac131d4
build(deps): bump taiki-e/install-action from 2.32.17 to 2.33.12 (#3347)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.32.17 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.32.17...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:39:28 +00:00
dependabot[bot] 7f15a95d8e
build(deps): bump JamesIves/github-pages-deploy-action from 4.5.0 to 4.6.0 (#3339)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-02 03:22:59 +01:00
Rob Ede 7fc73d58a9
ci: fix public api 2024-05-02 03:22:20 +01:00
dependabot[bot] ba7fd048b6
build(deps): bump codecov/codecov-action from 4.2.0 to 4.3.0 (#3331)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4.2.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.2.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-04-15 01:02:44 +00:00
dependabot[bot] d98938b125
build(deps): bump taiki-e/install-action from 2.32.9 to 2.32.17 (#3332)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.32.9 to 2.32.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.32.9...v2.32.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-04-15 01:02:30 +00:00
dependabot[bot] 5a5486b484
build(deps): bump taiki-e/install-action from 2.32.0 to 2.32.9 (#3325)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.32.0 to 2.32.9.
- [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.32.0...v2.32.9)

---
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-04-08 07:19:03 +00:00
dependabot[bot] 76b2b2734b
build(deps): bump codecov/codecov-action from 4.1.1 to 4.2.0 (#3326)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4.1.1 to 4.2.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.1...v4.2.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-04-08 07:17:45 +00:00
dependabot[bot] ccfa8d3817
build(deps): bump codecov/codecov-action from 4.1.0 to 4.1.1 (#3321)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4.1.0 to 4.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/v4.1.0...v4.1.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-04-01 01:41:43 +00:00
dependabot[bot] 09851f4a54
build(deps): bump taiki-e/install-action from 2.29.7 to 2.32.0 (#3322)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.29.7 to 2.32.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.29.7...v2.32.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-04-01 01:38:30 +00:00
dependabot[bot] db76ad0f61
build(deps): bump taiki-e/install-action from 2.28.16 to 2.29.7 (#3316)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-25 15:03:40 +00:00
dependabot[bot] 0383f4bdd1
build(deps): bump taiki-e/install-action from 2.28.10 to 2.28.16 (#3312)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.28.10 to 2.28.16.
- [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.10...v2.28.16)

---
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-03-18 02:47:10 +00:00
dependabot[bot] 9c3b4c61f7
build(deps): bump taiki-e/install-action from 2.28.0 to 2.28.10 (#3305)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-16 11:14:22 +00:00
LoveSy 52b0d5fbf9
CI: Free space before test (#3303) 2024-03-11 15:34:04 +00:00
Nelson Dominguez ba7bddeadc
docs(actix-web): add missing 'that' to doc comments for Compress middleware (#3304)
docs(actix-web): add missing 'that' in doc comments for Compress middleware
2024-03-06 00:17:18 +00:00
dependabot[bot] d2150a3312
build(deps): bump taiki-e/install-action from 2.27.9 to 2.28.0 (#3301)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.27.9 to 2.28.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.27.9...v2.28.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-03-04 02:18:50 +00:00
dependabot[bot] 58dd00bccf
build(deps): bump codecov/codecov-action from 4.0.2 to 4.1.0 (#3302)
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-04 02:18:38 +00:00
Rob Ede a4df623b0c
chore: bump env_logger to v0.11 2024-03-03 23:43:54 +00:00
Rob Ede 49020e79ae
chore: update base64 to v0.22 2024-03-03 22:18:29 +00:00
LoveSy c10f05a867
Add unicode feature to switch between regex and regex-lite crates as a trade-off between full unicode support and binary size (#3291)
* - Add `unicode` feature to switch between `regex` and `regex-lite`

as a trade-off between full unicode support and binary size.

* Update CHANGES.md

* Update CHANGES.md

* refactor: move regexset code selection to own module

* docs: add docs within RegexSet module

* chore: restore manifests

* test: ensure all actix-router codepaths are tested

---------

Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-03-03 15:50:16 +00:00
dependabot[bot] 994ea45d91
build(deps): bump codecov/codecov-action from 4.0.1 to 4.0.2 (#3296)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4.0.1 to 4.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.0.1...v4.0.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>
Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-03-02 18:40:38 +00:00
dependabot[bot] f8a0f3e188
build(deps): bump taiki-e/install-action from 2.27.2 to 2.27.9 (#3297)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.27.2 to 2.27.9.
- [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.2...v2.27.9)

---
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>
Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-03-02 18:24:18 +00:00
Rob Ede 7f0504e32b
chore: temp allow #[allow(non_local_definitions)] 2024-03-01 18:11:30 +00:00
dependabot[bot] 8c31d137aa
build(deps): bump taiki-e/install-action from 2.26.18 to 2.27.2 (#3294)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-02-19 12:31:10 +00:00
dependabot[bot] 289f749e9f
build(deps): bump taiki-e/install-action from 2.26.12 to 2.26.18 (#3289)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-16 13:38:38 +00:00
Rob Ede 82f8ddc38f
feat: multipart testing utilities (#3288) 2024-02-14 22:22:07 +00:00
Rob Ede 3819767fa0
test: fix trybuild tests 2024-02-13 02:14:03 +00:00
Rob Ede 9ce5e33b72
ci: workaround clap MSRV in dev deps 2024-02-13 01:42:54 +00:00
Rob Ede 1e08ebabf9
build: bump MSRV to 1.72 2024-02-13 01:24:34 +00:00
Rob Ede 022b052bd9
chore: clippy 2024-02-12 23:02:45 +00:00
Rob Ede 1e2ef6f92f
perf: remove unnecessary allocation when writing http dates (#3261) 2024-02-07 03:47:30 +00:00
dependabot[bot] 7e4e12b0aa
build(deps): bump taiki-e/install-action from 2.26.8 to 2.26.12 (#3280)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-05 14:38:02 +00:00
dependabot[bot] 373d4ca970
build(deps): bump codecov/codecov-action from 4.0.0 to 4.0.1 (#3279)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-05 01:27:57 +00:00
Rob Ede e518170a30
test: fix test_server 2024-02-04 03:40:58 +00:00
Rob Ede f5f6132f94
test: update rustls for test_server 2024-02-04 03:30:16 +00:00
Rob Ede d9b31b80ac
fix: standardize body stream error reporting 2024-02-04 03:11:48 +00:00
Rob Ede 2b8c528e54
chore(actix-web): prepare release 4.5.1 2024-02-04 01:22:36 +00:00
Rob Ede 59bc85fe0e
chore(actix-http-test): prepare release 3.2.0 2024-02-04 00:34:00 +00:00
Rob Ede 3f2fd2d59f
chore(actix-test): prepare release 0.1.3 2024-02-04 00:33:42 +00:00
Rob Ede 17ed73b33e
chore(actix-web-actors): prepare release 4.3.0 2024-02-04 00:33:38 +00:00
Rob Ede 73fa1184f1
chore(awc): prepare release 3.4.0 2024-02-04 00:32:57 +00:00
Rob Ede 8e9e9fbcdd
chore(actix-web): prepare release 4.5.0 2024-02-04 00:32:28 +00:00
Rob Ede 8db3de6ede
chore(actix-http): prepare release 3.6.0 2024-02-04 00:31:14 +00:00
Rob Ede 2125aca2c5
Rustls v0.22 support (#3275) 2024-02-03 23:55:01 +00:00
Dylan DPC b1eb57ac4f
Update Cargo.toml (#3276) 2024-02-03 16:20:07 +00:00
SleeplessOne1917 ae7736f134
Implement From<&HeaderMap> for http::HeaderMap (#3230)
* Add From impl for header map references

* Add From impl for header map references

* Remove Into<HeaderMap> via http::HeaderMap

* fix changelog

---------

Co-authored-by: SleeplessOne1917 <insomnia-void@protonmail.com>
Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-02-01 12:52:35 +00:00
dependabot[bot] c1f88f718b
build(deps): bump codecov/codecov-action from 3.1.4 to 4.0.0 (#3272)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-02-01 07:34:23 +00:00
dependabot[bot] 7a76ba7340
build(deps): bump taiki-e/install-action from 2.24.1 to 2.26.8 (#3271)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-01 07:34:11 +00:00
Rob Ede 8e458b34b7
chore: remove set -e 2024-02-01 06:33:58 +00:00
Rob Ede e89c881624
ci: use cargo-ci-cache-clean 2024-02-01 06:27:22 +00:00
Rob Ede 5246d24aba
ci: force openssl version 3.2.1 2024-02-01 06:01:28 +00:00
Rob Ede 643a80bff2
ci: workaround half crate msrv 2024-02-01 05:41:28 +00:00
Bruno Paulino 891ab083c6
actix-http: Bump h2 to fix a resource exhaustion vulnerability (#3262)
Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-01-24 14:17:42 +00:00
Rob Ede a7375b6876
ci: faster cargo-public-api install (#3255) 2024-01-22 02:19:19 +00:00
dependabot[bot] ea8cd6e976
build(deps): bump taiki-e/install-action from 2.25.1 to 2.25.9 (#3252)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-22 01:06:55 +00:00
Vojtech Kral d453b15ddd
docs: mention result is wrapped in Data in data_factory() docs (#3251) 2024-01-18 12:32:27 +00:00
John Vandenberg 2915bb7d90
chore: fix typos (#3248) 2024-01-16 11:29:06 +00:00
dependabot[bot] e442b00c8c
build(deps): bump taiki-e/install-action from 2.24.1 to 2.25.1 (#3246)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-15 01:24:48 +00:00
dependabot[bot] ba53c4f875
build(deps): bump actions-rust-lang/setup-rust-toolchain from 1.6.0 to 1.8.0 (#3247)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-15 01:24:21 +00:00
Rob Ede 08a9c66568
docs(files: update readme from crate docs 2024-01-10 04:03:29 +00:00
Rob Ede 83be07d77d
chore(actix-files): prepare release 0.6.5 2024-01-10 04:01:14 +00:00
Rob Ede 33da480709
format project 2024-01-10 04:00:20 +00:00
Dialga fcfc727295
actix-files: fix handling linebreaks in filenames (#3237)
Co-authored-by: Rob Ede <robjtede@icloud.com>
2024-01-10 03:56:15 +00:00
Rob Ede ac04d80d8e
docs: better docs for peer_addr methods 2024-01-08 15:17:40 +00:00
dependabot[bot] d2bd549eec
build(deps): bump taiki-e/install-action from 2.23.7 to 2.24.1 (#3239)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.23.7 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.23.7...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 01:25:54 +00:00
Rob Ede 46dde69d50
chore(actix-files): prepare release 0.6.4 2024-01-06 10:19:15 +00:00
Sven-Hendrik Haase febba786fa
actix-files: Properly handle newlines in file names (#3235) 2024-01-06 10:11:40 +00:00
dependabot[bot] 561cc440b2
build(deps): bump taiki-e/install-action from 2.23.0 to 2.23.7 (#3232)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.23.0 to 2.23.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.23.0...v2.23.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-01-01 14:10:13 +00:00
Rob Ede ccb90dd5a1
docs: update changelog 2023-12-25 02:36:17 +00:00
Rob Ede 1c88af50c0
docs: fix changelog 2023-12-25 02:35:22 +00:00
Rob Ede f4f459d420
chore(actix-http): prepare release 3.5.1 2023-12-25 02:30:14 +00:00
Rob Ede d14e98b62b
prevent hang when compressing Sized(0) bodies
fixes #3229
2023-12-25 02:27:51 +00:00
Rob Ede f4851b3914
chore(actix-router): prepare release 0.5.2 2023-12-24 16:47:58 +00:00
dependabot[bot] 68597b5426
build(deps): bump taiki-e/install-action from 2.22.0 to 2.23.0 (#3228)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.22.0 to 2.23.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.22.0...v2.23.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-23 19:54:02 +00:00
Rob Ede 9dc3ad754e
chore(actix-web): prepare release 4.4.1 2023-12-23 19:19:10 +00:00
Rob Ede 17060ed993
chore(awc): prepare release 3.3.0 2023-12-23 19:18:29 +00:00
Rob Ede 0d9ca4d939
chore(actix-http): prepare release 3.5.0 2023-12-23 19:17:56 +00:00
Rob Ede ff2904ee78
ci: prevent cargo-cache install failing MSRV builds 2023-12-23 19:13:11 +00:00
Rob Ede fdef224a06
docs: document internal Path fields 2023-12-23 18:49:17 +00:00
Rob Ede ede0201aa4
docs: fix derive readme version 2023-12-16 10:42:16 +00:00
Rob Ede 271edafd4d
docs: add router readme 2023-12-16 10:37:19 +00:00
Rob Ede 5e5e5d8315
chore: remove allow(uninlined_format_args) 2023-12-16 10:33:00 +00:00
Rob Ede c7a0af31d3
docs: doc and metadata tweaks 2023-12-16 10:33:00 +00:00
SleeplessOne1917 eefe8b0733
Implement From<HeaderMap> for http::HeaderMap (#3222)
* Implement From<HeaderMap> for http::HeaderMap

* Update changelog

* Apply clippy fix

* doc tweak

---------

Co-authored-by: SleeplessOne1917 <insomnia-void@protonmail.com>
Co-authored-by: Rob Ede <robjtede@icloud.com>
2023-12-16 10:08:45 +00:00
dependabot[bot] 1114a51b22
build(deps): bump taiki-e/install-action from 2.21.26 to 2.22.0 (#3219)
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 01:11:53 +00:00
Hangyuan 0a312037ea
Corrected a typo in mod.rs (#3218) 2023-12-10 15:53:05 +00:00
dependabot[bot] 37d304b0f2
build(deps): bump taiki-e/install-action from 2.21.19 to 2.21.26 (#3210)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.21.19 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.19...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 01:20:11 +00:00
dependabot[bot] 039f8fb193
build(deps): bump JamesIves/github-pages-deploy-action from 4.4.3 to 4.5.0 (#3211)
build(deps): bump JamesIves/github-pages-deploy-action

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 01:03:29 +00:00
dependabot[bot] 929ceb5eb5
build(deps): bump actions-rust-lang/setup-rust-toolchain from 1.5.0 to 1.6.0 (#3212)
build(deps): bump actions-rust-lang/setup-rust-toolchain

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 01:02:25 +00:00
dependabot[bot] e95c8fe5a6
build(deps): bump taiki-e/install-action from 2.21.17 to 2.21.19 (#3205)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.21.17 to 2.21.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.21.17...v2.21.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>
2023-11-27 02:54:42 +00:00
Paul 2fe5189954
Do not encode zero-sized response bodies (#3199)
* Do not encode zero-sized response bodies

* Test empty response remains empty after compression
2023-11-26 20:57:19 +00:00
dependabot[bot] 4accfab196
build(deps): bump taiki-e/install-action from 2.21.11 to 2.21.17 (#3195)
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 00:29:35 +00:00
Amit Upadhyay c0615f28ed
Make compression middleware prefer brotli over zstd over gzip (#3189)
* AcceptEncoding.preference() prefers brotli > zstd > gzip

* AcceptEncoding.{ranked,negotiate}() prefers brotli > zstd > gzip

* changelog entry

* use browser-realistic encoding tests

* fix choosing identity when q=0

---------

Co-authored-by: Rob Ede <robjtede@icloud.com>
2023-11-19 18:54:08 +00:00
Marcio Ordonez 9d1f75d349
fix: content type required flag (#3168)
* fix: content type required flag

* clarify comments for invalid json mime type

* remove pr reference

---------

Co-authored-by: Rob Ede <robjtede@icloud.com>
2023-11-19 11:22:55 +00:00
dependabot[bot] e50bceb914
build(deps): bump taiki-e/install-action from 2.21.7 to 2.21.11 (#3185)
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 02:00:08 +00:00
Rob Ede f5655721aa
ci: rename lint workflow 2023-11-10 14:06:38 +00:00
Rob Ede 989548e36a
chore: remove git from repo URLs 2023-11-10 14:06:38 +00:00
dependabot[bot] 7d2349afb9
build(deps): bump taiki-e/install-action from 2.21.3 to 2.21.7 (#3183)
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 02:17:07 +00:00
dependabot[bot] b78f6da05f
build(deps): update zstd requirement from 0.12 to 0.13 (#3165)
* build(deps): update zstd requirement from 0.12 to 0.13

Updates the requirements on [zstd](https://github.com/gyscos/zstd-rs) to permit the latest version.
- [Release notes](https://github.com/gyscos/zstd-rs/releases)
- [Commits](https://github.com/gyscos/zstd-rs/compare/v0.12.0...v0.13.0)

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

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

* chore: update changelogs

---------

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>
2023-10-30 01:18:45 +00:00
dependabot[bot] 3b8d4de0e0
build(deps): bump taiki-e/install-action from 2.20.14 to 2.21.3 (#3179)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.20.14 to 2.21.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.20.14...v2.21.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>
2023-10-30 01:12:42 +00:00
dependabot[bot] 40196f16be
build(deps): bump taiki-e/cache-cargo-install-action from 1.2.2 to 1.3.0 (#3167)
Bumps [taiki-e/cache-cargo-install-action](https://github.com/taiki-e/cache-cargo-install-action) from 1.2.2 to 1.3.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.2.2...v1.3.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>
2023-10-30 00:05:50 +00:00
Rob Ede 32ddf972c6
docs: add guarded-listings example 2023-10-30 00:03:22 +00:00
Rob Ede ce18f35e03
docs: improve HttpServer docs on worker / bind relationship wrt factory instantiations 2023-10-29 18:48:03 +00:00
Rob Ede d3d0208cbd
chore: fmt workflows 2023-10-29 01:56:10 +00:00
Rob Ede 9e51116da2
chore: remove unusable re-export 2023-10-29 01:53:03 +00:00
dependabot[bot] 3193b81a3e
build(deps): bump taiki-e/install-action from 2.20.2 to 2.20.14 (#3173)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.20.2 to 2.20.14.
- [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.20.2...v2.20.14)

---
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-10-23 01:21:06 +00:00
Rob Ede 3acdda48e0
ci: reduce dependabot rate for Cargo updater 2023-10-13 17:41:25 +02:00
dependabot[bot] 935d36c441
build(deps): bump taiki-e/install-action from 2.20.1 to 2.20.2 (#3163)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.20.1 to 2.20.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.20.1...v2.20.2)

---
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-10-12 08:41:10 +00:00
Rob Ede 05b4c4964f
update handler failure mode docs 2023-10-11 16:07:18 +02:00
dependabot[bot] fba766b4be
build(deps): bump taiki-e/install-action from 2.19.4 to 2.20.1 (#3160)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.19.4 to 2.20.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.19.4...v2.20.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>
2023-10-09 01:48:46 +00:00
dependabot[bot] 76a0385f94
build(deps): bump taiki-e/install-action from 2.19.3 to 2.19.4 (#3159)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-06 02:51:36 +01:00
dependabot[bot] f1c9b93b87
build(deps): bump taiki-e/install-action from 2.19.2 to 2.19.3 (#3156)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.19.2 to 2.19.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.19.2...v2.19.3)

---
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-10-03 09:45:32 +00:00
dependabot[bot] 55ddded315
build(deps): bump taiki-e/install-action from 2.19.1 to 2.19.2 (#3154)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.19.1 to 2.19.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.19.1...v2.19.2)

---
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-10-02 00:43:19 +00:00
dependabot[bot] 2cfe257fc2
build(deps): bump taiki-e/install-action from 2.18.17 to 2.19.1 (#3151)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.18.17 to 2.19.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.18.17...v2.19.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>
2023-09-29 01:58:58 +00:00
dependabot[bot] ccabcd83c0
build(deps): bump taiki-e/install-action from 2.18.16 to 2.18.17 (#3149)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.18.16 to 2.18.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.18.16...v2.18.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-09-27 01:25:32 +00:00
dependabot[bot] 13fed45bfa
build(deps): bump taiki-e/install-action from 2.18.14 to 2.18.16 (#3144)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.18.14 to 2.18.16.
- [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.18.14...v2.18.16)

---
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-09-25 01:28:45 +00:00
dependabot[bot] 8bd4b36ffe
build(deps): bump taiki-e/install-action from 2.18.13 to 2.18.14 (#3142)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.18.13 to 2.18.14.
- [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.18.13...v2.18.14)

---
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-09-22 04:36:18 +00:00
dependabot[bot] 801a51b312
build(deps): bump taiki-e/cache-cargo-install-action from 1.2.1 to 1.2.2 (#3139)
Bumps [taiki-e/cache-cargo-install-action](https://github.com/taiki-e/cache-cargo-install-action) from 1.2.1 to 1.2.2.
- [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.2.1...v1.2.2)

---
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>
2023-09-18 01:17:47 +00:00
dependabot[bot] b28e0fff4b
build(deps): bump taiki-e/install-action from 2.18.9 to 2.18.13 (#3138)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.18.9 to 2.18.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.18.9...v2.18.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>
2023-09-18 01:15:19 +00:00
Rob Ede 043bc88f73
docs: add note about rt::spawn compat with tokio::main 2023-09-15 22:20:48 +01:00
dependabot[bot] e1c48dba26
build(deps): bump taiki-e/install-action from 2.13.4 to 2.18.9 (#3133)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.13.4 to 2.18.9.
- [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.13.4...v2.18.9)

---
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-09-13 01:06:53 +00:00
dependabot[bot] 835a57afc6
build(deps): bump actions/checkout from 3 to 4 (#3130)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  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>
2023-09-12 04:44:47 +00:00
Rob Ede 81ac30f3df
ci: overspecify actions versions 2023-09-12 05:24:55 +01:00
Rob Ede d50eccb3f7
ci: workaround anstyle msrv 2023-09-12 05:16:41 +01:00
Rob Ede a7983351be
docs: fix Data doc 2023-09-12 04:45:36 +01:00
Rob Ede 215a52f565
chore: avoid single char error bindings 2023-09-03 19:09:42 +01:00
dependabot[bot] d445742974
Update trust-dns-resolver requirement from 0.22 to 0.23 (#3121)
* Update trust-dns-resolver requirement from 0.22 to 0.23

Updates the requirements on [trust-dns-resolver](https://github.com/bluejekyll/trust-dns) to permit the latest version.
- [Release notes](https://github.com/bluejekyll/trust-dns/releases)
- [Changelog](https://github.com/bluejekyll/trust-dns/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bluejekyll/trust-dns/compare/v0.22.0...v0.23.0)

---
updated-dependencies:
- dependency-name: trust-dns-resolver
  dependency-type: direct:production
...

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

* fixup post-upgrade

---------

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>
2023-09-03 04:40:41 +00:00
Rob Ede 76f6106f8f
fix: wrap attribute codegen regression when using expression (#3119) 2023-08-29 21:27:36 +01:00
Rob Ede 39abe3ae5e
docs: fix -http changelog 2023-08-29 01:57:19 +01:00
Rob Ede e6636f1279
chore(actix-test): prepare release 0.1.2 2023-08-29 01:55:17 +01:00
Rob Ede 2b40033a9c
chore(actix-web): prepare release 4.4.0 2023-08-29 01:54:40 +01:00
Rob Ede d2c0d472e9
chore(awc): prepare release 3.2.0 2023-08-29 01:53:14 +01:00
Rob Ede 45fdc08788
chore(actix-http): prepare release 3.4.0 2023-08-29 01:51:54 +01:00
Rob Ede a12d39c93e
chore(actix-web-codegen): prepare release 4.2.1 2023-08-29 01:19:56 +01:00
Rob Ede b422745b6c
chore(actix-multipart): prepare release 0.6.1 2023-08-29 01:18:37 +01:00
Rob Ede 4ed61466e7
chore(actix-multipart-derive): prepare release 0.6.1 2023-08-29 01:17:32 +01:00
Rob Ede ac95362340
refactor: simplify connector feature combos 2023-08-29 01:14:54 +01:00
Rob Ede 84eb8b306c
chore: remove broken links from changelogs 2023-08-29 01:14:33 +01:00
Rob Ede 384ca0a2cd
chore: remove dates from changelogs 2023-08-29 01:14:33 +01:00
Rob Ede 905c30af86
Actix Web Rustls v0.21 support (#3116) 2023-08-29 01:11:11 +01:00
Wyatt Herkamp cbf5e948db
Implement Deserialize and Default for actix_web::Data (#3109)
* Implement Default and Deserialize for Data

* FMT

* Change Log

* tweak changelog

* chore: whitespace

---------

Co-authored-by: Rob Ede <robjtede@icloud.com>
2023-08-27 22:47:05 +00:00
Rob Ede 55c15f5bbf
minimum viable rustls v0.21 support (#3112) 2023-08-27 00:07:11 +01:00
Rob Ede 14355b9442
ci: name msrv jobs (#3114) 2023-08-26 18:19:32 +01:00
Rob Ede d8df60bf4c
build: add justfile 2023-08-03 07:03:42 +01:00
Rob Ede eaabe7e686
ci: reinstate coverage 2023-08-03 06:58:31 +01:00
Rob Ede 12dbda986e
test: fix test_h2_connection_drop spurious hang
fixes #3061
2023-08-03 06:54:50 +01:00
Rob Ede 1c60978a89
chore: move codecov file 2023-08-03 06:28:45 +01:00
Rob Ede b4fcdffdc3
chore: update msrv to 1.68 (#3094) 2023-08-01 19:33:32 +01:00
Rob Ede 605cd7c540
add startup logging to basic example 2023-08-01 18:06:59 +01:00
Rob Ede 75a97f6b32
chore: remove clippy config file 2023-07-24 03:29:56 +01:00
Rob Ede ff8fd2f7b5
modernize ContentLength 2023-07-22 18:01:59 +01:00
Rob Ede 6a0ea51b15
add ContentLength typed header (#2490) 2023-07-22 03:16:01 +01:00
Rob Ede 8cdbab3416
refactor: remove web dev dep from http-test 2023-07-22 02:02:37 +01:00
Rob Ede 146011018e
add payload to_bytes helpers (#3083) 2023-07-22 02:02:29 +01:00
Rob Ede 3eb5a059ad
chore: address clippy warnings 2023-07-20 11:42:20 +01:00
cyqsimon 1040bc3d17
Add missing status code constructor methods on HttpResponse (#3042) 2023-07-20 10:36:49 +00:00
Rob Ede d22c9f9fb1
update syn to 2 in web codegen (#3081) 2023-07-20 10:49:01 +01:00
dependabot[bot] e25f3f8f1d
Bump JamesIves/github-pages-deploy-action from 4.4.1 to 4.4.3 (#3076)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-19 23:43:25 +01:00
dependabot[bot] 6d452d4977
Bump codecov/codecov-action from 1 to 3 (#3077)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Rob Ede <robjtede@icloud.com>
2023-07-19 23:42:40 +01:00
Vasiliy Taranov 67cee2915d
set up dependabot (#3019)
Co-authored-by: Rob Ede <robjtede@icloud.com>
2023-07-19 23:37:48 +01:00
Rob Ede db99da5daf
do not compress media types (#3075)
* misc: add temporary nix file

* Add test to check content type image/*

* misc: add unit test for expected behaviour jpeg

* feat(compress): add compress function to middleware

* feat(compress): use response content type to decide compress

* feat(compress): give more control to the user

* misc: improve default compress function

* add Compress::with_predicate

* remove predicate options

* assert auto traits on Compress

* fix changelog

---------

Co-authored-by: William R. Arellano <arellanowr@gmail.com>
2023-07-19 20:24:32 +01:00
Nathan Shaaban 80185ce741
Hide authorization header in httprequest debug output (#2953)
Co-authored-by: Nathan Shaaban <86252985+nshaaban-cPacket@users.noreply.github.com>
Co-authored-by: Rob Ede <robjtede@icloud.com>
2023-07-19 18:51:17 +00:00
Rob Ede 4272510261
doc amendments 2023-07-19 19:27:20 +01:00
Kristian Gaylord 908fb2606e
allow configuring number of test server workers (#3069)
Co-authored-by: Rob Ede <robjtede@icloud.com>
2023-07-19 16:48:43 +00:00
nerix b061f00421
Provide documentation in the middleware module (#3070)
Co-authored-by: Rob Ede <robjtede@icloud.com>
2023-07-19 10:24:14 +00:00
Rob Ede 3b9b38c44e
fix tempfile dep spec 2023-07-18 02:22:09 +01:00
Rob Ede a4c9361791
ci: fix windows openssl 2023-07-18 02:18:43 +01:00
James Rowe bf03207ca9
Add http2 optional feature (#3072)
Co-authored-by: Rob Ede <robjtede@icloud.com>
2023-07-17 20:59:10 +00:00
Rob Ede 79a38e0628
apply standard formatting 2023-07-17 02:38:12 +01:00
Rob Ede 60c76c5e10
revert http2 feature flag change 2023-07-17 02:19:26 +01:00
Rob Ede e4e839f4d1
only enable actix-http's http2 feature when TLS features are enabled
closes #3071
2023-07-17 01:51:10 +01:00
Rob Ede c34a18f64a
changelog file is optional in bump script 2023-07-17 01:47:26 +01:00
mitsubosh ce3af777a0
Fix typo (#3062) 2023-07-05 16:29:10 +00:00
Dylan DPC 0e8ed50e3a
align awc's h2 version (#3051)
Co-authored-by: Rob Ede <robjtede@icloud.com>
2023-07-02 00:56:12 +00:00
Raminder Singh 4eeb01415c
Fix failing test on Windows (#3037) 2023-07-02 00:36:06 +00:00
Rob Ede 241da6e081
update MSRV to 1.65 (#3059) 2023-07-02 01:09:15 +01:00
Rob Ede 1072d0dacf
address lints 2023-06-09 15:15:09 +01:00
Imamuzzaki Abu Salam 58c19b817f
docs(actix-web/README.md): update benchmark link (#3046)
Round 20 doesn't have actix score, but round 21 has. So I changed it to the round 21 link for everyone to see this is one of the best frameworks for Web/API.
2023-06-09 14:29:10 +01:00
moh-eulith 17218dc6c8
minor optimization: reserve buffer once length is known (ws) (#2950) 2023-05-07 15:13:10 +00:00
Rob Ede 6fdda45ca3
update bitflags to v2 2023-05-06 11:38:51 +01:00
Rob Ede 8b2b755cde
fix guard mod docs 2023-05-06 11:37:11 +01:00
Yuki Okushi de1efa673f
Refine GHA workflows (#3023) 2023-04-24 04:46:57 +09:00
Surya 5d4f591875
fix RUSTSEC-2023-0034 by updating h2 (#3022) 2023-04-22 12:53:35 +00:00
Rob Ede e81dc768dc
expose h2c methods on HttpServer (#2999
* expose h2c methods on HttpServer

* update h2c docs
2023-04-06 03:11:28 +01:00
Rob Ede 97399e8c8c
simplify CI 2023-04-02 03:27:14 +01:00
Elijah 8dee8a1426
docs(actix-http-test): update test server example (#3007) 2023-03-31 18:09:13 +00:00
Rob Ede e68f87f84f
add API diff to CI (#3002) 2023-03-15 13:32:55 +00:00
Rob Ede 0f3068f488
ci(windows): use choco to install openssl (#3003
ci: remove openssl install on windows
2023-03-15 05:39:02 +00:00
Rob Ede 5e29726c4f
standardize error messages in actix-http 2023-03-13 17:17:02 +00:00
Rob Ede 442fa279da
uncomment error variant 2023-03-13 14:30:21 +00:00
Rob Ede bfdc29ebb8
normalize actix-files error messages 2023-03-13 14:22:50 +00:00
Rob Ede 0e7380659f
implement Error for BodyLimitExceeded 2023-03-13 13:40:09 +00:00
Rob Ede 44c5cdaa10
bound initial allocation in to_bytes_limited 2023-03-13 13:40:07 +00:00
Rob Ede 9e7a6fe57b
add body::to_bytes_limited (#3000
* add body::to_body_limit

* rename to_bytes_limited
2023-03-13 13:31:48 +00:00
Rob Ede dfaca18584
add compression_responses benchmark (#3001) 2023-03-12 15:32:07 +00:00
Rob Ede 19c9d858f2
support 16 extractors 2023-03-12 04:29:22 +00:00
Rob Ede 4131786127
remove old benchmarks 2023-03-11 23:20:02 +00:00
Rob Ede 0ba147ef71
update actions/checkout to v3 2023-03-11 23:19:03 +00:00
Rob Ede 3fc01c4887
refactor server binding 2023-03-11 22:17:52 +00:00
Rob Ede 4c4024c949
fix minimal version specs for mime 2023-03-11 22:14:58 +00:00
Rob Ede e0939a01fc
prepare actix-http release 3.3.1 2023-03-02 17:09:26 +00:00
Rob Ede 20c7c07dc0
fix http version req 2023-03-02 16:21:13 +00:00
Rob Ede d7c6774ad5
add resource method helpers (#2978) 2023-03-02 08:22:22 +00:00
Rob Ede 67efa4a4db
migrate to doc_auto_cfg 2023-02-26 21:55:25 +00:00
Rob Ede d77bcb0b7c
update date in unreleased changelog sections 2023-02-26 21:45:36 +00:00
Rob Ede c4db9a1ae2
prepare actix-multipart release 0.6.0 2023-02-26 21:44:57 +00:00
Rob Ede 740d0c0c9d
prepare actix-multipart-derive release 0.6.0 2023-02-26 21:44:14 +00:00
Rob Ede f27584046c
add todo for header names in next breaking release 2023-02-26 16:31:40 +00:00
Rob Ede 129b78f9c7
prepare actix-test release 0.1.1 2023-02-26 14:20:48 +00:00
Rob Ede ad27150c5f
fix doc tests 2023-02-26 14:14:04 +00:00
Rob Ede 8d5d6a2598
tweak err handlers docs 2023-02-26 13:28:19 +00:00
Rob Ede e97329eb2a
bump socket2 dep to 0.5 2023-02-26 13:28:19 +00:00
Kristian Gaylord fbfff3e751
actix-test: allow dynamic port setting (#2960)
Co-authored-by: Rob Ede <robjtede@icloud.com>
2023-02-26 05:25:36 +00:00
Rob Ede fdfb3d45db
remove direct dep on ahash for client pool 2023-02-26 03:50:36 +00:00
Rob Ede 4e05629368
specify safe tokio version range 2023-02-26 03:47:25 +00:00
Rob Ede e35ec28cd2
prepare actix-web release 4.3.1 2023-02-26 03:44:34 +00:00
Rob Ede 35006e9cae
prepare actix-web-codegen release 4.2.0 2023-02-26 03:42:27 +00:00
Rob Ede 115701eb86
prepare awc release 3.1.1 2023-02-26 03:34:47 +00:00
Rob Ede e2fed91efd
format markdown with prettier 2023-02-26 03:26:51 +00:00
Jacob Halsey d4b833ccf0
actix-multipart: Feature: Add typed multipart form extractor (#2883)
Co-authored-by: Rob Ede <robjtede@icloud.com>
2023-02-26 03:26:06 +00:00
Rob Ede 358c1cf85b
improve docs for app_config methods 2023-02-22 23:06:23 +00:00
Lioness100 42193bee29
fix typos (#2982) 2023-02-20 08:11:16 +00:00
Rob Ede dc08ea044b
clippy 2023-02-13 21:09:28 +00:00
Roy Wellington Ⅳ 85d88ffada
Fix minor typo in Markdown (#2977) 2023-02-12 02:47:42 +00:00
yinho999 bf19a0e761
added body manipulation example for error handlers (#2973)
Closes https://github.com/actix/actix-web/issues/2856
2023-02-09 20:37:01 +00:00
Joel Wurtz bf1f169be2
[awc] change client::Connect to be public (#2690) 2023-02-09 09:32:04 +00:00
Rob Ede 359d5d5c80
refactor codegen route guards 2023-02-06 17:06:47 +00:00
edgerunnergit 65c0545a7a
added support for creating custom methods with route macro (#2969)
Co-authored-by: Rob Ede <robjtede@icloud.com>
Closes https://github.com/actix/actix-web/issues/2893
2023-02-06 12:40:41 +00:00
Rob Ede b933ed4456
add tests for files_listing_renderer 2023-02-03 21:04:07 -05:00
Rob Ede 4bff1d0abe
require safe tokio version range
see https://rustsec.org/advisories/RUSTSEC-2023-0005
2023-02-03 20:35:19 -05:00
Rob Ede fa106da555
refactor: move Host guard into own module 2023-01-30 11:36:12 -05:00
Rob Ede c15016dafb
prepare actix-files release 0.6.3 2023-01-21 19:03:19 +00:00
316 changed files with 10959 additions and 4478 deletions

View file

@ -1,17 +1,10 @@
[alias]
lint = "clippy --workspace --tests --examples --bins -- -Dclippy::todo"
lint-all = "clippy --workspace --all-features --tests --examples --bins -- -Dclippy::todo"
lint = "clippy --workspace --all-targets -- -Dclippy::todo"
lint-all = "clippy --workspace --all-features --all-targets -- -Dclippy::todo"
# lib checking
ci-check-min = "hack --workspace check --no-default-features"
ci-check-default = "hack --workspace check"
ci-check-default-tests = "check --workspace --tests"
ci-check-all-feature-powerset="hack --workspace --feature-powerset --skip=__compress,experimental-io-uring check"
ci-check-all-feature-powerset-linux="hack --workspace --feature-powerset --skip=__compress check"
# testing
ci-doctest-default = "test --workspace --doc --no-fail-fast -- --nocapture"
ci-doctest = "test --workspace --all-features --doc --no-fail-fast -- --nocapture"
# compile docs as docs.rs would
# RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc --no-deps --workspace
ci-check-all-feature-powerset="hack --workspace --feature-powerset --depth=4 --skip=__compress,experimental-io-uring check"
ci-check-all-feature-powerset-linux="hack --workspace --feature-powerset --depth=4 --skip=__compress check"

View file

@ -3,34 +3,40 @@ name: Bug Report
about: Create a bug report.
---
Your issue may already be reported!
Please search on the [Actix Web issue tracker](https://github.com/actix/actix-web/issues) before creating one.
Your issue may already be reported! Please search on the [Actix Web issue tracker](https://github.com/actix/actix-web/issues) before creating one.
## Expected Behavior
<!--- If you're describing a bug, tell us what should happen -->
<!--- If you're suggesting a change/improvement, tell us how it should work -->
## Current Behavior
<!--- If describing a bug, tell us what happens instead of the expected behavior -->
<!--- If suggesting a change/improvement, explain the difference from current behavior -->
## Possible Solution
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
<!--- or ideas how to implement the addition or change -->
## Steps to Reproduce (for bugs)
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
<!--- reproduce this bug. Include code to reproduce, if relevant -->
1.
2.
3.
4.
## Context
<!--- How has this issue affected you? What are you trying to accomplish? -->
<!--- Providing context helps us come up with a solution that is most useful in the real world -->
## Your Environment
<!--- Include as many relevant details about the environment you experienced the bug in -->
- Rust Version (I.e, output of `rustc -V`):

View file

@ -2,12 +2,14 @@
<!-- Please fill out the following to get your PR reviewed quicker. -->
## PR Type
<!-- What kind of change does this PR make? -->
<!-- Bug Fix / Feature / Refactor / Code Style / Other -->
PR_TYPE
## PR Checklist
<!-- Check your PR fulfills the following items. -->
<!-- For draft PRs check the boxes as you complete them. -->
@ -17,11 +19,10 @@ PR_TYPE
- [ ] Format code with the latest stable rustfmt.
- [ ] (Team) Label with affected crates and semver status.
## 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: cargo
directory: /
schedule:
interval: weekly
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly

View file

@ -2,28 +2,27 @@ name: Benchmark
on:
push:
branches:
- master
branches: [master]
permissions:
contents: read # to fetch code (actions/checkout)
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
check_benchmark:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
profile: minimal
override: true
run: |
rustup set profile minimal
rustup install nightly
rustup override set nightly
- name: Check benchmark
uses: actions-rs/cargo@v1
with:
command: bench
args: --bench=server -- --sample-size=15
run: cargo bench --bench=server -- --sample-size=15

View file

@ -5,132 +5,87 @@ on:
branches: [master]
permissions:
contents: read # to fetch code (actions/checkout)
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-2022, triple: x86_64-pc-windows-msvc }
- { name: Windows, os: windows-latest, triple: x86_64-pc-windows-msvc }
version:
- nightly
- { name: nightly, version: nightly }
name: ${{ matrix.target.name }} / ${{ matrix.version }}
name: ${{ matrix.target.name }} / ${{ matrix.version.name }}
runs-on: ${{ matrix.target.os }}
env:
CI: 1
CARGO_INCREMENTAL: 0
VCPKGRS_DYNAMIC: 1
CARGO_UNSTABLE_SPARSE_REGISTRY: true
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Install nasm
if: matrix.target.os == 'windows-latest'
uses: ilammy/setup-nasm@v1.5.1
# install OpenSSL on Windows
# TODO: GitHub actions docs state that OpenSSL is
# already installed on these Windows machines somewhere
- name: Set vcpkg root
if: matrix.target.triple == 'x86_64-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
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.9.0
with:
toolchain: ${{ matrix.version }}-${{ matrix.target.triple }}
profile: minimal
override: true
toolchain: ${{ matrix.version.version }}
- name: Install cargo-hack
uses: taiki-e/install-action@cargo-hack
- 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 just, cargo-hack, cargo-nextest, cargo-ci-cache-clean
uses: taiki-e/install-action@v2.38.0
with:
tool: just,cargo-hack,cargo-nextest,cargo-ci-cache-clean
- name: check minimal
uses: actions-rs/cargo@v1
with: { command: ci-check-min }
run: cargo ci-check-min
- name: check default
uses: actions-rs/cargo@v1
with: { command: ci-check-default }
run: cargo ci-check-default
- name: tests
timeout-minutes: 60
run: |
cargo test --lib --tests -p=actix-router --all-features
cargo test --lib --tests -p=actix-http --all-features
cargo test --lib --tests -p=actix-web --features=rustls,openssl -- --skip=test_reading_deflate_encoding_large_random_rustls
cargo test --lib --tests -p=actix-web-codegen --all-features
cargo test --lib --tests -p=awc --all-features
cargo test --lib --tests -p=actix-http-test --all-features
cargo test --lib --tests -p=actix-test --all-features
cargo test --lib --tests -p=actix-files
cargo test --lib --tests -p=actix-multipart --all-features
cargo test --lib --tests -p=actix-web-actors --all-features
run: just test
- name: Clear the cargo caches
run: |
cargo install cargo-cache --version 0.8.2 --no-default-features --features ci-autoclean
cargo-cache
- name: CI cache clean
run: cargo-ci-cache-clean
ci_feature_powerset_check:
name: Verify Feature Combinations
runs-on: ubuntu-latest
env:
CI: 1
CARGO_INCREMENTAL: 0
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- name: Free Disk Space
run: ./scripts/free-disk-space.sh
- name: Install Rust
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
- name: Install cargo-hack
uses: taiki-e/install-action@cargo-hack
- name: Generate Cargo.lock
run: cargo generate-lockfile
- name: Cache Dependencies
uses: Swatinem/rust-cache@v1.2.0
uses: taiki-e/install-action@v2.38.0
with:
tool: cargo-hack
- name: check feature combinations
run: cargo ci-check-all-feature-powerset
- name: check feature combinations
run: cargo ci-check-all-feature-powerset-linux
nextest:
name: nextest
runs-on: ubuntu-latest
env:
CI: 1
CARGO_INCREMENTAL: 0
steps:
- uses: actions/checkout@v2
- uses: dtolnay/rust-toolchain@stable
- name: Install nextest
uses: taiki-e/install-action@nextest
- name: Generate Cargo.lock
run: cargo generate-lockfile
- name: Cache Dependencies
uses: Swatinem/rust-cache@v1.3.0
- name: Test with cargo-nextest
run: cargo nextest run

View file

@ -3,135 +3,119 @@ name: CI
on:
pull_request:
types: [opened, synchronize, reopened]
merge_group:
types: [checks_requested]
push:
branches: [master]
permissions:
contents: read # to fetch code (actions/checkout)
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-2022, triple: x86_64-pc-windows-msvc }
- { name: Windows, os: windows-latest, triple: x86_64-pc-windows-msvc }
version:
- 1.59.0 # MSRV
- stable
- { 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:
CI: 1
CARGO_INCREMENTAL: 0
VCPKGRS_DYNAMIC: 1
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Install nasm
if: matrix.target.os == 'windows-latest'
uses: ilammy/setup-nasm@v1.5.1
# install OpenSSL on Windows
# TODO: GitHub actions docs state that OpenSSL is
# already installed on these Windows machines somewhere
- name: Set vcpkg root
if: matrix.target.triple == 'x86_64-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
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: Setup mold linker
if: matrix.target.os == 'ubuntu-latest'
uses: rui314/setup-mold@v1
- name: Install Rust (${{ matrix.version.name }})
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
with:
toolchain: ${{ matrix.version }}-${{ matrix.target.triple }}
profile: minimal
override: true
toolchain: ${{ matrix.version.version }}
- name: Install cargo-hack
uses: taiki-e/install-action@cargo-hack
- name: Install just, cargo-hack, cargo-nextest, cargo-ci-cache-clean
uses: taiki-e/install-action@v2.38.0
with:
tool: just,cargo-hack,cargo-nextest,cargo-ci-cache-clean
- name: workaround MSRV issues
if: matrix.version != 'stable'
run: |
cargo install cargo-edit --version=0.8.0
cargo add const-str@0.3 --dev -p=actix-web
cargo add const-str@0.3 --dev -p=awc
- name: Generate Cargo.lock
uses: actions-rs/cargo@v1
with: { command: generate-lockfile }
- name: Cache Dependencies
uses: Swatinem/rust-cache@v1.2.0
- name: workaround MSRV issues
if: matrix.version != 'stable'
run: |
cargo update -p=zstd-sys --precise=2.0.1+zstd.1.5.2
if: matrix.version.name == 'msrv'
run: just downgrade-for-msrv
- name: check minimal
uses: actions-rs/cargo@v1
with: { command: ci-check-min }
run: cargo ci-check-min
- name: check default
uses: actions-rs/cargo@v1
with: { command: ci-check-default }
run: cargo ci-check-default
- name: tests
timeout-minutes: 60
run: |
cargo test --lib --tests -p=actix-router --all-features
cargo test --lib --tests -p=actix-http --all-features
cargo test --lib --tests -p=actix-web --features=rustls,openssl -- --skip=test_reading_deflate_encoding_large_random_rustls
cargo test --lib --tests -p=actix-web-codegen --all-features
cargo test --lib --tests -p=awc --all-features
cargo test --lib --tests -p=actix-http-test --all-features
cargo test --lib --tests -p=actix-test --all-features
cargo test --lib --tests -p=actix-files
cargo test --lib --tests -p=actix-multipart --all-features
cargo test --lib --tests -p=actix-web-actors --all-features
run: just test
- name: Clear the cargo caches
run: |
cargo install cargo-cache --version 0.8.2 --no-default-features --features ci-autoclean
cargo-cache
- name: CI cache clean
run: cargo-ci-cache-clean
io-uring:
name: io-uring tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- name: Generate Cargo.lock
run: cargo generate-lockfile
- name: Cache Dependencies
uses: Swatinem/rust-cache@v1.3.0
- name: Install Rust
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
with:
toolchain: nightly
- name: tests (io-uring)
timeout-minutes: 60
run: >
sudo bash -c "ulimit -Sl 512
&& ulimit -Hl 512
&& PATH=$PATH:/usr/share/rust/.cargo/bin
&& RUSTUP_TOOLCHAIN=stable cargo test --lib --tests -p=actix-files --all-features"
sudo bash -c "ulimit -Sl 512 && ulimit -Hl 512 && PATH=$PATH:/usr/share/rust/.cargo/bin && RUSTUP_TOOLCHAIN=stable cargo test --lib --tests -p=actix-files --all-features"
rustdoc:
name: doc tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- name: Install Rust (nightly)
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
with:
toolchain: nightly
- name: Generate Cargo.lock
run: cargo generate-lockfile
- name: Cache Dependencies
uses: Swatinem/rust-cache@v1.3.0
- name: Install just
uses: taiki-e/install-action@v2.38.0
with:
tool: just
- name: doc tests
run: cargo ci-doctest
timeout-minutes: 60
run: just test-docs

View file

@ -1,49 +0,0 @@
name: Lint
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
fmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: dtolnay/rust-toolchain@nightly
with: { components: rustfmt }
- run: cargo fmt --all -- --check
clippy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: dtolnay/rust-toolchain@stable
with: { components: clippy }
- name: Generate Cargo.lock
run: cargo generate-lockfile
- name: Cache Dependencies
uses: Swatinem/rust-cache@v1.2.0
- name: Check with Clippy
uses: actions-rs/clippy-check@v1
with:
args: --workspace --tests --examples --all-features
token: ${{ secrets.GITHUB_TOKEN }}
lint-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: dtolnay/rust-toolchain@stable
with: { components: rust-docs }
- name: Check for broken intra-doc links
uses: actions-rs/cargo@v1
env:
RUSTDOCFLAGS: "-D warnings"
with:
command: doc
args: --no-deps --all-features --workspace

View file

@ -1,36 +1,40 @@
# disabled because `cargo tarpaulin` currently segfaults
name: Coverage
on:
push:
branches: [master]
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
# job currently (1st Feb 2022) segfaults
coverage:
name: coverage
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Install stable
uses: actions-rs/toolchain@v1
- name: Install Rust (nightly)
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
with:
toolchain: stable-x86_64-unknown-linux-gnu
profile: minimal
override: true
toolchain: nightly
components: llvm-tools
- 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 just, cargo-llvm-cov, cargo-nextest
uses: taiki-e/install-action@v2.38.0
with:
tool: just,cargo-llvm-cov,cargo-nextest
- name: Generate coverage file
run: |
cargo install cargo-tarpaulin --vers "^0.13"
cargo tarpaulin --workspace --features=rustls,openssl --out Xml --verbose
- name: Upload to Codecov
uses: codecov/codecov-action@v1
with: { file: cobertura.xml }
- name: Generate code coverage
run: just test-coverage-codecov
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4.4.1
with:
files: codecov.json
fail_ci_if_error: true
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

116
.github/workflows/lint.yml vendored Normal file
View file

@ -0,0 +1,116 @@
name: Lint
on:
pull_request:
types: [opened, synchronize, reopened]
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
fmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust (nightly)
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
with:
toolchain: nightly
components: rustfmt
- name: Check with Rustfmt
run: cargo fmt --all -- --check
clippy:
permissions:
contents: read
checks: write # to add clippy checks to PR diffs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
with:
components: clippy
- name: Check with 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 --
-A unknown_lints -D clippy::todo -D clippy::dbg_macro
lint-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust (nightly)
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
with:
toolchain: nightly
components: rust-docs
- name: Check for broken intra-doc links
env:
RUSTDOCFLAGS: -D warnings
run: cargo +nightly doc --no-deps --workspace --all-features
check-external-types:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust (nightly-2024-05-01)
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
with:
toolchain: nightly-2024-05-01
- name: Install just
uses: taiki-e/install-action@v2.38.0
with:
tool: just
- name: Install cargo-check-external-types
uses: taiki-e/cache-cargo-install-action@v1.2.2
with:
tool: cargo-check-external-types
- name: check external types
run: just check-external-types-all +nightly-2024-05-01
public-api-diff:
runs-on: ubuntu-latest
steps:
- name: Checkout main branch
uses: actions/checkout@v4
with:
ref: ${{ github.base_ref }}
- name: Checkout PR branch
uses: actions/checkout@v4
- name: Install Rust (nightly-2024-06-07)
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
with:
toolchain: nightly-2024-06-07
- name: Install cargo-public-api
uses: taiki-e/install-action@v2.38.0
with:
tool: cargo-public-api
- name: Generate API diff
run: |
for f in $(find -mindepth 2 -maxdepth 2 -name Cargo.toml); do
cargo public-api --manifest-path "$f" --simplified diff ${{ github.event.pull_request.base.sha }}..${{ github.sha }}
done

View file

@ -1,32 +0,0 @@
name: Upload Documentation
on:
push:
branches: [master]
permissions: {}
jobs:
build:
permissions:
contents: write # to push changes in repo (jamesives/github-pages-deploy-action)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: dtolnay/rust-toolchain@nightly
- name: Build Docs
run: cargo +nightly doc --no-deps --workspace --all-features
env:
RUSTDOCFLAGS: --cfg=docsrs
- name: Tweak HTML
run: echo '<meta http-equiv="refresh" content="0;url=actix_web/index.html">' > target/doc/index.html
- name: Deploy to GitHub Pages
uses: JamesIves/github-pages-deploy-action@v4.4.1
with:
folder: target/doc
single-commit: true

4
.gitignore vendored
View file

@ -19,3 +19,7 @@ guide/build/
# Configuration directory generated by VSCode
.vscode
# code coverage
/lcov.info
/codecov.json

View file

@ -1,3 +0,0 @@
{
"proseWrap": "never"
}

5
.prettierrc.yml Normal file
View file

@ -0,0 +1,5 @@
overrides:
- files: "*.md"
options:
printWidth: 9999
proseWrap: never

3
.rustfmt.toml Normal file
View file

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

View file

@ -5,6 +5,7 @@ members = [
"actix-http-test",
"actix-http",
"actix-multipart",
"actix-multipart-derive",
"actix-router",
"actix-test",
"actix-web-actors",
@ -13,6 +14,13 @@ members = [
"awc",
]
[workspace.package]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-web"
license = "MIT OR Apache-2.0"
edition = "2021"
rust-version = "1.72"
[profile.dev]
# Disabling debug info speeds up builds a bunch and we don't rely on it for debugging that much.
debug = 0
@ -27,6 +35,7 @@ actix-files = { path = "actix-files" }
actix-http = { path = "actix-http" }
actix-http-test = { path = "actix-http-test" }
actix-multipart = { path = "actix-multipart" }
actix-multipart-derive = { path = "actix-multipart-derive" }
actix-router = { path = "actix-router" }
actix-test = { path = "actix-test" }
actix-web = { path = "actix-web" }

View file

@ -1,20 +1,38 @@
# Changes
## Unreleased - 2022-xx-xx
## Unreleased
## 0.6.6
- Update `tokio-uring` dependency to `0.4`.
- Minimum supported Rust version (MSRV) is now 1.72.
## 0.6.5
- Fix handling of special characters in filenames.
## 0.6.4
- Fix handling of newlines in filenames.
- Minimum supported Rust version (MSRV) is now 1.68 due to transitive `time` dependency.
## 0.6.3
- XHTML files now use `Content-Disposition: inline` instead of `attachment`. [#2903]
- Minimum supported Rust version (MSRV) is now 1.59 due to transitive `time` dependency.
- Update `tokio-uring` dependency to `0.4`.
[#2903]: https://github.com/actix/actix-web/pull/2903
## 0.6.2 - 2022-07-23
## 0.6.2
- Allow partial range responses for video content to start streaming sooner. [#2817]
- Minimum supported Rust version (MSRV) is now 1.57 due to transitive `time` dependency.
[#2817]: https://github.com/actix/actix-web/pull/2817
## 0.6.1
## 0.6.1 - 2022-06-11
- Add `NamedFile::{modified, metadata, content_type, content_disposition, encoding}()` getters. [#2021]
- Update `tokio-uring` dependency to `0.3`.
- Audio files now use `Content-Disposition: inline` instead of `attachment`. [#2645]
@ -23,46 +41,46 @@
[#2021]: https://github.com/actix/actix-web/pull/2021
[#2645]: https://github.com/actix/actix-web/pull/2645
## 0.6.0
## 0.6.0 - 2022-02-25
- No significant changes since `0.6.0-beta.16`.
## 0.6.0-beta.16
## 0.6.0-beta.16 - 2022-01-31
- No significant changes since `0.6.0-beta.15`.
## 0.6.0-beta.15
## 0.6.0-beta.15 - 2022-01-21
- No significant changes since `0.6.0-beta.14`.
## 0.6.0-beta.14
## 0.6.0-beta.14 - 2022-01-14
- The `prefer_utf8` option introduced in `0.4.0` is now true by default. [#2583]
[#2583]: https://github.com/actix/actix-web/pull/2583
## 0.6.0-beta.13
## 0.6.0-beta.13 - 2022-01-04
- The `Files` service now rejects requests with URL paths that include `%2F` (decoded: `/`). [#2398]
- The `Files` service now correctly decodes `%25` in the URL path to `%` for the file path. [#2398]
- Minimum supported Rust version (MSRV) is now 1.54.
[#2398]: https://github.com/actix/actix-web/pull/2398
## 0.6.0-beta.12
## 0.6.0-beta.12 - 2021-12-29
- No significant changes since `0.6.0-beta.11`.
## 0.6.0-beta.11
## 0.6.0-beta.11 - 2021-12-27
- No significant changes since `0.6.0-beta.10`.
## 0.6.0-beta.10
## 0.6.0-beta.10 - 2021-12-11
- No significant changes since `0.6.0-beta.9`.
## 0.6.0-beta.9
## 0.6.0-beta.9 - 2021-11-22
- Add crate feature `experimental-io-uring`, enabling async file I/O to be utilized. This feature is only available on Linux OSes with recent kernel versions. This feature is semver-exempt. [#2408]
- Add `NamedFile::open_async`. [#2408]
- Fix 304 Not Modified responses to omit the Content-Length header, as per the spec. [#2453]
@ -73,24 +91,24 @@
[#2408]: https://github.com/actix/actix-web/pull/2408
[#2453]: https://github.com/actix/actix-web/pull/2453
## 0.6.0-beta.8
## 0.6.0-beta.8 - 2021-10-20
- Minimum supported Rust version (MSRV) is now 1.52.
## 0.6.0-beta.7
## 0.6.0-beta.7 - 2021-09-09
- Minimum supported Rust version (MSRV) is now 1.51.
## 0.6.0-beta.6
## 0.6.0-beta.6 - 2021-06-26
- Added `Files::path_filter()`. [#2274]
- `Files::show_files_listing()` can now be used with `Files::index_file()` to show files listing as a fallback when the index file is not found. [#2228]
[#2274]: https://github.com/actix/actix-web/pull/2274
[#2228]: https://github.com/actix/actix-web/pull/2228
## 0.6.0-beta.5
## 0.6.0-beta.5 - 2021-06-17
- `NamedFile` now implements `ServiceFactory` and `HttpServiceFactory` making it much more useful in routing. For example, it can be used directly as a default service. [#2135]
- For symbolic links, `Content-Disposition` header no longer shows the filename of the original file. [#2156]
- `Files::redirect_to_slash_directory()` now works as expected when used with `Files::show_files_listing()`. [#2225]
@ -101,58 +119,58 @@
[#2225]: https://github.com/actix/actix-web/pull/2225
[#2257]: https://github.com/actix/actix-web/pull/2257
## 0.6.0-beta.4
## 0.6.0-beta.4 - 2021-04-02
- Add support for `.guard` in `Files` to selectively filter `Files` services. [#2046]
[#2046]: https://github.com/actix/actix-web/pull/2046
## 0.6.0-beta.3
## 0.6.0-beta.3 - 2021-03-09
- No notable changes.
## 0.6.0-beta.2
## 0.6.0-beta.2 - 2021-02-10
- Fix If-Modified-Since and If-Unmodified-Since to not compare using sub-second timestamps. [#1887]
- Replace `v_htmlescape` with `askama_escape`. [#1953]
[#1887]: https://github.com/actix/actix-web/pull/1887
[#1953]: https://github.com/actix/actix-web/pull/1953
## 0.6.0-beta.1
## 0.6.0-beta.1 - 2021-01-07
- `HttpRange::parse` now has its own error type.
- Update `bytes` to `1.0`. [#1813]
[#1813]: https://github.com/actix/actix-web/pull/1813
## 0.5.0
## 0.5.0 - 2020-12-26
- Optionally support hidden files/directories. [#1811]
[#1811]: https://github.com/actix/actix-web/pull/1811
## 0.4.1
## 0.4.1 - 2020-11-24
- Clarify order of parameters in `Files::new` and improve docs.
## 0.4.0
## 0.4.0 - 2020-10-06
- Add `Files::prefer_utf8` option that adds UTF-8 charset on certain response types. [#1714]
[#1714]: https://github.com/actix/actix-web/pull/1714
## 0.3.0
## 0.3.0 - 2020-09-11
- No significant changes from 0.3.0-beta.1.
## 0.3.0-beta.1
## 0.3.0-beta.1 - 2020-07-15
- Update `v_htmlescape` to 0.10
- Update `actix-web` and `actix-http` dependencies to beta.1
## 0.3.0-alpha.1
## 0.3.0-alpha.1 - 2020-05-23
- Update `actix-web` and `actix-http` dependencies to alpha
- Fix some typos in the docs
- Bump minimum supported Rust version to 1.40
@ -160,73 +178,73 @@
[#1384]: https://github.com/actix/actix-web/pull/1384
## 0.2.1
## 0.2.1 - 2019-12-22
- Use the same format for file URLs regardless of platforms
## 0.2.0
## 0.2.0 - 2019-12-20
- Fix BodyEncoding trait import #1220
## 0.2.0-alpha.1
## 0.2.0-alpha.1 - 2019-12-07
- Migrate to `std::future`
## 0.1.7
## 0.1.7 - 2019-11-06
- Add an additional `filename*` param in the `Content-Disposition` header of
`actix_files::NamedFile` to be more compatible. (#1151)
- Add an additional `filename*` param in the `Content-Disposition` header of `actix_files::NamedFile` to be more compatible. (#1151)
## 0.1.6
## 0.1.6 - 2019-10-14
- Add option to redirect to a slash-ended path `Files` #1132
## 0.1.5
## 0.1.5 - 2019-10-08
- Bump up `mime_guess` crate version to 2.0.1
- Bump up `percent-encoding` crate version to 2.1
- Allow user defined request guards for `Files` #1113
## 0.1.4
## 0.1.4 - 2019-07-20
- Allow to disable `Content-Disposition` header #686
## 0.1.3
## 0.1.3 - 2019-06-28
- Do not set `Content-Length` header, let actix-http set it #930
## 0.1.2
## 0.1.2 - 2019-06-13
- Content-Length is 0 for NamedFile HEAD request #914
- Fix ring dependency from actix-web default features for #741
## 0.1.1
## 0.1.1 - 2019-06-01
- Static files are incorrectly served as both chunked and with length #812
## 0.1.0
## 0.1.0 - 2019-05-25
- NamedFile last-modified check always fails due to nano-seconds in file modified date #820
## 0.1.0-beta.4
## 0.1.0-beta.4 - 2019-05-12
- Update actix-web to beta.4
## 0.1.0-beta.1
## 0.1.0-beta.1 - 2019-04-20
- Update actix-web to beta.1
## 0.1.0-alpha.6
## 0.1.0-alpha.6 - 2019-04-14
- Update actix-web to alpha6
## 0.1.0-alpha.4
## 0.1.0-alpha.4 - 2019-04-08
- Update actix-web to alpha4
## 0.1.0-alpha.2
## 0.1.0-alpha.2 - 2019-04-02
- Add default handler support
## 0.1.0-alpha.1
## 0.1.0-alpha.1 - 2019-03-28
- Initial impl

View file

@ -1,6 +1,6 @@
[package]
name = "actix-files"
version = "0.6.2"
version = "0.6.6"
authors = [
"Nikolay Kim <fafhrd91@gmail.com>",
"Rob Ede <robjtede@icloud.com>",
@ -11,11 +11,16 @@ homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-web"
categories = ["asynchronous", "web-programming::http-server"]
license = "MIT OR Apache-2.0"
edition = "2018"
edition = "2021"
[lib]
name = "actix_files"
path = "src/lib.rs"
[package.metadata.cargo_check_external_types]
allowed_external_types = [
"actix_http::*",
"actix_service::*",
"actix_web::*",
"http::*",
"mime::*",
]
[features]
experimental-io-uring = ["actix-web/experimental-io-uring", "tokio-uring"]
@ -26,25 +31,26 @@ actix-service = "2"
actix-utils = "3"
actix-web = { version = "4", default-features = false }
bitflags = "1"
bitflags = "2"
bytes = "1"
derive_more = "0.99.5"
futures-core = { version = "0.3.17", default-features = false, features = ["alloc"] }
http-range = "0.1.4"
log = "0.4"
mime = "0.3"
mime = "0.3.9"
mime_guess = "2.0.1"
percent-encoding = "2.1"
pin-project-lite = "0.2.7"
v_htmlescape= "0.15"
v_htmlescape = "0.15.5"
# experimental-io-uring
[target.'cfg(target_os = "linux")'.dependencies]
tokio-uring = { version = "0.4", optional = true, features = ["bytes"] }
actix-server = { version = "2.2", optional = true } # ensure matching tokio-uring versions
tokio-uring = { version = "0.5", optional = true, features = ["bytes"] }
actix-server = { version = "2.4", optional = true } # ensure matching tokio-uring versions
[dev-dependencies]
actix-rt = "2.7"
actix-test = "0.1"
actix-web = "4"
env_logger = "0.11"
tempfile = "3.2"

View file

@ -1,18 +1,32 @@
# actix-files
# `actix-files`
> Static file serving for Actix Web
<!-- prettier-ignore-start -->
[![crates.io](https://img.shields.io/crates/v/actix-files?label=latest)](https://crates.io/crates/actix-files)
[![Documentation](https://docs.rs/actix-files/badge.svg?version=0.6.2)](https://docs.rs/actix-files/0.6.2)
![Version](https://img.shields.io/badge/rustc-1.59+-ab6000.svg)
[![Documentation](https://docs.rs/actix-files/badge.svg?version=0.6.6)](https://docs.rs/actix-files/0.6.6)
![Version](https://img.shields.io/badge/rustc-1.72+-ab6000.svg)
![License](https://img.shields.io/crates/l/actix-files.svg)
<br />
[![dependency status](https://deps.rs/crate/actix-files/0.6.2/status.svg)](https://deps.rs/crate/actix-files/0.6.2)
[![dependency status](https://deps.rs/crate/actix-files/0.6.6/status.svg)](https://deps.rs/crate/actix-files/0.6.6)
[![Download](https://img.shields.io/crates/d/actix-files.svg)](https://crates.io/crates/actix-files)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)
## Documentation & Resources
<!-- prettier-ignore-end -->
- [API Documentation](https://docs.rs/actix-files)
- [Example Project](https://github.com/actix/examples/tree/master/basics/static-files)
- Minimum Supported Rust Version (MSRV): 1.54
<!-- cargo-rdme start -->
Static file serving for Actix Web.
Provides a non-blocking service for serving static files from disk.
## Examples
```rust
use actix_web::App;
use actix_files::Files;
let app = App::new()
.service(Files::new("/static", ".").prefer_utf8(true));
```
<!-- cargo-rdme end -->

View file

@ -0,0 +1,33 @@
use actix_files::Files;
use actix_web::{get, guard, middleware, App, HttpServer, Responder};
const EXAMPLES_DIR: &str = concat![env!("CARGO_MANIFEST_DIR"), "/examples"];
#[get("/")]
async fn index() -> impl Responder {
"Hello world!"
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
log::info!("starting HTTP server at http://localhost:8080");
HttpServer::new(|| {
App::new()
.service(index)
.service(
Files::new("/assets", EXAMPLES_DIR)
.show_files_listing()
.guard(guard::Header("show-listing", "?1")),
)
.service(Files::new("/assets", EXAMPLES_DIR))
.wrap(middleware::Compress::default())
.wrap(middleware::Logger::default())
})
.bind(("127.0.0.1", 8080))?
.workers(2)
.run()
.await
}

View file

@ -7,11 +7,10 @@ use std::{
};
use actix_web::{error::Error, web::Bytes};
use futures_core::{ready, Stream};
use pin_project_lite::pin_project;
#[cfg(feature = "experimental-io-uring")]
use bytes::BytesMut;
use futures_core::{ready, Stream};
use pin_project_lite::pin_project;
use super::named::File;

View file

@ -1,4 +1,9 @@
use std::{fmt::Write, fs::DirEntry, io, path::Path, path::PathBuf};
use std::{
fmt::Write,
fs::DirEntry,
io,
path::{Path, PathBuf},
};
use actix_web::{dev::ServiceResponse, HttpRequest, HttpResponse};
use percent_encoding::{utf8_percent_encode, CONTROLS};

View file

@ -4,46 +4,45 @@ use derive_more::Display;
/// Errors which can occur when serving static files.
#[derive(Debug, PartialEq, Eq, Display)]
pub enum FilesError {
/// Path is not a directory
/// Path is not a directory.
#[allow(dead_code)]
#[display(fmt = "Path is not a directory. Unable to serve static files")]
#[display(fmt = "path is not a directory. Unable to serve static files")]
IsNotDirectory,
/// Cannot render directory
#[display(fmt = "Unable to render directory without index file")]
/// Cannot render directory.
#[display(fmt = "unable to render directory without index file")]
IsDirectory,
}
/// Return `NotFound` for `FilesError`
impl ResponseError for FilesError {
/// Returns `404 Not Found`.
fn status_code(&self) -> StatusCode {
StatusCode::NOT_FOUND
}
}
#[allow(clippy::enum_variant_names)]
#[derive(Debug, PartialEq, Eq, Display)]
#[non_exhaustive]
pub enum UriSegmentError {
/// The segment started with the wrapped invalid character.
#[display(fmt = "The segment started with the wrapped invalid character")]
/// Segment started with the wrapped invalid character.
#[display(fmt = "segment started with invalid character: ('{_0}')")]
BadStart(char),
/// The segment contained the wrapped invalid character.
#[display(fmt = "The segment contained the wrapped invalid character")]
/// Segment contained the wrapped invalid character.
#[display(fmt = "segment contained invalid character ('{_0}')")]
BadChar(char),
/// The segment ended with the wrapped invalid character.
#[display(fmt = "The segment ended with the wrapped invalid character")]
/// Segment ended with the wrapped invalid character.
#[display(fmt = "segment ended with invalid character: ('{_0}')")]
BadEnd(char),
/// The path is not a valid UTF-8 string after doing percent decoding.
#[display(fmt = "The path is not a valid UTF-8 string after percent-decoding")]
/// Path is not a valid UTF-8 string after percent-decoding.
#[display(fmt = "path is not a valid UTF-8 string after percent-decoding")]
NotValidUtf8,
}
/// Return `BadRequest` for `UriSegmentError`
impl ResponseError for UriSegmentError {
/// Returns `400 Bad Request`.
fn status_code(&self) -> StatusCode {
StatusCode::BAD_REQUEST
}

View file

@ -8,8 +8,7 @@ use std::{
use actix_service::{boxed, IntoServiceFactory, ServiceFactory, ServiceFactoryExt};
use actix_web::{
dev::{
AppService, HttpServiceFactory, RequestHead, ResourceDef, ServiceRequest,
ServiceResponse,
AppService, HttpServiceFactory, RequestHead, ResourceDef, ServiceRequest, ServiceResponse,
},
error::Error,
guard::Guard,
@ -142,7 +141,7 @@ impl Files {
self
}
/// Set custom directory renderer
/// Set custom directory renderer.
pub fn files_listing_renderer<F>(mut self, f: F) -> Self
where
for<'r, 's> F:
@ -152,7 +151,7 @@ impl Files {
self
}
/// Specifies mime override callback
/// Specifies MIME override callback.
pub fn mime_override<F>(mut self, f: F) -> Self
where
F: Fn(&mime::Name<'_>) -> DispositionType + 'static,
@ -236,7 +235,7 @@ impl Files {
/// request starts being handled by the file service, it will not be able to back-out and try
/// the next service, you will simply get a 404 (or 405) error response.
///
/// To allow `POST` requests to retrieve files, see [`Files::use_guards`].
/// To allow `POST` requests to retrieve files, see [`Files::method_guard()`].
///
/// # Examples
/// ```
@ -301,12 +300,8 @@ impl Files {
pub fn default_handler<F, U>(mut self, f: F) -> Self
where
F: IntoServiceFactory<U, ServiceRequest>,
U: ServiceFactory<
ServiceRequest,
Config = (),
Response = ServiceResponse,
Error = Error,
> + 'static,
U: ServiceFactory<ServiceRequest, Config = (), Response = ServiceResponse, Error = Error>
+ 'static,
{
// create and configure default resource
self.default = Rc::new(RefCell::new(Some(Rc::new(boxed::factory(
@ -390,3 +385,46 @@ impl ServiceFactory<ServiceRequest> for Files {
}
}
}
#[cfg(test)]
mod tests {
use actix_web::{
http::StatusCode,
test::{self, TestRequest},
App, HttpResponse,
};
use super::*;
#[actix_web::test]
async fn custom_files_listing_renderer() {
let srv = test::init_service(
App::new().service(
Files::new("/", "./tests")
.show_files_listing()
.files_listing_renderer(|dir, req| {
Ok(ServiceResponse::new(
req.clone(),
HttpResponse::Ok().body(dir.path.to_str().unwrap().to_owned()),
))
}),
),
)
.await;
let req = TestRequest::with_uri("/").to_request();
let res = test::call_service(&srv, req).await;
assert_eq!(res.status(), StatusCode::OK);
let body = test::read_body(res).await;
let body_str = std::str::from_utf8(&body).unwrap();
let actual_path = Path::new(&body_str);
let expected_path = Path::new("actix-files/tests");
assert!(
actual_path.ends_with(expected_path),
"body {:?} does not end with {:?}",
actual_path,
expected_path
);
}
}

View file

@ -13,7 +13,11 @@
#![deny(rust_2018_idioms, nonstandard_style)]
#![warn(future_incompatible, missing_docs, missing_debug_implementations)]
#![allow(clippy::uninlined_format_args)]
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
use std::path::Path;
use actix_service::boxed::{BoxService, BoxServiceFactory};
use actix_web::{
@ -22,7 +26,6 @@ use actix_web::{
http::header::DispositionType,
};
use mime_guess::from_ext;
use std::path::Path;
mod chunked;
mod directory;
@ -34,16 +37,15 @@ mod path_buf;
mod range;
mod service;
pub use self::chunked::ChunkedReadFile;
pub use self::directory::Directory;
pub use self::files::Files;
pub use self::named::NamedFile;
pub use self::range::HttpRange;
pub use self::service::FilesService;
use self::directory::{directory_listing, DirectoryRenderer};
use self::error::FilesError;
use self::path_buf::PathBufWrap;
pub use self::{
chunked::ChunkedReadFile, directory::Directory, files::Files, named::NamedFile,
range::HttpRange, service::FilesService,
};
use self::{
directory::{directory_listing, DirectoryRenderer},
error::FilesError,
path_buf::PathBufWrap,
};
type HttpService = BoxService<ServiceRequest, ServiceResponse, Error>;
type HttpNewService = BoxServiceFactory<(), ServiceRequest, ServiceResponse, Error, ()>;
@ -63,6 +65,7 @@ type PathFilter = dyn Fn(&Path, &RequestHead) -> bool;
#[cfg(test)]
mod tests {
use std::{
fmt::Write as _,
fs::{self},
ops::Add,
time::{Duration, SystemTime},
@ -72,7 +75,7 @@ mod tests {
dev::ServiceFactory,
guard,
http::{
header::{self, ContentDisposition, DispositionParam, DispositionType},
header::{self, ContentDisposition, DispositionParam},
Method, StatusCode,
},
middleware::Compress,
@ -551,10 +554,9 @@ mod tests {
#[actix_rt::test]
async fn test_static_files_with_spaces() {
let srv = test::init_service(
App::new().service(Files::new("/", ".").index_file("Cargo.toml")),
)
.await;
let srv =
test::init_service(App::new().service(Files::new("/", ".").index_file("Cargo.toml")))
.await;
let request = TestRequest::get()
.uri("/tests/test%20space.binary")
.to_request();
@ -566,6 +568,30 @@ mod tests {
assert_eq!(bytes, data);
}
#[cfg(not(target_os = "windows"))]
#[actix_rt::test]
async fn test_static_files_with_special_characters() {
// Create the file we want to test against ad-hoc. We can't check it in as otherwise
// Windows can't even checkout this repository.
let temp_dir = tempfile::tempdir().unwrap();
let file_with_newlines = temp_dir.path().join("test\n\x0B\x0C\rnewline.text");
fs::write(&file_with_newlines, "Look at my newlines").unwrap();
let srv = test::init_service(
App::new().service(Files::new("/", temp_dir.path()).index_file("Cargo.toml")),
)
.await;
let request = TestRequest::get()
.uri("/test%0A%0B%0C%0Dnewline.text")
.to_request();
let response = test::call_service(&srv, request).await;
assert_eq!(response.status(), StatusCode::OK);
let bytes = test::read_body(response).await;
let data = web::Bytes::from(fs::read(file_with_newlines).unwrap());
assert_eq!(bytes, data);
}
#[actix_rt::test]
async fn test_files_not_allowed() {
let srv = test::init_service(App::new().service(Files::new("/", "."))).await;
@ -664,8 +690,7 @@ mod tests {
#[actix_rt::test]
async fn test_static_files() {
let srv =
test::init_service(App::new().service(Files::new("/", ".").show_files_listing()))
.await;
test::init_service(App::new().service(Files::new("/", ".").show_files_listing())).await;
let req = TestRequest::with_uri("/missing").to_request();
let resp = test::call_service(&srv, req).await;
@ -678,8 +703,7 @@ mod tests {
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
let srv =
test::init_service(App::new().service(Files::new("/", ".").show_files_listing()))
.await;
test::init_service(App::new().service(Files::new("/", ".").show_files_listing())).await;
let req = TestRequest::with_uri("/tests").to_request();
let resp = test::call_service(&srv, req).await;
assert_eq!(
@ -840,19 +864,21 @@ mod tests {
#[actix_rt::test]
async fn test_percent_encoding_2() {
let tmpdir = tempfile::tempdir().unwrap();
let temp_dir = tempfile::tempdir().unwrap();
let filename = match cfg!(unix) {
true => "ض:?#[]{}<>()@!$&'`|*+,;= %20.test",
true => "ض:?#[]{}<>()@!$&'`|*+,;= %20\n.test",
false => "ض#[]{}()@!$&'`+,;= %20.test",
};
let filename_encoded = filename
.as_bytes()
.iter()
.map(|c| format!("%{:02X}", c))
.collect::<String>();
std::fs::File::create(tmpdir.path().join(filename)).unwrap();
.fold(String::new(), |mut buf, c| {
write!(&mut buf, "%{:02X}", c).unwrap();
buf
});
std::fs::File::create(temp_dir.path().join(filename)).unwrap();
let srv = test::init_service(App::new().service(Files::new("", tmpdir.path()))).await;
let srv = test::init_service(App::new().service(Files::new("/", temp_dir.path()))).await;
let req = TestRequest::get()
.uri(&format!("/{}", filename_encoded))

View file

@ -8,13 +8,13 @@ use std::{
use actix_web::{
body::{self, BoxBody, SizedStream},
dev::{
self, AppService, HttpServiceFactory, ResourceDef, Service, ServiceFactory,
ServiceRequest, ServiceResponse,
self, AppService, HttpServiceFactory, ResourceDef, Service, ServiceFactory, ServiceRequest,
ServiceResponse,
},
http::{
header::{
self, Charset, ContentDisposition, ContentEncoding, DispositionParam,
DispositionType, ExtendedValue, HeaderValue,
self, Charset, ContentDisposition, ContentEncoding, DispositionParam, DispositionType,
ExtendedValue, HeaderValue,
},
StatusCode,
},
@ -24,11 +24,11 @@ use bitflags::bitflags;
use derive_more::{Deref, DerefMut};
use futures_core::future::LocalBoxFuture;
use mime::Mime;
use mime_guess::from_path;
use crate::{encoding::equiv_utf8_text, range::HttpRange};
bitflags! {
#[derive(Debug, Clone, Copy)]
pub(crate) struct Flags: u8 {
const ETAG = 0b0000_0001;
const LAST_MD = 0b0000_0010;
@ -84,6 +84,7 @@ pub struct NamedFile {
#[cfg(not(feature = "experimental-io-uring"))]
pub(crate) use std::fs::File;
#[cfg(feature = "experimental-io-uring")]
pub(crate) use tokio_uring::fs::File;
@ -126,7 +127,7 @@ impl NamedFile {
}
};
let ct = from_path(&path).first_or_octet_stream();
let ct = mime_guess::from_path(&path).first_or_octet_stream();
let disposition = match ct.type_() {
mime::IMAGE | mime::TEXT | mime::AUDIO | mime::VIDEO => DispositionType::Inline,
@ -138,8 +139,13 @@ impl NamedFile {
_ => DispositionType::Attachment,
};
let mut parameters =
vec![DispositionParam::Filename(String::from(filename.as_ref()))];
// replace special characters in filenames which could occur on some filesystems
let filename_s = filename
.replace('\n', "%0A") // \n line break
.replace('\x0B', "%0B") // \v vertical tab
.replace('\x0C', "%0C") // \f form feed
.replace('\r', "%0D"); // \r carriage return
let mut parameters = vec![DispositionParam::Filename(filename_s)];
if !filename.is_ascii() {
parameters.push(DispositionParam::FilenameExt(ExtendedValue {

View file

@ -30,7 +30,7 @@ impl PathBufWrap {
let mut segment_count = path.matches('/').count() + 1;
// we can decode the whole path here (instead of per-segment decoding)
// because we will reject `%2F` in paths using `segement_count`.
// because we will reject `%2F` in paths using `segment_count`.
let path = percent_encoding::percent_decode_str(path)
.decode_utf8()
.map_err(|_| UriSegmentError::NotValidUtf8)?;
@ -97,8 +97,6 @@ impl FromRequest for PathBufWrap {
#[cfg(test)]
mod tests {
use std::iter::FromIterator;
use super::*;
#[test]

View file

@ -1,4 +1,36 @@
use derive_more::{Display, Error};
use std::fmt;
use derive_more::Error;
/// Copy of `http_range::HttpRangeParseError`.
#[derive(Debug, Clone)]
enum HttpRangeParseError {
InvalidRange,
NoOverlap,
}
impl From<http_range::HttpRangeParseError> for HttpRangeParseError {
fn from(err: http_range::HttpRangeParseError) -> Self {
match err {
http_range::HttpRangeParseError::InvalidRange => Self::InvalidRange,
http_range::HttpRangeParseError::NoOverlap => Self::NoOverlap,
}
}
}
#[derive(Debug, Clone, Error)]
#[non_exhaustive]
pub struct ParseRangeErr(#[error(not(source))] HttpRangeParseError);
impl fmt::Display for ParseRangeErr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("invalid Range header: ")?;
f.write_str(match self.0 {
HttpRangeParseError::InvalidRange => "invalid syntax",
HttpRangeParseError::NoOverlap => "range starts after end of content",
})
}
}
/// HTTP Range header representation.
#[derive(Debug, Clone, Copy)]
@ -10,26 +42,22 @@ pub struct HttpRange {
pub length: u64,
}
#[derive(Debug, Clone, Display, Error)]
#[display(fmt = "Parse HTTP Range failed")]
pub struct ParseRangeErr(#[error(not(source))] ());
impl HttpRange {
/// Parses Range HTTP header string as per RFC 2616.
///
/// `header` is HTTP Range header (e.g. `bytes=bytes=0-9`).
/// `size` is full size of response (file).
pub fn parse(header: &str, size: u64) -> Result<Vec<HttpRange>, ParseRangeErr> {
match http_range::HttpRange::parse(header, size) {
Ok(ranges) => Ok(ranges
.iter()
.map(|range| HttpRange {
start: range.start,
length: range.length,
})
.collect()),
Err(_) => Err(ParseRangeErr(())),
}
let ranges =
http_range::HttpRange::parse(header, size).map_err(|err| ParseRangeErr(err.into()))?;
Ok(ranges
.iter()
.map(|range| HttpRange {
start: range.start,
length: range.length,
})
.collect())
}
}

View file

@ -62,11 +62,7 @@ impl FilesService {
}
}
fn serve_named_file(
&self,
req: ServiceRequest,
mut named_file: NamedFile,
) -> ServiceResponse {
fn serve_named_file(&self, req: ServiceRequest, mut named_file: NamedFile) -> ServiceResponse {
if let Some(ref mime_override) = self.mime_override {
let new_disposition = mime_override(&named_file.content_type.type_());
named_file.content_disposition.disposition = new_disposition;
@ -120,13 +116,11 @@ impl Service<ServiceRequest> for FilesService {
));
}
let path_on_disk = match PathBufWrap::parse_path(
req.match_info().unprocessed(),
this.hidden_files,
) {
Ok(item) => item,
Err(err) => return Ok(req.error_response(err)),
};
let path_on_disk =
match PathBufWrap::parse_path(req.match_info().unprocessed(), this.hidden_files) {
Ok(item) => item,
Err(err) => return Ok(req.error_response(err)),
};
if let Some(filter) = &this.path_filter {
if !filter(path_on_disk.as_ref(), req.head()) {
@ -177,8 +171,7 @@ impl Service<ServiceRequest> for FilesService {
match NamedFile::open_async(&path).await {
Ok(mut named_file) => {
if let Some(ref mime_override) = this.mime_override {
let new_disposition =
mime_override(&named_file.content_type.type_());
let new_disposition = mime_override(&named_file.content_type.type_());
named_file.content_disposition.disposition = new_disposition;
}
named_file.flags = this.file_flags;

View file

@ -24,8 +24,7 @@ async fn test_utf8_file_contents() {
// disable UTF-8 attribute
let srv =
test::init_service(App::new().service(Files::new("/", "./tests").prefer_utf8(false)))
.await;
test::init_service(App::new().service(Files::new("/", "./tests").prefer_utf8(false))).await;
let req = TestRequest::with_uri("/utf8.txt").to_request();
let res = test::call_service(&srv, req).await;

View file

@ -12,9 +12,7 @@ async fn test_guard_filter() {
let srv = test::init_service(
App::new()
.service(Files::new("/", "./tests/fixtures/guards/first").guard(Host("first.com")))
.service(
Files::new("/", "./tests/fixtures/guards/second").guard(Host("second.com")),
),
.service(Files::new("/", "./tests/fixtures/guards/second").guard(Host("second.com"))),
)
.await;

View file

@ -9,8 +9,7 @@ use actix_web::{
async fn test_directory_traversal_prevention() {
let srv = test::init_service(App::new().service(Files::new("/", "./tests"))).await;
let req =
TestRequest::with_uri("/../../../../../../../../../../../etc/passwd").to_request();
let req = TestRequest::with_uri("/../../../../../../../../../../../etc/passwd").to_request();
let res = test::call_service(&srv, req).await;
assert_eq!(res.status(), StatusCode::NOT_FOUND);

View file

@ -1,13 +1,19 @@
# Changes
## Unreleased - 2022-xx-xx
## Unreleased
- Minimum supported Rust version (MSRV) is now 1.72.
## 3.2.0
- Minimum supported Rust version (MSRV) is now 1.68 due to transitive `time` dependency.
## 3.1.0
## 3.1.0 - 2023-01-21
- Minimum supported Rust version (MSRV) is now 1.59.
## 3.0.0
## 3.0.0 - 2022-07-24
- `TestServer::stop` is now async and will wait for the server and system to shutdown. [#2442]
- Added `TestServer::client_headers` method. [#2097]
- Update `actix-server` dependency to `2`.
@ -19,78 +25,79 @@
[#2097]: https://github.com/actix/actix-web/pull/2097
[#1813]: https://github.com/actix/actix-web/pull/1813
<details>
<summary>3.0.0 Pre-Releases</summary>
## 3.0.0-beta.13 - 2022-02-16
## 3.0.0-beta.13
- No significant changes since `3.0.0-beta.12`.
## 3.0.0-beta.12
## 3.0.0-beta.12 - 2022-01-31
- No significant changes since `3.0.0-beta.11`.
## 3.0.0-beta.11
## 3.0.0-beta.11 - 2022-01-04
- Minimum supported Rust version (MSRV) is now 1.54.
## 3.0.0-beta.10
## 3.0.0-beta.10 - 2021-12-27
- Update `actix-server` to `2.0.0-rc.2`. [#2550]
[#2550]: https://github.com/actix/actix-web/pull/2550
## 3.0.0-beta.9
## 3.0.0-beta.9 - 2021-12-11
- No significant changes since `3.0.0-beta.8`.
## 3.0.0-beta.8
## 3.0.0-beta.8 - 2021-11-30
- Update `actix-tls` to `3.0.0-rc.1`. [#2474]
[#2474]: https://github.com/actix/actix-web/pull/2474
## 3.0.0-beta.7
## 3.0.0-beta.7 - 2021-11-22
- Fix compatibility with experimental `io-uring` feature of `actix-rt`. [#2408]
[#2408]: https://github.com/actix/actix-web/pull/2408
## 3.0.0-beta.6
## 3.0.0-beta.6 - 2021-11-15
- `TestServer::stop` is now async and will wait for the server and system to shutdown. [#2442]
- Update `actix-server` to `2.0.0-beta.9`. [#2442]
- Minimum supported Rust version (MSRV) is now 1.52.
[#2442]: https://github.com/actix/actix-web/pull/2442
## 3.0.0-beta.5
## 3.0.0-beta.5 - 2021-09-09
- Minimum supported Rust version (MSRV) is now 1.51.
## 3.0.0-beta.4
## 3.0.0-beta.4 - 2021-04-02
- Added `TestServer::client_headers` method. [#2097]
[#2097]: https://github.com/actix/actix-web/pull/2097
## 3.0.0-beta.3
## 3.0.0-beta.3 - 2021-03-09
- No notable changes.
## 3.0.0-beta.2
## 3.0.0-beta.2 - 2021-02-10
- No notable changes.
## 3.0.0-beta.1
## 3.0.0-beta.1 - 2021-01-07
- Update `bytes` to `1.0`. [#1813]
[#1813]: https://github.com/actix/actix-web/pull/1813
</details>
## 2.1.0 - 2020-11-25
## 2.1.0
- Add ability to set address for `TestServer`. [#1645]
- Upgrade `base64` to `0.13`.
- Upgrade `serde_urlencoded` to `0.7`. [#1773]
@ -98,12 +105,12 @@
[#1773]: https://github.com/actix/actix-web/pull/1773
[#1645]: https://github.com/actix/actix-web/pull/1645
## 2.0.0
## 2.0.0 - 2020-09-11
- Update actix-codec and actix-utils dependencies.
## 2.0.0-alpha.1
## 2.0.0-alpha.1 - 2020-05-23
- Update the `time` dependency to 0.2.7
- Update `actix-connect` dependency to 2.0.0-alpha.2
- Make `test_server` `async` fn.
@ -112,56 +119,57 @@
- Update `base64` dependency to 0.12
- Update `env_logger` dependency to 0.7
## 1.0.0 - 2019-12-13
## 1.0.0
- Replaced `TestServer::start()` with `test_server()`
## 1.0.0-alpha.3
## 1.0.0-alpha.3 - 2019-12-07
- Migrate to `std::future`
## 0.2.5
## 0.2.5 - 2019-09-17
- Update serde_urlencoded to "0.6.1"
- Increase TestServerRuntime timeouts from 500ms to 3000ms
- Do not override current `System`
## 0.2.4
## 0.2.4 - 2019-07-18
- Update actix-server to 0.6
## 0.2.3
## 0.2.3 - 2019-07-16
- Add `delete`, `options`, `patch` methods to `TestServerRunner`
## 0.2.2
## 0.2.2 - 2019-06-16
- Add .put() and .sput() methods
## 0.2.1
## 0.2.1 - 2019-06-05
- Add license files
## 0.2.0
## 0.2.0 - 2019-05-12
- Update awc and actix-http deps
## 0.1.1
## 0.1.1 - 2019-04-24
- Always make new connection for http client
## 0.1.0
## 0.1.0 - 2019-04-16
- No changes
## 0.1.0-alpha.3
## 0.1.0-alpha.3 - 2019-04-02
- Request functions accept path #743
## 0.1.0-alpha.2
## 0.1.0-alpha.2 - 2019-03-29
- Added TestServerRuntime::load_body() method
- Update actix-http and awc libraries
## 0.1.0-alpha.1
## 0.1.0-alpha.1 - 2019-03-28
- Initial impl

View file

@ -1,11 +1,11 @@
[package]
name = "actix-http-test"
version = "3.1.0"
version = "3.2.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Various helpers for Actix applications to use during testing"
keywords = ["http", "web", "framework", "async", "futures"]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-web.git"
repository = "https://github.com/actix/actix-web"
categories = [
"network-programming",
"asynchronous",
@ -13,14 +13,22 @@ categories = [
"web-programming::websocket",
]
license = "MIT OR Apache-2.0"
edition = "2018"
edition = "2021"
[package.metadata.docs.rs]
features = []
[lib]
name = "actix_http_test"
path = "src/lib.rs"
[package.metadata.cargo_check_external_types]
allowed_external_types = [
"actix_codec::*",
"actix_http::*",
"actix_server::*",
"awc::*",
"bytes::*",
"futures_core::*",
"http::*",
"tokio::*",
]
[features]
default = []
@ -39,16 +47,15 @@ awc = { version = "3", default-features = false }
bytes = "1"
futures-core = { version = "0.3.17", default-features = false }
http = "0.2.5"
http = "0.2.7"
log = "0.4"
socket2 = "0.4"
serde = "1.0"
serde_json = "1.0"
socket2 = "0.5"
serde = "1"
serde_json = "1"
slab = "0.4"
serde_urlencoded = "0.7"
tls-openssl = { version = "0.10.9", package = "openssl", optional = true }
tokio = { version = "1.18.4", features = ["sync"] }
tls-openssl = { version = "0.10.55", package = "openssl", optional = true }
tokio = { version = "1.24.2", features = ["sync"] }
[dev-dependencies]
actix-web = { version = "4", default-features = false, features = ["cookies"] }
actix-http = "3"

View file

@ -1,17 +1,20 @@
# actix-http-test
# `actix-http-test`
> Various helpers for Actix applications to use during testing.
<!-- prettier-ignore-start -->
[![crates.io](https://img.shields.io/crates/v/actix-http-test?label=latest)](https://crates.io/crates/actix-http-test)
[![Documentation](https://docs.rs/actix-http-test/badge.svg?version=3.1.0)](https://docs.rs/actix-http-test/3.1.0)
![Version](https://img.shields.io/badge/rustc-1.59+-ab6000.svg)
[![Documentation](https://docs.rs/actix-http-test/badge.svg?version=3.2.0)](https://docs.rs/actix-http-test/3.2.0)
![Version](https://img.shields.io/badge/rustc-1.72+-ab6000.svg)
![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-http-test)
<br>
[![Dependency Status](https://deps.rs/crate/actix-http-test/3.1.0/status.svg)](https://deps.rs/crate/actix-http-test/3.1.0)
[![Dependency Status](https://deps.rs/crate/actix-http-test/3.2.0/status.svg)](https://deps.rs/crate/actix-http-test/3.2.0)
[![Download](https://img.shields.io/crates/d/actix-http-test.svg)](https://crates.io/crates/actix-http-test)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)
## Documentation & Resources
<!-- prettier-ignore-end -->
- [API Documentation](https://docs.rs/actix-http-test)
- Minimum Supported Rust Version (MSRV): 1.54
<!-- cargo-rdme start -->
Various helpers for Actix applications to use during testing.
<!-- cargo-rdme end -->

View file

@ -2,9 +2,9 @@
#![deny(rust_2018_idioms, nonstandard_style)]
#![warn(future_incompatible)]
#![allow(clippy::uninlined_format_args)]
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#[cfg(feature = "openssl")]
extern crate tls_openssl as openssl;
@ -30,27 +30,31 @@ use tokio::sync::mpsc;
/// for HTTP applications.
///
/// # Examples
/// ```no_run
/// use actix_http::HttpService;
///
/// ```
/// use actix_http::{HttpService, Response, Error, StatusCode};
/// use actix_http_test::test_server;
/// use actix_web::{web, App, HttpResponse, Error};
/// use actix_service::{fn_service, map_config, ServiceFactoryExt as _};
///
/// async fn my_handler() -> Result<HttpResponse, Error> {
/// Ok(HttpResponse::Ok().into())
/// }
///
/// #[actix_web::test]
/// #[actix_rt::test]
/// # async fn hidden_test() {}
/// async fn test_example() {
/// let mut srv = TestServer::start(||
/// HttpService::new(
/// App::new().service(web::resource("/").to(my_handler))
/// )
/// );
/// let srv = test_server(|| {
/// HttpService::build()
/// .h1(fn_service(|req| async move {
/// Ok::<_, Error>(Response::ok())
/// }))
/// .tcp()
/// .map_err(|_| ())
/// })
/// .await;
///
/// let req = srv.get("/");
/// let response = req.send().await.unwrap();
/// assert!(response.status().is_success());
///
/// assert_eq!(response.status(), StatusCode::OK);
/// }
/// # actix_rt::System::new().block_on(test_example());
/// ```
pub async fn test_server<F: ServerServiceFactory<TcpStream>>(factory: F) -> TestServer {
let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap();

File diff suppressed because it is too large Load diff

View file

@ -1,30 +1,59 @@
[package]
name = "actix-http"
version = "3.3.0"
version = "3.7.0"
authors = [
"Nikolay Kim <fafhrd91@gmail.com>",
"Rob Ede <robjtede@icloud.com>",
]
description = "HTTP primitives for the Actix ecosystem"
description = "HTTP types and services for the Actix ecosystem"
keywords = ["actix", "http", "framework", "async", "futures"]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-web.git"
repository = "https://github.com/actix/actix-web"
categories = [
"network-programming",
"asynchronous",
"web-programming::http-server",
"web-programming::websocket",
]
license = "MIT OR Apache-2.0"
edition = "2018"
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[package.metadata.docs.rs]
# features that docs.rs will build with
features = ["http2", "ws", "openssl", "rustls", "compress-brotli", "compress-gzip", "compress-zstd"]
rustdoc-args = ["--cfg", "docsrs"]
features = [
"http2",
"ws",
"openssl",
"rustls-0_20",
"rustls-0_21",
"rustls-0_22",
"rustls-0_23",
"compress-brotli",
"compress-gzip",
"compress-zstd",
]
[lib]
name = "actix_http"
path = "src/lib.rs"
[package.metadata.cargo_check_external_types]
allowed_external_types = [
"actix_codec::*",
"actix_service::*",
"actix_tls::*",
"actix_utils::*",
"bytes::*",
"bytestring::*",
"encoding_rs::*",
"futures_core::*",
"h2::*",
"http::*",
"httparse::*",
"language_tags::*",
"mime::*",
"openssl::*",
"rustls::*",
"tokio_util::*",
"tokio::*",
]
[features]
default = []
@ -43,15 +72,27 @@ ws = [
# TLS via OpenSSL
openssl = ["actix-tls/accept", "actix-tls/openssl"]
# TLS via Rustls
rustls = ["actix-tls/accept", "actix-tls/rustls"]
# TLS via Rustls v0.20
rustls = ["rustls-0_20"]
# TLS via Rustls v0.20
rustls-0_20 = ["actix-tls/accept", "actix-tls/rustls-0_20"]
# TLS via Rustls v0.21
rustls-0_21 = ["actix-tls/accept", "actix-tls/rustls-0_21"]
# TLS via Rustls v0.22
rustls-0_22 = ["actix-tls/accept", "actix-tls/rustls-0_22"]
# TLS via Rustls v0.23
rustls-0_23 = ["actix-tls/accept", "actix-tls/rustls-0_23"]
# Compression codecs
compress-brotli = ["__compress", "brotli"]
compress-gzip = ["__compress", "flate2"]
compress-zstd = ["__compress", "zstd"]
# Internal (PRIVATE!) features used to aid testing and cheking feature status.
# Internal (PRIVATE!) features used to aid testing and checking feature status.
# Don't rely on these whatsoever. They are semver-exempt and may disappear at anytime.
__compress = []
@ -61,82 +102,80 @@ actix-codec = "0.5"
actix-utils = "3"
actix-rt = { version = "2.2", default-features = false }
ahash = "0.7"
bitflags = "1.2"
ahash = "0.8"
bitflags = "2"
bytes = "1"
bytestring = "1"
derive_more = "0.99.5"
encoding_rs = "0.8"
futures-core = { version = "0.3.17", default-features = false, features = ["alloc"] }
http = "0.2.5"
http = "0.2.7"
httparse = "1.5.1"
httpdate = "1.0.1"
itoa = "1"
language-tags = "0.3"
mime = "0.3"
mime = "0.3.4"
percent-encoding = "2.1"
pin-project-lite = "0.2"
smallvec = "1.6.1"
tokio = { version = "1.18.4", features = [] }
tokio = { version = "1.24.2", features = [] }
tokio-util = { version = "0.7", features = ["io", "codec"] }
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
# http2
h2 = { version = "0.3.9", optional = true }
h2 = { version = "0.3.26", optional = true }
# websockets
local-channel = { version = "0.1", optional = true }
base64 = { version = "0.21", optional = true }
base64 = { version = "0.22", optional = true }
rand = { version = "0.8", optional = true }
sha1 = { version = "0.10", optional = true }
# openssl/rustls
actix-tls = { version = "3", default-features = false, optional = true }
actix-tls = { version = "3.4", default-features = false, optional = true }
# compress-*
brotli = { version = "3.3.3", optional = true }
brotli = { version = "6", optional = true }
flate2 = { version = "1.0.13", optional = true }
zstd = { version = "0.12", optional = true }
zstd = { version = "0.13", optional = true }
[dev-dependencies]
actix-http-test = { version = "3", features = ["openssl"] }
actix-server = "2"
actix-tls = { version = "3", features = ["openssl"] }
actix-tls = { version = "3.4", features = ["openssl", "rustls-0_23-webpki-roots"] }
actix-web = "4"
async-stream = "0.3"
criterion = { version = "0.4", features = ["html_reports"] }
env_logger = "0.9"
criterion = { version = "0.5", features = ["html_reports"] }
divan = "0.1.8"
env_logger = "0.11"
futures-util = { version = "0.3.17", default-features = false, features = ["alloc"] }
memchr = "2.4"
once_cell = "1.9"
rcgen = "0.9"
rcgen = "0.13"
regex = "1.3"
rustversion = "1"
rustls-pemfile = "1"
rustls-pemfile = "2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
static_assertions = "1"
tls-openssl = { package = "openssl", version = "0.10.9" }
tls-rustls = { package = "rustls", version = "0.20.0" }
tokio = { version = "1.18.4", features = ["net", "rt", "macros"] }
tls-openssl = { package = "openssl", version = "0.10.55" }
tls-rustls_023 = { package = "rustls", version = "0.23" }
tokio = { version = "1.24.2", features = ["net", "rt", "macros"] }
[[example]]
name = "ws"
required-features = ["ws", "rustls"]
required-features = ["ws", "rustls-0_23"]
[[example]]
name = "tls_rustls"
required-features = ["http2", "rustls-0_23"]
[[bench]]
name = "write-camel-case"
name = "response-body-compression"
harness = false
required-features = ["compress-brotli", "compress-gzip", "compress-zstd"]
[[bench]]
name = "status-line"
harness = false
[[bench]]
name = "uninit-headers"
harness = false
[[bench]]
name = "quality-value"
name = "date-formatting"
harness = false

View file

@ -1,22 +1,21 @@
# actix-http
# `actix-http`
> HTTP primitives for the Actix ecosystem.
> HTTP types and services for the Actix ecosystem.
<!-- prettier-ignore-start -->
[![crates.io](https://img.shields.io/crates/v/actix-http?label=latest)](https://crates.io/crates/actix-http)
[![Documentation](https://docs.rs/actix-http/badge.svg?version=3.3.0)](https://docs.rs/actix-http/3.3.0)
![Version](https://img.shields.io/badge/rustc-1.59+-ab6000.svg)
[![Documentation](https://docs.rs/actix-http/badge.svg?version=3.7.0)](https://docs.rs/actix-http/3.7.0)
![Version](https://img.shields.io/badge/rustc-1.72+-ab6000.svg)
![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-http.svg)
<br />
[![dependency status](https://deps.rs/crate/actix-http/3.3.0/status.svg)](https://deps.rs/crate/actix-http/3.3.0)
[![dependency status](https://deps.rs/crate/actix-http/3.7.0/status.svg)](https://deps.rs/crate/actix-http/3.7.0)
[![Download](https://img.shields.io/crates/d/actix-http.svg)](https://crates.io/crates/actix-http)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)
## Documentation & Resources
<!-- prettier-ignore-end -->
- [API Documentation](https://docs.rs/actix-http)
- Minimum Supported Rust Version (MSRV): 1.54
## Example
## Examples
```rust
use std::{env, io};
@ -49,18 +48,3 @@ async fn main() -> io::Result<()> {
.await
}
```
## 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))
at your option.
## Code of Conduct
Contribution to the actix-http crate is organized under the terms of the
Contributor Covenant, the maintainer of actix-http, @fafhrd91, promises to
intervene to uphold that code of conduct.

View file

@ -0,0 +1,20 @@
use std::time::SystemTime;
use actix_http::header::HttpDate;
use divan::{black_box, AllocProfiler, Bencher};
#[global_allocator]
static ALLOC: AllocProfiler = AllocProfiler::system();
#[divan::bench]
fn date_formatting(b: Bencher<'_, '_>) {
let now = SystemTime::now();
b.bench(|| {
black_box(HttpDate::from(black_box(now)).to_string());
})
}
fn main() {
divan::main();
}

View file

@ -1,97 +0,0 @@
#![allow(clippy::uninlined_format_args)]
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
const CODES: &[u16] = &[0, 1000, 201, 800, 550];
fn bench_quality_display_impls(c: &mut Criterion) {
let mut group = c.benchmark_group("quality value display impls");
for i in CODES.iter() {
group.bench_with_input(BenchmarkId::new("New (fast?)", i), i, |b, &i| {
b.iter(|| _new::Quality(i).to_string())
});
group.bench_with_input(BenchmarkId::new("Naive", i), i, |b, &i| {
b.iter(|| _naive::Quality(i).to_string())
});
}
group.finish();
}
criterion_group!(benches, bench_quality_display_impls);
criterion_main!(benches);
mod _new {
use std::fmt;
pub struct Quality(pub(crate) u16);
impl fmt::Display for Quality {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
0 => f.write_str("0"),
1000 => f.write_str("1"),
// some number in the range 1999
x => {
f.write_str("0.")?;
// this implementation avoids string allocation otherwise required
// for `.trim_end_matches('0')`
if x < 10 {
f.write_str("00")?;
// 0 is handled so it's not possible to have a trailing 0, we can just return
itoa_fmt(f, x)
} else if x < 100 {
f.write_str("0")?;
if x % 10 == 0 {
// trailing 0, divide by 10 and write
itoa_fmt(f, x / 10)
} else {
itoa_fmt(f, x)
}
} else {
// x is in range 101999
if x % 100 == 0 {
// two trailing 0s, divide by 100 and write
itoa_fmt(f, x / 100)
} else if x % 10 == 0 {
// one trailing 0, divide by 10 and write
itoa_fmt(f, x / 10)
} else {
itoa_fmt(f, x)
}
}
}
}
}
}
pub fn itoa_fmt<W: fmt::Write, V: itoa::Integer>(mut wr: W, value: V) -> fmt::Result {
let mut buf = itoa::Buffer::new();
wr.write_str(buf.format(value))
}
}
mod _naive {
use std::fmt;
pub struct Quality(pub(crate) u16);
impl fmt::Display for Quality {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
0 => f.write_str("0"),
1000 => f.write_str("1"),
x => {
write!(f, "{}", format!("{:03}", x).trim_end_matches('0'))
}
}
}
}
}

View file

@ -0,0 +1,88 @@
use std::convert::Infallible;
use actix_http::{encoding::Encoder, ContentEncoding, Request, Response, StatusCode};
use actix_service::{fn_service, Service as _};
use criterion::{black_box, criterion_group, criterion_main, Criterion};
static BODY: &[u8] = include_bytes!("../Cargo.toml");
fn compression_responses(c: &mut Criterion) {
let mut group = c.benchmark_group("compression responses");
group.bench_function("identity", |b| {
let rt = actix_rt::Runtime::new().unwrap();
let identity_svc = fn_service(|_: Request| async move {
let mut res = Response::with_body(StatusCode::OK, ());
let body = black_box(Encoder::response(
ContentEncoding::Identity,
res.head_mut(),
BODY,
));
Ok::<_, Infallible>(black_box(res.set_body(black_box(body))))
});
b.iter(|| {
rt.block_on(identity_svc.call(Request::new())).unwrap();
});
});
group.bench_function("gzip", |b| {
let rt = actix_rt::Runtime::new().unwrap();
let identity_svc = fn_service(|_: Request| async move {
let mut res = Response::with_body(StatusCode::OK, ());
let body = black_box(Encoder::response(
ContentEncoding::Gzip,
res.head_mut(),
BODY,
));
Ok::<_, Infallible>(black_box(res.set_body(black_box(body))))
});
b.iter(|| {
rt.block_on(identity_svc.call(Request::new())).unwrap();
});
});
group.bench_function("br", |b| {
let rt = actix_rt::Runtime::new().unwrap();
let identity_svc = fn_service(|_: Request| async move {
let mut res = Response::with_body(StatusCode::OK, ());
let body = black_box(Encoder::response(
ContentEncoding::Brotli,
res.head_mut(),
BODY,
));
Ok::<_, Infallible>(black_box(res.set_body(black_box(body))))
});
b.iter(|| {
rt.block_on(identity_svc.call(Request::new())).unwrap();
});
});
group.bench_function("zstd", |b| {
let rt = actix_rt::Runtime::new().unwrap();
let identity_svc = fn_service(|_: Request| async move {
let mut res = Response::with_body(StatusCode::OK, ());
let body = black_box(Encoder::response(
ContentEncoding::Zstd,
res.head_mut(),
BODY,
));
Ok::<_, Infallible>(black_box(res.set_body(black_box(body))))
});
b.iter(|| {
rt.block_on(identity_svc.call(Request::new())).unwrap();
});
});
group.finish();
}
criterion_group!(benches, compression_responses);
criterion_main!(benches);

View file

@ -1,214 +0,0 @@
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use bytes::BytesMut;
use http::Version;
const CODES: &[u16] = &[201, 303, 404, 515];
fn bench_write_status_line_11(c: &mut Criterion) {
let mut group = c.benchmark_group("write_status_line v1.1");
let version = Version::HTTP_11;
for i in CODES.iter() {
group.bench_with_input(BenchmarkId::new("Original (unsafe)", i), i, |b, &i| {
b.iter(|| {
let mut b = BytesMut::with_capacity(35);
_original::write_status_line(version, i, &mut b);
})
});
group.bench_with_input(BenchmarkId::new("New (safe)", i), i, |b, &i| {
b.iter(|| {
let mut b = BytesMut::with_capacity(35);
_new::write_status_line(version, i, &mut b);
})
});
group.bench_with_input(BenchmarkId::new("Naive", i), i, |b, &i| {
b.iter(|| {
let mut b = BytesMut::with_capacity(35);
_naive::write_status_line(version, i, &mut b);
})
});
}
group.finish();
}
fn bench_write_status_line_10(c: &mut Criterion) {
let mut group = c.benchmark_group("write_status_line v1.0");
let version = Version::HTTP_10;
for i in CODES.iter() {
group.bench_with_input(BenchmarkId::new("Original (unsafe)", i), i, |b, &i| {
b.iter(|| {
let mut b = BytesMut::with_capacity(35);
_original::write_status_line(version, i, &mut b);
})
});
group.bench_with_input(BenchmarkId::new("New (safe)", i), i, |b, &i| {
b.iter(|| {
let mut b = BytesMut::with_capacity(35);
_new::write_status_line(version, i, &mut b);
})
});
group.bench_with_input(BenchmarkId::new("Naive", i), i, |b, &i| {
b.iter(|| {
let mut b = BytesMut::with_capacity(35);
_naive::write_status_line(version, i, &mut b);
})
});
}
group.finish();
}
fn bench_write_status_line_09(c: &mut Criterion) {
let mut group = c.benchmark_group("write_status_line v0.9");
let version = Version::HTTP_09;
for i in CODES.iter() {
group.bench_with_input(BenchmarkId::new("Original (unsafe)", i), i, |b, &i| {
b.iter(|| {
let mut b = BytesMut::with_capacity(35);
_original::write_status_line(version, i, &mut b);
})
});
group.bench_with_input(BenchmarkId::new("New (safe)", i), i, |b, &i| {
b.iter(|| {
let mut b = BytesMut::with_capacity(35);
_new::write_status_line(version, i, &mut b);
})
});
group.bench_with_input(BenchmarkId::new("Naive", i), i, |b, &i| {
b.iter(|| {
let mut b = BytesMut::with_capacity(35);
_naive::write_status_line(version, i, &mut b);
})
});
}
group.finish();
}
criterion_group!(
benches,
bench_write_status_line_11,
bench_write_status_line_10,
bench_write_status_line_09
);
criterion_main!(benches);
mod _naive {
use bytes::{BufMut, BytesMut};
use http::Version;
pub(crate) fn write_status_line(version: Version, n: u16, bytes: &mut BytesMut) {
match version {
Version::HTTP_11 => bytes.put_slice(b"HTTP/1.1 "),
Version::HTTP_10 => bytes.put_slice(b"HTTP/1.0 "),
Version::HTTP_09 => bytes.put_slice(b"HTTP/0.9 "),
_ => {
// other HTTP version handlers do not use this method
}
}
bytes.put_slice(n.to_string().as_bytes());
}
}
mod _new {
use bytes::{BufMut, BytesMut};
use http::Version;
const DIGITS_START: u8 = b'0';
pub(crate) fn write_status_line(version: Version, n: u16, bytes: &mut BytesMut) {
match version {
Version::HTTP_11 => bytes.put_slice(b"HTTP/1.1 "),
Version::HTTP_10 => bytes.put_slice(b"HTTP/1.0 "),
Version::HTTP_09 => bytes.put_slice(b"HTTP/0.9 "),
_ => {
// other HTTP version handlers do not use this method
}
}
let d100 = (n / 100) as u8;
let d10 = ((n / 10) % 10) as u8;
let d1 = (n % 10) as u8;
bytes.put_u8(DIGITS_START + d100);
bytes.put_u8(DIGITS_START + d10);
bytes.put_u8(DIGITS_START + d1);
bytes.put_u8(b' ');
}
}
mod _original {
use std::ptr;
use bytes::{BufMut, BytesMut};
use http::Version;
const DEC_DIGITS_LUT: &[u8] = b"0001020304050607080910111213141516171819\
2021222324252627282930313233343536373839\
4041424344454647484950515253545556575859\
6061626364656667686970717273747576777879\
8081828384858687888990919293949596979899";
pub(crate) const STATUS_LINE_BUF_SIZE: usize = 13;
pub(crate) fn write_status_line(version: Version, mut n: u16, bytes: &mut BytesMut) {
let mut buf: [u8; STATUS_LINE_BUF_SIZE] = *b"HTTP/1.1 ";
match version {
Version::HTTP_2 => buf[5] = b'2',
Version::HTTP_10 => buf[7] = b'0',
Version::HTTP_09 => {
buf[5] = b'0';
buf[7] = b'9';
}
_ => {}
}
let mut curr: isize = 12;
let buf_ptr = buf.as_mut_ptr();
let lut_ptr = DEC_DIGITS_LUT.as_ptr();
let four = n > 999;
// decode 2 more chars, if > 2 chars
let d1 = (n % 100) << 1;
n /= 100;
curr -= 2;
unsafe {
ptr::copy_nonoverlapping(lut_ptr.offset(d1 as isize), buf_ptr.offset(curr), 2);
}
// decode last 1 or 2 chars
if n < 10 {
curr -= 1;
unsafe {
*buf_ptr.offset(curr) = (n as u8) + b'0';
}
} else {
let d1 = n << 1;
curr -= 2;
unsafe {
ptr::copy_nonoverlapping(lut_ptr.offset(d1 as isize), buf_ptr.offset(curr), 2);
}
}
bytes.put_slice(&buf);
if four {
bytes.put_u8(b' ');
}
}
}

View file

@ -1,135 +0,0 @@
use criterion::{criterion_group, criterion_main, Criterion};
use bytes::BytesMut;
// A Miri run detects UB, seen on this playground:
// https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f5d9aa166aa48df8dca05fce2b6c3915
fn bench_header_parsing(c: &mut Criterion) {
c.bench_function("Original (Unsound) [short]", |b| {
b.iter(|| {
let mut buf = BytesMut::from(REQ_SHORT);
_original::parse_headers(&mut buf);
})
});
c.bench_function("New (safe) [short]", |b| {
b.iter(|| {
let mut buf = BytesMut::from(REQ_SHORT);
_new::parse_headers(&mut buf);
})
});
c.bench_function("Original (Unsound) [realistic]", |b| {
b.iter(|| {
let mut buf = BytesMut::from(REQ);
_original::parse_headers(&mut buf);
})
});
c.bench_function("New (safe) [realistic]", |b| {
b.iter(|| {
let mut buf = BytesMut::from(REQ);
_new::parse_headers(&mut buf);
})
});
}
criterion_group!(benches, bench_header_parsing);
criterion_main!(benches);
const MAX_HEADERS: usize = 96;
const EMPTY_HEADER_ARRAY: [httparse::Header<'static>; MAX_HEADERS] =
[httparse::EMPTY_HEADER; MAX_HEADERS];
#[derive(Clone, Copy)]
struct HeaderIndex {
name: (usize, usize),
value: (usize, usize),
}
const EMPTY_HEADER_INDEX: HeaderIndex = HeaderIndex {
name: (0, 0),
value: (0, 0),
};
const EMPTY_HEADER_INDEX_ARRAY: [HeaderIndex; MAX_HEADERS] = [EMPTY_HEADER_INDEX; MAX_HEADERS];
impl HeaderIndex {
fn record(bytes: &[u8], headers: &[httparse::Header<'_>], indices: &mut [HeaderIndex]) {
let bytes_ptr = bytes.as_ptr() as usize;
for (header, indices) in headers.iter().zip(indices.iter_mut()) {
let name_start = header.name.as_ptr() as usize - bytes_ptr;
let name_end = name_start + header.name.len();
indices.name = (name_start, name_end);
let value_start = header.value.as_ptr() as usize - bytes_ptr;
let value_end = value_start + header.value.len();
indices.value = (value_start, value_end);
}
}
}
// test cases taken from:
// https://github.com/seanmonstar/httparse/blob/master/benches/parse.rs
const REQ_SHORT: &[u8] = b"\
GET / HTTP/1.0\r\n\
Host: example.com\r\n\
Cookie: session=60; user_id=1\r\n\r\n";
const REQ: &[u8] = b"\
GET /wp-content/uploads/2010/03/hello-kitty-darth-vader-pink.jpg HTTP/1.1\r\n\
Host: www.kittyhell.com\r\n\
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; ja-JP-mac; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 Pathtraq/0.9\r\n\
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n\
Accept-Language: ja,en-us;q=0.7,en;q=0.3\r\n\
Accept-Encoding: gzip,deflate\r\n\
Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7\r\n\
Keep-Alive: 115\r\n\
Connection: keep-alive\r\n\
Cookie: wp_ozh_wsa_visits=2; wp_ozh_wsa_visit_lasttime=xxxxxxxxxx; __utma=xxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.x; __utmz=xxxxxxxxx.xxxxxxxxxx.x.x.utmccn=(referral)|utmcsr=reader.livedoor.com|utmcct=/reader/|utmcmd=referral|padding=under256\r\n\r\n";
mod _new {
use super::*;
pub fn parse_headers(src: &mut BytesMut) -> usize {
let mut headers: [HeaderIndex; MAX_HEADERS] = EMPTY_HEADER_INDEX_ARRAY;
let mut parsed: [httparse::Header<'_>; MAX_HEADERS] = EMPTY_HEADER_ARRAY;
let mut req = httparse::Request::new(&mut parsed);
match req.parse(src).unwrap() {
httparse::Status::Complete(_len) => {
HeaderIndex::record(src, req.headers, &mut headers);
req.headers.len()
}
_ => unreachable!(),
}
}
}
mod _original {
use super::*;
use std::mem::MaybeUninit;
pub fn parse_headers(src: &mut BytesMut) -> usize {
#![allow(invalid_value, clippy::uninit_assumed_init)]
let mut headers: [HeaderIndex; MAX_HEADERS] =
unsafe { MaybeUninit::uninit().assume_init() };
#[allow(invalid_value)]
let mut parsed: [httparse::Header<'_>; MAX_HEADERS] =
unsafe { MaybeUninit::uninit().assume_init() };
let mut req = httparse::Request::new(&mut parsed);
match req.parse(src).unwrap() {
httparse::Status::Complete(_len) => {
HeaderIndex::record(src, req.headers, &mut headers);
req.headers.len()
}
_ => unreachable!(),
}
}
}

View file

@ -1,93 +0,0 @@
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
fn bench_write_camel_case(c: &mut Criterion) {
let mut group = c.benchmark_group("write_camel_case");
let names = ["connection", "Transfer-Encoding", "transfer-encoding"];
for &i in &names {
let bts = i.as_bytes();
group.bench_with_input(BenchmarkId::new("Original", i), bts, |b, bts| {
b.iter(|| {
let mut buf = black_box([0; 24]);
_original::write_camel_case(black_box(bts), &mut buf)
});
});
group.bench_with_input(BenchmarkId::new("New", i), bts, |b, bts| {
b.iter(|| {
let mut buf = black_box([0; 24]);
let len = black_box(bts.len());
_new::write_camel_case(black_box(bts), buf.as_mut_ptr(), len)
});
});
}
group.finish();
}
criterion_group!(benches, bench_write_camel_case);
criterion_main!(benches);
mod _new {
pub fn write_camel_case(value: &[u8], buf: *mut u8, len: usize) {
// first copy entire (potentially wrong) slice to output
let buffer = unsafe {
std::ptr::copy_nonoverlapping(value.as_ptr(), buf, len);
std::slice::from_raw_parts_mut(buf, len)
};
let mut iter = value.iter();
// first character should be uppercase
if let Some(c @ b'a'..=b'z') = iter.next() {
buffer[0] = c & 0b1101_1111;
}
// track 1 ahead of the current position since that's the location being assigned to
let mut index = 2;
// remaining characters after hyphens should also be uppercase
while let Some(&c) = iter.next() {
if c == b'-' {
// advance iter by one and uppercase if needed
if let Some(c @ b'a'..=b'z') = iter.next() {
buffer[index] = c & 0b1101_1111;
}
}
index += 1;
}
}
}
mod _original {
pub fn write_camel_case(value: &[u8], buffer: &mut [u8]) {
let mut index = 0;
let key = value;
let mut key_iter = key.iter();
if let Some(c) = key_iter.next() {
if *c >= b'a' && *c <= b'z' {
buffer[index] = *c ^ b' ';
index += 1;
}
} else {
return;
}
while let Some(c) = key_iter.next() {
buffer[index] = *c;
index += 1;
if *c == b'-' {
if let Some(c) = key_iter.next() {
if *c >= b'a' && *c <= b'z' {
buffer[index] = *c ^ b' ';
index += 1;
}
}
}
}
}
}

View file

@ -8,7 +8,7 @@
use std::{convert::Infallible, io};
use actix_http::{HttpService, Request, Response, StatusCode};
use actix_http::{body::BodyStream, HttpService, Request, Response, StatusCode};
use actix_server::Server;
#[tokio::main(flavor = "current_thread")]
@ -19,7 +19,12 @@ async fn main() -> io::Result<()> {
.bind("h2c-detect", ("127.0.0.1", 8080), || {
HttpService::build()
.finish(|_req: Request| async move {
Ok::<_, Infallible>(Response::build(StatusCode::OK).body("Hello!"))
Ok::<_, Infallible>(Response::build(StatusCode::OK).body(BodyStream::new(
futures_util::stream::iter([
Ok::<_, String>("123".into()),
Err("wertyuikmnbvcxdfty6t".to_owned()),
]),
)))
})
.tcp_auto_h2c()
})?

View file

@ -23,10 +23,7 @@ async fn main() -> io::Result<()> {
res.insert_header(("x-head", HeaderValue::from_static("dummy value!")));
let forty_two = req.conn_data::<u32>().unwrap().to_string();
res.insert_header((
"x-forty-two",
HeaderValue::from_str(&forty_two).unwrap(),
));
res.insert_header(("x-forty-two", HeaderValue::from_str(&forty_two).unwrap()));
Ok::<_, Infallible>(res.body("Hello world!"))
})

View file

@ -0,0 +1,76 @@
//! Demonstrates TLS configuration (via Rustls) for HTTP/1.1 and HTTP/2 connections.
//!
//! Test using cURL:
//!
//! ```console
//! $ curl --insecure https://127.0.0.1:8443
//! Hello World!
//! Protocol: HTTP/2.0
//!
//! $ curl --insecure --http1.1 https://127.0.0.1:8443
//! Hello World!
//! Protocol: HTTP/1.1
//! ```
extern crate tls_rustls_023 as rustls;
use std::io;
use actix_http::{Error, HttpService, Request, Response};
use actix_utils::future::ok;
#[actix_rt::main]
async fn main() -> io::Result<()> {
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
tracing::info!("starting HTTP server at https://127.0.0.1:8443");
actix_server::Server::build()
.bind("echo", ("127.0.0.1", 8443), || {
HttpService::build()
.finish(|req: Request| {
let body = format!(
"Hello World!\n\
Protocol: {:?}",
req.head().version
);
ok::<_, Error>(Response::ok().set_body(body))
})
.rustls_0_23(rustls_config())
})?
.run()
.await
}
fn rustls_config() -> rustls::ServerConfig {
let rcgen::CertifiedKey { cert, key_pair } =
rcgen::generate_simple_self_signed(["localhost".to_owned()]).unwrap();
let cert_file = cert.pem();
let key_file = key_pair.serialize_pem();
let cert_file = &mut io::BufReader::new(cert_file.as_bytes());
let key_file = &mut io::BufReader::new(key_file.as_bytes());
let cert_chain = rustls_pemfile::certs(cert_file)
.collect::<Result<Vec<_>, _>>()
.unwrap();
let mut keys = rustls_pemfile::pkcs8_private_keys(key_file)
.collect::<Result<Vec<_>, _>>()
.unwrap();
let mut config = rustls::ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(
cert_chain,
rustls::pki_types::PrivateKeyDer::Pkcs8(keys.remove(0)),
)
.unwrap();
const H1_ALPN: &[u8] = b"http/1.1";
const H2_ALPN: &[u8] = b"h2";
config.alpn_protocols.push(H2_ALPN.to_vec());
config.alpn_protocols.push(H1_ALPN.to_vec());
config
}

View file

@ -1,7 +1,7 @@
//! Sets up a WebSocket server over TCP and TLS.
//! Sends a heartbeat message every 4 seconds but does not respond to any incoming frames.
extern crate tls_rustls as rustls;
extern crate tls_rustls_023 as rustls;
use std::{
io,
@ -28,7 +28,9 @@ async fn main() -> io::Result<()> {
HttpService::build().h1(handler).tcp()
})?
.bind("tls", ("127.0.0.1", 8443), || {
HttpService::build().finish(handler).rustls(tls_config())
HttpService::build()
.finish(handler)
.rustls_0_23(tls_config())
})?
.run()
.await
@ -83,27 +85,27 @@ impl Stream for Heartbeat {
fn tls_config() -> rustls::ServerConfig {
use std::io::BufReader;
use rustls::{Certificate, PrivateKey};
use rustls_pemfile::{certs, pkcs8_private_keys};
let cert = rcgen::generate_simple_self_signed(vec!["localhost".to_owned()]).unwrap();
let cert_file = cert.serialize_pem().unwrap();
let key_file = cert.serialize_private_key_pem();
let rcgen::CertifiedKey { cert, key_pair } =
rcgen::generate_simple_self_signed(["localhost".to_owned()]).unwrap();
let cert_file = cert.pem();
let key_file = key_pair.serialize_pem();
let cert_file = &mut BufReader::new(cert_file.as_bytes());
let key_file = &mut BufReader::new(key_file.as_bytes());
let cert_chain = certs(cert_file)
.unwrap()
.into_iter()
.map(Certificate)
.collect();
let mut keys = pkcs8_private_keys(key_file).unwrap();
let cert_chain = certs(cert_file).collect::<Result<Vec<_>, _>>().unwrap();
let mut keys = pkcs8_private_keys(key_file)
.collect::<Result<Vec<_>, _>>()
.unwrap();
let mut config = rustls::ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth()
.with_single_cert(cert_chain, PrivateKey(keys.remove(0)))
.with_single_cert(
cert_chain,
rustls::pki_types::PrivateKeyDer::Pkcs8(keys.remove(0)),
)
.unwrap();
config.alpn_protocols.push(b"http/1.1".to_vec());

View file

@ -47,9 +47,8 @@ where
/// Attempts to pull out the next value of the underlying [`Stream`].
///
/// Empty values are skipped to prevent [`BodyStream`]'s transmission being
/// ended on a zero-length chunk, but rather proceed until the underlying
/// [`Stream`] ends.
/// Empty values are skipped to prevent [`BodyStream`]'s transmission being ended on a
/// zero-length chunk, but rather proceed until the underlying [`Stream`] ends.
fn poll_next(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,

View file

@ -77,12 +77,8 @@ impl MessageBody for BoxBody {
cx: &mut Context<'_>,
) -> Poll<Option<Result<Bytes, Self::Error>>> {
match &mut self.0 {
BoxBodyInner::None(body) => {
Pin::new(body).poll_next(cx).map_err(|err| match err {})
}
BoxBodyInner::Bytes(body) => {
Pin::new(body).poll_next(cx).map_err(|err| match err {})
}
BoxBodyInner::None(body) => Pin::new(body).poll_next(cx).map_err(|err| match err {}),
BoxBodyInner::Bytes(body) => Pin::new(body).poll_next(cx).map_err(|err| match err {}),
BoxBodyInner::Stream(body) => Pin::new(body).poll_next(cx),
}
}
@ -104,7 +100,6 @@ impl MessageBody for BoxBody {
#[cfg(test)]
mod tests {
use static_assertions::{assert_impl_all, assert_not_impl_any};
use super::*;

View file

@ -531,7 +531,6 @@ where
mod tests {
use actix_rt::pin;
use actix_utils::future::poll_fn;
use bytes::{Bytes, BytesMut};
use futures_util::stream;
use super::*;
@ -555,6 +554,7 @@ mod tests {
};
}
#[allow(unused_allocation)] // triggered by `Box::new(()).size()`
#[actix_rt::test]
async fn boxing_equivalence() {
assert_eq!(().size(), BodySize::Sized(0));

View file

@ -14,12 +14,14 @@ mod size;
mod sized_stream;
mod utils;
pub use self::body_stream::BodyStream;
pub use self::boxed::BoxBody;
pub use self::either::EitherBody;
pub use self::message_body::MessageBody;
pub(crate) use self::message_body::MessageBodyMapErr;
pub use self::none::None;
pub use self::size::BodySize;
pub use self::sized_stream::SizedStream;
pub use self::utils::to_bytes;
pub use self::{
body_stream::BodyStream,
boxed::BoxBody,
either::EitherBody,
message_body::MessageBody,
none::None,
size::BodySize,
sized_stream::SizedStream,
utils::{to_bytes, to_bytes_limited, BodyLimitExceeded},
};

View file

@ -3,75 +3,196 @@ use std::task::Poll;
use actix_rt::pin;
use actix_utils::future::poll_fn;
use bytes::{Bytes, BytesMut};
use derive_more::{Display, Error};
use futures_core::ready;
use super::{BodySize, MessageBody};
/// Collects the body produced by a `MessageBody` implementation into `Bytes`.
/// Collects all the bytes produced by `body`.
///
/// Any errors produced by the body stream are returned immediately.
///
/// Consider using [`to_bytes_limited`] instead to protect against memory exhaustion.
///
/// # Examples
///
/// ```
/// use actix_http::body::{self, to_bytes};
/// use bytes::Bytes;
///
/// # async fn test_to_bytes() {
/// # actix_rt::System::new().block_on(async {
/// let body = body::None::new();
/// let bytes = to_bytes(body).await.unwrap();
/// assert!(bytes.is_empty());
///
/// let body = Bytes::from_static(b"123");
/// let bytes = to_bytes(body).await.unwrap();
/// assert_eq!(bytes, b"123"[..]);
/// # }
/// assert_eq!(bytes, "123");
/// # });
/// ```
pub async fn to_bytes<B: MessageBody>(body: B) -> Result<Bytes, B::Error> {
to_bytes_limited(body, usize::MAX)
.await
.expect("body should never yield more than usize::MAX bytes")
}
/// Error type returned from [`to_bytes_limited`] when body produced exceeds limit.
#[derive(Debug, Display, Error)]
#[display(fmt = "limit exceeded while collecting body bytes")]
#[non_exhaustive]
pub struct BodyLimitExceeded;
/// Collects the bytes produced by `body`, up to `limit` bytes.
///
/// If a chunk read from `poll_next` causes the total number of bytes read to exceed `limit`, an
/// `Err(BodyLimitExceeded)` is returned.
///
/// Any errors produced by the body stream are returned immediately as `Ok(Err(B::Error))`.
///
/// # Examples
///
/// ```
/// use actix_http::body::{self, to_bytes_limited};
/// use bytes::Bytes;
///
/// # actix_rt::System::new().block_on(async {
/// let body = body::None::new();
/// let bytes = to_bytes_limited(body, 10).await.unwrap().unwrap();
/// assert!(bytes.is_empty());
///
/// let body = Bytes::from_static(b"123");
/// let bytes = to_bytes_limited(body, 10).await.unwrap().unwrap();
/// assert_eq!(bytes, "123");
///
/// let body = Bytes::from_static(b"123");
/// assert!(to_bytes_limited(body, 2).await.is_err());
/// # });
/// ```
pub async fn to_bytes_limited<B: MessageBody>(
body: B,
limit: usize,
) -> Result<Result<Bytes, B::Error>, BodyLimitExceeded> {
/// Sensible default (32kB) for initial, bounded allocation when collecting body bytes.
const INITIAL_ALLOC_BYTES: usize = 32 * 1024;
let cap = match body.size() {
BodySize::None | BodySize::Sized(0) => return Ok(Bytes::new()),
BodySize::Sized(size) => size as usize,
// good enough first guess for chunk size
BodySize::Stream => 32_768,
BodySize::None | BodySize::Sized(0) => return Ok(Ok(Bytes::new())),
BodySize::Sized(size) if size as usize > limit => return Err(BodyLimitExceeded),
BodySize::Sized(size) => (size as usize).min(INITIAL_ALLOC_BYTES),
BodySize::Stream => INITIAL_ALLOC_BYTES,
};
let mut exceeded_limit = false;
let mut buf = BytesMut::with_capacity(cap);
pin!(body);
poll_fn(|cx| loop {
match poll_fn(|cx| loop {
let body = body.as_mut();
match ready!(body.poll_next(cx)) {
Some(Ok(bytes)) => buf.extend_from_slice(&bytes),
Some(Ok(bytes)) => {
// if limit is exceeded...
if buf.len() + bytes.len() > limit {
// ...set flag to true and break out of poll_fn
exceeded_limit = true;
return Poll::Ready(Ok(()));
}
buf.extend_from_slice(&bytes)
}
None => return Poll::Ready(Ok(())),
Some(Err(err)) => return Poll::Ready(Err(err)),
}
})
.await?;
.await
{
// propagate error returned from body poll
Err(err) => Ok(Err(err)),
Ok(buf.freeze())
// limit was exceeded while reading body
Ok(()) if exceeded_limit => Err(BodyLimitExceeded),
// otherwise return body buffer
Ok(()) => Ok(Ok(buf.freeze())),
}
}
#[cfg(test)]
mod test {
mod tests {
use std::io;
use futures_util::{stream, StreamExt as _};
use super::*;
use crate::{body::BodyStream, Error};
use crate::{
body::{BodyStream, SizedStream},
Error,
};
#[actix_rt::test]
async fn test_to_bytes() {
async fn to_bytes_complete() {
let bytes = to_bytes(()).await.unwrap();
assert!(bytes.is_empty());
let body = Bytes::from_static(b"123");
let bytes = to_bytes(body).await.unwrap();
assert_eq!(bytes, b"123"[..]);
}
#[actix_rt::test]
async fn to_bytes_streams() {
let stream = stream::iter(vec![Bytes::from_static(b"123"), Bytes::from_static(b"abc")])
.map(Ok::<_, Error>);
let body = BodyStream::new(stream);
let bytes = to_bytes(body).await.unwrap();
assert_eq!(bytes, b"123abc"[..]);
}
#[actix_rt::test]
async fn to_bytes_limited_complete() {
let bytes = to_bytes_limited((), 0).await.unwrap().unwrap();
assert!(bytes.is_empty());
let bytes = to_bytes_limited((), 1).await.unwrap().unwrap();
assert!(bytes.is_empty());
assert!(to_bytes_limited(Bytes::from_static(b"12"), 0)
.await
.is_err());
assert!(to_bytes_limited(Bytes::from_static(b"12"), 1)
.await
.is_err());
assert!(to_bytes_limited(Bytes::from_static(b"12"), 2).await.is_ok());
assert!(to_bytes_limited(Bytes::from_static(b"12"), 3).await.is_ok());
}
#[actix_rt::test]
async fn to_bytes_limited_streams() {
// hinting a larger body fails
let body = SizedStream::new(8, stream::empty().map(Ok::<_, Error>));
assert!(to_bytes_limited(body, 3).await.is_err());
// hinting a smaller body is okay
let body = SizedStream::new(3, stream::empty().map(Ok::<_, Error>));
assert!(to_bytes_limited(body, 3).await.unwrap().unwrap().is_empty());
// hinting a smaller body then returning a larger one fails
let stream = stream::iter(vec![Bytes::from_static(b"1234")]).map(Ok::<_, Error>);
let body = SizedStream::new(3, stream);
assert!(to_bytes_limited(body, 3).await.is_err());
let stream = stream::iter(vec![Bytes::from_static(b"123"), Bytes::from_static(b"abc")])
.map(Ok::<_, Error>);
let body = BodyStream::new(stream);
assert!(to_bytes_limited(body, 3).await.is_err());
}
#[actix_rt::test]
async fn to_body_limit_error() {
let err_stream = stream::once(async { Err(io::Error::new(io::ErrorKind::Other, "")) });
let body = SizedStream::new(8, err_stream);
// not too big, but propagates error from body stream
assert!(to_bytes_limited(body, 10).await.unwrap().is_err());
}
}

View file

@ -211,7 +211,6 @@ where
/// Finish service configuration and create a service for the HTTP/2 protocol.
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub fn h2<F, B>(self, service: F) -> crate::h2::H2Service<T, S, B>
where
F: IntoServiceFactory<S, Request>,

View file

@ -132,15 +132,15 @@ impl ServiceConfig {
#[cfg(test)]
mod tests {
use super::*;
use crate::{date::DATE_VALUE_LENGTH, notify_on_drop};
use actix_rt::{
task::yield_now,
time::{sleep, sleep_until},
};
use memchr::memmem;
use super::*;
use crate::{date::DATE_VALUE_LENGTH, notify_on_drop};
#[actix_rt::test]
async fn test_date_service_update() {
let settings =

View file

@ -28,7 +28,7 @@ impl Date {
fn update(&mut self) {
self.pos = 0;
write!(self, "{}", httpdate::fmt_http_date(SystemTime::now())).unwrap();
write!(self, "{}", httpdate::HttpDate::from(SystemTime::now())).unwrap();
}
}

View file

@ -9,11 +9,9 @@ use std::{
use actix_rt::task::{spawn_blocking, JoinHandle};
use bytes::Bytes;
use futures_core::{ready, Stream};
#[cfg(feature = "compress-gzip")]
use flate2::write::{GzDecoder, ZlibDecoder};
use futures_core::{ready, Stream};
#[cfg(feature = "compress-zstd")]
use zstd::stream::write::Decoder as ZstdDecoder;
@ -49,9 +47,9 @@ where
))),
#[cfg(feature = "compress-gzip")]
ContentEncoding::Deflate => Some(ContentDecoder::Deflate(Box::new(
ZlibDecoder::new(Writer::new()),
))),
ContentEncoding::Deflate => Some(ContentDecoder::Deflate(Box::new(ZlibDecoder::new(
Writer::new(),
)))),
#[cfg(feature = "compress-gzip")]
ContentEncoding::Gzip => Some(ContentDecoder::Gzip(Box::new(GzDecoder::new(
@ -193,7 +191,7 @@ impl ContentDecoder {
Ok(None)
}
}
Err(e) => Err(e),
Err(err) => Err(err),
},
#[cfg(feature = "compress-gzip")]
@ -207,7 +205,7 @@ impl ContentDecoder {
Ok(None)
}
}
Err(e) => Err(e),
Err(err) => Err(err),
},
#[cfg(feature = "compress-gzip")]
@ -220,7 +218,7 @@ impl ContentDecoder {
Ok(None)
}
}
Err(e) => Err(e),
Err(err) => Err(err),
},
#[cfg(feature = "compress-zstd")]
@ -233,7 +231,7 @@ impl ContentDecoder {
Ok(None)
}
}
Err(e) => Err(e),
Err(err) => Err(err),
},
}
}
@ -252,7 +250,7 @@ impl ContentDecoder {
Ok(None)
}
}
Err(e) => Err(e),
Err(err) => Err(err),
},
#[cfg(feature = "compress-gzip")]
@ -267,7 +265,7 @@ impl ContentDecoder {
Ok(None)
}
}
Err(e) => Err(e),
Err(err) => Err(err),
},
#[cfg(feature = "compress-gzip")]
@ -282,7 +280,7 @@ impl ContentDecoder {
Ok(None)
}
}
Err(e) => Err(e),
Err(err) => Err(err),
},
#[cfg(feature = "compress-zstd")]
@ -297,7 +295,7 @@ impl ContentDecoder {
Ok(None)
}
}
Err(e) => Err(e),
Err(err) => Err(err),
},
}
}

View file

@ -11,12 +11,10 @@ use std::{
use actix_rt::task::{spawn_blocking, JoinHandle};
use bytes::Bytes;
use derive_more::Display;
use futures_core::ready;
use pin_project_lite::pin_project;
#[cfg(feature = "compress-gzip")]
use flate2::write::{GzEncoder, ZlibEncoder};
use futures_core::ready;
use pin_project_lite::pin_project;
use tracing::trace;
#[cfg(feature = "compress-zstd")]
use zstd::stream::write::Encoder as ZstdEncoder;
@ -52,10 +50,21 @@ impl<B: MessageBody> Encoder<B> {
}
}
fn empty() -> Self {
Encoder {
body: EncoderBody::Full { body: Bytes::new() },
encoder: None,
fut: None,
eof: true,
}
}
pub fn response(encoding: ContentEncoding, head: &mut ResponseHead, body: B) -> Self {
// no need to compress an empty body
if matches!(body.size(), BodySize::None) {
return Self::none();
// no need to compress empty bodies
match body.size() {
BodySize::None => return Self::none(),
BodySize::Sized(0) => return Self::empty(),
_ => {}
}
let should_encode = !(head.headers().contains_key(&CONTENT_ENCODING)

View file

@ -7,13 +7,12 @@ use bytes::{Bytes, BytesMut};
mod decoder;
mod encoder;
pub use self::decoder::Decoder;
pub use self::encoder::Encoder;
pub use self::{decoder::Decoder, encoder::Encoder};
/// Special-purpose writer for streaming (de-)compression.
///
/// Pre-allocates 8KiB of capacity.
pub(self) struct Writer {
struct Writer {
buf: BytesMut,
}

View file

@ -3,12 +3,11 @@
use std::{error::Error as StdError, fmt, io, str::Utf8Error, string::FromUtf8Error};
use derive_more::{Display, Error, From};
pub use http::{status::InvalidStatusCode, Error as HttpError};
use http::{uri::InvalidUri, StatusCode};
use crate::{body::BoxBody, Response};
pub use http::Error as HttpError;
pub struct Error {
inner: Box<ErrorInner>,
}
@ -161,44 +160,44 @@ impl From<crate::ws::ProtocolError> for Error {
#[non_exhaustive]
pub enum ParseError {
/// An invalid `Method`, such as `GE.T`.
#[display(fmt = "Invalid Method specified")]
#[display(fmt = "invalid method specified")]
Method,
/// An invalid `Uri`, such as `exam ple.domain`.
#[display(fmt = "Uri error: {}", _0)]
#[display(fmt = "URI error: {}", _0)]
Uri(InvalidUri),
/// An invalid `HttpVersion`, such as `HTP/1.1`
#[display(fmt = "Invalid HTTP version specified")]
#[display(fmt = "invalid HTTP version specified")]
Version,
/// An invalid `Header`.
#[display(fmt = "Invalid Header provided")]
#[display(fmt = "invalid Header provided")]
Header,
/// A message head is too large to be reasonable.
#[display(fmt = "Message head is too large")]
#[display(fmt = "message head is too large")]
TooLarge,
/// A message reached EOF, but is not complete.
#[display(fmt = "Message is incomplete")]
#[display(fmt = "message is incomplete")]
Incomplete,
/// An invalid `Status`, such as `1337 ELITE`.
#[display(fmt = "Invalid Status provided")]
#[display(fmt = "invalid status provided")]
Status,
/// A timeout occurred waiting for an IO event.
#[allow(dead_code)]
#[display(fmt = "Timeout")]
#[display(fmt = "timeout")]
Timeout,
/// An `io::Error` that occurred while trying to read or write to a network stream.
#[display(fmt = "IO error: {}", _0)]
/// An I/O error that occurred while trying to read or write to a network stream.
#[display(fmt = "I/O error: {}", _0)]
Io(io::Error),
/// Parsing a field as string failed.
#[display(fmt = "UTF8 error: {}", _0)]
#[display(fmt = "UTF-8 error: {}", _0)]
Utf8(Utf8Error),
}
@ -257,22 +256,19 @@ impl From<ParseError> for Response<BoxBody> {
#[non_exhaustive]
pub enum PayloadError {
/// A payload reached EOF, but is not complete.
#[display(
fmt = "A payload reached EOF, but is not complete. Inner error: {:?}",
_0
)]
#[display(fmt = "payload reached EOF before completing: {:?}", _0)]
Incomplete(Option<io::Error>),
/// Content encoding stream corruption.
#[display(fmt = "Can not decode content-encoding.")]
#[display(fmt = "can not decode content-encoding")]
EncodingCorrupted,
/// Payload reached size limit.
#[display(fmt = "Payload reached size limit.")]
#[display(fmt = "payload reached size limit")]
Overflow,
/// Payload length is unknown.
#[display(fmt = "Payload length is unknown.")]
#[display(fmt = "payload length is unknown")]
UnknownLength,
/// HTTP/2 payload error.
@ -294,7 +290,6 @@ impl std::error::Error for PayloadError {
PayloadError::Overflow => None,
PayloadError::UnknownLength => None,
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
PayloadError::Http2Payload(err) => Some(err),
PayloadError::Io(err) => Some(err),
}
@ -331,44 +326,44 @@ impl From<PayloadError> for Error {
#[non_exhaustive]
pub enum DispatchError {
/// Service error.
#[display(fmt = "Service Error")]
#[display(fmt = "service error")]
Service(Response<BoxBody>),
/// Body streaming error.
#[display(fmt = "Body error: {}", _0)]
#[display(fmt = "body error: {}", _0)]
Body(Box<dyn StdError>),
/// Upgrade service error.
#[display(fmt = "upgrade error")]
Upgrade,
/// An `io::Error` that occurred while trying to read or write to a network stream.
#[display(fmt = "IO error: {}", _0)]
#[display(fmt = "I/O error: {}", _0)]
Io(io::Error),
/// Request parse error.
#[display(fmt = "Request parse error: {}", _0)]
#[display(fmt = "request parse error: {}", _0)]
Parse(ParseError),
/// HTTP/2 error.
#[display(fmt = "{}", _0)]
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
H2(h2::Error),
/// The first request did not complete within the specified timeout.
#[display(fmt = "The first request did not complete within the specified timeout")]
#[display(fmt = "request did not complete within the specified timeout")]
SlowRequestTimeout,
/// Disconnect timeout. Makes sense for ssl streams.
#[display(fmt = "Connection shutdown timeout")]
/// Disconnect timeout. Makes sense for TLS streams.
#[display(fmt = "connection shutdown timeout")]
DisconnectTimeout,
/// Handler dropped payload before reading EOF.
#[display(fmt = "Handler dropped payload before reading EOF")]
#[display(fmt = "handler dropped payload before reading EOF")]
HandlerDroppedPayload,
/// Internal error.
#[display(fmt = "Internal error")]
#[display(fmt = "internal error")]
InternalError,
}
@ -393,20 +388,18 @@ impl StdError for DispatchError {
#[cfg_attr(test, derive(PartialEq, Eq))]
#[non_exhaustive]
pub enum ContentTypeError {
/// Can not parse content type
#[display(fmt = "Can not parse content type")]
/// Can not parse content type.
#[display(fmt = "could not parse content type")]
ParseError,
/// Unknown content encoding
#[display(fmt = "Unknown content encoding")]
/// Unknown content encoding.
#[display(fmt = "unknown content encoding")]
UnknownEncoding,
}
#[cfg(test)]
mod tests {
use std::io;
use http::{Error as HttpError, StatusCode};
use http::Error as HttpError;
use super::*;
@ -426,7 +419,7 @@ mod tests {
let err: Error = ParseError::Io(orig).into();
assert_eq!(
format!("{}", err),
"error parsing HTTP message: IO error: other"
"error parsing HTTP message: I/O error: other"
);
}
@ -453,7 +446,7 @@ mod tests {
let err = PayloadError::Incomplete(None);
assert_eq!(
err.to_string(),
"A payload reached EOF, but is not complete. Inner error: None"
"payload reached EOF before completing: None"
);
}
@ -473,7 +466,7 @@ mod tests {
match ParseError::from($from) {
e @ $error => {
let desc = format!("{}", e);
assert_eq!(desc, format!("IO error: {}", $from));
assert_eq!(desc, format!("I/O error: {}", $from));
}
_ => unreachable!("{:?}", $from),
}

View file

@ -16,6 +16,7 @@ use crate::{
};
bitflags! {
#[derive(Debug, Clone, Copy)]
struct Flags: u8 {
const HEAD = 0b0000_0001;
const KEEP_ALIVE_ENABLED = 0b0000_1000;

View file

@ -9,11 +9,10 @@ use super::{
decoder::{self, PayloadDecoder, PayloadItem, PayloadType},
encoder, Message, MessageType,
};
use crate::{
body::BodySize, error::ParseError, ConnectionType, Request, Response, ServiceConfig,
};
use crate::{body::BodySize, error::ParseError, ConnectionType, Request, Response, ServiceConfig};
bitflags! {
#[derive(Debug, Clone, Copy)]
struct Flags: u8 {
const HEAD = 0b0000_0001;
const KEEP_ALIVE_ENABLED = 0b0000_0010;
@ -199,9 +198,6 @@ impl Encoder<Message<(Response<()>, BodySize)>> for Codec {
#[cfg(test)]
mod tests {
use bytes::BytesMut;
use http::Method;
use super::*;
use crate::HttpMessage as _;

View file

@ -1,4 +1,4 @@
use std::{convert::TryFrom, io, marker::PhantomData, mem::MaybeUninit, task::Poll};
use std::{io, marker::PhantomData, mem::MaybeUninit, task::Poll};
use actix_codec::Decoder;
use bytes::{Bytes, BytesMut};
@ -94,9 +94,7 @@ pub(crate) trait MessageType: Sized {
// SAFETY: httparse already checks header value is only visible ASCII bytes
// from_maybe_shared_unchecked contains debug assertions so they are omitted here
let value = unsafe {
HeaderValue::from_maybe_shared_unchecked(
slice.slice(idx.value.0..idx.value.1),
)
HeaderValue::from_maybe_shared_unchecked(slice.slice(idx.value.0..idx.value.1))
};
match name {
@ -275,8 +273,7 @@ impl MessageType for Request {
let mut msg = Request::new();
// convert headers
let mut length =
msg.set_headers(&src.split_to(len).freeze(), &headers[..h_len], ver)?;
let mut length = msg.set_headers(&src.split_to(len).freeze(), &headers[..h_len], ver)?;
// disallow HTTP/1.0 POST requests that do not contain a Content-Length headers
// see https://datatracker.ietf.org/doc/html/rfc1945#section-7.2.2
@ -356,8 +353,8 @@ impl MessageType for ResponseHead {
Version::HTTP_10
};
let status = StatusCode::from_u16(res.code.unwrap())
.map_err(|_| ParseError::Status)?;
let status =
StatusCode::from_u16(res.code.unwrap()).map_err(|_| ParseError::Status)?;
HeaderIndex::record(src, res.headers, &mut headers);
(len, version, status, res.headers.len())
@ -378,8 +375,7 @@ impl MessageType for ResponseHead {
msg.version = ver;
// convert headers
let mut length =
msg.set_headers(&src.split_to(len).freeze(), &headers[..h_len], ver)?;
let mut length = msg.set_headers(&src.split_to(len).freeze(), &headers[..h_len], ver)?;
// Remove CL value if 0 now that all headers and HTTP/1.0 special cases are processed.
// Protects against some request smuggling attacks.
@ -536,7 +532,7 @@ impl Decoder for PayloadDecoder {
*state = match state.step(src, size, &mut buf) {
Poll::Pending => return Ok(None),
Poll::Ready(Ok(state)) => state,
Poll::Ready(Err(e)) => return Err(e),
Poll::Ready(Err(err)) => return Err(err),
};
if *state == ChunkedState::End {
@ -567,15 +563,8 @@ impl Decoder for PayloadDecoder {
#[cfg(test)]
mod tests {
use bytes::{Bytes, BytesMut};
use http::{Method, Version};
use super::*;
use crate::{
error::ParseError,
header::{HeaderName, SET_COOKIE},
HttpMessage as _,
};
use crate::{header::SET_COOKIE, HttpMessage as _};
impl PayloadType {
pub(crate) fn unwrap(self) -> PayloadDecoder {

View file

@ -19,6 +19,13 @@ use tokio::io::{AsyncRead, AsyncWrite};
use tokio_util::codec::{Decoder as _, Encoder as _};
use tracing::{error, trace};
use super::{
codec::Codec,
decoder::MAX_BUFFER_SIZE,
payload::{Payload, PayloadSender, PayloadStatus},
timer::TimerState,
Message, MessageType,
};
use crate::{
body::{BodySize, BoxBody, MessageBody},
config::ServiceConfig,
@ -27,19 +34,12 @@ use crate::{
Error, Extensions, OnConnectData, Request, Response, StatusCode,
};
use super::{
codec::Codec,
decoder::MAX_BUFFER_SIZE,
payload::{Payload, PayloadSender, PayloadStatus},
timer::TimerState,
Message, MessageType,
};
const LW_BUFFER_SIZE: usize = 1024;
const HW_BUFFER_SIZE: usize = 1024 * 8;
const MAX_PIPELINED_MESSAGES: usize = 16;
bitflags! {
#[derive(Debug, Clone, Copy)]
pub struct Flags: u8 {
/// Set when stream is read for first time.
const STARTED = 0b0000_0001;
@ -212,9 +212,7 @@ where
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::None => write!(f, "State::None"),
Self::ExpectCall { .. } => {
f.debug_struct("State::ExpectCall").finish_non_exhaustive()
}
Self::ExpectCall { .. } => f.debug_struct("State::ExpectCall").finish_non_exhaustive(),
Self::ServiceCall { .. } => {
f.debug_struct("State::ServiceCall").finish_non_exhaustive()
}
@ -275,9 +273,7 @@ where
head_timer: TimerState::new(config.client_request_deadline().is_some()),
ka_timer: TimerState::new(config.keep_alive().enabled()),
shutdown_timer: TimerState::new(
config.client_disconnect_deadline().is_some(),
),
shutdown_timer: TimerState::new(config.client_disconnect_deadline().is_some()),
io: Some(io),
read_buf: BytesMut::with_capacity(HW_BUFFER_SIZE),
@ -455,9 +451,7 @@ where
}
// return with upgrade request and poll it exclusively
Some(DispatcherMessage::Upgrade(req)) => {
return Ok(PollResponse::Upgrade(req))
}
Some(DispatcherMessage::Upgrade(req)) => return Ok(PollResponse::Upgrade(req)),
// all messages are dealt with
None => {
@ -518,8 +512,10 @@ where
}
Poll::Ready(Some(Err(err))) => {
let err = err.into();
tracing::error!("Response payload stream error: {err:?}");
this.flags.insert(Flags::FINISHED);
return Err(DispatchError::Body(err.into()));
return Err(DispatchError::Body(err));
}
Poll::Pending => return Ok(PollResponse::DoNothing),
@ -555,6 +551,7 @@ where
}
Poll::Ready(Some(Err(err))) => {
tracing::error!("Response payload stream error: {err:?}");
this.flags.insert(Flags::FINISHED);
return Err(DispatchError::Body(
Error::new_body().with_cause(err).into(),
@ -674,9 +671,7 @@ where
}
_ => {
unreachable!(
"State must be set to ServiceCall or ExceptCall in handle_request"
)
unreachable!("State must be set to ServiceCall or ExceptCall in handle_request")
}
}
}
@ -685,10 +680,7 @@ where
/// Process one incoming request.
///
/// Returns true if any meaningful work was done.
fn poll_request(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Result<bool, DispatchError> {
fn poll_request(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Result<bool, DispatchError> {
let pipeline_queue_full = self.messages.len() >= MAX_PIPELINED_MESSAGES;
let can_not_read = !self.can_read(cx);
@ -714,7 +706,7 @@ where
req.head_mut().peer_addr = *this.peer_addr;
req.conn_data = this.conn_data.as_ref().map(Rc::clone);
req.conn_data.clone_from(this.conn_data);
match this.codec.message_type() {
// request has no payload
@ -858,10 +850,7 @@ where
Ok(())
}
fn poll_ka_timer(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Result<(), DispatchError> {
fn poll_ka_timer(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Result<(), DispatchError> {
let this = self.as_mut().project();
if let TimerState::Active { timer } = this.ka_timer {
debug_assert!(
@ -926,10 +915,7 @@ where
}
/// Poll head, keep-alive, and disconnect timer.
fn poll_timers(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Result<(), DispatchError> {
fn poll_timers(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Result<(), DispatchError> {
self.as_mut().poll_head_timer(cx)?;
self.as_mut().poll_ka_timer(cx)?;
self.as_mut().poll_shutdown_timer(cx)?;
@ -943,10 +929,7 @@ where
/// - `std::io::ErrorKind::ConnectionReset` after partial read;
/// - all data read done.
#[inline(always)] // TODO: bench this inline
fn read_available(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Result<bool, DispatchError> {
fn read_available(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Result<bool, DispatchError> {
let this = self.project();
if this.flags.contains(Flags::READ_DISCONNECT) {

View file

@ -1,14 +1,11 @@
use std::{future::Future, str, task::Poll, time::Duration};
use actix_rt::{pin, time::sleep};
use actix_service::fn_service;
use actix_utils::future::{ready, Ready};
use bytes::Bytes;
use futures_util::future::lazy;
use actix_codec::Framed;
use actix_service::Service;
use bytes::{Buf, BytesMut};
use actix_rt::{pin, time::sleep};
use actix_service::{fn_service, Service};
use actix_utils::future::{ready, Ready};
use bytes::{Buf, Bytes, BytesMut};
use futures_util::future::lazy;
use super::dispatcher::{Dispatcher, DispatcherState, DispatcherStateProj, Flags};
use crate::{
@ -43,8 +40,8 @@ fn status_service(
fn_service(move |_req: Request| ready(Ok::<_, Error>(Response::new(status))))
}
fn echo_path_service(
) -> impl Service<Request, Response = Response<impl MessageBody>, Error = Error> {
fn echo_path_service() -> impl Service<Request, Response = Response<impl MessageBody>, Error = Error>
{
fn_service(|req: Request| {
let path = req.path().as_bytes();
ready(Ok::<_, Error>(
@ -53,8 +50,8 @@ fn echo_path_service(
})
}
fn drop_payload_service(
) -> impl Service<Request, Response = Response<&'static str>, Error = Error> {
fn drop_payload_service() -> impl Service<Request, Response = Response<&'static str>, Error = Error>
{
fn_service(|mut req: Request| async move {
let _ = req.take_payload();
Ok::<_, Error>(Response::with_body(StatusCode::OK, "payload dropped"))
@ -932,7 +929,6 @@ fn http_msg(msg: impl AsRef<str>) -> BytesMut {
.as_ref()
.trim()
.split('\n')
.into_iter()
.map(|line| [line.trim_start(), "\r"].concat())
.collect::<Vec<_>>()
.join("\n");

View file

@ -17,14 +17,16 @@ mod timer;
mod upgrade;
mod utils;
pub use self::client::{ClientCodec, ClientPayloadCodec};
pub use self::codec::Codec;
pub use self::dispatcher::Dispatcher;
pub use self::expect::ExpectHandler;
pub use self::payload::Payload;
pub use self::service::{H1Service, H1ServiceHandler};
pub use self::upgrade::UpgradeHandler;
pub use self::utils::SendResponse;
pub use self::{
client::{ClientCodec, ClientPayloadCodec},
codec::Codec,
dispatcher::Dispatcher,
expect::ExpectHandler,
payload::Payload,
service::{H1Service, H1ServiceHandler},
upgrade::UpgradeHandler,
utils::SendResponse,
};
#[derive(Debug)]
/// Codec message

View file

@ -117,6 +117,7 @@ impl PayloadSender {
}
}
#[allow(clippy::needless_pass_by_ref_mut)]
#[inline]
pub fn need_read(&self, cx: &mut Context<'_>) -> PayloadStatus {
// we check need_read only if Payload (other side) is alive,
@ -174,7 +175,7 @@ impl Inner {
/// Register future waiting data from payload.
/// Waker would be used in `Inner::wake`
fn register(&mut self, cx: &mut Context<'_>) {
fn register(&mut self, cx: &Context<'_>) {
if self
.task
.as_ref()
@ -186,7 +187,7 @@ impl Inner {
// Register future feeding data to payload.
/// Waker would be used in `Inner::wake_io`
fn register_io(&mut self, cx: &mut Context<'_>) {
fn register_io(&mut self, cx: &Context<'_>) {
if self
.io_task
.as_ref()
@ -221,7 +222,7 @@ impl Inner {
fn poll_next(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
cx: &Context<'_>,
) -> Poll<Option<Result<Bytes, PayloadError>>> {
if let Some(data) = self.items.pop_front() {
self.len -= data.len();

View file

@ -15,6 +15,7 @@ use actix_utils::future::ready;
use futures_core::future::LocalBoxFuture;
use tracing::error;
use super::{codec::Codec, dispatcher::Dispatcher, ExpectHandler, UpgradeHandler};
use crate::{
body::{BoxBody, MessageBody},
config::ServiceConfig,
@ -23,8 +24,6 @@ use crate::{
ConnectCallback, OnConnectData, Request, Response,
};
use super::{codec::Codec, dispatcher::Dispatcher, ExpectHandler, UpgradeHandler};
/// `ServiceFactory` implementation for HTTP1 transport
pub struct H1Service<T, S, B, X = ExpectHandler, U = UpgradeHandler> {
srv: S,
@ -82,13 +81,8 @@ where
/// Create simple tcp stream service
pub fn tcp(
self,
) -> impl ServiceFactory<
TcpStream,
Config = (),
Response = (),
Error = DispatchError,
InitError = (),
> {
) -> impl ServiceFactory<TcpStream, Config = (), Response = (), Error = DispatchError, InitError = ()>
{
fn_service(|io: TcpStream| {
let peer_addr = io.peer_addr().ok();
ready(Ok((io, peer_addr)))
@ -99,8 +93,6 @@ where
#[cfg(feature = "openssl")]
mod openssl {
use super::*;
use actix_tls::accept::{
openssl::{
reexports::{Error as SslError, SslAcceptor},
@ -109,6 +101,8 @@ mod openssl {
TlsError,
};
use super::*;
impl<S, B, X, U> H1Service<TlsStream<TcpStream>, S, B, X, U>
where
S: ServiceFactory<Request, Config = ()>,
@ -134,7 +128,6 @@ mod openssl {
U::InitError: fmt::Debug,
{
/// Create OpenSSL based service.
#[cfg_attr(docsrs, doc(cfg(feature = "openssl")))]
pub fn openssl(
self,
acceptor: SslAcceptor,
@ -159,14 +152,13 @@ mod openssl {
}
}
#[cfg(feature = "rustls")]
mod rustls {
#[cfg(feature = "rustls-0_20")]
mod rustls_0_20 {
use std::io;
use actix_service::ServiceFactoryExt as _;
use actix_tls::accept::{
rustls::{reexports::ServerConfig, Acceptor, TlsStream},
rustls_0_20::{reexports::ServerConfig, Acceptor, TlsStream},
TlsError,
};
@ -196,8 +188,7 @@ mod rustls {
U::Error: fmt::Display + Into<Response<BoxBody>>,
U::InitError: fmt::Debug,
{
/// Create Rustls based service.
#[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
/// Create Rustls v0.20 based service.
pub fn rustls(
self,
config: ServerConfig,
@ -222,6 +213,189 @@ mod rustls {
}
}
#[cfg(feature = "rustls-0_21")]
mod rustls_0_21 {
use std::io;
use actix_service::ServiceFactoryExt as _;
use actix_tls::accept::{
rustls_0_21::{reexports::ServerConfig, Acceptor, TlsStream},
TlsError,
};
use super::*;
impl<S, B, X, U> H1Service<TlsStream<TcpStream>, S, B, X, U>
where
S: ServiceFactory<Request, Config = ()>,
S::Future: 'static,
S::Error: Into<Response<BoxBody>>,
S::InitError: fmt::Debug,
S::Response: Into<Response<B>>,
B: MessageBody,
X: ServiceFactory<Request, Config = (), Response = Request>,
X::Future: 'static,
X::Error: Into<Response<BoxBody>>,
X::InitError: fmt::Debug,
U: ServiceFactory<
(Request, Framed<TlsStream<TcpStream>, Codec>),
Config = (),
Response = (),
>,
U::Future: 'static,
U::Error: fmt::Display + Into<Response<BoxBody>>,
U::InitError: fmt::Debug,
{
/// Create Rustls v0.21 based service.
pub fn rustls_021(
self,
config: ServerConfig,
) -> impl ServiceFactory<
TcpStream,
Config = (),
Response = (),
Error = TlsError<io::Error, DispatchError>,
InitError = (),
> {
Acceptor::new(config)
.map_init_err(|_| {
unreachable!("TLS acceptor service factory does not error on init")
})
.map_err(TlsError::into_service_error)
.map(|io: TlsStream<TcpStream>| {
let peer_addr = io.get_ref().0.peer_addr().ok();
(io, peer_addr)
})
.and_then(self.map_err(TlsError::Service))
}
}
}
#[cfg(feature = "rustls-0_22")]
mod rustls_0_22 {
use std::io;
use actix_service::ServiceFactoryExt as _;
use actix_tls::accept::{
rustls_0_22::{reexports::ServerConfig, Acceptor, TlsStream},
TlsError,
};
use super::*;
impl<S, B, X, U> H1Service<TlsStream<TcpStream>, S, B, X, U>
where
S: ServiceFactory<Request, Config = ()>,
S::Future: 'static,
S::Error: Into<Response<BoxBody>>,
S::InitError: fmt::Debug,
S::Response: Into<Response<B>>,
B: MessageBody,
X: ServiceFactory<Request, Config = (), Response = Request>,
X::Future: 'static,
X::Error: Into<Response<BoxBody>>,
X::InitError: fmt::Debug,
U: ServiceFactory<
(Request, Framed<TlsStream<TcpStream>, Codec>),
Config = (),
Response = (),
>,
U::Future: 'static,
U::Error: fmt::Display + Into<Response<BoxBody>>,
U::InitError: fmt::Debug,
{
/// Create Rustls v0.22 based service.
pub fn rustls_0_22(
self,
config: ServerConfig,
) -> impl ServiceFactory<
TcpStream,
Config = (),
Response = (),
Error = TlsError<io::Error, DispatchError>,
InitError = (),
> {
Acceptor::new(config)
.map_init_err(|_| {
unreachable!("TLS acceptor service factory does not error on init")
})
.map_err(TlsError::into_service_error)
.map(|io: TlsStream<TcpStream>| {
let peer_addr = io.get_ref().0.peer_addr().ok();
(io, peer_addr)
})
.and_then(self.map_err(TlsError::Service))
}
}
}
#[cfg(feature = "rustls-0_23")]
mod rustls_0_23 {
use std::io;
use actix_service::ServiceFactoryExt as _;
use actix_tls::accept::{
rustls_0_23::{reexports::ServerConfig, Acceptor, TlsStream},
TlsError,
};
use super::*;
impl<S, B, X, U> H1Service<TlsStream<TcpStream>, S, B, X, U>
where
S: ServiceFactory<Request, Config = ()>,
S::Future: 'static,
S::Error: Into<Response<BoxBody>>,
S::InitError: fmt::Debug,
S::Response: Into<Response<B>>,
B: MessageBody,
X: ServiceFactory<Request, Config = (), Response = Request>,
X::Future: 'static,
X::Error: Into<Response<BoxBody>>,
X::InitError: fmt::Debug,
U: ServiceFactory<
(Request, Framed<TlsStream<TcpStream>, Codec>),
Config = (),
Response = (),
>,
U::Future: 'static,
U::Error: fmt::Display + Into<Response<BoxBody>>,
U::InitError: fmt::Debug,
{
/// Create Rustls v0.23 based service.
pub fn rustls_0_23(
self,
config: ServerConfig,
) -> impl ServiceFactory<
TcpStream,
Config = (),
Response = (),
Error = TlsError<io::Error, DispatchError>,
InitError = (),
> {
Acceptor::new(config)
.map_init_err(|_| {
unreachable!("TLS acceptor service factory does not error on init")
})
.map_err(TlsError::into_service_error)
.map(|io: TlsStream<TcpStream>| {
let peer_addr = io.get_ref().0.peer_addr().ok();
(io, peer_addr)
})
.and_then(self.map_err(TlsError::Service))
}
}
}
impl<T, S, B, X, U> H1Service<T, S, B, X, U>
where
S: ServiceFactory<Request, Config = ()>,

View file

@ -4,7 +4,7 @@ use std::{
future::Future,
marker::PhantomData,
net,
pin::Pin,
pin::{pin, Pin},
rc::Rc,
task::{Context, Poll},
};
@ -20,7 +20,6 @@ use h2::{
Ping, PingPong,
};
use pin_project_lite::pin_project;
use tracing::{error, trace, warn};
use crate::{
body::{BodySize, BoxBody, MessageBody},
@ -127,7 +126,7 @@ where
head.headers = parts.headers.into();
head.peer_addr = this.peer_addr;
req.conn_data = this.conn_data.as_ref().map(Rc::clone);
req.conn_data.clone_from(&this.conn_data);
let fut = this.flow.service.call(req);
let config = this.config.clone();
@ -147,11 +146,13 @@ where
if let Err(err) = res {
match err {
DispatchError::SendResponse(err) => {
trace!("Error sending HTTP/2 response: {:?}", err)
tracing::trace!("Error sending response: {err:?}");
}
DispatchError::SendData(err) => {
tracing::warn!("Send data error: {err:?}");
}
DispatchError::SendData(err) => warn!("{:?}", err),
DispatchError::ResponseBody(err) => {
error!("Response payload stream error: {:?}", err)
tracing::error!("Response payload stream error: {err:?}");
}
}
}
@ -228,9 +229,9 @@ where
return Ok(());
}
// poll response body and send chunks to client
actix_rt::pin!(body);
let mut body = pin!(body);
// poll response body and send chunks to client
while let Some(res) = poll_fn(|cx| body.as_mut().poll_next(cx)).await {
let mut chunk = res.map_err(|err| DispatchError::ResponseBody(err.into()))?;

View file

@ -23,8 +23,7 @@ use crate::{
mod dispatcher;
mod service;
pub use self::dispatcher::Dispatcher;
pub use self::service::H2Service;
pub use self::{dispatcher::Dispatcher, service::H2Service};
/// HTTP/2 peer stream.
pub struct Payload {
@ -58,10 +57,7 @@ impl Stream for Payload {
}
}
pub(crate) fn handshake_with_timeout<T>(
io: T,
config: &ServiceConfig,
) -> HandshakeWithTimeout<T>
pub(crate) fn handshake_with_timeout<T>(io: T, config: &ServiceConfig) -> HandshakeWithTimeout<T>
where
T: AsyncRead + AsyncWrite + Unpin,
{

View file

@ -16,6 +16,7 @@ use actix_utils::future::ready;
use futures_core::{future::LocalBoxFuture, ready};
use tracing::{error, trace};
use super::{dispatcher::Dispatcher, handshake_with_timeout, HandshakeWithTimeout};
use crate::{
body::{BoxBody, MessageBody},
config::ServiceConfig,
@ -24,8 +25,6 @@ use crate::{
ConnectCallback, OnConnectData, Request, Response,
};
use super::{dispatcher::Dispatcher, handshake_with_timeout, HandshakeWithTimeout};
/// `ServiceFactory` implementation for HTTP/2 transport
pub struct H2Service<T, S, B> {
srv: S,
@ -117,7 +116,6 @@ mod openssl {
B: MessageBody + 'static,
{
/// Create OpenSSL based service.
#[cfg_attr(docsrs, doc(cfg(feature = "openssl")))]
pub fn openssl(
self,
acceptor: SslAcceptor,
@ -142,8 +140,8 @@ mod openssl {
}
}
#[cfg(feature = "rustls")]
mod rustls {
#[cfg(feature = "rustls-0_20")]
mod rustls_0_20 {
use std::io;
use actix_service::ServiceFactoryExt as _;
@ -164,8 +162,7 @@ mod rustls {
B: MessageBody + 'static,
{
/// Create Rustls based service.
#[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
/// Create Rustls v0.20 based service.
pub fn rustls(
self,
mut config: ServerConfig,
@ -194,6 +191,159 @@ mod rustls {
}
}
#[cfg(feature = "rustls-0_21")]
mod rustls_0_21 {
use std::io;
use actix_service::ServiceFactoryExt as _;
use actix_tls::accept::{
rustls_0_21::{reexports::ServerConfig, Acceptor, TlsStream},
TlsError,
};
use super::*;
impl<S, B> H2Service<TlsStream<TcpStream>, S, B>
where
S: ServiceFactory<Request, Config = ()>,
S::Future: 'static,
S::Error: Into<Response<BoxBody>> + 'static,
S::Response: Into<Response<B>> + 'static,
<S::Service as Service<Request>>::Future: 'static,
B: MessageBody + 'static,
{
/// Create Rustls v0.21 based service.
pub fn rustls_021(
self,
mut config: ServerConfig,
) -> impl ServiceFactory<
TcpStream,
Config = (),
Response = (),
Error = TlsError<io::Error, DispatchError>,
InitError = S::InitError,
> {
let mut protos = vec![b"h2".to_vec()];
protos.extend_from_slice(&config.alpn_protocols);
config.alpn_protocols = protos;
Acceptor::new(config)
.map_init_err(|_| {
unreachable!("TLS acceptor service factory does not error on init")
})
.map_err(TlsError::into_service_error)
.map(|io: TlsStream<TcpStream>| {
let peer_addr = io.get_ref().0.peer_addr().ok();
(io, peer_addr)
})
.and_then(self.map_err(TlsError::Service))
}
}
}
#[cfg(feature = "rustls-0_22")]
mod rustls_0_22 {
use std::io;
use actix_service::ServiceFactoryExt as _;
use actix_tls::accept::{
rustls_0_22::{reexports::ServerConfig, Acceptor, TlsStream},
TlsError,
};
use super::*;
impl<S, B> H2Service<TlsStream<TcpStream>, S, B>
where
S: ServiceFactory<Request, Config = ()>,
S::Future: 'static,
S::Error: Into<Response<BoxBody>> + 'static,
S::Response: Into<Response<B>> + 'static,
<S::Service as Service<Request>>::Future: 'static,
B: MessageBody + 'static,
{
/// Create Rustls v0.22 based service.
pub fn rustls_0_22(
self,
mut config: ServerConfig,
) -> impl ServiceFactory<
TcpStream,
Config = (),
Response = (),
Error = TlsError<io::Error, DispatchError>,
InitError = S::InitError,
> {
let mut protos = vec![b"h2".to_vec()];
protos.extend_from_slice(&config.alpn_protocols);
config.alpn_protocols = protos;
Acceptor::new(config)
.map_init_err(|_| {
unreachable!("TLS acceptor service factory does not error on init")
})
.map_err(TlsError::into_service_error)
.map(|io: TlsStream<TcpStream>| {
let peer_addr = io.get_ref().0.peer_addr().ok();
(io, peer_addr)
})
.and_then(self.map_err(TlsError::Service))
}
}
}
#[cfg(feature = "rustls-0_23")]
mod rustls_0_23 {
use std::io;
use actix_service::ServiceFactoryExt as _;
use actix_tls::accept::{
rustls_0_23::{reexports::ServerConfig, Acceptor, TlsStream},
TlsError,
};
use super::*;
impl<S, B> H2Service<TlsStream<TcpStream>, S, B>
where
S: ServiceFactory<Request, Config = ()>,
S::Future: 'static,
S::Error: Into<Response<BoxBody>> + 'static,
S::Response: Into<Response<B>> + 'static,
<S::Service as Service<Request>>::Future: 'static,
B: MessageBody + 'static,
{
/// Create Rustls v0.23 based service.
pub fn rustls_0_23(
self,
mut config: ServerConfig,
) -> impl ServiceFactory<
TcpStream,
Config = (),
Response = (),
Error = TlsError<io::Error, DispatchError>,
InitError = S::InitError,
> {
let mut protos = vec![b"h2".to_vec()];
protos.extend_from_slice(&config.alpn_protocols);
config.alpn_protocols = protos;
Acceptor::new(config)
.map_init_err(|_| {
unreachable!("TLS acceptor service factory does not error on init")
})
.map_err(TlsError::into_service_error)
.map(|io: TlsStream<TcpStream>| {
let peer_addr = io.get_ref().0.peer_addr().ok();
(io, peer_addr)
})
.and_then(self.map_err(TlsError::Service))
}
}
}
impl<T, S, B> ServiceFactory<(T, Option<net::SocketAddr>)> for H2Service<T, S, B>
where
T: AsyncRead + AsyncWrite + Unpin + 'static,

View file

@ -8,12 +8,14 @@ use http::header::HeaderName;
/// request.
///
/// See [RFC 9211](https://www.rfc-editor.org/rfc/rfc9211) for full semantics.
// TODO(breaking): replace with http's version
pub const CACHE_STATUS: HeaderName = HeaderName::from_static("cache-status");
/// Response header field that allows origin servers to control the behavior of CDN caches
/// interposed between them and clients separately from other caches that might handle the response.
///
/// See [RFC 9213](https://www.rfc-editor.org/rfc/rfc9213) for full semantics.
// TODO(breaking): replace with http's version
pub const CDN_CACHE_CONTROL: HeaderName = HeaderName::from_static("cdn-cache-control");
/// Response header that prevents a document from loading any cross-origin resources that don't

View file

@ -1,7 +1,5 @@
//! [`TryIntoHeaderPair`] trait and implementations.
use std::convert::TryFrom as _;
use super::{
Header, HeaderName, HeaderValue, InvalidHeaderName, InvalidHeaderValue, TryIntoHeaderValue,
};

View file

@ -1,7 +1,5 @@
//! [`TryIntoHeaderValue`] trait and implementations.
use std::convert::TryFrom as _;
use bytes::Bytes;
use http::{header::InvalidHeaderValue, Error as HttpError, HeaderValue};
use mime::Mime;

View file

@ -636,10 +636,24 @@ impl<'a> IntoIterator for &'a HeaderMap {
}
}
/// Convert `http::HeaderMap` to our `HeaderMap`.
/// Convert a `http::HeaderMap` to our `HeaderMap`.
impl From<http::HeaderMap> for HeaderMap {
fn from(mut map: http::HeaderMap) -> HeaderMap {
HeaderMap::from_drain(map.drain())
fn from(mut map: http::HeaderMap) -> Self {
Self::from_drain(map.drain())
}
}
/// Convert our `HeaderMap` to a `http::HeaderMap`.
impl From<HeaderMap> for http::HeaderMap {
fn from(map: HeaderMap) -> Self {
Self::from_iter(map)
}
}
/// Convert our `&HeaderMap` to a `http::HeaderMap`.
impl From<&HeaderMap> for http::HeaderMap {
fn from(map: &HeaderMap) -> Self {
map.to_owned().into()
}
}
@ -1120,9 +1134,7 @@ mod tests {
assert!(vals.next().is_none());
}
fn owned_pair<'a>(
(name, val): (&'a HeaderName, &'a HeaderValue),
) -> (HeaderName, HeaderValue) {
fn owned_pair<'a>((name, val): (&'a HeaderName, &'a HeaderValue)) -> (HeaderName, HeaderValue) {
(name.clone(), val.clone())
}
}

View file

@ -3,33 +3,30 @@
// declaring new header consts will yield this error
#![allow(clippy::declare_interior_mutable_const)]
use percent_encoding::{AsciiSet, CONTROLS};
// re-export from http except header map related items
pub use ::http::header::{
HeaderName, HeaderValue, InvalidHeaderName, InvalidHeaderValue, ToStrError,
};
// re-export const header names, list is explicit so that any updates to `common` module do not
// conflict with this set
pub use ::http::header::{
ACCEPT, ACCEPT_CHARSET, ACCEPT_ENCODING, ACCEPT_LANGUAGE, ACCEPT_RANGES,
ACCESS_CONTROL_ALLOW_CREDENTIALS, ACCESS_CONTROL_ALLOW_HEADERS,
ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_EXPOSE_HEADERS,
ACCESS_CONTROL_MAX_AGE, ACCESS_CONTROL_REQUEST_HEADERS, ACCESS_CONTROL_REQUEST_METHOD, AGE,
ALLOW, ALT_SVC, AUTHORIZATION, CACHE_CONTROL, CONNECTION, CONTENT_DISPOSITION,
CONTENT_ENCODING, CONTENT_LANGUAGE, CONTENT_LENGTH, CONTENT_LOCATION, CONTENT_RANGE,
CONTENT_SECURITY_POLICY, CONTENT_SECURITY_POLICY_REPORT_ONLY, CONTENT_TYPE, COOKIE, DATE,
DNT, ETAG, EXPECT, EXPIRES, FORWARDED, FROM, HOST, IF_MATCH, IF_MODIFIED_SINCE,
IF_NONE_MATCH, IF_RANGE, IF_UNMODIFIED_SINCE, LAST_MODIFIED, LINK, LOCATION, MAX_FORWARDS,
ORIGIN, PRAGMA, PROXY_AUTHENTICATE, PROXY_AUTHORIZATION, PUBLIC_KEY_PINS,
PUBLIC_KEY_PINS_REPORT_ONLY, RANGE, REFERER, REFERRER_POLICY, REFRESH, RETRY_AFTER,
SEC_WEBSOCKET_ACCEPT, SEC_WEBSOCKET_EXTENSIONS, SEC_WEBSOCKET_KEY, SEC_WEBSOCKET_PROTOCOL,
SEC_WEBSOCKET_VERSION, SERVER, SET_COOKIE, STRICT_TRANSPORT_SECURITY, TE, TRAILER,
TRANSFER_ENCODING, UPGRADE, UPGRADE_INSECURE_REQUESTS, USER_AGENT, VARY, VIA, WARNING,
WWW_AUTHENTICATE, X_CONTENT_TYPE_OPTIONS, X_DNS_PREFETCH_CONTROL, X_FRAME_OPTIONS,
X_XSS_PROTECTION,
ACCESS_CONTROL_ALLOW_CREDENTIALS, ACCESS_CONTROL_ALLOW_HEADERS, ACCESS_CONTROL_ALLOW_METHODS,
ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_EXPOSE_HEADERS, ACCESS_CONTROL_MAX_AGE,
ACCESS_CONTROL_REQUEST_HEADERS, ACCESS_CONTROL_REQUEST_METHOD, AGE, ALLOW, ALT_SVC,
AUTHORIZATION, CACHE_CONTROL, CONNECTION, CONTENT_DISPOSITION, CONTENT_ENCODING,
CONTENT_LANGUAGE, CONTENT_LENGTH, CONTENT_LOCATION, CONTENT_RANGE, CONTENT_SECURITY_POLICY,
CONTENT_SECURITY_POLICY_REPORT_ONLY, CONTENT_TYPE, COOKIE, DATE, DNT, ETAG, EXPECT, EXPIRES,
FORWARDED, FROM, HOST, IF_MATCH, IF_MODIFIED_SINCE, IF_NONE_MATCH, IF_RANGE,
IF_UNMODIFIED_SINCE, LAST_MODIFIED, LINK, LOCATION, MAX_FORWARDS, ORIGIN, PRAGMA,
PROXY_AUTHENTICATE, PROXY_AUTHORIZATION, PUBLIC_KEY_PINS, PUBLIC_KEY_PINS_REPORT_ONLY, RANGE,
REFERER, REFERRER_POLICY, REFRESH, RETRY_AFTER, SEC_WEBSOCKET_ACCEPT, SEC_WEBSOCKET_EXTENSIONS,
SEC_WEBSOCKET_KEY, SEC_WEBSOCKET_PROTOCOL, SEC_WEBSOCKET_VERSION, SERVER, SET_COOKIE,
STRICT_TRANSPORT_SECURITY, TE, TRAILER, TRANSFER_ENCODING, UPGRADE, UPGRADE_INSECURE_REQUESTS,
USER_AGENT, VARY, VIA, WARNING, WWW_AUTHENTICATE, X_CONTENT_TYPE_OPTIONS,
X_DNS_PREFETCH_CONTROL, X_FRAME_OPTIONS, X_XSS_PROTECTION,
};
use percent_encoding::{AsciiSet, CONTROLS};
use crate::{error::ParseError, HttpMessage};
@ -43,23 +40,22 @@ mod utils;
pub use self::{
as_name::AsHeaderName,
// re-export list is explicit so that any updates to `http` do not conflict with this set
common::{
CACHE_STATUS, CDN_CACHE_CONTROL, CROSS_ORIGIN_EMBEDDER_POLICY, CROSS_ORIGIN_OPENER_POLICY,
CROSS_ORIGIN_RESOURCE_POLICY, PERMISSIONS_POLICY, X_FORWARDED_FOR, X_FORWARDED_HOST,
X_FORWARDED_PROTO,
},
into_pair::TryIntoHeaderPair,
into_value::TryIntoHeaderValue,
map::HeaderMap,
shared::{
parse_extended_value, q, Charset, ContentEncoding, ExtendedValue, HttpDate,
LanguageTag, Quality, QualityItem,
parse_extended_value, q, Charset, ContentEncoding, ExtendedValue, HttpDate, LanguageTag,
Quality, QualityItem,
},
utils::{fmt_comma_delimited, from_comma_delimited, from_one_raw_str, http_percent_encode},
};
// re-export list is explicit so that any updates to `http` do not conflict with this set
pub use self::common::{
CACHE_STATUS, CDN_CACHE_CONTROL, CROSS_ORIGIN_EMBEDDER_POLICY, CROSS_ORIGIN_OPENER_POLICY,
CROSS_ORIGIN_RESOURCE_POLICY, PERMISSIONS_POLICY, X_FORWARDED_FOR, X_FORWARDED_HOST,
X_FORWARDED_PROTO,
};
/// An interface for types that already represent a valid header.
pub trait Header: TryIntoHeaderValue {
/// Returns the name of the header field.

View file

@ -1,4 +1,4 @@
use std::{convert::TryFrom, str::FromStr};
use std::str::FromStr;
use derive_more::{Display, Error};
use http::header::InvalidHeaderValue;

View file

@ -24,8 +24,7 @@ impl FromStr for HttpDate {
impl fmt::Display for HttpDate {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let date_str = httpdate::fmt_http_date(self.0);
f.write_str(&date_str)
httpdate::HttpDate::from(self.0).fmt(f)
}
}
@ -37,7 +36,7 @@ impl TryIntoHeaderValue for HttpDate {
let mut wrt = MutWriter(&mut buf);
// unwrap: date output is known to be well formed and of known length
write!(wrt, "{}", httpdate::fmt_http_date(self.0)).unwrap();
write!(wrt, "{}", self).unwrap();
HeaderValue::from_maybe_shared(buf.split().freeze())
}

View file

@ -1,5 +1,7 @@
//! Originally taken from `hyper::header::shared`.
pub use language_tags::LanguageTag;
mod charset;
mod content_encoding;
mod extended;
@ -7,10 +9,11 @@ mod http_date;
mod quality;
mod quality_item;
pub use self::charset::Charset;
pub use self::content_encoding::ContentEncoding;
pub use self::extended::{parse_extended_value, ExtendedValue};
pub use self::http_date::HttpDate;
pub use self::quality::{q, Quality};
pub use self::quality_item::QualityItem;
pub use language_tags::LanguageTag;
pub use self::{
charset::Charset,
content_encoding::ContentEncoding,
extended::{parse_extended_value, ExtendedValue},
http_date::HttpDate,
quality::{q, Quality},
quality_item::QualityItem,
};

View file

@ -1,7 +1,4 @@
use std::{
convert::{TryFrom, TryInto},
fmt,
};
use std::fmt;
use derive_more::{Display, Error};

View file

@ -1,8 +1,7 @@
use std::{cmp, convert::TryFrom as _, fmt, str};
use crate::error::ParseError;
use std::{cmp, fmt, str};
use super::Quality;
use crate::error::ParseError;
/// Represents an item with a quality value as defined
/// in [RFC 7231 §5.3.1](https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.1).

View file

@ -80,18 +80,18 @@ mod tests {
#[test]
fn comma_delimited_parsing() {
let headers = vec![];
let headers = [];
let res: Vec<usize> = from_comma_delimited(headers.iter()).unwrap();
assert_eq!(res, vec![0; 0]);
let headers = vec![
let headers = [
HeaderValue::from_static("1, 2"),
HeaderValue::from_static("3,4"),
];
let res: Vec<usize> = from_comma_delimited(headers.iter()).unwrap();
assert_eq!(res, vec![1, 2, 3, 4]);
let headers = vec![
let headers = [
HeaderValue::from_static(""),
HeaderValue::from_static(","),
HeaderValue::from_static(" "),

View file

@ -61,9 +61,7 @@ pub trait HttpMessage: Sized {
fn encoding(&self) -> Result<&'static Encoding, ContentTypeError> {
if let Some(mime_type) = self.mime_type()? {
if let Some(charset) = mime_type.get_param("charset") {
if let Some(enc) =
Encoding::for_label_no_replacement(charset.as_str().as_bytes())
{
if let Some(enc) = Encoding::for_label_no_replacement(charset.as_str().as_bytes()) {
Ok(enc)
} else {
Err(ContentTypeError::UnknownEncoding)
@ -146,7 +144,7 @@ mod tests {
.finish();
assert_eq!(req.content_type(), "text/plain");
let req = TestRequest::default()
.insert_header(("content-type", "application/json; charset=utf=8"))
.insert_header(("content-type", "application/json; charset=utf-8"))
.finish();
assert_eq!(req.content_type(), "application/json");
let req = TestRequest::default().finish();

View file

@ -1,11 +1,15 @@
//! HTTP primitives for the Actix ecosystem.
//! HTTP types and services for the Actix ecosystem.
//!
//! ## Crate Features
//!
//! | Feature | Functionality |
//! | ------------------- | ------------------------------------------- |
//! | `http2` | HTTP/2 support via [h2]. |
//! | `openssl` | TLS support via [OpenSSL]. |
//! | `rustls` | TLS support via [rustls]. |
//! | `rustls` | TLS support via [rustls] 0.20. |
//! | `rustls-0_21` | TLS support via [rustls] 0.21. |
//! | `rustls-0_22` | TLS support via [rustls] 0.22. |
//! | `rustls-0_23` | TLS support via [rustls] 0.23. |
//! | `compress-brotli` | Payload compression support: Brotli. |
//! | `compress-gzip` | Payload compression support: Deflate, Gzip. |
//! | `compress-zstd` | Payload compression support: Zstd. |
@ -21,15 +25,13 @@
#![allow(
clippy::type_complexity,
clippy::too_many_arguments,
clippy::borrow_interior_mutable_const,
clippy::uninlined_format_args
clippy::borrow_interior_mutable_const
)]
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
pub use ::http::{uri, uri::Uri};
pub use ::http::{Method, StatusCode, Version};
pub use http::{uri, uri::Uri, Method, StatusCode, Version};
pub mod body;
mod builder;
@ -41,7 +43,6 @@ pub mod error;
mod extensions;
pub mod h1;
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub mod h2;
pub mod header;
mod helpers;
@ -56,26 +57,32 @@ mod responses;
mod service;
pub mod test;
#[cfg(feature = "ws")]
#[cfg_attr(docsrs, doc(cfg(feature = "ws")))]
pub mod ws;
pub use self::builder::HttpServiceBuilder;
pub use self::config::ServiceConfig;
pub use self::error::Error;
pub use self::extensions::Extensions;
pub use self::header::ContentEncoding;
pub use self::http_message::HttpMessage;
pub use self::keep_alive::KeepAlive;
pub use self::message::ConnectionType;
pub use self::message::Message;
#[allow(deprecated)]
pub use self::payload::{BoxedPayloadStream, Payload, PayloadStream};
pub use self::requests::{Request, RequestHead, RequestHeadType};
pub use self::responses::{Response, ResponseBuilder, ResponseHead};
pub use self::service::HttpService;
#[cfg(any(feature = "openssl", feature = "rustls"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "openssl", feature = "rustls"))))]
pub use self::payload::PayloadStream;
#[cfg(any(
feature = "openssl",
feature = "rustls-0_20",
feature = "rustls-0_21",
feature = "rustls-0_22",
feature = "rustls-0_23",
))]
pub use self::service::TlsAcceptorConfig;
pub use self::{
builder::HttpServiceBuilder,
config::ServiceConfig,
error::Error,
extensions::Extensions,
header::ContentEncoding,
http_message::HttpMessage,
keep_alive::KeepAlive,
message::{ConnectionType, Message},
payload::{BoxedPayloadStream, Payload},
requests::{Request, RequestHead, RequestHeadType},
responses::{Response, ResponseBuilder, ResponseHead},
service::HttpService,
};
/// A major HTTP protocol version.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]

View file

@ -16,6 +16,7 @@ pub enum ConnectionType {
}
bitflags! {
#[derive(Debug, Clone, Copy)]
pub(crate) struct Flags: u8 {
const CLOSE = 0b0000_0001;
const KEEP_ALIVE = 0b0000_0010;

View file

@ -5,7 +5,7 @@
use std::cell::RefCell;
thread_local! {
static NOTIFY_DROPPED: RefCell<Option<bool>> = RefCell::new(None);
static NOTIFY_DROPPED: RefCell<Option<bool>> = const { RefCell::new(None) };
}
/// Check if the spawned task is dropped.

View file

@ -16,7 +16,10 @@ pub struct RequestHead {
pub uri: Uri,
pub version: Version,
pub headers: HeaderMap,
/// Will only be None when called in unit tests unless set manually.
pub peer_addr: Option<net::SocketAddr>,
flags: Flags,
}

View file

@ -3,5 +3,7 @@
mod head;
mod request;
pub use self::head::{RequestHead, RequestHeadType};
pub use self::request::Request;
pub use self::{
head::{RequestHead, RequestHeadType},
request::Request,
};

View file

@ -10,8 +10,7 @@ use std::{
use http::{header, Method, Uri, Version};
use crate::{
header::HeaderMap, BoxedPayloadStream, Extensions, HttpMessage, Message, Payload,
RequestHead,
header::HeaderMap, BoxedPayloadStream, Extensions, HttpMessage, Message, Payload, RequestHead,
};
/// An HTTP request.
@ -174,7 +173,7 @@ impl<P> Request<P> {
/// Peer address is the directly connected peer's socket address. If a proxy is used in front of
/// the Actix Web server, then it would be address of this proxy.
///
/// Will only return None when called in unit tests.
/// Will only return None when called in unit tests unless set manually.
#[inline]
pub fn peer_addr(&self) -> Option<net::SocketAddr> {
self.head().peer_addr
@ -234,7 +233,6 @@ impl<P> fmt::Debug for Request<P> {
#[cfg(test)]
mod tests {
use super::*;
use std::convert::TryFrom;
#[test]
fn test_basics() {

View file

@ -93,7 +93,7 @@ impl ResponseBuilder {
Ok((key, value)) => {
parts.headers.insert(key, value);
}
Err(e) => self.err = Some(e.into()),
Err(err) => self.err = Some(err.into()),
};
}
@ -119,7 +119,7 @@ impl ResponseBuilder {
if let Some(parts) = self.inner() {
match header.try_into_pair() {
Ok((key, value)) => parts.headers.append(key, value),
Err(e) => self.err = Some(e.into()),
Err(err) => self.err = Some(err.into()),
};
}
@ -193,7 +193,7 @@ impl ResponseBuilder {
Ok(value) => {
parts.headers.insert(header::CONTENT_TYPE, value);
}
Err(e) => self.err = Some(e.into()),
Err(err) => self.err = Some(err.into()),
};
}
self

View file

@ -5,7 +5,5 @@ mod head;
#[allow(clippy::module_inception)]
mod response;
pub use self::builder::ResponseBuilder;
pub(crate) use self::head::BoxedResponseHead;
pub use self::head::ResponseHead;
pub use self::response::Response;
pub use self::{builder::ResponseBuilder, head::ResponseHead, response::Response};

View file

@ -30,9 +30,9 @@ use crate::{
///
/// # Automatic HTTP Version Selection
/// There are two ways to select the HTTP version of an incoming connection:
/// - One is to rely on the ALPN information that is provided when using a TLS (HTTPS); both
/// versions are supported automatically when using either of the `.rustls()` or `.openssl()`
/// finalizing methods.
/// - One is to rely on the ALPN information that is provided when using TLS (HTTPS); both versions
/// are supported automatically when using either of the `.rustls()` or `.openssl()` finalizing
/// methods.
/// - The other is to read the first few bytes of the TCP stream. This is the only viable approach
/// for supporting H2C, which allows the HTTP/2 protocol to work over plaintext connections. Use
/// the `.tcp_auto_h2c()` finalizing method to enable this behavior.
@ -200,13 +200,8 @@ where
/// The resulting service only supports HTTP/1.x.
pub fn tcp(
self,
) -> impl ServiceFactory<
TcpStream,
Config = (),
Response = (),
Error = DispatchError,
InitError = (),
> {
) -> impl ServiceFactory<TcpStream, Config = (), Response = (), Error = DispatchError, InitError = ()>
{
fn_service(|io: TcpStream| async {
let peer_addr = io.peer_addr().ok();
Ok((io, Protocol::Http1, peer_addr))
@ -217,16 +212,10 @@ where
/// Creates TCP stream service from HTTP service that automatically selects HTTP/1.x or HTTP/2
/// on plaintext connections.
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub fn tcp_auto_h2c(
self,
) -> impl ServiceFactory<
TcpStream,
Config = (),
Response = (),
Error = DispatchError,
InitError = (),
> {
) -> impl ServiceFactory<TcpStream, Config = (), Response = (), Error = DispatchError, InitError = ()>
{
fn_service(move |io: TcpStream| async move {
// subset of HTTP/2 preface defined by RFC 9113 §3.4
// this subset was chosen to maximize likelihood that peeking only once will allow us to
@ -252,14 +241,25 @@ where
}
/// Configuration options used when accepting TLS connection.
#[cfg(any(feature = "openssl", feature = "rustls"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "openssl", feature = "rustls"))))]
#[cfg(any(
feature = "openssl",
feature = "rustls-0_20",
feature = "rustls-0_21",
feature = "rustls-0_22",
feature = "rustls-0_23",
))]
#[derive(Debug, Default)]
pub struct TlsAcceptorConfig {
pub(crate) handshake_timeout: Option<std::time::Duration>,
}
#[cfg(any(feature = "openssl", feature = "rustls"))]
#[cfg(any(
feature = "openssl",
feature = "rustls-0_20",
feature = "rustls-0_21",
feature = "rustls-0_22",
feature = "rustls-0_23",
))]
impl TlsAcceptorConfig {
/// Set TLS handshake timeout duration.
pub fn handshake_timeout(self, dur: std::time::Duration) -> Self {
@ -309,7 +309,6 @@ mod openssl {
U::InitError: fmt::Debug,
{
/// Create OpenSSL based service.
#[cfg_attr(docsrs, doc(cfg(feature = "openssl")))]
pub fn openssl(
self,
acceptor: SslAcceptor,
@ -324,7 +323,6 @@ mod openssl {
}
/// Create OpenSSL based service with custom TLS acceptor configuration.
#[cfg_attr(docsrs, doc(cfg(feature = "openssl")))]
pub fn openssl_with_config(
self,
acceptor: SslAcceptor,
@ -366,13 +364,13 @@ mod openssl {
}
}
#[cfg(feature = "rustls")]
mod rustls {
#[cfg(feature = "rustls-0_20")]
mod rustls_0_20 {
use std::io;
use actix_service::ServiceFactoryExt as _;
use actix_tls::accept::{
rustls::{reexports::ServerConfig, Acceptor, TlsStream},
rustls_0_20::{reexports::ServerConfig, Acceptor, TlsStream},
TlsError,
};
@ -403,8 +401,7 @@ mod rustls {
U::Error: fmt::Display + Into<Response<BoxBody>>,
U::InitError: fmt::Debug,
{
/// Create Rustls based service.
#[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
/// Create Rustls v0.20 based service.
pub fn rustls(
self,
config: ServerConfig,
@ -418,8 +415,7 @@ mod rustls {
self.rustls_with_config(config, TlsAcceptorConfig::default())
}
/// Create Rustls based service with custom TLS acceptor configuration.
#[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
/// Create Rustls v0.20 based service with custom TLS acceptor configuration.
pub fn rustls_with_config(
self,
mut config: ServerConfig,
@ -464,6 +460,294 @@ mod rustls {
}
}
#[cfg(feature = "rustls-0_21")]
mod rustls_0_21 {
use std::io;
use actix_service::ServiceFactoryExt as _;
use actix_tls::accept::{
rustls_0_21::{reexports::ServerConfig, Acceptor, TlsStream},
TlsError,
};
use super::*;
impl<S, B, X, U> HttpService<TlsStream<TcpStream>, S, B, X, U>
where
S: ServiceFactory<Request, Config = ()>,
S::Future: 'static,
S::Error: Into<Response<BoxBody>> + 'static,
S::InitError: fmt::Debug,
S::Response: Into<Response<B>> + 'static,
<S::Service as Service<Request>>::Future: 'static,
B: MessageBody + 'static,
X: ServiceFactory<Request, Config = (), Response = Request>,
X::Future: 'static,
X::Error: Into<Response<BoxBody>>,
X::InitError: fmt::Debug,
U: ServiceFactory<
(Request, Framed<TlsStream<TcpStream>, h1::Codec>),
Config = (),
Response = (),
>,
U::Future: 'static,
U::Error: fmt::Display + Into<Response<BoxBody>>,
U::InitError: fmt::Debug,
{
/// Create Rustls v0.21 based service.
pub fn rustls_021(
self,
config: ServerConfig,
) -> impl ServiceFactory<
TcpStream,
Config = (),
Response = (),
Error = TlsError<io::Error, DispatchError>,
InitError = (),
> {
self.rustls_021_with_config(config, TlsAcceptorConfig::default())
}
/// Create Rustls v0.21 based service with custom TLS acceptor configuration.
pub fn rustls_021_with_config(
self,
mut config: ServerConfig,
tls_acceptor_config: TlsAcceptorConfig,
) -> impl ServiceFactory<
TcpStream,
Config = (),
Response = (),
Error = TlsError<io::Error, DispatchError>,
InitError = (),
> {
let mut protos = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
protos.extend_from_slice(&config.alpn_protocols);
config.alpn_protocols = protos;
let mut acceptor = Acceptor::new(config);
if let Some(handshake_timeout) = tls_acceptor_config.handshake_timeout {
acceptor.set_handshake_timeout(handshake_timeout);
}
acceptor
.map_init_err(|_| {
unreachable!("TLS acceptor service factory does not error on init")
})
.map_err(TlsError::into_service_error)
.and_then(|io: TlsStream<TcpStream>| async {
let proto = if let Some(protos) = io.get_ref().1.alpn_protocol() {
if protos.windows(2).any(|window| window == b"h2") {
Protocol::Http2
} else {
Protocol::Http1
}
} else {
Protocol::Http1
};
let peer_addr = io.get_ref().0.peer_addr().ok();
Ok((io, proto, peer_addr))
})
.and_then(self.map_err(TlsError::Service))
}
}
}
#[cfg(feature = "rustls-0_22")]
mod rustls_0_22 {
use std::io;
use actix_service::ServiceFactoryExt as _;
use actix_tls::accept::{
rustls_0_22::{reexports::ServerConfig, Acceptor, TlsStream},
TlsError,
};
use super::*;
impl<S, B, X, U> HttpService<TlsStream<TcpStream>, S, B, X, U>
where
S: ServiceFactory<Request, Config = ()>,
S::Future: 'static,
S::Error: Into<Response<BoxBody>> + 'static,
S::InitError: fmt::Debug,
S::Response: Into<Response<B>> + 'static,
<S::Service as Service<Request>>::Future: 'static,
B: MessageBody + 'static,
X: ServiceFactory<Request, Config = (), Response = Request>,
X::Future: 'static,
X::Error: Into<Response<BoxBody>>,
X::InitError: fmt::Debug,
U: ServiceFactory<
(Request, Framed<TlsStream<TcpStream>, h1::Codec>),
Config = (),
Response = (),
>,
U::Future: 'static,
U::Error: fmt::Display + Into<Response<BoxBody>>,
U::InitError: fmt::Debug,
{
/// Create Rustls v0.22 based service.
pub fn rustls_0_22(
self,
config: ServerConfig,
) -> impl ServiceFactory<
TcpStream,
Config = (),
Response = (),
Error = TlsError<io::Error, DispatchError>,
InitError = (),
> {
self.rustls_0_22_with_config(config, TlsAcceptorConfig::default())
}
/// Create Rustls v0.22 based service with custom TLS acceptor configuration.
pub fn rustls_0_22_with_config(
self,
mut config: ServerConfig,
tls_acceptor_config: TlsAcceptorConfig,
) -> impl ServiceFactory<
TcpStream,
Config = (),
Response = (),
Error = TlsError<io::Error, DispatchError>,
InitError = (),
> {
let mut protos = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
protos.extend_from_slice(&config.alpn_protocols);
config.alpn_protocols = protos;
let mut acceptor = Acceptor::new(config);
if let Some(handshake_timeout) = tls_acceptor_config.handshake_timeout {
acceptor.set_handshake_timeout(handshake_timeout);
}
acceptor
.map_init_err(|_| {
unreachable!("TLS acceptor service factory does not error on init")
})
.map_err(TlsError::into_service_error)
.and_then(|io: TlsStream<TcpStream>| async {
let proto = if let Some(protos) = io.get_ref().1.alpn_protocol() {
if protos.windows(2).any(|window| window == b"h2") {
Protocol::Http2
} else {
Protocol::Http1
}
} else {
Protocol::Http1
};
let peer_addr = io.get_ref().0.peer_addr().ok();
Ok((io, proto, peer_addr))
})
.and_then(self.map_err(TlsError::Service))
}
}
}
#[cfg(feature = "rustls-0_23")]
mod rustls_0_23 {
use std::io;
use actix_service::ServiceFactoryExt as _;
use actix_tls::accept::{
rustls_0_23::{reexports::ServerConfig, Acceptor, TlsStream},
TlsError,
};
use super::*;
impl<S, B, X, U> HttpService<TlsStream<TcpStream>, S, B, X, U>
where
S: ServiceFactory<Request, Config = ()>,
S::Future: 'static,
S::Error: Into<Response<BoxBody>> + 'static,
S::InitError: fmt::Debug,
S::Response: Into<Response<B>> + 'static,
<S::Service as Service<Request>>::Future: 'static,
B: MessageBody + 'static,
X: ServiceFactory<Request, Config = (), Response = Request>,
X::Future: 'static,
X::Error: Into<Response<BoxBody>>,
X::InitError: fmt::Debug,
U: ServiceFactory<
(Request, Framed<TlsStream<TcpStream>, h1::Codec>),
Config = (),
Response = (),
>,
U::Future: 'static,
U::Error: fmt::Display + Into<Response<BoxBody>>,
U::InitError: fmt::Debug,
{
/// Create Rustls v0.23 based service.
pub fn rustls_0_23(
self,
config: ServerConfig,
) -> impl ServiceFactory<
TcpStream,
Config = (),
Response = (),
Error = TlsError<io::Error, DispatchError>,
InitError = (),
> {
self.rustls_0_23_with_config(config, TlsAcceptorConfig::default())
}
/// Create Rustls v0.23 based service with custom TLS acceptor configuration.
pub fn rustls_0_23_with_config(
self,
mut config: ServerConfig,
tls_acceptor_config: TlsAcceptorConfig,
) -> impl ServiceFactory<
TcpStream,
Config = (),
Response = (),
Error = TlsError<io::Error, DispatchError>,
InitError = (),
> {
let mut protos = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
protos.extend_from_slice(&config.alpn_protocols);
config.alpn_protocols = protos;
let mut acceptor = Acceptor::new(config);
if let Some(handshake_timeout) = tls_acceptor_config.handshake_timeout {
acceptor.set_handshake_timeout(handshake_timeout);
}
acceptor
.map_init_err(|_| {
unreachable!("TLS acceptor service factory does not error on init")
})
.map_err(TlsError::into_service_error)
.and_then(|io: TlsStream<TcpStream>| async {
let proto = if let Some(protos) = io.get_ref().1.alpn_protocol() {
if protos.windows(2).any(|window| window == b"h2") {
Protocol::Http2
} else {
Protocol::Http1
}
} else {
Protocol::Http1
};
let peer_addr = io.get_ref().0.peer_addr().ok();
Ok((io, proto, peer_addr))
})
.and_then(self.map_err(TlsError::Service))
}
}
}
impl<T, S, B, X, U> ServiceFactory<(T, Protocol, Option<net::SocketAddr>)>
for HttpService<T, S, B, X, U>
where
@ -569,10 +853,7 @@ where
}
}
pub(super) fn _poll_ready(
&self,
cx: &mut Context<'_>,
) -> Poll<Result<(), Response<BoxBody>>> {
pub(super) fn _poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Response<BoxBody>>> {
ready!(self.flow.expect.poll_ready(cx).map_err(Into::into))?;
ready!(self.flow.service.poll_ready(cx).map_err(Into::into))?;
@ -631,10 +912,7 @@ where
})
}
fn call(
&self,
(io, proto, peer_addr): (T, Protocol, Option<net::SocketAddr>),
) -> Self::Future {
fn call(&self, (io, proto, peer_addr): (T, Protocol, Option<net::SocketAddr>)) -> Self::Future {
let conn_data = OnConnectData::from_io(&io, self.on_connect_ext.as_deref());
match proto {

View file

@ -74,6 +74,7 @@ pub struct Codec {
}
bitflags! {
#[derive(Debug, Clone, Copy)]
struct Flags: u8 {
const SERVER = 0b0000_0001;
const CONTINUATION = 0b0000_0010;
@ -295,7 +296,7 @@ impl Decoder for Codec {
}
}
Ok(None) => Ok(None),
Err(e) => Err(e),
Err(err) => Err(err),
}
}
}

View file

@ -70,15 +70,14 @@ mod inner {
task::{Context, Poll},
};
use actix_codec::Framed;
use actix_service::{IntoService, Service};
use futures_core::stream::Stream;
use local_channel::mpsc;
use pin_project_lite::pin_project;
use tracing::debug;
use actix_codec::Framed;
use tokio::io::{AsyncRead, AsyncWrite};
use tokio_util::codec::{Decoder, Encoder};
use tracing::debug;
use crate::{body::BoxBody, Response};
@ -413,9 +412,7 @@ mod inner {
}
State::Error(_) => {
// flush write buffer
if !this.framed.is_write_buf_empty()
&& this.framed.flush(cx).is_pending()
{
if !this.framed.is_write_buf_empty() && this.framed.flush(cx).is_pending() {
return Poll::Pending;
}
Poll::Ready(Err(this.state.take_error()))

View file

@ -1,4 +1,4 @@
use std::convert::TryFrom;
use std::cmp::min;
use bytes::{Buf, BufMut, BytesMut};
use tracing::debug;
@ -96,6 +96,10 @@ impl Parser {
// not enough data
if src.len() < idx + length {
let min_length = min(length, max_size);
if src.capacity() < idx + min_length {
src.reserve(idx + min_length - src.capacity());
}
return Ok(None);
}
@ -174,14 +178,14 @@ impl Parser {
};
if payload_len < 126 {
dst.reserve(p_len + 2 + if mask { 4 } else { 0 });
dst.reserve(p_len + 2);
dst.put_slice(&[one, two | payload_len as u8]);
} else if payload_len <= 65_535 {
dst.reserve(p_len + 4 + if mask { 4 } else { 0 });
dst.reserve(p_len + 4);
dst.put_slice(&[one, two | 126]);
dst.put_u16(payload_len as u16);
} else {
dst.reserve(p_len + 10 + if mask { 4 } else { 0 });
dst.reserve(p_len + 10);
dst.put_slice(&[one, two | 127]);
dst.put_u64(payload_len as u64);
};
@ -217,9 +221,10 @@ impl Parser {
#[cfg(test)]
mod tests {
use super::*;
use bytes::Bytes;
use super::*;
struct F {
finished: bool,
opcode: OpCode,

View file

@ -50,7 +50,7 @@ mod tests {
#[test]
fn test_apply_mask() {
let mask = [0x6d, 0xb6, 0xb2, 0x80];
let unmasked = vec![
let unmasked = [
0xf3, 0x00, 0x01, 0x02, 0x03, 0x80, 0x81, 0x82, 0xff, 0xfe, 0x00, 0x17, 0x74, 0xf9,
0x12, 0x03,
];

View file

@ -8,8 +8,7 @@ use std::io;
use derive_more::{Display, Error, From};
use http::{header, Method, StatusCode};
use crate::body::BoxBody;
use crate::{header::HeaderValue, RequestHead, Response, ResponseBuilder};
use crate::{body::BoxBody, header::HeaderValue, RequestHead, Response, ResponseBuilder};
mod codec;
mod dispatcher;
@ -17,48 +16,50 @@ mod frame;
mod mask;
mod proto;
pub use self::codec::{Codec, Frame, Item, Message};
pub use self::dispatcher::Dispatcher;
pub use self::frame::Parser;
pub use self::proto::{hash_key, CloseCode, CloseReason, OpCode};
pub use self::{
codec::{Codec, Frame, Item, Message},
dispatcher::Dispatcher,
frame::Parser,
proto::{hash_key, CloseCode, CloseReason, OpCode},
};
/// WebSocket protocol errors.
#[derive(Debug, Display, Error, From)]
pub enum ProtocolError {
/// Received an unmasked frame from client.
#[display(fmt = "Received an unmasked frame from client.")]
#[display(fmt = "received an unmasked frame from client")]
UnmaskedFrame,
/// Received a masked frame from server.
#[display(fmt = "Received a masked frame from server.")]
#[display(fmt = "received a masked frame from server")]
MaskedFrame,
/// Encountered invalid opcode.
#[display(fmt = "Invalid opcode: {}.", _0)]
#[display(fmt = "invalid opcode ({})", _0)]
InvalidOpcode(#[error(not(source))] u8),
/// Invalid control frame length
#[display(fmt = "Invalid control frame length: {}.", _0)]
#[display(fmt = "invalid control frame length ({})", _0)]
InvalidLength(#[error(not(source))] usize),
/// Bad opcode.
#[display(fmt = "Bad opcode.")]
#[display(fmt = "bad opcode")]
BadOpCode,
/// A payload reached size limit.
#[display(fmt = "A payload reached size limit.")]
#[display(fmt = "payload reached size limit")]
Overflow,
/// Continuation is not started.
#[display(fmt = "Continuation is not started.")]
/// Continuation has not started.
#[display(fmt = "continuation has not started")]
ContinuationNotStarted,
/// Received new continuation but it is already started.
#[display(fmt = "Received new continuation but it is already started.")]
#[display(fmt = "received new continuation but it has already started")]
ContinuationStarted,
/// Unknown continuation fragment.
#[display(fmt = "Unknown continuation fragment: {}.", _0)]
#[display(fmt = "unknown continuation fragment: {}", _0)]
ContinuationFragment(#[error(not(source))] OpCode),
/// I/O error.
@ -70,27 +71,27 @@ pub enum ProtocolError {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Display, Error)]
pub enum HandshakeError {
/// Only get method is allowed.
#[display(fmt = "Method not allowed.")]
#[display(fmt = "method not allowed")]
GetMethodRequired,
/// Upgrade header if not set to WebSocket.
#[display(fmt = "WebSocket upgrade is expected.")]
#[display(fmt = "WebSocket upgrade is expected")]
NoWebsocketUpgrade,
/// Connection header is not set to upgrade.
#[display(fmt = "Connection upgrade is expected.")]
#[display(fmt = "connection upgrade is expected")]
NoConnectionUpgrade,
/// WebSocket version header is not set.
#[display(fmt = "WebSocket version header is required.")]
#[display(fmt = "WebSocket version header is required")]
NoVersionHeader,
/// Unsupported WebSocket version.
#[display(fmt = "Unsupported WebSocket version.")]
#[display(fmt = "unsupported WebSocket version")]
UnsupportedVersion,
/// WebSocket key is not set or wrong.
#[display(fmt = "Unknown websocket key.")]
#[display(fmt = "unknown WebSocket key")]
BadWebsocketKey,
}
@ -219,10 +220,8 @@ pub fn handshake_response(req: &RequestHead) -> ResponseBuilder {
#[cfg(test)]
mod tests {
use crate::{header, Method};
use super::*;
use crate::test::TestRequest;
use crate::{header, test::TestRequest};
#[test]
fn test_handshake() {

View file

@ -1,7 +1,4 @@
use std::{
convert::{From, Into},
fmt,
};
use std::fmt;
use base64::prelude::*;
use tracing::error;

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