1use std::sync::{Arc, Mutex, mpsc};
3use std::{convert::Infallible, fmt, future::Future, io, net, net::SocketAddr};
4use std::{pin::Pin, rc::Rc, thread, time};
5
6#[cfg(feature = "cookie")]
7use coo_kie::Cookie;
8use serde::{Serialize, de::DeserializeOwned};
9use urly::Url;
10use uuid::Uuid;
11
12use crate::client::error::ClientPayloadError;
13use crate::client::{Client, ClientConfig, ClientRequest, ClientResponse};
14use crate::error::Error;
15use crate::http::error::{HttpError, ResponseError};
16use crate::http::header::{CONTENT_TYPE, HeaderName, HeaderValue};
17use crate::http::test::TestRequest as HttpTestRequest;
18use crate::http::{self, HttpService, Method, Payload, Request, Response, StatusCode, Version};
19#[cfg(feature = "ws")]
20use crate::io::Sealed;
21use crate::router::{Path, ResourceDef};
22use crate::service::boxed::{self, BoxServiceFactory};
23use crate::service::{IntoServiceFactory, Pipeline, fn_service};
24use crate::time::{Millis, Seconds};
25use crate::util::{Bytes, BytesMut, Stream, stream_recv};
26#[cfg(feature = "ws")]
27use crate::ws::{WsClient, WsClientConfig, WsConnection, error::WsClientError};
28use crate::{Service, ServiceFactory, SharedCfg, io::IoConfig, rt::System, server::Server};
29
30use crate::web::config::WebAppConfig;
31use crate::web::httprequest::HttpRequest;
32use crate::web::rmap::ResourceMap;
33use crate::web::{FromRequest, HttpResponse, Responder, State, WebRequest, WebResponse};
34
35fn set_peer_addr(head: &mut http::Message<http::RequestHead>, addr: Option<SocketAddr>) {
36 #[derive(Debug)]
37 struct TestIo {
38 io: crate::io::Io,
39 _peer: crate::io::testing::IoTest,
40 }
41
42 impl http::IoAccess for TestIo {
43 fn get(&self) -> Option<&crate::io::IoRef> {
44 Some(self.io.as_ref())
45 }
46
47 fn take(&self) -> Option<(crate::io::IoBoxed, http::h1::Codec)> {
48 None
49 }
50 }
51
52 if let Some(addr) = addr {
53 let (client, server) = crate::io::testing::IoTest::create();
54 let io = crate::io::Io::new(server.set_peer_addr(addr), SharedCfg::default());
55 head.io = http::CurrentIo::new(Rc::new(TestIo { io, _peer: client }));
56 }
57}
58
59pub fn ok_service<St: State, In>()
61-> impl Service<St, WebRequest<In>, Res = WebResponse, Error = std::convert::Infallible> {
62 default_service::<St, In>(StatusCode::OK)
63}
64
65pub fn default_service<St: State, In>(
67 status_code: StatusCode,
68) -> impl Service<St, WebRequest<In>, Res = WebResponse, Error = Infallible> {
69 fn_service(async move |req: WebRequest<In>| {
70 Ok::<_, Infallible>(req.into_response(HttpResponse::builder(status_code).build()))
71 })
72}
73
74pub async fn init_service<R, S, E>(app: R) -> Pipeline<Request, Response, E>
98where
99 R: IntoServiceFactory<S, (), Request>,
100 S: ServiceFactory<(), Request, Res = Response, Error = E> + 'static,
101 S::InitError: fmt::Debug,
102{
103 let srv = app.into_factory().map_init_err(|e| log::error!("{e:?}"));
104 srv.pipeline(()).await.unwrap()
105}
106
107pub async fn init_service_st<St, R, S, E>(st: St, app: R) -> Pipeline<Request, Response, E>
132where
133 St: 'static,
134 R: IntoServiceFactory<S, St, Request>,
135 S: ServiceFactory<St, Request, Res = Response, Error = E> + 'static,
136 S::InitError: fmt::Debug,
137{
138 let srv = app.into_factory().map_init_err(|e| log::error!("{e:?}"));
139 srv.pipeline(st).await.unwrap()
140}
141
142pub async fn call_service<R, E>(app: &Pipeline<R, Response, E>, req: R) -> Response
166where
167 R: 'static,
168 E: fmt::Debug + 'static,
169{
170 app.call(req).await.unwrap()
171}
172
173pub async fn read_response<E>(app: &Pipeline<Request, Response, E>, req: Request) -> Bytes
199where
200 E: 'static,
201{
202 let mut resp = app
203 .call(req)
204 .await
205 .unwrap_or_else(|_| panic!("read_response failed at application call"));
206
207 let mut body = resp.take_body();
208 let mut bytes = BytesMut::new();
209 while let Some(item) = stream_recv(&mut body).await {
210 bytes.extend_from_slice(&item.unwrap());
211 }
212 bytes.freeze()
213}
214
215pub async fn read_body(res: impl Into<Response>) -> Bytes {
242 let mut body = res.into().take_body();
243 let mut bytes = BytesMut::new();
244 while let Some(item) = stream_recv(&mut body).await {
245 bytes.extend_from_slice(&item.unwrap());
246 }
247 bytes.freeze()
248}
249
250pub async fn load_stream<S, E>(mut stream: S) -> Result<Bytes, E>
252where
253 S: Stream<Item = Result<Bytes, E>> + Unpin,
254{
255 let mut data = BytesMut::new();
256 while let Some(item) = stream_recv(&mut stream).await {
257 data.extend_from_slice(&item?);
258 }
259 Ok(data.freeze())
260}
261
262pub async fn read_response_json<T, E>(app: &Pipeline<Request, Response, E>, req: Request) -> T
298where
299 T: DeserializeOwned,
300 E: 'static,
301{
302 let body = read_response(app, req).await;
303
304 serde_json::from_slice(&body)
305 .unwrap_or_else(|e| panic!("read_response_json failed during deserialization, {e:?}"))
306}
307
308pub async fn from_request<St, T: FromRequest<St>>(
310 st: &St,
311 req: &HttpRequest,
312 payload: &mut Payload,
313) -> Result<T, T::Error> {
314 T::from_request(st, req, payload).await
315}
316
317pub async fn respond_to<T: Responder>(slf: T, req: &HttpRequest) -> HttpResponse {
319 T::respond_to(slf, &(), req).await
320}
321
322#[derive(Debug)]
357pub struct TestRequest<St = ()> {
358 req: HttpTestRequest,
359 rmap: ResourceMap,
360 path: Path<Url>,
361 peer_addr: Option<SocketAddr>,
362 state: St,
363 config: WebAppConfig,
364}
365
366impl Default for TestRequest {
367 fn default() -> TestRequest {
368 TestRequest {
369 req: HttpTestRequest::default(),
370 rmap: ResourceMap::new(ResourceDef::new("")),
371 path: Path::new(Url::new()),
372 peer_addr: None,
373 state: (),
374 config: WebAppConfig::new(),
375 }
376 }
377}
378
379impl TestRequest {
380 #[must_use]
381 pub fn with_uri(path: &str) -> TestRequest {
383 TestRequest::default().uri(path)
384 }
385
386 #[must_use]
387 pub fn with_header<K, V>(key: K, value: V) -> TestRequest
389 where
390 HeaderName: TryFrom<K>,
391 HeaderValue: TryFrom<V>,
392 <HeaderName as TryFrom<K>>::Error: Into<HttpError>,
393 {
394 TestRequest::default().header(key, value)
395 }
396
397 #[must_use]
398 pub fn get() -> TestRequest {
400 TestRequest::default().method(Method::GET)
401 }
402
403 #[must_use]
404 pub fn post() -> TestRequest {
406 TestRequest::default().method(Method::POST)
407 }
408
409 #[must_use]
410 pub fn put() -> TestRequest {
412 TestRequest::default().method(Method::PUT)
413 }
414
415 #[must_use]
416 pub fn patch() -> TestRequest {
418 TestRequest::default().method(Method::PATCH)
419 }
420
421 #[must_use]
422 pub fn delete() -> TestRequest {
424 TestRequest::default().method(Method::DELETE)
425 }
426}
427
428impl<St> TestRequest<St> {
429 #[must_use]
430 pub fn version(mut self, ver: Version) -> Self {
432 self.req.version(ver);
433 self
434 }
435
436 #[must_use]
437 pub fn method(mut self, meth: Method) -> Self {
439 self.req.method(meth);
440 self
441 }
442
443 #[must_use]
444 pub fn uri(mut self, path: &str) -> Self {
446 self.req.uri(path);
447 self
448 }
449
450 #[must_use]
451 pub fn header<K, V>(mut self, key: K, value: V) -> Self
453 where
454 HeaderName: TryFrom<K>,
455 HeaderValue: TryFrom<V>,
456 <HeaderName as TryFrom<K>>::Error: Into<HttpError>,
457 {
458 self.req.header(key, value);
459 self
460 }
461
462 #[must_use]
463 #[cfg(feature = "cookie")]
464 pub fn cookie<C>(mut self, cookie: C) -> Self
466 where
467 C: Into<Cookie<'static>>,
468 {
469 self.req.cookie(cookie);
470 self
471 }
472
473 #[must_use]
474 pub fn param(mut self, name: &'static str, value: &'static str) -> Self {
476 self.path.add_static(name, value);
477 self
478 }
479
480 #[must_use]
481 pub fn peer_addr(mut self, addr: SocketAddr) -> Self {
486 self.peer_addr = Some(addr);
487 self
488 }
489
490 #[must_use]
491 pub fn payload<B: Into<Bytes>>(mut self, data: B) -> Self {
493 self.req.set_payload(data);
494 self
495 }
496
497 #[must_use]
498 pub fn form<T: Serialize>(mut self, data: &T) -> Self {
502 let bytes = serde_urlencoded::to_string(data)
503 .expect("Failed to serialize test data as a urlencoded form");
504 self.req.set_payload(bytes);
505 self.req
506 .header(CONTENT_TYPE, "application/x-www-form-urlencoded");
507 self
508 }
509
510 #[must_use]
511 pub fn json<T: Serialize>(mut self, data: &T) -> Self {
515 let bytes = serde_json::to_string(data).expect("Failed to serialize test data to json");
516 self.req.set_payload(bytes);
517 self.req.header(CONTENT_TYPE, "application/json");
518 self
519 }
520
521 #[must_use]
522 pub fn state<NewSt: 'static>(self, state: NewSt) -> TestRequest<NewSt> {
524 TestRequest {
525 state,
526 req: self.req,
527 rmap: self.rmap,
528 path: self.path,
529 peer_addr: self.peer_addr,
530 config: self.config,
531 }
532 }
533
534 #[must_use]
535 pub fn app_state<T: Send + Sync + 'static>(mut self, data: T) -> Self {
540 self.config = self.config.set_state(data);
541 self
542 }
543
544 #[must_use]
545 #[cfg(test)]
546 pub(crate) fn rmap(mut self, rmap: ResourceMap) -> Self {
548 self.rmap = rmap;
549 self
550 }
551
552 #[must_use]
553 pub fn to_request(mut self) -> Request {
555 self.req.build()
556 }
557
558 #[must_use]
559 pub fn to_srv_request(mut self) -> WebRequest<St> {
561 let (mut head, payload) = self.req.build().into_parts();
562 set_peer_addr(&mut head, self.peer_addr);
563 *self.path.get_mut() = head.uri.clone();
564 let cfg = SharedCfg::new("TEST").add(self.config).build();
565
566 WebRequest::new(
567 HttpRequest::new(self.path, head, Rc::new(self.rmap), cfg.get()),
568 payload,
569 self.state,
570 )
571 }
572
573 #[must_use]
574 pub fn to_srv_response(self, res: HttpResponse) -> WebResponse {
576 self.to_srv_request().into_response(res)
577 }
578
579 #[must_use]
580 pub fn to_http_request(mut self) -> HttpRequest {
582 let (mut head, _) = self.req.build().into_parts();
583 set_peer_addr(&mut head, self.peer_addr);
584 *self.path.get_mut() = head.uri.clone();
585 let cfg = SharedCfg::new("TEST").add(self.config).build();
586
587 HttpRequest::new(self.path, head, Rc::new(self.rmap), cfg.get())
588 }
589
590 #[must_use]
591 pub fn to_http_parts(mut self) -> (HttpRequest, Payload, St) {
593 let (mut head, payload) = self.req.build().into_parts();
594 set_peer_addr(&mut head, self.peer_addr);
595 *self.path.get_mut() = head.uri.clone();
596 let cfg = SharedCfg::new("TEST").add(self.config).build();
597
598 let req = HttpRequest::new(self.path, head, Rc::new(self.rmap), cfg.get());
599
600 (req, payload, self.state)
601 }
602}
603
604pub fn server<F, I, Sf>(factory: F) -> TestServer
631where
632 F: AsyncFn(&()) -> I + Send + Clone + 'static,
633 I: IntoServiceFactory<Sf, (), Request>,
634 Sf: ServiceFactory<(), Request> + 'static,
635 Sf::Res: Into<Response>,
636 Sf::Error: ResponseError,
637 Sf::InitError: fmt::Debug,
638{
639 server_with(TestServerConfig::default(), factory)
640}
641
642pub fn server_with<F, I, Sf>(cfg: TestServerConfig, factory: F) -> TestServer
668where
669 F: AsyncFn(&()) -> I + Send + Clone + 'static,
670 I: IntoServiceFactory<Sf, (), Request>,
671 Sf: ServiceFactory<(), Request> + 'static,
672 Sf::Res: Into<Response>,
673 Sf::Error: ResponseError + 'static,
674 Sf::InitError: fmt::Debug,
675{
676 let factory = Mutex::new(factory);
679 let factory: AppFactory = Arc::new(move || {
680 let factory = factory.lock().unwrap().clone();
681 Box::pin(async move {
682 boxed::factory(
683 factory(&())
684 .await
685 .into_factory()
686 .map(Into::into)
687 .map_err(AppError::new)
688 .map_init_err(|e| io::Error::other(format!("{e:?}"))),
689 )
690 })
691 });
692 start_server(cfg, factory)
693}
694
695type AppFactory = Arc<
696 dyn Fn() -> Pin<
697 Box<dyn Future<Output = BoxServiceFactory<(), Request, Response, AppError, io::Error>>>,
698 > + Send
699 + Sync,
700>;
701
702struct AppError(Box<dyn ResponseError>);
704
705impl AppError {
706 fn new<E: ResponseError + 'static>(err: E) -> Self {
707 AppError(Box::new(err))
708 }
709}
710
711impl fmt::Debug for AppError {
712 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
713 fmt::Debug::fmt(&self.0, f)
714 }
715}
716
717impl fmt::Display for AppError {
718 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
719 fmt::Display::fmt(&self.0, f)
720 }
721}
722
723impl std::error::Error for AppError {
724 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
725 self.0.source()
726 }
727}
728
729impl ResponseError for AppError {
730 fn error_response(&self) -> Response {
731 self.0.error_response()
732 }
733}
734
735fn start_server(cfg: TestServerConfig, factory: AppFactory) -> TestServer {
736 let sys = System::current().config();
737 let name = System::current().name().to_string();
738
739 let id = Uuid::now_v7();
740 let (tx, rx) = mpsc::channel();
741 log::debug!("Starting {name:?} web server {id:?}");
742
743 let ssl = match cfg.stream {
744 StreamType::Tcp => false,
745 #[cfg(feature = "openssl")]
746 StreamType::Openssl(_) => true,
747 #[cfg(feature = "rustls")]
748 StreamType::Rustls(_) => true,
749 };
750
751 thread::spawn(move || {
753 let sys = System::with_config(&name, sys);
754 let factory = async move |&(): &()| factory().await;
755
756 let ctimeout = cfg.client_timeout;
757 let port = cfg.port;
758 let tcp = cfg
759 .listener
760 .unwrap_or_else(|| net::TcpListener::bind(format!("127.0.0.1:{port}")).unwrap());
761 let local_addr = tcp.local_addr().unwrap();
762
763 sys.run(move || {
764 let builder = crate::server::build().workers(1).disable_signals();
765 let secure = match cfg.stream {
766 StreamType::Tcp => false,
767 #[cfg(feature = "openssl")]
768 StreamType::Openssl(_) => true,
769 #[cfg(feature = "rustls")]
770 StreamType::Rustls(_) => true,
771 };
772 let c = cfg.srv_cfg.clone().unwrap_or_else(|| {
773 SharedCfg::new("WEB-SRV")
774 .add(IoConfig::new())
775 .add(http::HttpServiceConfig::new().set_headers_read_rate(
776 ctimeout,
777 Seconds::ZERO,
778 256,
779 ))
780 .add(WebAppConfig::with(
781 &name,
782 secure,
783 local_addr,
784 format!("{local_addr}"),
785 ))
786 .into()
787 });
788
789 let srv = match cfg.stream {
790 StreamType::Tcp => match cfg.tp {
791 HttpVer::Http1 => builder.listen("test", tcp, c, async move |st| {
792 HttpService::h1(factory(st).await)
793 }),
794 HttpVer::Http2 => builder.listen("test", tcp, c, async move |st| {
795 HttpService::h2(factory(st).await)
796 }),
797 HttpVer::Both => builder.listen("test", tcp, c, async move |st| {
798 HttpService::new(factory(st).await)
799 }),
800 },
801 #[cfg(feature = "openssl")]
802 StreamType::Openssl(acceptor) => match cfg.tp {
803 HttpVer::Http1 => builder.listen("test", tcp, c, async move |st| {
804 http::openssl(acceptor.clone(), HttpService::h1(factory(st).await))
805 }),
806 HttpVer::Http2 => builder.listen("test", tcp, c, async move |st| {
807 http::openssl(acceptor.clone(), HttpService::h2(factory(st).await))
808 }),
809 HttpVer::Both => builder.listen("test", tcp, c, async move |st| {
810 http::openssl(acceptor.clone(), HttpService::new(factory(st).await))
811 }),
812 },
813 #[cfg(feature = "rustls")]
814 StreamType::Rustls(config) => match cfg.tp {
815 HttpVer::Http1 => builder.listen("test", tcp, c, async move |st| {
816 http::rustls(
817 config.clone(),
818 http::ALPN_PROTO_H1,
819 HttpService::h1(factory(st).await),
820 )
821 }),
822 HttpVer::Http2 => builder.listen("test", tcp, c, async move |st| {
823 http::rustls(
824 config.clone(),
825 http::ALPN_PROTO_H2,
826 HttpService::h2(factory(st).await),
827 )
828 }),
829 HttpVer::Both => builder.listen("test", tcp, c, async move |st| {
830 http::rustls(
831 config.clone(),
832 http::ALPN_PROTOS,
833 HttpService::new(factory(st).await),
834 )
835 }),
836 },
837 }
838 .unwrap()
839 .run();
840
841 tx.send((System::current(), srv, local_addr)).unwrap();
842 Ok(())
843 })
844 });
845 let (system, server, addr) = rx.recv().unwrap();
846 thread::sleep(Millis(25).into());
847
848 let cfg = cfg.client_cfg.clone().unwrap_or_else(|| {
849 SharedCfg::new("TEST-CLIENT")
850 .add(IoConfig::new().set_connect_timeout(Millis(90_000)))
851 .add(ntex_tls::TlsConfig::new().set_handshake_timeout(Seconds(5)))
852 .add(
853 ntex_h2::ServiceConfig::new()
854 .set_max_header_list_size(256 * 1024)
855 .set_max_header_continuation_frames(96),
856 )
857 .add(
858 ClientConfig::new()
859 .set_response_timeout(Seconds(30))
860 .set_response_payload_timeout(Seconds(30)),
861 )
862 .add(
863 WsClientConfig::new()
864 .set_address(addr)
865 .set_handshake_timeout(Seconds(60)),
866 )
867 .build()
868 });
869
870 let client = {
871 #[cfg(feature = "openssl")]
872 {
873 use tls_openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
874
875 let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
876 builder.set_verify(SslVerifyMode::NONE);
877 let _ = builder
878 .set_alpn_protos(b"\x02h2\x08http/1.1")
879 .map_err(|e| log::error!("Cannot set alpn protocol: {e:?}"));
880 Client::builder()
881 .openssl(builder.build())
882 .build(cfg.clone())
883 }
884 #[cfg(not(feature = "openssl"))]
885 {
886 Client::builder().build(cfg.clone())
887 }
888 };
889
890 TestServer {
891 id,
892 cfg,
893 addr,
894 client,
895 system,
896 ssl,
897 server,
898 }
899}
900
901#[derive(Debug)]
902pub struct TestServerConfig {
904 tp: HttpVer,
905 stream: StreamType,
906 client_timeout: Seconds,
907 port: u16,
908 listener: Option<net::TcpListener>,
909 srv_cfg: Option<SharedCfg>,
910 client_cfg: Option<SharedCfg>,
911}
912
913#[derive(Clone, Debug)]
914enum HttpVer {
915 Http1,
916 Http2,
917 Both,
918}
919
920#[derive(Clone)]
921#[allow(clippy::large_enum_variant)]
922enum StreamType {
923 Tcp,
924 #[cfg(feature = "openssl")]
925 Openssl(tls_openssl::ssl::SslAcceptor),
926 #[cfg(feature = "rustls")]
927 Rustls(tls_rustls::ServerConfig),
928}
929
930impl fmt::Debug for StreamType {
931 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
932 match self {
933 StreamType::Tcp => write!(f, "StreamType::Tcp"),
934 #[cfg(feature = "openssl")]
935 StreamType::Openssl(_) => write!(f, "StreamType::Openssl"),
936 #[cfg(feature = "rustls")]
937 StreamType::Rustls(_) => write!(f, "StreamType::Rustls"),
938 }
939 }
940}
941
942impl Default for TestServerConfig {
943 fn default() -> Self {
944 TestServerConfig::new()
945 }
946}
947
948#[must_use]
949pub fn config() -> TestServerConfig {
951 TestServerConfig::new()
952}
953
954impl TestServerConfig {
955 #[must_use]
956 pub(crate) fn new() -> TestServerConfig {
958 TestServerConfig {
959 tp: HttpVer::Both,
960 stream: StreamType::Tcp,
961 client_timeout: Seconds(5),
962 port: 0,
963 listener: None,
964 srv_cfg: None,
965 client_cfg: None,
966 }
967 }
968
969 #[must_use]
970 pub fn h1(mut self) -> Self {
972 self.tp = HttpVer::Http1;
973 self
974 }
975
976 #[must_use]
977 pub fn h2(mut self) -> Self {
979 self.tp = HttpVer::Http2;
980 self
981 }
982
983 #[must_use]
984 #[cfg(feature = "openssl")]
986 pub fn openssl(mut self, acceptor: tls_openssl::ssl::SslAcceptor) -> Self {
987 self.stream = StreamType::Openssl(acceptor);
988 self
989 }
990
991 #[must_use]
992 #[cfg(feature = "rustls")]
994 pub fn rustls(mut self, config: tls_rustls::ServerConfig) -> Self {
995 self.stream = StreamType::Rustls(config);
996 self
997 }
998
999 #[must_use]
1000 pub fn client_timeout(mut self, val: Seconds) -> Self {
1002 self.client_timeout = val;
1003 self
1004 }
1005
1006 #[must_use]
1007 pub fn port(mut self, port: u16) -> Self {
1009 self.port = port;
1010 self
1011 }
1012
1013 #[must_use]
1014 pub fn listener(mut self, listener: net::TcpListener) -> Self {
1018 self.listener = Some(listener);
1019 self
1020 }
1021
1022 #[must_use]
1023 pub fn server_cfg(mut self, cfg: impl Into<SharedCfg>) -> Self {
1025 self.srv_cfg = Some(cfg.into());
1026 self
1027 }
1028
1029 #[must_use]
1030 pub fn client_cfg(mut self, cfg: impl Into<SharedCfg>) -> Self {
1032 self.client_cfg = Some(cfg.into());
1033 self
1034 }
1035}
1036
1037#[derive(Debug)]
1038pub struct TestServer {
1040 id: Uuid,
1041 cfg: SharedCfg,
1042 addr: net::SocketAddr,
1043 client: Client,
1044 system: crate::rt::System,
1045 ssl: bool,
1046 server: Server,
1047}
1048
1049impl TestServer {
1050 pub fn addr(&self) -> net::SocketAddr {
1052 self.addr
1053 }
1054
1055 pub fn url(&self, uri: &str) -> String {
1057 let scheme = if self.ssl { "https" } else { "http" };
1058
1059 if uri.starts_with('/') {
1060 format!("{}://localhost:{}{}", scheme, self.addr.port(), uri)
1061 } else {
1062 format!("{}://localhost:{}/{}", scheme, self.addr.port(), uri)
1063 }
1064 }
1065
1066 pub fn get<S: AsRef<str>>(&self, path: S) -> ClientRequest {
1068 self.client.get(self.url(path.as_ref()).as_str())
1069 }
1070
1071 pub fn post<S: AsRef<str>>(&self, path: S) -> ClientRequest {
1073 self.client.post(self.url(path.as_ref()).as_str())
1074 }
1075
1076 pub fn head<S: AsRef<str>>(&self, path: S) -> ClientRequest {
1078 self.client.head(self.url(path.as_ref()).as_str())
1079 }
1080
1081 pub fn put<S: AsRef<str>>(&self, path: S) -> ClientRequest {
1083 self.client.put(self.url(path.as_ref()).as_str())
1084 }
1085
1086 pub fn patch<S: AsRef<str>>(&self, path: S) -> ClientRequest {
1088 self.client.patch(self.url(path.as_ref()).as_str())
1089 }
1090
1091 pub fn delete<S: AsRef<str>>(&self, path: S) -> ClientRequest {
1093 self.client.delete(self.url(path.as_ref()).as_str())
1094 }
1095
1096 pub fn options<S: AsRef<str>>(&self, path: S) -> ClientRequest {
1098 self.client.options(self.url(path.as_ref()).as_str())
1099 }
1100
1101 pub fn query<S: AsRef<str>>(&self, path: S) -> ClientRequest {
1103 self.client.query(self.url(path.as_ref()).as_str())
1104 }
1105
1106 pub fn request<S: AsRef<str>>(&self, method: Method, path: S) -> ClientRequest {
1111 let path = path.as_ref();
1112 if path.contains("://") {
1113 self.client.request(method, path)
1114 } else {
1115 self.client.request(method, self.url(path).as_str())
1116 }
1117 }
1118
1119 pub async fn load_body(
1121 &self,
1122 response: ClientResponse,
1123 ) -> Result<Bytes, Error<ClientPayloadError>> {
1124 response.body().limit(10_485_760).await
1125 }
1126
1127 #[cfg(feature = "ws")]
1128 pub async fn ws_at(&self, path: &str) -> Result<WsConnection<Sealed>, Error<WsClientError>> {
1130 if self.ssl {
1131 #[cfg(feature = "openssl")]
1132 {
1133 use tls_openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
1134
1135 let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
1136 builder.set_verify(SslVerifyMode::NONE);
1137 let _ = builder
1138 .set_alpn_protos(b"\x08http/1.1")
1139 .map_err(|e| log::error!("Cannot set alpn protocol: {e:?}"));
1140
1141 WsClient::new(self.url(path), &self.cfg)
1142 .openssl(builder.build())
1143 .connect()
1144 .await
1145 .map(WsConnection::seal)
1146 }
1147 #[cfg(not(feature = "openssl"))]
1148 {
1149 panic!("openssl feature is required")
1150 }
1151 } else {
1152 WsClient::new(self.url(path), &self.cfg)
1153 .connect()
1154 .await
1155 .map(WsConnection::seal)
1156 }
1157 }
1158
1159 #[cfg(feature = "ws")]
1160 pub async fn ws(&self) -> Result<WsConnection<Sealed>, Error<WsClientError>> {
1162 self.ws_at("/").await
1163 }
1164
1165 pub async fn stop(&self) {
1167 self.server.stop(true).await;
1168 }
1169}
1170
1171impl Drop for TestServer {
1172 fn drop(&mut self) {
1173 log::debug!("Stopping test web server {:?}", self.id);
1174 drop(self.server.stop(false));
1175 thread::sleep(time::Duration::from_millis(75));
1176 self.system.stop();
1177 thread::sleep(time::Duration::from_millis(25));
1178 }
1179}
1180
1181#[cfg(test)]
1182mod tests {
1183 use serde::{Deserialize, Serialize};
1184
1185 use super::*;
1186 use crate::http::{HttpMessage, header};
1187 use crate::web::{self, App};
1188
1189 #[crate::rt_test]
1190 async fn test_basics() {
1191 let req = TestRequest::with_header(header::CONTENT_TYPE, "application/json")
1192 .version(Version::HTTP_2)
1193 .header(header::DATE, "some date")
1194 .param("test", "123")
1195 .app_state(20u64)
1196 .peer_addr("127.0.0.1:8081".parse().unwrap())
1197 .to_http_request();
1198 assert!(req.headers().contains_key(header::CONTENT_TYPE));
1199 assert!(req.headers().contains_key(header::DATE));
1200 assert_eq!(req.peer_addr(), Some("127.0.0.1:8081".parse().unwrap()));
1201 assert_eq!(&req.match_info()["test"], "123");
1202 assert_eq!(req.version(), Version::HTTP_2);
1203 let data = req.app_state::<u64>().unwrap();
1204 assert_eq!(*data, 20);
1205 assert_eq!(format!("{:?}", StreamType::Tcp), "StreamType::Tcp");
1206
1207 let (_, pl, ()) = TestRequest::with_header(header::CONTENT_TYPE, "application/json")
1208 .to_srv_request()
1209 .into_parts();
1210 let res = load_stream(pl).await.unwrap();
1211 assert_eq!(res, &b""[..]);
1212 }
1213
1214 #[crate::rt_test]
1215 async fn test_request_methods() {
1216 let app = init_service(
1217 App::new().service(
1218 web::resource("/index.html")
1219 .route(web::put().to(async || HttpResponse::Ok().body("put!")))
1220 .route(web::patch().to(async || HttpResponse::Ok().body("patch!")))
1221 .route(web::delete().to(async || HttpResponse::Ok().body("delete!"))),
1222 ),
1223 )
1224 .await;
1225
1226 let put_req = TestRequest::put()
1227 .uri("/index.html")
1228 .header(header::CONTENT_TYPE, "application/json")
1229 .to_request();
1230
1231 let result = read_response(&app, put_req).await;
1232 assert_eq!(result, Bytes::from_static(b"put!"));
1233
1234 let patch_req = TestRequest::patch()
1235 .uri("/index.html")
1236 .header(header::CONTENT_TYPE, "application/json")
1237 .to_request();
1238
1239 let result = read_response(&app, patch_req).await;
1240 assert_eq!(result, Bytes::from_static(b"patch!"));
1241
1242 let delete_req = TestRequest::delete().uri("/index.html").to_request();
1243 let result = read_response(&app, delete_req).await;
1244 assert_eq!(result, Bytes::from_static(b"delete!"));
1245 }
1246
1247 #[crate::rt_test]
1248 async fn test_response() {
1249 let app = init_service(
1250 App::new().service(
1251 web::resource("/index.html")
1252 .route(web::post().to(async || HttpResponse::Ok().body("welcome!"))),
1253 ),
1254 )
1255 .await;
1256
1257 let req = TestRequest::post()
1258 .uri("/index.html")
1259 .header(header::CONTENT_TYPE, "application/json")
1260 .to_request();
1261
1262 let result = read_response(&app, req).await;
1263 assert_eq!(result, Bytes::from_static(b"welcome!"));
1264 }
1265
1266 #[derive(Serialize, Deserialize)]
1267 struct Person {
1268 id: String,
1269 name: String,
1270 }
1271
1272 #[crate::rt_test]
1273 async fn test_response_json() {
1274 let app = init_service(
1275 App::new().service(web::resource("/people").route(web::post().to(
1276 async |person: web::types::Json<Person>| {
1277 HttpResponse::Ok().json(&person.into_inner())
1278 },
1279 ))),
1280 )
1281 .await;
1282
1283 let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes();
1284
1285 let req = TestRequest::post()
1286 .uri("/people")
1287 .header(header::CONTENT_TYPE, "application/json")
1288 .payload(payload)
1289 .to_request();
1290
1291 let result: Person = read_response_json(&app, req).await;
1292 assert_eq!(&result.id, "12345");
1293 }
1294
1295 #[crate::rt_test]
1296 async fn test_request_response_form() {
1297 let app = init_service(
1298 App::new().service(web::resource("/people").route(web::post().to(
1299 async |person: web::types::Form<Person>| {
1300 HttpResponse::Ok().json(&person.into_inner())
1301 },
1302 ))),
1303 )
1304 .await;
1305
1306 let payload = Person {
1307 id: "12345".to_string(),
1308 name: "User name".to_string(),
1309 };
1310
1311 let req = TestRequest::post()
1312 .uri("/people")
1313 .form(&payload)
1314 .to_request();
1315
1316 assert_eq!(req.content_type(), "application/x-www-form-urlencoded");
1317
1318 let result: Person = read_response_json(&app, req).await;
1319 assert_eq!(&result.id, "12345");
1320 assert_eq!(&result.name, "User name");
1321 }
1322
1323 #[crate::rt_test]
1324 async fn test_request_response_json() {
1325 let app = init_service(
1326 App::new().service(web::resource("/people").route(web::post().to(
1327 async |person: web::types::Json<Person>| {
1328 HttpResponse::Ok().json(&person.into_inner())
1329 },
1330 ))),
1331 )
1332 .await;
1333
1334 let payload = Person {
1335 id: "12345".to_string(),
1336 name: "User name".to_string(),
1337 };
1338
1339 let req = TestRequest::post()
1340 .uri("/people")
1341 .json(&payload)
1342 .to_request();
1343
1344 assert_eq!(req.content_type(), "application/json");
1345
1346 let result: Person = read_response_json(&app, req).await;
1347 assert_eq!(&result.id, "12345");
1348 assert_eq!(&result.name, "User name");
1349 }
1350
1351 #[crate::rt_test]
1352 async fn test_async_with_block() {
1353 async fn async_with_block() -> Result<HttpResponse, Infallible> {
1354 let res = web::block(move || Some(4usize).ok_or("wrong")).await;
1355
1356 #[allow(clippy::match_wild_err_arm)]
1357 match res {
1358 Ok(value) => Ok(HttpResponse::Ok()
1359 .content_type("text/plain")
1360 .body(format!("Async with block value: {value}"))),
1361 Err(_) => panic!("Unexpected"),
1362 }
1363 }
1364
1365 let app =
1366 init_service(App::new().service(web::resource("/index.html").to(async_with_block)))
1367 .await;
1368
1369 let req = TestRequest::post().uri("/index.html").to_request();
1370 let res = app.call(req).await.unwrap();
1371 assert!(res.status().is_success());
1372 }
1373
1374 #[crate::rt_test]
1375 async fn test_test_methods() {
1376 let srv = server(async |()| {
1377 App::new().service(
1378 web::resource("/").route((
1379 web::route()
1380 .method(Method::PUT)
1381 .to(async || HttpResponse::Ok()),
1382 web::route()
1383 .method(Method::PATCH)
1384 .to(async || HttpResponse::Ok()),
1385 web::route()
1386 .method(Method::DELETE)
1387 .to(async || HttpResponse::Ok()),
1388 web::route()
1389 .method(Method::OPTIONS)
1390 .to(async || HttpResponse::Ok()),
1391 )),
1392 )
1393 });
1394
1395 assert_eq!(srv.put("/").send().await.unwrap().status(), StatusCode::OK);
1396 assert_eq!(
1397 srv.patch("/").send().await.unwrap().status(),
1398 StatusCode::OK
1399 );
1400 assert_eq!(
1401 srv.delete("/").send().await.unwrap().status(),
1402 StatusCode::OK
1403 );
1404 assert_eq!(
1405 srv.options("/").send().await.unwrap().status(),
1406 StatusCode::OK
1407 );
1408
1409 let res = srv.put("").send().await.unwrap();
1410 assert_eq!(srv.load_body(res).await.unwrap(), Bytes::new());
1411 }
1412
1413 #[crate::rt_test]
1414 async fn test_request_state() {
1415 let (_, _, st) = TestRequest::default().state(10usize).to_http_parts();
1416 assert_eq!(st, 10);
1417
1418 let req = TestRequest::default()
1419 .state(web::AppState::new(5usize))
1420 .to_srv_request();
1421 assert_eq!(**req.st(), 5);
1422 }
1423
1424 #[crate::rt_test]
1425 async fn test_server_config() {
1426 let cfg = config()
1427 .h1()
1428 .port(0)
1429 .client_cfg(SharedCfg::new("CUSTOM-CLIENT"));
1430 let dbg = format!("{cfg:?}");
1431 assert!(dbg.contains("StreamType::Tcp"), "{dbg}");
1432
1433 let srv = server_with(cfg, async |()| {
1434 App::new().service(web::resource("/").to(async || HttpResponse::Ok()))
1435 });
1436 let res = srv.get("/").send().await.unwrap();
1437 assert_eq!(res.status(), StatusCode::OK);
1438 assert_eq!(res.version(), http::Version::HTTP_11);
1439 srv.stop().await;
1440 }
1441
1442 #[crate::rt_test]
1443 async fn test_server_listener_ignores_port() {
1444 let lst = net::TcpListener::bind("127.0.0.1:0").unwrap();
1446 let addr = lst.local_addr().unwrap();
1447
1448 let srv = server_with(config().port(addr.port()).listener(lst), async |()| {
1449 App::new().service(web::resource("/").to(async || HttpResponse::Ok()))
1450 });
1451 assert_eq!(srv.addr(), addr);
1452 let res = srv.get("/").send().await.unwrap();
1453 assert_eq!(res.status(), StatusCode::OK);
1454 }
1455
1456 #[cfg(feature = "openssl")]
1457 fn ssl_acceptor(h2: bool) -> tls_openssl::ssl::SslAcceptor {
1458 use tls_openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
1459
1460 let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
1461 builder
1462 .set_private_key_file("./tests/key.pem", SslFiletype::PEM)
1463 .unwrap();
1464 builder
1465 .set_certificate_chain_file("./tests/cert.pem")
1466 .unwrap();
1467 builder.set_alpn_select_callback(move |_, protos| {
1468 if h2 && protos.windows(3).any(|w| w == b"\x02h2") {
1469 Ok(b"h2")
1470 } else {
1471 Ok(b"http/1.1")
1472 }
1473 });
1474 builder.build()
1475 }
1476
1477 #[cfg(feature = "openssl")]
1478 #[crate::rt_test]
1479 async fn test_server_openssl() {
1480 for (cfg, h2, version) in [
1481 (config().h1(), false, http::Version::HTTP_11),
1482 (config().h2(), true, http::Version::HTTP_2),
1483 (config(), true, http::Version::HTTP_2),
1484 (config(), false, http::Version::HTTP_11),
1485 ] {
1486 let cfg = cfg.openssl(ssl_acceptor(h2));
1487 assert!(format!("{cfg:?}").contains("StreamType::Openssl"));
1488
1489 let srv = server_with(cfg, async |()| {
1490 App::new().service(web::resource("/").to(async || HttpResponse::Ok()))
1491 });
1492 assert!(srv.url("/").starts_with("https://"));
1493 let res = srv.get("/").send().await.unwrap();
1494 assert_eq!(res.status(), StatusCode::OK);
1495 assert_eq!(res.version(), version);
1496 }
1497 }
1498
1499 #[cfg(all(feature = "openssl", feature = "ws"))]
1500 #[crate::rt_test]
1501 async fn test_server_openssl_ws() {
1502 let srv = server_with(config().h1().openssl(ssl_acceptor(false)), async |()| {
1503 App::new().service(
1504 web::resource("/").route(web::to(async |req: web::HttpRequest| {
1505 let _ = web::ws::start(&req, None, async |_: web::ws::Frame| {
1506 Ok::<_, io::Error>(None)
1507 })
1508 .await;
1509 })),
1510 )
1511 });
1512 assert!(srv.ws().await.is_ok());
1513 }
1514
1515 #[cfg(feature = "rustls")]
1516 #[test]
1517 fn test_server_config_rustls_debug() {
1518 use std::{fs::File, io::BufReader};
1519
1520 let cert_file = &mut BufReader::new(File::open("tests/cert.pem").unwrap());
1521 let key_file = &mut BufReader::new(File::open("tests/key.pem").unwrap());
1522 let cert_chain = rustls_pemfile::certs(cert_file)
1523 .map(|r| r.unwrap())
1524 .collect();
1525 let key = rustls_pemfile::private_key(key_file).unwrap().unwrap();
1526 let cfg = tls_rustls::ServerConfig::builder()
1527 .with_no_client_auth()
1528 .with_single_cert(cert_chain, key)
1529 .unwrap();
1530 assert!(format!("{:?}", config().rustls(cfg)).contains("StreamType::Rustls"));
1531 }
1532
1533 #[cfg(feature = "cookie")]
1534 #[test]
1535 fn test_response_cookies() {
1536 let req = TestRequest::default()
1537 .cookie(
1538 coo_kie::Cookie::build(("name", "value"))
1539 .domain("www.rust-lang.org")
1540 .path("/test")
1541 .http_only(true)
1542 .max_age(::time::Duration::days(1)),
1543 )
1544 .to_http_request();
1545
1546 let cookies = req.cookies().unwrap();
1547 assert_eq!(cookies.len(), 1);
1548 assert_eq!(cookies[0].name(), "name");
1549 }
1550}