Skip to content

Commit

Permalink
mock: Move from multipart to multer
Browse files Browse the repository at this point in the history
The multipart crate has been unmaintained for a while, replace with
multer which e.g. also used by axum. Unfortunately multer is async only,
while the wiremock Response trait is sync[0]. So that does require some
hoops to jump through for now.

0: LukeMathWalker/wiremock-rs#84

Signed-off-by: Sjoerd Simons <[email protected]>
  • Loading branch information
sjoerdsimons committed Feb 18, 2024
1 parent 2cd3234 commit 4d5689b
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 39 deletions.
3 changes: 2 additions & 1 deletion gitlab-runner-mock/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ serde = "1.0.125"
serde_json = "1.0.64"
url = "2.2.1"
thiserror = "1.0.24"
multipart = { version = "0.18.0", features = [] }
http = "1.0.0"
multer = "3.0.0"
futures = "0.3.15"
82 changes: 44 additions & 38 deletions gitlab-runner-mock/src/api/artifacts.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use std::convert::Infallible;

use http::header::CONTENT_TYPE;
use http::StatusCode;
use multipart::server::Multipart;
use std::io::Read;
use multer::Multipart;
use wiremock::ResponseTemplate;
use wiremock::{Request, Respond};

Expand Down Expand Up @@ -44,47 +45,52 @@ impl Respond for JobArtifactsUploader {
.get(&CONTENT_TYPE)
.expect("Missing content type");

let boundary = ct
.to_str()
.expect("Invalid content type header value")
.split_once("boundary=")
.map(|x| x.1)
.expect("Missing boundary");

let mut multipart =
Multipart::with_body(std::io::Cursor::new(&request.body), boundary);
let boundary = ct.to_str().expect("Invalid content type header value");
let boundary = multer::parse_boundary(boundary).expect("Failed to parse boundary");
let mut multipart = Multipart::new(
futures::stream::iter(
request
.body
.chunks(10254)
.map(|chunk| Ok::<_, Infallible>(Vec::from(chunk))),
),
boundary,
);

let mut filename = None;
let mut data = Vec::new();
let mut artifact_type = None;
let mut artifact_format = None;
while let Some(mut part) = multipart.read_entry().unwrap() {
match &*part.headers.name {
"file" => {
filename = part.headers.filename.clone();
part.data
.read_to_end(&mut data)
.expect("failed to read multipart data");
}
"artifact_format" => {
let mut value = String::new();
part.data
.read_to_string(&mut value)
.expect("failed to read artifact format");
artifact_format = Some(value);
}
"artifact_type" => {
let mut value = String::new();
part.data
.read_to_string(&mut value)
.expect("failed to read artifact type");
artifact_type = Some(value);
}
_ => {
unimplemented!("Unknown field in request: {}", &*part.headers.name);
let mut artifact_type: Option<String> = None;
let mut artifact_format: Option<String> = None;

// All inputs are non-blocking; so this actually never blocks or waits or anything
// external.
futures::executor::block_on(async {
while let Some(field) = multipart.next_field().await.unwrap() {
match field.name().expect("Field without a name") {
"file" => {
filename = field.file_name().map(String::from);
data = field
.bytes()
.await
.expect("failed to read multipart data")
.to_vec();
}
"artifact_format" => {
let value =
field.text().await.expect("failed to read artifact format");
artifact_format = Some(value);
}
"artifact_type" => {
let value =
field.text().await.expect("failed to read artifact type");
artifact_type = Some(value);
}
name => {
unimplemented!("Unknown field in request: {}", name);
}
}
}
}
});
job.add_artifact(
filename,
data,
Expand Down

0 comments on commit 4d5689b

Please sign in to comment.