1use std::{borrow::Cow, error::Error};
6
7use faststr::FastStr;
8use http::{
9 header::{HeaderMap, HeaderName, HeaderValue},
10 method::Method,
11 uri::{PathAndQuery, Scheme, Uri},
12 version::Version,
13};
14use motore::layer::Layer;
15use volo::{
16 client::{Apply, OneShotService, WithOptService},
17 net::Address,
18};
19
20use super::{CallOpt, insert_header, target::Target};
21use crate::{
22 body::Body,
23 context::ClientContext,
24 error::{
25 BoxError, ClientError,
26 client::{Result, builder_error},
27 },
28 request::Request,
29 response::Response,
30 utils::consts,
31};
32
33pub struct RequestBuilder<S, B = Body> {
35 inner: S,
36 target: Target,
37 version: Option<Version>,
38 request: Request<B>,
39 status: Result<()>,
40}
41
42impl<S> RequestBuilder<S> {
43 pub(super) fn new(inner: S) -> Self {
44 Self {
45 inner,
46 target: Default::default(),
47 version: None,
48 request: Request::default(),
49 status: Ok(()),
50 }
51 }
52
53 pub fn data<D>(mut self, data: D) -> Self
55 where
56 D: TryInto<Body>,
57 D::Error: Error + Send + Sync + 'static,
58 {
59 if self.status.is_err() {
60 return self;
61 }
62
63 let body = match data.try_into() {
64 Ok(body) => body,
65 Err(err) => {
66 self.status = Err(builder_error(err));
67 return self;
68 }
69 };
70
71 let (parts, _) = self.request.into_parts();
72 self.request = Request::from_parts(parts, body);
73
74 self
75 }
76
77 #[cfg(feature = "json")]
79 pub fn json<T>(mut self, json: &T) -> Self
80 where
81 T: serde::Serialize,
82 {
83 if self.status.is_err() {
84 return self;
85 }
86
87 let json = match crate::utils::json::serialize(json) {
88 Ok(json) => json,
89 Err(err) => {
90 self.status = Err(builder_error(err));
91 return self;
92 }
93 };
94
95 let (mut parts, _) = self.request.into_parts();
96 parts.headers.insert(
97 http::header::CONTENT_TYPE,
98 crate::utils::consts::APPLICATION_JSON,
99 );
100 self.request = Request::from_parts(parts, Body::from(json));
101
102 self
103 }
104
105 #[cfg(feature = "form")]
107 pub fn form<T>(mut self, form: &T) -> Self
108 where
109 T: serde::Serialize,
110 {
111 if self.status.is_err() {
112 return self;
113 }
114
115 let form = match serde_urlencoded::to_string(form) {
116 Ok(form) => form,
117 Err(err) => {
118 self.status = Err(builder_error(err));
119 return self;
120 }
121 };
122
123 let (mut parts, _) = self.request.into_parts();
124 parts.headers.insert(
125 http::header::CONTENT_TYPE,
126 crate::utils::consts::APPLICATION_WWW_FORM_URLENCODED,
127 );
128 self.request = Request::from_parts(parts, Body::from(form));
129
130 self
131 }
132
133 #[cfg(feature = "multipart")]
140 pub fn multipart(mut self, form: crate::client::multipart::Form) -> Self {
141 if self.status.is_err() {
142 return self;
143 }
144
145 let content_type = form.content_type();
146 let (mut parts, _) = self.request.into_parts();
147 parts
148 .headers
149 .insert(http::header::CONTENT_TYPE, content_type);
150 self.request = Request::from_parts(parts, form.into_body());
151
152 self
153 }
154}
155
156impl<S, B> RequestBuilder<S, B> {
157 pub fn method(mut self, method: Method) -> Self {
159 *self.request.method_mut() = method;
160 self
161 }
162
163 pub fn method_ref(&self) -> &Method {
165 self.request.method()
166 }
167
168 pub fn uri<U>(mut self, uri: U) -> Self
177 where
178 U: TryInto<Uri>,
179 U::Error: Into<BoxError>,
180 {
181 if self.status.is_err() {
182 return self;
183 }
184 let uri = match uri.try_into() {
185 Ok(uri) => uri,
186 Err(err) => {
187 self.status = Err(builder_error(err));
188 return self;
189 }
190 };
191 if uri.host().is_some() {
192 match Target::from_uri(&uri) {
193 Ok(target) => self.target = target,
194 Err(err) => {
195 self.status = Err(err);
196 return self;
197 }
198 }
199 }
200 let rela_uri = uri
201 .path_and_query()
202 .map(PathAndQuery::to_owned)
203 .unwrap_or_else(|| PathAndQuery::from_static("/"))
204 .into();
205 *self.request.uri_mut() = rela_uri;
206
207 self
208 }
209
210 #[cfg(feature = "query")]
212 pub fn set_query<T>(mut self, query: &T) -> Self
213 where
214 T: serde::Serialize,
215 {
216 if self.status.is_err() {
217 return self;
218 }
219 let query_str = match serde_urlencoded::to_string(query) {
220 Ok(query) => query,
221 Err(err) => {
222 self.status = Err(builder_error(err));
223 return self;
224 }
225 };
226
227 let path_str = self.request.uri().path();
229 let mut path = String::with_capacity(path_str.len() + 1 + query_str.len());
230 path.push_str(path_str);
231 path.push('?');
232 path.push_str(&query_str);
233 let Ok(uri) = Uri::from_maybe_shared(path) else {
234 unreachable!();
236 };
237
238 *self.request.uri_mut() = uri;
239
240 self
241 }
242
243 pub fn uri_ref(&self) -> &Uri {
245 self.request.uri()
246 }
247
248 pub fn version(mut self, version: Version) -> Self {
252 self.version = Some(version);
253 self
254 }
255
256 pub fn version_ref(&self) -> Option<Version> {
258 self.version
259 }
260
261 pub fn header<K, V>(mut self, key: K, value: V) -> Self
263 where
264 K: TryInto<HeaderName>,
265 K::Error: Error + Send + Sync + 'static,
266 V: TryInto<HeaderValue>,
267 V::Error: Error + Send + Sync + 'static,
268 {
269 if self.status.is_err() {
270 return self;
271 }
272
273 if let Err(err) = insert_header(self.request.headers_mut(), key, value) {
274 self.status = Err(err);
275 }
276
277 self
278 }
279
280 pub fn headers(&self) -> &HeaderMap {
282 self.request.headers()
283 }
284
285 pub fn headers_mut(&mut self) -> &mut HeaderMap {
287 self.request.headers_mut()
288 }
289
290 pub fn address<A>(mut self, address: A) -> Self
292 where
293 A: Into<Address>,
294 {
295 self.target = Target::from(address.into());
296 self
297 }
298
299 pub fn host<H>(mut self, host: H) -> Self
306 where
307 H: Into<Cow<'static, str>>,
308 {
309 self.target = unsafe {
311 Target::new_host_unchecked(
312 Scheme::HTTP,
313 FastStr::from(host.into()),
314 consts::HTTP_DEFAULT_PORT,
315 )
316 };
317 self
318 }
319
320 pub fn with_scheme(mut self, scheme: Scheme) -> Self {
322 if self.status.is_err() {
323 return self;
324 }
325 if let Err(err) = self.target.set_scheme(scheme) {
326 self.status = Err(err);
327 }
328 self
329 }
330
331 pub fn with_port(mut self, port: u16) -> Self {
333 if self.status.is_err() {
334 return self;
335 }
336 if let Err(err) = self.target.set_port(port) {
337 self.status = Err(err);
338 }
339 self
340 }
341
342 pub fn target_ref(&self) -> &Target {
344 &self.target
345 }
346
347 pub fn target_mut(&mut self) -> &mut Target {
349 &mut self.target
350 }
351
352 pub fn body<B2>(self, body: B2) -> RequestBuilder<S, B2> {
354 let (parts, _) = self.request.into_parts();
355 let request = Request::from_parts(parts, body);
356
357 RequestBuilder {
358 inner: self.inner,
359 target: self.target,
360 version: self.version,
361 request,
362 status: self.status,
363 }
364 }
365
366 pub fn body_ref(&self) -> &B {
368 self.request.body()
369 }
370
371 pub fn layer<L>(self, layer: L) -> RequestBuilder<L::Service, B>
375 where
376 L: Layer<S>,
377 {
378 RequestBuilder {
379 inner: layer.layer(self.inner),
380 target: self.target,
381 version: self.version,
382 request: self.request,
383 status: self.status,
384 }
385 }
386
387 pub fn with_callopt(self, callopt: CallOpt) -> RequestBuilder<WithOptService<S, CallOpt>, B> {
389 self.layer(WithOptLayer::new(callopt))
390 }
391
392 fn set_version(&mut self) {
393 let ver = match self.version {
394 Some(ver) => ver,
395 None => {
396 if cfg!(feature = "http1") {
398 Version::HTTP_11
399 } else {
400 Version::HTTP_2
401 }
402 }
403 };
404 *self.request.version_mut() = ver;
405 }
406
407 pub async fn send<RespBody>(mut self) -> Result<Response<RespBody>>
409 where
410 S: OneShotService<
411 ClientContext,
412 Request<B>,
413 Response = Response<RespBody>,
414 Error = ClientError,
415 > + Send
416 + Sync
417 + 'static,
418 B: Send + 'static,
419 {
420 self.set_version();
421 self.status?;
422
423 let mut cx = ClientContext::new();
424 self.target.apply(&mut cx)?;
425 self.inner.call(&mut cx, self.request).await
426 }
427}
428
429struct WithOptLayer {
430 opt: CallOpt,
431}
432
433impl WithOptLayer {
434 const fn new(opt: CallOpt) -> Self {
435 Self { opt }
436 }
437}
438
439impl<S> Layer<S> for WithOptLayer {
440 type Service = WithOptService<S, CallOpt>;
441
442 fn layer(self, inner: S) -> Self::Service {
443 WithOptService::new(inner, self.opt)
444 }
445}
446
447#[cfg(all(test, feature = "multipart"))]
448mod tests {
449 use std::future::Future;
450
451 use http::header::CONTENT_TYPE;
452 use motore::service::Service;
453
454 use super::*;
455 use crate::{
456 body::BodyConversion,
457 client::{Client, multipart::Form, test_helpers::MockTransport},
458 };
459
460 struct InspectMultipartRequest;
461
462 impl Service<ClientContext, Request> for InspectMultipartRequest {
463 type Response = Response;
464 type Error = ClientError;
465
466 fn call(
467 &self,
468 _: &mut ClientContext,
469 req: Request,
470 ) -> impl Future<Output = Result<Self::Response>> + Send {
471 async move {
472 assert_eq!(req.method(), Method::POST);
473 assert_eq!(req.uri(), "/upload");
474
475 let content_type = req
476 .headers()
477 .get(CONTENT_TYPE)
478 .expect("multipart should set content-type")
479 .to_str()
480 .unwrap()
481 .to_owned();
482 let boundary = content_type
483 .strip_prefix("multipart/form-data; boundary=")
484 .expect("content-type should include multipart boundary")
485 .to_owned();
486
487 let (_, body) = req.into_parts();
488 let body = body.into_string().await.unwrap();
489 let expected = format!(
490 "--{boundary}\r\nContent-Disposition: form-data; \
491 name=\"field\"\r\n\r\nvalue\r\n--{boundary}--\r\n"
492 );
493 assert_eq!(body, expected);
494
495 Ok(Response::default())
496 }
497 }
498 }
499
500 #[tokio::test]
501 async fn multipart_sets_content_type_and_body() {
502 let client = Client::builder()
503 .mock(MockTransport::service(InspectMultipartRequest))
504 .unwrap();
505 let form = Form::new().text("field", "value");
506
507 client.post("/upload").multipart(form).send().await.unwrap();
508 }
509}