1use std::rc::Rc;
20
21mod builder;
22mod cfg;
23mod codec;
24mod connection;
25mod connector;
26pub mod error;
27mod h1proto;
28mod h2proto;
29mod pool;
30mod request;
31mod response;
32mod sender;
33mod service;
34mod test;
35
36pub use self::builder::ClientBuilder;
37pub use self::cfg::ClientConfig;
38pub use self::request::ClientRequest;
39pub use self::response::{ClientResponse, JsonBody, MessageBody};
40pub use self::service::{ServiceRequest, ServiceResponse};
41pub use self::test::TestResponse;
42
43pub(crate) use self::codec::{ClientCodec, ClientPayloadCodec};
44#[cfg(feature = "ws")]
45pub(crate) use self::h1proto::host_header;
46use urly::Url;
47
48use crate::client::error::{ConnectError, InvalidUrl};
49use crate::http::{HeaderMap, Method, RequestHead, body::BodySize};
50use crate::service::{cfg::SharedCfg, pipeline::PipelineState};
51use crate::{Cfg, Pipeline, error::Error, io::IoBoxed};
52
53type ConnectorPipeline = PipelineState<SharedCfg, Connect, IoBoxed, Error<ConnectError>>;
54
55#[derive(Debug, Clone)]
56pub(crate) struct Connect {
57 pub(crate) uri: Url,
58 pub(crate) addr: Option<std::net::SocketAddr>,
59}
60
61#[derive(Debug, Clone)]
98pub struct Client {
99 cfg: Cfg<ClientConfig>,
100 svc: Rc<Pipeline<ServiceRequest, ServiceResponse, Error<error::ClientError>>>,
101}
102
103impl Default for Client {
104 fn default() -> Self {
105 Client::new()
106 }
107}
108
109impl Client {
110 pub fn new() -> Client {
112 ClientBuilder::new().build(SharedCfg::default())
113 }
114
115 pub fn builder() -> ClientBuilder {
117 ClientBuilder::new()
118 }
119
120 pub fn with_config(cfg: impl Into<SharedCfg>) -> Client {
122 ClientBuilder::new().build(cfg.into())
123 }
124
125 pub(crate) fn with_service(
126 cfg: Cfg<ClientConfig>,
127 svc: Pipeline<ServiceRequest, ServiceResponse, Error<error::ClientError>>,
128 ) -> Self {
129 Client {
130 cfg,
131 svc: Rc::new(svc),
132 }
133 }
134
135 pub async fn ready(&self) -> Result<(), Error<error::ClientError>> {
137 self.svc.ready().await
138 }
139
140 pub fn request<U>(&self, method: Method, url: U) -> ClientRequest
142 where
143 Url: TryFrom<U>,
144 <Url as TryFrom<U>>::Error: Into<InvalidUrl>,
145 {
146 let mut req = ClientRequest::new(method, url, self.cfg.clone(), self.svc.bind());
147 for (key, value) in self.cfg.headers() {
148 req = req.set_header_if_none(key.clone(), value.clone());
149 }
150 req
151 }
152
153 pub fn request_from<U>(&self, url: U, head: &RequestHead) -> ClientRequest
158 where
159 Url: TryFrom<U>,
160 <Url as TryFrom<U>>::Error: Into<InvalidUrl>,
161 {
162 let mut req = self.request(head.method.clone(), url);
163 for (key, value) in &head.headers {
164 if !self.cfg.headers().contains_key(key) {
166 req.headers_mut().append(key.clone(), value.clone());
167 }
168 }
169 req
170 }
171
172 pub fn get<U>(&self, url: U) -> ClientRequest
174 where
175 Url: TryFrom<U>,
176 <Url as TryFrom<U>>::Error: Into<InvalidUrl>,
177 {
178 self.request(Method::GET, url)
179 }
180
181 pub fn head<U>(&self, url: U) -> ClientRequest
183 where
184 Url: TryFrom<U>,
185 <Url as TryFrom<U>>::Error: Into<InvalidUrl>,
186 {
187 self.request(Method::HEAD, url)
188 }
189
190 pub fn put<U>(&self, url: U) -> ClientRequest
192 where
193 Url: TryFrom<U>,
194 <Url as TryFrom<U>>::Error: Into<InvalidUrl>,
195 {
196 self.request(Method::PUT, url)
197 }
198
199 pub fn post<U>(&self, url: U) -> ClientRequest
201 where
202 Url: TryFrom<U>,
203 <Url as TryFrom<U>>::Error: Into<InvalidUrl>,
204 {
205 self.request(Method::POST, url)
206 }
207
208 pub fn patch<U>(&self, url: U) -> ClientRequest
210 where
211 Url: TryFrom<U>,
212 <Url as TryFrom<U>>::Error: Into<InvalidUrl>,
213 {
214 self.request(Method::PATCH, url)
215 }
216
217 pub fn delete<U>(&self, url: U) -> ClientRequest
219 where
220 Url: TryFrom<U>,
221 <Url as TryFrom<U>>::Error: Into<InvalidUrl>,
222 {
223 self.request(Method::DELETE, url)
224 }
225
226 pub fn query<U>(&self, url: U) -> ClientRequest
228 where
229 Url: TryFrom<U>,
230 <Url as TryFrom<U>>::Error: Into<InvalidUrl>,
231 {
232 self.request(Method::QUERY, url)
233 }
234
235 pub fn options<U>(&self, url: U) -> ClientRequest
237 where
238 Url: TryFrom<U>,
239 <Url as TryFrom<U>>::Error: Into<InvalidUrl>,
240 {
241 self.request(Method::OPTIONS, url)
242 }
243}
244
245#[derive(Debug)]
246pub(crate) struct ClientRawRequest {
247 pub(crate) head: crate::http::Message<RequestHead>,
248 pub(crate) headers: Option<HeaderMap>,
249 pub(crate) size: BodySize,
250}
251
252#[cfg(test)]
253mod tests {
254 use super::*;
255 use crate::http::header::{self, HeaderValue};
256
257 #[crate::rt_test]
258 async fn request_from_head() {
259 let client = Client::builder().build(
260 SharedCfg::new("TEST").add(ClientConfig::new().set_header("x-default", "cfg").unwrap()),
261 );
262 let mut head = RequestHead {
263 method: Method::PATCH,
264 ..Default::default()
265 };
266 head.headers
267 .insert(header::ACCEPT, HeaderValue::from_static("text/html"));
268 head.headers
269 .append(header::ACCEPT, HeaderValue::from_static("text/plain"));
270 head.headers.insert(
271 "x-default".try_into().unwrap(),
272 HeaderValue::from_static("head"),
273 );
274
275 let req = client.request_from("http://localhost/", &head);
276 assert_eq!(req.get_method(), Method::PATCH);
277 let accept: Vec<_> = req.headers().get_all(header::ACCEPT).collect();
278 assert_eq!(accept, ["text/html", "text/plain"]);
279 let default: Vec<_> = req.headers().get_all("x-default").collect();
281 assert_eq!(default, ["cfg"]);
282
283 let req = Client::default().request_from("http://localhost/", &head);
284 assert_eq!(req.headers().get("x-default").unwrap(), "head");
285 }
286}