mirror of https://github.com/fafhrd91/actix-web
Merge branch 'master' into headermap2
This commit is contained in:
commit
9e20b806ae
|
@ -40,12 +40,12 @@ jobs:
|
||||||
echo 'OPENSSL_DIR=C:\Program Files\OpenSSL' | Out-File -FilePath $env:GITHUB_ENV -Append
|
echo 'OPENSSL_DIR=C:\Program Files\OpenSSL' | Out-File -FilePath $env:GITHUB_ENV -Append
|
||||||
|
|
||||||
- name: Install Rust (${{ matrix.version.name }})
|
- name: Install Rust (${{ matrix.version.name }})
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.6.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ matrix.version.version }}
|
toolchain: ${{ matrix.version.version }}
|
||||||
|
|
||||||
- name: Install cargo-hack
|
- name: Install cargo-hack
|
||||||
uses: taiki-e/install-action@v2.23.7
|
uses: taiki-e/install-action@v2.25.9
|
||||||
with:
|
with:
|
||||||
tool: cargo-hack
|
tool: cargo-hack
|
||||||
|
|
||||||
|
@ -82,10 +82,10 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.6.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
||||||
|
|
||||||
- name: Install cargo-hack
|
- name: Install cargo-hack
|
||||||
uses: taiki-e/install-action@v2.23.7
|
uses: taiki-e/install-action@v2.25.9
|
||||||
with:
|
with:
|
||||||
tool: cargo-hack
|
tool: cargo-hack
|
||||||
|
|
||||||
|
@ -103,10 +103,10 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.6.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
||||||
|
|
||||||
- name: Install nextest
|
- name: Install nextest
|
||||||
uses: taiki-e/install-action@v2.23.7
|
uses: taiki-e/install-action@v2.25.9
|
||||||
with:
|
with:
|
||||||
tool: nextest
|
tool: nextest
|
||||||
|
|
||||||
|
|
|
@ -45,12 +45,12 @@ jobs:
|
||||||
echo 'OPENSSL_DIR=C:\Program Files\OpenSSL' | Out-File -FilePath $env:GITHUB_ENV -Append
|
echo 'OPENSSL_DIR=C:\Program Files\OpenSSL' | Out-File -FilePath $env:GITHUB_ENV -Append
|
||||||
|
|
||||||
- name: Install Rust (${{ matrix.version.name }})
|
- name: Install Rust (${{ matrix.version.name }})
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.6.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ matrix.version.version }}
|
toolchain: ${{ matrix.version.version }}
|
||||||
|
|
||||||
- name: Install cargo-hack
|
- name: Install cargo-hack
|
||||||
uses: taiki-e/install-action@v2.23.7
|
uses: taiki-e/install-action@v2.25.9
|
||||||
with:
|
with:
|
||||||
tool: cargo-hack
|
tool: cargo-hack
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.6.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
||||||
with:
|
with:
|
||||||
toolchain: nightly
|
toolchain: nightly
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Rust (nightly)
|
- name: Install Rust (nightly)
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.6.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
||||||
with:
|
with:
|
||||||
toolchain: nightly
|
toolchain: nightly
|
||||||
|
|
||||||
|
|
|
@ -18,12 +18,12 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.6.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
||||||
with:
|
with:
|
||||||
components: llvm-tools-preview
|
components: llvm-tools-preview
|
||||||
|
|
||||||
- name: Install cargo-llvm-cov
|
- name: Install cargo-llvm-cov
|
||||||
uses: taiki-e/install-action@v2.23.7
|
uses: taiki-e/install-action@v2.25.9
|
||||||
with:
|
with:
|
||||||
tool: cargo-llvm-cov
|
tool: cargo-llvm-cov
|
||||||
|
|
||||||
|
|
|
@ -17,12 +17,13 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: actions-rust-lang/setup-rust-toolchain@v1.6.0
|
- name: Install Rust (nightly)
|
||||||
|
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
||||||
with:
|
with:
|
||||||
toolchain: nightly
|
toolchain: nightly
|
||||||
components: rustfmt
|
components: rustfmt
|
||||||
|
|
||||||
- name: Check with rustfmt
|
- name: Check with Rustfmt
|
||||||
run: cargo fmt --all -- --check
|
run: cargo fmt --all -- --check
|
||||||
|
|
||||||
clippy:
|
clippy:
|
||||||
|
@ -35,7 +36,7 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.6.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
||||||
with:
|
with:
|
||||||
components: clippy
|
components: clippy
|
||||||
|
|
||||||
|
@ -53,7 +54,8 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: actions-rust-lang/setup-rust-toolchain@v1.6.0
|
- name: Install Rust (nightly)
|
||||||
|
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
||||||
with:
|
with:
|
||||||
toolchain: nightly
|
toolchain: nightly
|
||||||
components: rust-docs
|
components: rust-docs
|
||||||
|
@ -66,21 +68,25 @@ jobs:
|
||||||
public-api-diff:
|
public-api-diff:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Checkout main branch
|
||||||
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.base_ref }}
|
ref: ${{ github.base_ref }}
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
- name: Checkout PR branch
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: actions-rust-lang/setup-rust-toolchain@v1.6.0
|
- name: Install Rust
|
||||||
|
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
||||||
with:
|
with:
|
||||||
toolchain: nightly-2023-08-25
|
toolchain: nightly-2023-08-25
|
||||||
|
|
||||||
- uses: taiki-e/cache-cargo-install-action@v1.3.0
|
- name: Install cargo-public-api
|
||||||
|
uses: taiki-e/install-action@v2.24.1
|
||||||
with:
|
with:
|
||||||
tool: cargo-public-api
|
tool: cargo-public-api
|
||||||
|
|
||||||
- name: generate API diff
|
- name: Generate API diff
|
||||||
run: |
|
run: |
|
||||||
for f in $(find -mindepth 2 -maxdepth 2 -name Cargo.toml); do
|
for f in $(find -mindepth 2 -maxdepth 2 -name Cargo.toml); do
|
||||||
cargo public-api --manifest-path "$f" diff ${{ github.event.pull_request.base.sha }}..${{ github.sha }}
|
cargo public-api --manifest-path "$f" diff ${{ github.event.pull_request.base.sha }}..${{ github.sha }}
|
||||||
|
|
|
@ -22,7 +22,7 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.6.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
||||||
with:
|
with:
|
||||||
toolchain: nightly
|
toolchain: nightly
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
overrides:
|
overrides:
|
||||||
- files: '*.md'
|
- files: "*.md"
|
||||||
options:
|
options:
|
||||||
printWidth: 9999
|
printWidth: 9999
|
||||||
proseWrap: never
|
proseWrap: never
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
## 0.6.5
|
||||||
|
|
||||||
|
- Fix handling of special characters in filenames.
|
||||||
|
|
||||||
## 0.6.4
|
## 0.6.4
|
||||||
|
|
||||||
- Fix handling of newlines in filenames.
|
- Fix handling of newlines in filenames.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "actix-files"
|
name = "actix-files"
|
||||||
version = "0.6.4"
|
version = "0.6.5"
|
||||||
authors = [
|
authors = [
|
||||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||||
"Rob Ede <robjtede@icloud.com>",
|
"Rob Ede <robjtede@icloud.com>",
|
||||||
|
|
|
@ -1,18 +1,32 @@
|
||||||
# actix-files
|
# `actix-files`
|
||||||
|
|
||||||
> Static file serving for Actix Web
|
<!-- prettier-ignore-start -->
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-files)
|
[](https://crates.io/crates/actix-files)
|
||||||
[](https://docs.rs/actix-files/0.6.4)
|
[](https://docs.rs/actix-files/0.6.5)
|
||||||

|

|
||||||

|

|
||||||
<br />
|
<br />
|
||||||
[](https://deps.rs/crate/actix-files/0.6.4)
|
[](https://deps.rs/crate/actix-files/0.6.5)
|
||||||
[](https://crates.io/crates/actix-files)
|
[](https://crates.io/crates/actix-files)
|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
## Documentation & Resources
|
<!-- prettier-ignore-end -->
|
||||||
|
|
||||||
- [API Documentation](https://docs.rs/actix-files)
|
<!-- cargo-rdme start -->
|
||||||
- [Example Project](https://github.com/actix/examples/tree/master/basics/static-files)
|
|
||||||
- Minimum Supported Rust Version (MSRV): 1.68
|
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 -->
|
||||||
|
|
|
@ -569,18 +569,20 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_static_files_with_newlines() {
|
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
|
// 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.
|
// Windows can't even checkout this repository.
|
||||||
let temp_dir = tempfile::tempdir().unwrap();
|
let temp_dir = tempfile::tempdir().unwrap();
|
||||||
let file_with_newlines = temp_dir.path().join("test\nnewline.text");
|
let file_with_newlines = temp_dir.path().join("test\n\x0B\x0C\rnewline.text");
|
||||||
fs::write(&file_with_newlines, "Look at my newlines").unwrap();
|
fs::write(&file_with_newlines, "Look at my newlines").unwrap();
|
||||||
|
|
||||||
let srv = test::init_service(
|
let srv = test::init_service(
|
||||||
App::new().service(Files::new("/", temp_dir.path()).index_file("Cargo.toml")),
|
App::new().service(Files::new("/", temp_dir.path()).index_file("Cargo.toml")),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let request = TestRequest::get().uri("/test%0Anewline.text").to_request();
|
let request = TestRequest::get()
|
||||||
|
.uri("/test%0A%0B%0C%0Dnewline.text")
|
||||||
|
.to_request();
|
||||||
let response = test::call_service(&srv, request).await;
|
let response = test::call_service(&srv, request).await;
|
||||||
assert_eq!(response.status(), StatusCode::OK);
|
assert_eq!(response.status(), StatusCode::OK);
|
||||||
|
|
||||||
|
|
|
@ -139,8 +139,12 @@ impl NamedFile {
|
||||||
_ => DispositionType::Attachment,
|
_ => DispositionType::Attachment,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Replace newlines in filenames which could occur on some filesystems.
|
// replace special characters in filenames which could occur on some filesystems
|
||||||
let filename_s = filename.replace('\n', "%0A");
|
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)];
|
let mut parameters = vec![DispositionParam::Filename(filename_s)];
|
||||||
|
|
||||||
if !filename.is_ascii() {
|
if !filename.is_ascii() {
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
# actix-http-test
|
# `actix-http-test`
|
||||||
|
|
||||||
> Various helpers for Actix applications to use during testing.
|
> Various helpers for Actix applications to use during testing.
|
||||||
|
|
||||||
|
<!-- prettier-ignore-start -->
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-http-test)
|
[](https://crates.io/crates/actix-http-test)
|
||||||
[](https://docs.rs/actix-http-test/3.1.0)
|
[](https://docs.rs/actix-http-test/3.1.0)
|
||||||

|

|
||||||
|
@ -11,6 +13,8 @@
|
||||||
[](https://crates.io/crates/actix-http-test)
|
[](https://crates.io/crates/actix-http-test)
|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
|
<!-- prettier-ignore-end -->
|
||||||
|
|
||||||
## Documentation & Resources
|
## Documentation & Resources
|
||||||
|
|
||||||
- [API Documentation](https://docs.rs/actix-http-test)
|
- [API Documentation](https://docs.rs/actix-http-test)
|
||||||
|
|
|
@ -89,7 +89,7 @@ tokio-util = { version = "0.7", features = ["io", "codec"] }
|
||||||
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
|
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
|
||||||
|
|
||||||
# http2
|
# http2
|
||||||
h2 = { version = "0.3.17", optional = true }
|
h2 = { version = "0.3.24", optional = true }
|
||||||
|
|
||||||
# websockets
|
# websockets
|
||||||
local-channel = { version = "0.1", optional = true }
|
local-channel = { version = "0.1", optional = true }
|
||||||
|
|
|
@ -16,7 +16,10 @@ pub struct RequestHead {
|
||||||
pub uri: Uri,
|
pub uri: Uri,
|
||||||
pub version: Version,
|
pub version: Version,
|
||||||
pub headers: HeaderMap,
|
pub headers: HeaderMap,
|
||||||
|
|
||||||
|
/// Will only be None when called in unit tests unless set manually.
|
||||||
pub peer_addr: Option<net::SocketAddr>,
|
pub peer_addr: Option<net::SocketAddr>,
|
||||||
|
|
||||||
flags: Flags,
|
flags: Flags,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -173,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
|
/// 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.
|
/// 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]
|
#[inline]
|
||||||
pub fn peer_addr(&self) -> Option<net::SocketAddr> {
|
pub fn peer_addr(&self) -> Option<net::SocketAddr> {
|
||||||
self.head().peer_addr
|
self.head().peer_addr
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
# actix-multipart-derive
|
# `actix-multipart-derive`
|
||||||
|
|
||||||
> The derive macro implementation for actix-multipart-derive.
|
> The derive macro implementation for actix-multipart-derive.
|
||||||
|
|
||||||
|
<!-- prettier-ignore-start -->
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-multipart-derive)
|
[](https://crates.io/crates/actix-multipart-derive)
|
||||||
[](https://docs.rs/actix-multipart-derive/0.6.1)
|
[](https://docs.rs/actix-multipart-derive/0.6.1)
|
||||||

|

|
||||||
|
@ -11,6 +13,8 @@
|
||||||
[](https://crates.io/crates/actix-multipart-derive)
|
[](https://crates.io/crates/actix-multipart-derive)
|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
|
<!-- prettier-ignore-end -->
|
||||||
|
|
||||||
## Documentation & Resources
|
## Documentation & Resources
|
||||||
|
|
||||||
- [API Documentation](https://docs.rs/actix-multipart-derive)
|
- [API Documentation](https://docs.rs/actix-multipart-derive)
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
# actix-multipart
|
# `actix-multipart`
|
||||||
|
|
||||||
> Multipart form support for Actix Web.
|
> Multipart form support for Actix Web.
|
||||||
|
|
||||||
|
<!-- prettier-ignore-start -->
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-multipart)
|
[](https://crates.io/crates/actix-multipart)
|
||||||
[](https://docs.rs/actix-multipart/0.6.1)
|
[](https://docs.rs/actix-multipart/0.6.1)
|
||||||

|

|
||||||
|
@ -11,6 +13,8 @@
|
||||||
[](https://crates.io/crates/actix-multipart)
|
[](https://crates.io/crates/actix-multipart)
|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
|
<!-- prettier-ignore-end -->
|
||||||
|
|
||||||
## Documentation & Resources
|
## Documentation & Resources
|
||||||
|
|
||||||
- [API Documentation](https://docs.rs/actix-multipart)
|
- [API Documentation](https://docs.rs/actix-multipart)
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
# `actix-router`
|
# `actix-router`
|
||||||
|
|
||||||
|
<!-- prettier-ignore-start -->
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-router)
|
[](https://crates.io/crates/actix-router)
|
||||||
[](https://docs.rs/actix-router/0.5.2)
|
[](https://docs.rs/actix-router/0.5.2)
|
||||||

|

|
||||||
|
@ -9,6 +11,8 @@
|
||||||
[](https://crates.io/crates/actix-router)
|
[](https://crates.io/crates/actix-router)
|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
|
<!-- prettier-ignore-end -->
|
||||||
|
|
||||||
<!-- cargo-rdme start -->
|
<!-- cargo-rdme start -->
|
||||||
|
|
||||||
Resource path matching and router.
|
Resource path matching and router.
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
# actix-web-actors
|
# `actix-web-actors`
|
||||||
|
|
||||||
> Actix actors support for Actix Web.
|
> Actix actors support for Actix Web.
|
||||||
|
|
||||||
|
<!-- prettier-ignore-start -->
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-web-actors)
|
[](https://crates.io/crates/actix-web-actors)
|
||||||
[](https://docs.rs/actix-web-actors/4.2.0)
|
[](https://docs.rs/actix-web-actors/4.2.0)
|
||||||

|

|
||||||
|
@ -11,6 +13,8 @@
|
||||||
[](https://crates.io/crates/actix-web-actors)
|
[](https://crates.io/crates/actix-web-actors)
|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
|
<!-- prettier-ignore-end -->
|
||||||
|
|
||||||
## Documentation & Resources
|
## Documentation & Resources
|
||||||
|
|
||||||
- [API Documentation](https://docs.rs/actix-web-actors)
|
- [API Documentation](https://docs.rs/actix-web-actors)
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
# actix-web-codegen
|
# `actix-web-codegen`
|
||||||
|
|
||||||
> Routing and runtime macros for Actix Web.
|
> Routing and runtime macros for Actix Web.
|
||||||
|
|
||||||
|
<!-- prettier-ignore-start -->
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-web-codegen)
|
[](https://crates.io/crates/actix-web-codegen)
|
||||||
[](https://docs.rs/actix-web-codegen/4.2.2)
|
[](https://docs.rs/actix-web-codegen/4.2.2)
|
||||||

|

|
||||||
|
@ -11,6 +13,8 @@
|
||||||
[](https://crates.io/crates/actix-web-codegen)
|
[](https://crates.io/crates/actix-web-codegen)
|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
|
<!-- prettier-ignore-end -->
|
||||||
|
|
||||||
## Documentation & Resources
|
## Documentation & Resources
|
||||||
|
|
||||||
- [API Documentation](https://docs.rs/actix-web-codegen)
|
- [API Documentation](https://docs.rs/actix-web-codegen)
|
||||||
|
|
|
@ -372,13 +372,13 @@ You may need to review the [guidance on shared mutable state](https://docs.rs/ac
|
||||||
HttpServer::new(|| {
|
HttpServer::new(|| {
|
||||||
- App::new()
|
- App::new()
|
||||||
- .data(MyState::default())
|
- .data(MyState::default())
|
||||||
- .service(hander)
|
- .service(handler)
|
||||||
|
|
||||||
+ let my_state: Data<MyState> = Data::new(MyState::default());
|
+ let my_state: Data<MyState> = Data::new(MyState::default());
|
||||||
+
|
+
|
||||||
+ App::new()
|
+ App::new()
|
||||||
+ .app_data(my_state)
|
+ .app_data(my_state)
|
||||||
+ .service(hander)
|
+ .service(handler)
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -129,6 +129,8 @@ where
|
||||||
///
|
///
|
||||||
/// Data items are constructed during application initialization, before the server starts
|
/// Data items are constructed during application initialization, before the server starts
|
||||||
/// accepting requests.
|
/// accepting requests.
|
||||||
|
///
|
||||||
|
/// The returned data value `D` is wrapped as [`Data<D>`].
|
||||||
pub fn data_factory<F, Out, D, E>(mut self, data: F) -> Self
|
pub fn data_factory<F, Out, D, E>(mut self, data: F) -> Self
|
||||||
where
|
where
|
||||||
F: Fn() -> Out + 'static,
|
F: Fn() -> Out + 'static,
|
||||||
|
|
|
@ -69,7 +69,7 @@ pub(crate) type FnDataFactory =
|
||||||
/// HttpResponse::Ok()
|
/// HttpResponse::Ok()
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// /// Alteratively, use the `HttpRequest::app_data` method to access data in a handler.
|
/// /// Alternatively, use the `HttpRequest::app_data` method to access data in a handler.
|
||||||
/// async fn index_alt(req: HttpRequest) -> impl Responder {
|
/// async fn index_alt(req: HttpRequest) -> impl Responder {
|
||||||
/// let data = req.app_data::<Data<Mutex<MyData>>>().unwrap();
|
/// let data = req.app_data::<Data<Mutex<MyData>>>().unwrap();
|
||||||
/// let mut my_data = data.lock().unwrap();
|
/// let mut my_data = data.lock().unwrap();
|
||||||
|
|
|
@ -20,7 +20,7 @@ use crate::http::header::Accept;
|
||||||
pub struct Acceptable {
|
pub struct Acceptable {
|
||||||
mime: mime::Mime,
|
mime: mime::Mime,
|
||||||
|
|
||||||
/// Wether to match `*/*` mime type.
|
/// Whether to match `*/*` mime type.
|
||||||
///
|
///
|
||||||
/// Defaults to false because it's not very useful otherwise.
|
/// Defaults to false because it's not very useful otherwise.
|
||||||
match_star_star: bool,
|
match_star_star: bool,
|
||||||
|
|
|
@ -2,7 +2,7 @@ use actix_http::{header, uri::Uri, RequestHead};
|
||||||
|
|
||||||
use super::{Guard, GuardContext};
|
use super::{Guard, GuardContext};
|
||||||
|
|
||||||
/// Creates a guard that matches requests targetting a specific host.
|
/// Creates a guard that matches requests targeting a specific host.
|
||||||
///
|
///
|
||||||
/// # Matching Host
|
/// # Matching Host
|
||||||
/// This guard will:
|
/// This guard will:
|
||||||
|
|
|
@ -221,12 +221,9 @@ impl ServiceRequest {
|
||||||
|
|
||||||
/// Returns peer's socket address.
|
/// Returns peer's socket address.
|
||||||
///
|
///
|
||||||
/// Peer address is the directly connected peer's socket address. If a proxy is used in front of
|
/// See [`HttpRequest::peer_addr`] for more details.
|
||||||
/// the Actix Web server, then it would be address of this proxy.
|
|
||||||
///
|
///
|
||||||
/// To get client connection information `ConnectionInfo` should be used.
|
/// [`HttpRequest::peer_addr`]: crate::HttpRequest::peer_addr
|
||||||
///
|
|
||||||
/// Will only return None when called in unit tests.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn peer_addr(&self) -> Option<net::SocketAddr> {
|
pub fn peer_addr(&self) -> Option<net::SocketAddr> {
|
||||||
self.head().peer_addr
|
self.head().peer_addr
|
||||||
|
|
|
@ -86,76 +86,77 @@ impl Default for TestRequest {
|
||||||
|
|
||||||
#[allow(clippy::wrong_self_convention)]
|
#[allow(clippy::wrong_self_convention)]
|
||||||
impl TestRequest {
|
impl TestRequest {
|
||||||
/// Create TestRequest and set request uri
|
/// Constructs test request and sets request URI.
|
||||||
pub fn with_uri(path: &str) -> TestRequest {
|
pub fn with_uri(uri: &str) -> TestRequest {
|
||||||
TestRequest::default().uri(path)
|
TestRequest::default().uri(uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create TestRequest and set method to `Method::GET`
|
/// Constructs test request with GET method.
|
||||||
pub fn get() -> TestRequest {
|
pub fn get() -> TestRequest {
|
||||||
TestRequest::default().method(Method::GET)
|
TestRequest::default().method(Method::GET)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create TestRequest and set method to `Method::POST`
|
/// Constructs test request with POST method.
|
||||||
pub fn post() -> TestRequest {
|
pub fn post() -> TestRequest {
|
||||||
TestRequest::default().method(Method::POST)
|
TestRequest::default().method(Method::POST)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create TestRequest and set method to `Method::PUT`
|
/// Constructs test request with PUT method.
|
||||||
pub fn put() -> TestRequest {
|
pub fn put() -> TestRequest {
|
||||||
TestRequest::default().method(Method::PUT)
|
TestRequest::default().method(Method::PUT)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create TestRequest and set method to `Method::PATCH`
|
/// Constructs test request with PATCH method.
|
||||||
pub fn patch() -> TestRequest {
|
pub fn patch() -> TestRequest {
|
||||||
TestRequest::default().method(Method::PATCH)
|
TestRequest::default().method(Method::PATCH)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create TestRequest and set method to `Method::DELETE`
|
/// Constructs test request with DELETE method.
|
||||||
pub fn delete() -> TestRequest {
|
pub fn delete() -> TestRequest {
|
||||||
TestRequest::default().method(Method::DELETE)
|
TestRequest::default().method(Method::DELETE)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set HTTP version of this request
|
/// Sets HTTP version of this request.
|
||||||
pub fn version(mut self, ver: Version) -> Self {
|
pub fn version(mut self, ver: Version) -> Self {
|
||||||
self.req.version(ver);
|
self.req.version(ver);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set HTTP method of this request
|
/// Sets method of this request.
|
||||||
pub fn method(mut self, meth: Method) -> Self {
|
pub fn method(mut self, meth: Method) -> Self {
|
||||||
self.req.method(meth);
|
self.req.method(meth);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set HTTP URI of this request
|
/// Sets URI of this request.
|
||||||
pub fn uri(mut self, path: &str) -> Self {
|
pub fn uri(mut self, path: &str) -> Self {
|
||||||
self.req.uri(path);
|
self.req.uri(path);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a header, replacing any that were set with an equivalent field name.
|
/// Inserts a header, replacing any that were set with an equivalent field name.
|
||||||
pub fn insert_header(mut self, header: impl TryIntoHeaderPair) -> Self {
|
pub fn insert_header(mut self, header: impl TryIntoHeaderPair) -> Self {
|
||||||
self.req.insert_header(header);
|
self.req.insert_header(header);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Append a header, keeping any that were set with an equivalent field name.
|
/// Appends a header, keeping any that were set with an equivalent field name.
|
||||||
pub fn append_header(mut self, header: impl TryIntoHeaderPair) -> Self {
|
pub fn append_header(mut self, header: impl TryIntoHeaderPair) -> Self {
|
||||||
self.req.append_header(header);
|
self.req.append_header(header);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set cookie for this request.
|
/// Sets cookie for this request.
|
||||||
#[cfg(feature = "cookies")]
|
#[cfg(feature = "cookies")]
|
||||||
pub fn cookie(mut self, cookie: Cookie<'_>) -> Self {
|
pub fn cookie(mut self, cookie: Cookie<'_>) -> Self {
|
||||||
self.cookies.add(cookie.into_owned());
|
self.cookies.add(cookie.into_owned());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set request path pattern parameter.
|
/// Sets request path pattern parameter.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use actix_web::test::TestRequest;
|
/// use actix_web::test::TestRequest;
|
||||||
///
|
///
|
||||||
|
@ -171,19 +172,19 @@ impl TestRequest {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set peer addr.
|
/// Sets peer address.
|
||||||
pub fn peer_addr(mut self, addr: SocketAddr) -> Self {
|
pub fn peer_addr(mut self, addr: SocketAddr) -> Self {
|
||||||
self.peer_addr = Some(addr);
|
self.peer_addr = Some(addr);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set request payload.
|
/// Sets request payload.
|
||||||
pub fn set_payload(mut self, data: impl Into<Bytes>) -> Self {
|
pub fn set_payload(mut self, data: impl Into<Bytes>) -> Self {
|
||||||
self.req.set_payload(data);
|
self.req.set_payload(data);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serialize `data` to a URL encoded form and set it as the request payload.
|
/// Serializes `data` to a URL encoded form and set it as the request payload.
|
||||||
///
|
///
|
||||||
/// The `Content-Type` header is set to `application/x-www-form-urlencoded`.
|
/// The `Content-Type` header is set to `application/x-www-form-urlencoded`.
|
||||||
pub fn set_form(mut self, data: impl Serialize) -> Self {
|
pub fn set_form(mut self, data: impl Serialize) -> Self {
|
||||||
|
@ -194,7 +195,7 @@ impl TestRequest {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serialize `data` to JSON and set it as the request payload.
|
/// Serializes `data` to JSON and set it as the request payload.
|
||||||
///
|
///
|
||||||
/// The `Content-Type` header is set to `application/json`.
|
/// The `Content-Type` header is set to `application/json`.
|
||||||
pub fn set_json(mut self, data: impl Serialize) -> Self {
|
pub fn set_json(mut self, data: impl Serialize) -> Self {
|
||||||
|
@ -204,27 +205,33 @@ impl TestRequest {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set application data. This is equivalent of `App::data()` method
|
/// Inserts application data.
|
||||||
/// for testing purpose.
|
///
|
||||||
pub fn data<T: 'static>(mut self, data: T) -> Self {
|
/// This is equivalent of `App::app_data()` method for testing purpose.
|
||||||
self.app_data.insert(Data::new(data));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set application data. This is equivalent of `App::app_data()` method
|
|
||||||
/// for testing purpose.
|
|
||||||
pub fn app_data<T: 'static>(mut self, data: T) -> Self {
|
pub fn app_data<T: 'static>(mut self, data: T) -> Self {
|
||||||
self.app_data.insert(data);
|
self.app_data.insert(data);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Inserts application data.
|
||||||
|
///
|
||||||
|
/// This is equivalent of `App::data()` method for testing purpose.
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn data<T: 'static>(mut self, data: T) -> Self {
|
||||||
|
self.app_data.insert(Data::new(data));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets resource map.
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
/// Set request config
|
|
||||||
pub(crate) fn rmap(mut self, rmap: ResourceMap) -> Self {
|
pub(crate) fn rmap(mut self, rmap: ResourceMap) -> Self {
|
||||||
self.rmap = rmap;
|
self.rmap = rmap;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Finalizes test request.
|
||||||
|
///
|
||||||
|
/// This request builder will be useless after calling `finish()`.
|
||||||
fn finish(&mut self) -> Request {
|
fn finish(&mut self) -> Request {
|
||||||
// mut used when cookie feature is enabled
|
// mut used when cookie feature is enabled
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
|
@ -251,14 +258,14 @@ impl TestRequest {
|
||||||
req
|
req
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Complete request creation and generate `Request` instance
|
/// Finalizes request creation and returns `Request` instance.
|
||||||
pub fn to_request(mut self) -> Request {
|
pub fn to_request(mut self) -> Request {
|
||||||
let mut req = self.finish();
|
let mut req = self.finish();
|
||||||
req.head_mut().peer_addr = self.peer_addr;
|
req.head_mut().peer_addr = self.peer_addr;
|
||||||
req
|
req
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Complete request creation and generate `ServiceRequest` instance
|
/// Finalizes request creation and returns `ServiceRequest` instance.
|
||||||
pub fn to_srv_request(mut self) -> ServiceRequest {
|
pub fn to_srv_request(mut self) -> ServiceRequest {
|
||||||
let (mut head, payload) = self.finish().into_parts();
|
let (mut head, payload) = self.finish().into_parts();
|
||||||
head.peer_addr = self.peer_addr;
|
head.peer_addr = self.peer_addr;
|
||||||
|
@ -279,12 +286,12 @@ impl TestRequest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Complete request creation and generate `ServiceResponse` instance
|
/// Finalizes request creation and returns `ServiceResponse` instance.
|
||||||
pub fn to_srv_response<B>(self, res: HttpResponse<B>) -> ServiceResponse<B> {
|
pub fn to_srv_response<B>(self, res: HttpResponse<B>) -> ServiceResponse<B> {
|
||||||
self.to_srv_request().into_response(res)
|
self.to_srv_request().into_response(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Complete request creation and generate `HttpRequest` instance
|
/// Finalizes request creation and returns `HttpRequest` instance.
|
||||||
pub fn to_http_request(mut self) -> HttpRequest {
|
pub fn to_http_request(mut self) -> HttpRequest {
|
||||||
let (mut head, _) = self.finish().into_parts();
|
let (mut head, _) = self.finish().into_parts();
|
||||||
head.peer_addr = self.peer_addr;
|
head.peer_addr = self.peer_addr;
|
||||||
|
@ -302,7 +309,7 @@ impl TestRequest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Complete request creation and generate `HttpRequest` and `Payload` instances
|
/// Finalizes request creation and returns `HttpRequest` and `Payload` pair.
|
||||||
pub fn to_http_parts(mut self) -> (HttpRequest, Payload) {
|
pub fn to_http_parts(mut self) -> (HttpRequest, Payload) {
|
||||||
let (mut head, payload) = self.finish().into_parts();
|
let (mut head, payload) = self.finish().into_parts();
|
||||||
head.peer_addr = self.peer_addr;
|
head.peer_addr = self.peer_addr;
|
||||||
|
@ -322,7 +329,7 @@ impl TestRequest {
|
||||||
(req, payload)
|
(req, payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Complete request creation, calls service and waits for response future completion.
|
/// Finalizes request creation, calls service, and waits for response future completion.
|
||||||
pub async fn send_request<S, B, E>(self, app: &S) -> S::Response
|
pub async fn send_request<S, B, E>(self, app: &S) -> S::Response
|
||||||
where
|
where
|
||||||
S: Service<Request, Response = ServiceResponse<B>, Error = E>,
|
S: Service<Request, Response = ServiceResponse<B>, Error = E>,
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
# awc (Actix Web Client)
|
# `awc` (Actix Web Client)
|
||||||
|
|
||||||
> Async HTTP and WebSocket client library.
|
> Async HTTP and WebSocket client library.
|
||||||
|
|
||||||
|
<!-- prettier-ignore-start -->
|
||||||
|
|
||||||
[](https://crates.io/crates/awc)
|
[](https://crates.io/crates/awc)
|
||||||
[](https://docs.rs/awc/3.3.0)
|
[](https://docs.rs/awc/3.3.0)
|
||||||

|

|
||||||
[](https://deps.rs/crate/awc/3.3.0)
|
[](https://deps.rs/crate/awc/3.3.0)
|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
|
<!-- prettier-ignore-end -->
|
||||||
|
|
||||||
## Documentation & Resources
|
## Documentation & Resources
|
||||||
|
|
||||||
- [API Documentation](https://docs.rs/awc)
|
- [API Documentation](https://docs.rs/awc)
|
||||||
|
|
10
justfile
10
justfile
|
@ -1,6 +1,11 @@
|
||||||
_list:
|
_list:
|
||||||
@just --list
|
@just --list
|
||||||
|
|
||||||
|
# Format workspace.
|
||||||
|
fmt:
|
||||||
|
cargo +nightly fmt
|
||||||
|
npx -y prettier --write $(fd --type=file --hidden --extension=md --extension=yml)
|
||||||
|
|
||||||
# Document crates in workspace.
|
# Document crates in workspace.
|
||||||
doc:
|
doc:
|
||||||
RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc --no-deps --workspace --features=rustls,openssl
|
RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc --no-deps --workspace --features=rustls,openssl
|
||||||
|
@ -9,3 +14,8 @@ doc:
|
||||||
doc-watch:
|
doc-watch:
|
||||||
RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc --no-deps --workspace --features=rustls,openssl --open
|
RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc --no-deps --workspace --features=rustls,openssl --open
|
||||||
cargo watch -- RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc --no-deps --workspace --features=rustls,openssl
|
cargo watch -- RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc --no-deps --workspace --features=rustls,openssl
|
||||||
|
|
||||||
|
# Update READMEs from crate root documentation.
|
||||||
|
update-readmes: && fmt
|
||||||
|
cd ./actix-files && cargo rdme --force
|
||||||
|
cd ./actix-router && cargo rdme --force
|
||||||
|
|
16
scripts/bump
16
scripts/bump
|
@ -112,17 +112,25 @@ echo
|
||||||
read -p "Update all references: (y/N) " UPDATE_REFERENCES
|
read -p "Update all references: (y/N) " UPDATE_REFERENCES
|
||||||
UPDATE_REFERENCES="${UPDATE_REFERENCES:-n}"
|
UPDATE_REFERENCES="${UPDATE_REFERENCES:-n}"
|
||||||
|
|
||||||
|
|
||||||
if [ "$UPDATE_REFERENCES" = 'y' ] || [ "$UPDATE_REFERENCES" = 'Y' ]; then
|
if [ "$UPDATE_REFERENCES" = 'y' ] || [ "$UPDATE_REFERENCES" = 'Y' ]; then
|
||||||
|
if [[ $NEW_VERSION == *".0.0" ]]; then
|
||||||
|
NEW_VERSION_SPEC="${NEW_VERSION%.0.0}"
|
||||||
|
elif [[ $NEW_VERSION == *".0" ]]; then
|
||||||
|
NEW_VERSION_SPEC="${NEW_VERSION%.0}"
|
||||||
|
else
|
||||||
|
NEW_VERSION_SPEC="$NEW_VERSION"
|
||||||
|
fi
|
||||||
|
|
||||||
for f in $(fd Cargo.toml); do
|
for f in $(fd Cargo.toml); do
|
||||||
sed -i.bak -E \
|
sed -i.bak -E \
|
||||||
"s/^(${PACKAGE_NAME} ?= ?\")[^\"]+(\")$/\1${NEW_VERSION}\2/g" $f
|
"s/^(${PACKAGE_NAME} ?= ?\")[^\"]+(\")$/\1${NEW_VERSION_SPEC}\2/g" $f
|
||||||
sed -i.bak -E \
|
sed -i.bak -E \
|
||||||
"s/^(${PACKAGE_NAME} ?=.*version ?= ?\")[^\"]+(\".*)$/\1${NEW_VERSION}\2/g" $f
|
"s/^(${PACKAGE_NAME} ?=.*version ?= ?\")[^\"]+(\".*)$/\1${NEW_VERSION_SPEC}\2/g" $f
|
||||||
sed -i.bak -E \
|
sed -i.bak -E \
|
||||||
"s/^(.*package ?= ?\"${PACKAGE_NAME}\".*version ?= ?\")[^\"]+(\".*)$/\1${NEW_VERSION}\2/g" $f
|
"s/^(.*package ?= ?\"${PACKAGE_NAME}\".*version ?= ?\")[^\"]+(\".*)$/\1${NEW_VERSION_SPEC}\2/g" $f
|
||||||
sed -i.bak -E \
|
sed -i.bak -E \
|
||||||
"s/^(.*version ?= ?\")[^\"]+(\".*package ?= ?\"${PACKAGE_NAME}\".*)$/\1${NEW_VERSION}\2/g" $f
|
"s/^(.*version ?= ?\")[^\"]+(\".*package ?= ?\"${PACKAGE_NAME}\".*)$/\1${NEW_VERSION_SPEC}\2/g" $f
|
||||||
|
|
||||||
# remove backup file
|
# remove backup file
|
||||||
rm -f $f.bak
|
rm -f $f.bak
|
||||||
|
|
Loading…
Reference in New Issue