Skip to content

Commit 9fd452a

Browse files
committed
Add implementation
1 parent dac010c commit 9fd452a

File tree

12 files changed

+497
-1
lines changed

12 files changed

+497
-1
lines changed

.github/workflows/test.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: Test
2+
3+
on:
4+
push:
5+
branches: [master, develop]
6+
pull_request:
7+
branches: [master, develop]
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- uses: actions/checkout@v2
15+
- name: Run tests
16+
run: cargo test --verbose

.gitignore

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Created by https://www.gitignore.io/api/rust,macos
2+
# Edit at https://www.gitignore.io/?templates=rust,macos
3+
4+
### macOS ###
5+
# General
6+
.DS_Store
7+
.AppleDouble
8+
.LSOverride
9+
10+
# Icon must end with two \r
11+
Icon
12+
13+
# Thumbnails
14+
._*
15+
16+
# Files that might appear in the root of a volume
17+
.DocumentRevisions-V100
18+
.fseventsd
19+
.Spotlight-V100
20+
.TemporaryItems
21+
.Trashes
22+
.VolumeIcon.icns
23+
.com.apple.timemachine.donotpresent
24+
25+
# Directories potentially created on remote AFP share
26+
.AppleDB
27+
.AppleDesktop
28+
Network Trash Folder
29+
Temporary Items
30+
.apdisk
31+
32+
### Rust ###
33+
# Generated by Cargo
34+
# will have compiled files and executables
35+
/target/
36+
37+
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
38+
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
39+
Cargo.lock
40+
41+
# These are backup files generated by rustfmt
42+
**/*.rs.bk
43+
44+
### Others ###
45+
.halt.releez.yml
46+
routerify-multipart.iml
47+
/.idea
48+
49+
# End of https://www.gitignore.io/api/rust,macos

Cargo.toml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
[package]
2+
name = "routerify-multipart"
3+
version = "1.0.0"
4+
description = "A multipart/form-data parser for Routerify."
5+
homepage = "https://github.com/routerify/routerify-multipart"
6+
repository = "https://github.com/routerify/routerify-multipart"
7+
keywords = ["routerify", "hyper-rs", "hyper", "multipart", "multipart-formdata"]
8+
categories = ["asynchronous", "web-programming", "web-programming::http-server"]
9+
authors = ["Rousan Ali <[email protected]>"]
10+
readme = "README.md"
11+
license = "MIT"
12+
edition = "2018"
13+
14+
[package.metadata.docs.rs]
15+
all-features = true
16+
17+
[package.metadata.playground]
18+
features = ["all"]
19+
20+
[features]
21+
default = []
22+
all = ["json"]
23+
json = ["multer/json"]
24+
25+
[dependencies]
26+
routerify = "1.1"
27+
hyper = "0.13"
28+
multer = "1.0"
29+
30+
[dev-dependencies]
31+
tokio = { version = "0.2", features = ["full"] }

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2020 Rousan Ali
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,91 @@
1+
[![Github Actions Status](https://github.com/routerify/routerify-multipart/workflows/Test/badge.svg)](https://github.com/routerify/routerify-multipart/actions)
2+
[![crates.io](https://img.shields.io/crates/v/routerify-multipart.svg)](https://crates.io/crates/routerify-multipart)
3+
[![Documentation](https://docs.rs/routerify-multipart/badge.svg)](https://docs.rs/routerify-multipart)
4+
[![MIT](https://img.shields.io/crates/l/routerify-multipart.svg)](./LICENSE)
5+
16
# routerify-multipart
2-
A multipart/form-data parser for Routerify
7+
8+
A `multipart/form-data` parser for [`Routerify`](https://github.com/routerify/routerify).
9+
10+
[Docs](https://docs.rs/routerify-multipart)
11+
12+
## Install
13+
14+
Add this to your `Cargo.toml` file:
15+
16+
```toml
17+
[dependencies]
18+
routerify = "1.1"
19+
routerify-multipart = "1.0"
20+
```
21+
22+
## Example
23+
24+
```rust
25+
use hyper::{Body, Request, Response, Server, StatusCode};
26+
use routerify::{Error, Router, RouterService};
27+
// Import `RequestMultipartExt` trait.
28+
use routerify_multipart::RequestMultipartExt;
29+
use std::net::SocketAddr;
30+
31+
// A handler to handle file uploading in `multipart/form-data` content-type.
32+
async fn file_upload_handler(req: Request<Body>) -> Result<Response<Body>, Error> {
33+
// Convert the request into a `Multipart` instance.
34+
let mut multipart = match req.into_multipart() {
35+
Ok(m) => m,
36+
Err(err) => {
37+
return Ok(Response::builder()
38+
.status(StatusCode::BAD_REQUEST)
39+
.body(Body::from(format!("Bad Request: {}", err)))
40+
.unwrap());
41+
}
42+
};
43+
44+
// Iterate over the fields.
45+
while let Some(mut field) = multipart.next_field().await.map_err(|err| Error::wrap(err))? {
46+
// Get the field name.
47+
let name = field.name();
48+
// Get the field's filename if provided in "Content-Disposition" header.
49+
let file_name = field.file_name();
50+
51+
println!("Name {:?}, File name: {:?}", name, file_name);
52+
53+
// Process the field data chunks e.g. store them in a file.
54+
while let Some(chunk) = field.chunk().await.map_err(|err| Error::wrap(err))? {
55+
// Do something with field chunk.
56+
println!("Chunk: {:?}", chunk);
57+
}
58+
}
59+
60+
Ok(Response::new(Body::from("Success")))
61+
}
62+
63+
// Create a router.
64+
fn router() -> Router<Body, Error> {
65+
// Register the handlers.
66+
Router::builder().post("/upload", file_upload_handler).build().unwrap()
67+
}
68+
69+
#[tokio::main]
70+
async fn main() {
71+
let router = router();
72+
73+
// Create a Service from the router above to handle incoming requests.
74+
let service = RouterService::new(router).unwrap();
75+
76+
// The address on which the server will be listening.
77+
let addr = SocketAddr::from(([127, 0, 0, 1], 3001));
78+
79+
// Create a server by passing the created service to `.serve` method.
80+
let server = Server::bind(&addr).serve(service);
81+
82+
println!("App is running on: {}", addr);
83+
if let Err(err) = server.await {
84+
eprintln!("Server error: {}", err);
85+
}
86+
}
87+
```
88+
89+
## Contributing
90+
91+
Your PRs and suggestions are always welcome.

examples/example.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
use hyper::{Body, Request, Response, Server, StatusCode};
2+
use routerify::{Error, Router, RouterService};
3+
// Import `RequestMultipartExt` trait.
4+
use routerify_multipart::RequestMultipartExt;
5+
use std::net::SocketAddr;
6+
7+
// A handler to handle file uploading in `multipart/form-data` content-type.
8+
async fn file_upload_handler(req: Request<Body>) -> Result<Response<Body>, Error> {
9+
// Convert the request into a `Multipart` instance.
10+
let mut multipart = match req.into_multipart() {
11+
Ok(m) => m,
12+
Err(err) => {
13+
return Ok(Response::builder()
14+
.status(StatusCode::BAD_REQUEST)
15+
.body(Body::from(format!("Bad Request: {}", err)))
16+
.unwrap());
17+
}
18+
};
19+
20+
// Iterate over the fields.
21+
while let Some(mut field) = multipart.next_field().await.map_err(|err| Error::wrap(err))? {
22+
// Get the field name.
23+
let name = field.name();
24+
// Get the field's filename if provided in "Content-Disposition" header.
25+
let file_name = field.file_name();
26+
27+
println!("Name {:?}, File name: {:?}", name, file_name);
28+
29+
// Process the field data chunks e.g. store them in a file.
30+
while let Some(chunk) = field.chunk().await.map_err(|err| Error::wrap(err))? {
31+
// Do something with field chunk.
32+
println!("Chunk: {:?}", chunk);
33+
}
34+
}
35+
36+
Ok(Response::new(Body::from("Success")))
37+
}
38+
39+
// Create a router.
40+
fn router() -> Router<Body, Error> {
41+
// Register the handlers.
42+
Router::builder().post("/upload", file_upload_handler).build().unwrap()
43+
}
44+
45+
#[tokio::main]
46+
async fn main() {
47+
let router = router();
48+
49+
// Create a Service from the router above to handle incoming requests.
50+
let service = RouterService::new(router).unwrap();
51+
52+
// The address on which the server will be listening.
53+
let addr = SocketAddr::from(([127, 0, 0, 1], 3001));
54+
55+
// Create a server by passing the created service to `.serve` method.
56+
let server = Server::bind(&addr).serve(service);
57+
58+
println!("App is running on: {}", addr);
59+
if let Err(err) = server.await {
60+
eprintln!("Server error: {}", err);
61+
}
62+
}

examples/test.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
use hyper::{Body, Request, Response, Server};
2+
use routerify::{Error, Router, RouterService};
3+
use routerify_multipart::RequestMultipartExt;
4+
use std::net::SocketAddr;
5+
6+
async fn file_upload_handler(req: Request<Body>) -> Result<Response<Body>, Error> {
7+
let mut multipart = req.into_multipart().map_err(|err| Error::wrap(err))?;
8+
9+
while let Some(field) = multipart.next_field().await.map_err(|err| Error::wrap(err))? {
10+
let name = field.name();
11+
let file_name = field.file_name();
12+
13+
println!("Name {:?}, File name: {:?}", name, file_name);
14+
15+
let content = field.text().await.map_err(|err| Error::wrap(err))?;
16+
println!("Content {:?}", content);
17+
}
18+
19+
Ok(Response::new(Body::from("Success")))
20+
}
21+
22+
fn router() -> Router<Body, Error> {
23+
Router::builder().post("/upload", file_upload_handler).build().unwrap()
24+
}
25+
26+
#[tokio::main]
27+
async fn main() {
28+
let router = router();
29+
30+
// Create a Service from the router above to handle incoming requests.
31+
let service = RouterService::new(router).unwrap();
32+
33+
// The address on which the server will be listening.
34+
let addr = SocketAddr::from(([127, 0, 0, 1], 3001));
35+
36+
// Create a server by passing the created service to `.serve` method.
37+
let server = Server::bind(&addr).serve(service);
38+
39+
println!("App is running on: {}", addr);
40+
if let Err(err) = server.await {
41+
eprintln!("Server error: {}", err);
42+
}
43+
}

releez.yml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
version: 1.0.0
2+
checklist:
3+
- name: Checkout develop and sync with remote
4+
type: auto
5+
run:
6+
- git checkout develop
7+
- git push
8+
- name: Check syntax
9+
type: auto
10+
run:
11+
- cargo check --release --features="all"
12+
- name: Run tests
13+
type: auto
14+
run:
15+
- cargo test --release --features="all"
16+
- name: Start a new release branch
17+
type: auto
18+
run:
19+
- git flow release start "v${VERSION}"
20+
- name: Make sure code is formatted
21+
type: auto
22+
run:
23+
- cargo fmt
24+
- name: Bump version
25+
type: manual
26+
instructions:
27+
- Please update version with ${VERSION} in Cargo.toml file.
28+
- Please update version with ${VERSION} in README.md file if needed.
29+
- name: Commit changes
30+
type: auto
31+
run:
32+
- git add --all && git commit -m "Bump version"
33+
- name: Finish release branch
34+
type: auto
35+
run:
36+
- git flow release finish -s "v${VERSION}"
37+
- name: Push branches and tags to Github
38+
type: auto
39+
run:
40+
- git checkout master
41+
- git push origin master
42+
- git push origin develop
43+
- git push --tags
44+
- name: Edit tag on Github
45+
type: manual
46+
instructions:
47+
- Tag is pushed to Github(https://github.com/routerify/routerify-multipart/releases). Edit it there and make it a release.
48+
- name: Publish to crates.io
49+
type: auto
50+
confirm: Are you sure to publish it to crates.io?
51+
run:
52+
- cargo publish
53+
- git checkout develop

rustfmt.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
max_width = 120
2+
tab_spaces = 4

0 commit comments

Comments
 (0)