@@ -5,6 +5,7 @@ import (
55 "errors"
66 "fmt"
77 "io"
8+ "net"
89 "net/http"
910 "os"
1011
@@ -17,16 +18,17 @@ import (
1718const hello = "Hello World!"
1819
1920var _ = Describe("Server", func() {
21+
2022 handler := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
2123 rw.Write([]byte(hello))
2224 })
23−
2425 Context("NewServer", func() {
2526 type newServerTableInput struct {
2627 opts Opts
2728 expectedErr error
2829 expectHTTPListener bool
2930 expectTLSListener bool
31+ fdAddr string
3032 ipv6 bool
3133 }
3234
@@ -34,6 +36,15 @@ var _ = Describe("Server", func() {
3436 if in.ipv6 {
3537 skipDevContainer()
3638 }
39+
40+ if in.fdAddr != "" {
41+ l, err := net.Listen("tcp", in.fdAddr)
42+ Expect(err).ToNot(HaveOccurred())
43+ f, err := l.(*net.TCPListener).File()
44+ Expect(err).ToNot(HaveOccurred())
45+ in.opts.fdFiles = []*os.File{f}
46+ }
47+
3748 srv, err := NewServer(in.opts)
3849 if in.expectedErr != nil {
3950 Expect(err).To(MatchError(ContainSubstring(in.expectedErr.Error())))
@@ -55,6 +66,46 @@ var _ = Describe("Server", func() {
5566 Expect(s.tlsListener.Close()).To(Succeed())
5667 }
5768 },
69+ Entry("with a valid non-lowercase fd IPv4 bind address", &newServerTableInput{
70+ opts: Opts{
71+ Handler: handler,
72+ BindAddress: "Fd:3",
73+ },
74+ expectedErr: nil,
75+ expectHTTPListener: true,
76+ expectTLSListener: false,
77+ fdAddr: "127.0.0.1:0",
78+ }),
79+ Entry("with a valid fd IPv4 bind address", &newServerTableInput{
80+ opts: Opts{
81+ Handler: handler,
82+ BindAddress: "fd:3",
83+ },
84+ expectedErr: nil,
85+ expectHTTPListener: true,
86+ expectTLSListener: false,
87+ fdAddr: "127.0.0.1:0",
88+ }),
89+ Entry("with a invalid fd named bind address", &newServerTableInput{
90+ opts: Opts{
91+ Handler: handler,
92+ BindAddress: "fd:hello",
93+ },
94+ expectedErr: fmt.Errorf("error setting up listener: listen (file, %s) failed: listen failed: fd with name is not implemented yet", "hello"),
95+ expectHTTPListener: true,
96+ expectTLSListener: false,
97+ fdAddr: "127.0.0.1:0",
98+ }),
99+ Entry("with a invalid fd IPv4 bind address", &newServerTableInput{
100+ opts: Opts{
101+ Handler: handler,
102+ BindAddress: "fd:4",
103+ },
104+ expectedErr: fmt.Errorf("error setting up listener: listen (file, %d) failed: listen failed: fd outside of range of available file descriptors", 4),
105+ expectHTTPListener: true,
106+ expectTLSListener: false,
107+ fdAddr: "127.0.0.1:0",
108+ }),
58109 Entry("with an ipv4 valid http bind address", &newServerTableInput{
59110 opts: Opts{
60111 Handler: handler,
@@ -86,6 +137,21 @@ var _ = Describe("Server", func() {
86137 expectHTTPListener: false,
87138 expectTLSListener: true,
88139 }),
140+ Entry("with a both a fd valid http and ipv4 valid https bind address, and valid TLS config", &newServerTableInput{
141+ opts: Opts{
142+ Handler: handler,
143+ BindAddress: "fd:3",
144+ SecureBindAddress: "127.0.0.1:0",
145+ TLS: &options.TLS{
146+ Key: &ipv4KeyDataSource,
147+ Cert: &ipv4CertDataSource,
148+ },
149+ },
150+ expectedErr: nil,
151+ expectHTTPListener: true,
152+ expectTLSListener: true,
153+ fdAddr: "127.0.0.1:0",
154+ }),
89155 Entry("with a both a ipv4 valid http and ipv4 valid https bind address, and valid TLS config", &newServerTableInput{
90156 opts: Opts{
91157 Handler: handler,
@@ -300,6 +366,27 @@ var _ = Describe("Server", func() {
300366 expectHTTPListener: false,
301367 expectTLSListener: true,
302368 }),
369+ Entry("with valid fd IPv6 bind address", &newServerTableInput{
370+ opts: Opts{
371+ Handler: handler,
372+ BindAddress: "fd:3",
373+ },
374+ expectedErr: nil,
375+ expectHTTPListener: true,
376+ expectTLSListener: false,
377+ fdAddr: "[::1]:0",
378+ ipv6: true,
379+ }),
380+ Entry("with a invalid fd IPv6 bind address", &newServerTableInput{
381+ opts: Opts{
382+ Handler: handler,
383+ BindAddress: "fd:4",
384+ },
385+ expectedErr: fmt.Errorf("error setting up listener: listen (file, %d) failed: listen failed: fd outside of range of available file descriptors", 4),
386+ expectHTTPListener: true,
387+ expectTLSListener: false,
388+ fdAddr: "[::1]:0",
389+ }),
303390 Entry("with an ipv6 valid http bind address", &newServerTableInput{
304391 opts: Opts{
305392 Handler: handler,
@@ -334,6 +421,22 @@ var _ = Describe("Server", func() {
334421 expectTLSListener: true,
335422 ipv6: true,
336423 }),
424+ Entry("with a both a fd valid http and ipv6 valid https bind address, and valid TLS config", &newServerTableInput{
425+ opts: Opts{
426+ Handler: handler,
427+ BindAddress: "fd:3",
428+ SecureBindAddress: "[::1]:0",
429+ TLS: &options.TLS{
430+ Key: &ipv6KeyDataSource,
431+ Cert: &ipv6CertDataSource,
432+ },
433+ },
434+ expectedErr: nil,
435+ expectHTTPListener: true,
436+ expectTLSListener: true,
437+ fdAddr: "[::1]:0",
438+ ipv6: true,
439+ }),
337440 Entry("with a both a ipv6 valid http and ipv6 valid https bind address, and valid TLS config", &newServerTableInput{
338441 opts: Opts{
339442 Handler: handler,
@@ -563,6 +666,58 @@ var _ = Describe("Server", func() {
563666
564667 })
565668
669+ Context("with an fd ipv4 http server", func() {
670+ var listenAddr string
671+
672+ BeforeEach(func() {
673+ l, err := net.Listen("tcp", "127.0.0.1:0")
674+ Expect(err).ToNot(HaveOccurred())
675+ f, err := l.(*net.TCPListener).File()
676+ Expect(err).ToNot(HaveOccurred())
677+
678+ srv, err = NewServer(Opts{
679+ Handler: handler,
680+ BindAddress: "fd:3",
681+ fdFiles: []*os.File{f},
682+ })
683+ Expect(err).ToNot(HaveOccurred())
684+
685+ listenAddr = fmt.Sprintf("http://%s/", l.Addr().String())
686+ })
687+
688+ It("Starts the server and serves the handler", func() {
689+ go func() {
690+ defer GinkgoRecover()
691+ Expect(srv.Start(ctx)).To(Succeed())
692+ }()
693+
694+ resp, err := httpGet(ctx, listenAddr)
695+ Expect(err).ToNot(HaveOccurred())
696+ Expect(resp.StatusCode).To(Equal(http.StatusOK))
697+
698+ body, err := io.ReadAll(resp.Body)
699+ Expect(err).ToNot(HaveOccurred())
700+ Expect(string(body)).To(Equal(hello))
701+ })
702+
703+ It("Stops the server when the context is cancelled", func() {
704+ go func() {
705+ defer GinkgoRecover()
706+ Expect(srv.Start(ctx)).To(Succeed())
707+ }()
708+
709+ _, err := httpGet(ctx, listenAddr)
710+ Expect(err).ToNot(HaveOccurred())
711+
712+ cancel()
713+
714+ Eventually(func() error {
715+ _, err := httpGet(ctx, listenAddr)
716+ return err
717+ }).Should(HaveOccurred())
718+ })
719+ })
720+
566721 Context("with an ipv4 http server", func() {
567722 var listenAddr string
568723
@@ -682,6 +837,88 @@ var _ = Describe("Server", func() {
682837 })
683838 })
684839
840+ Context("with a fd ipv4 http and an ipv4 https server", func() {
841+ var listenAddr, secureListenAddr string
842+
843+ BeforeEach(func() {
844+ l, err := net.Listen("tcp", "127.0.0.1:0")
845+ Expect(err).ToNot(HaveOccurred())
846+ f, err := l.(*net.TCPListener).File()
847+ Expect(err).ToNot(HaveOccurred())
848+
849+ srv, err = NewServer(Opts{
850+ Handler: handler,
851+ BindAddress: "fd:3",
852+ fdFiles: []*os.File{f},
853+ SecureBindAddress: "127.0.0.1:0",
854+ TLS: &options.TLS{
855+ Key: &ipv4KeyDataSource,
856+ Cert: &ipv4CertDataSource,
857+ },
858+ })
859+ Expect(err).ToNot(HaveOccurred())
860+
861+ s, ok := srv.(*server)
862+ Expect(ok).To(BeTrue())
863+
864+ listenAddr = fmt.Sprintf("http://%s/", l.Addr().String())
865+ secureListenAddr = fmt.Sprintf("https://%s/", s.tlsListener.Addr().String())
866+ })
867+
868+ It("Starts the server and serves the handler on http", func() {
869+ go func() {
870+ defer GinkgoRecover()
871+ Expect(srv.Start(ctx)).To(Succeed())
872+ }()
873+
874+ resp, err := httpGet(ctx, listenAddr)
875+ Expect(err).ToNot(HaveOccurred())
876+ Expect(resp.StatusCode).To(Equal(http.StatusOK))
877+
878+ body, err := io.ReadAll(resp.Body)
879+ Expect(err).ToNot(HaveOccurred())
880+ Expect(string(body)).To(Equal(hello))
881+ })
882+
883+ It("Starts the server and serves the handler on https", func() {
884+ go func() {
885+ defer GinkgoRecover()
886+ Expect(srv.Start(ctx)).To(Succeed())
887+ }()
888+
889+ resp, err := httpGet(ctx, secureListenAddr)
890+ Expect(err).ToNot(HaveOccurred())
891+ Expect(resp.StatusCode).To(Equal(http.StatusOK))
892+
893+ body, err := io.ReadAll(resp.Body)
894+ Expect(err).ToNot(HaveOccurred())
895+ Expect(string(body)).To(Equal(hello))
896+ })
897+
898+ It("Stops both servers when the context is cancelled", func() {
899+ go func() {
900+ defer GinkgoRecover()
901+ Expect(srv.Start(ctx)).To(Succeed())
902+ }()
903+
904+ _, err := httpGet(ctx, listenAddr)
905+ Expect(err).ToNot(HaveOccurred())
906+ _, err = httpGet(ctx, secureListenAddr)
907+ Expect(err).ToNot(HaveOccurred())
908+
909+ cancel()
910+
911+ Eventually(func() error {
912+ _, err := httpGet(ctx, listenAddr)
913+ return err
914+ }).Should(HaveOccurred())
915+ Eventually(func() error {
916+ _, err := httpGet(ctx, secureListenAddr)
917+ return err
918+ }).Should(HaveOccurred())
919+ })
920+ })
921+
685922 Context("with both an ipv4 http and an ipv4 https server", func() {
686923 var listenAddr, secureListenAddr string
687924
@@ -880,6 +1117,89 @@ var _ = Describe("Server", func() {
8801117 })
8811118 })
8821119
1120+ Context("with an fd ipv6 http and an ipv6 https server", func() {
1121+ var listenAddr, secureListenAddr string
1122+
1123+ BeforeEach(func() {
1124+ skipDevContainer()
1125+ l, err := net.Listen("tcp", "[::1]:0")
1126+ Expect(err).ToNot(HaveOccurred())
1127+ f, err := l.(*net.TCPListener).File()
1128+ Expect(err).ToNot(HaveOccurred())
1129+
1130+ srv, err = NewServer(Opts{
1131+ Handler: handler,
1132+ BindAddress: "fd:3",
1133+ fdFiles: []*os.File{f},
1134+ SecureBindAddress: "[::1]:0",
1135+ TLS: &options.TLS{
1136+ Key: &ipv6KeyDataSource,
1137+ Cert: &ipv6CertDataSource,
1138+ },
1139+ })
1140+ Expect(err).ToNot(HaveOccurred())
1141+
1142+ s, ok := srv.(*server)
1143+ Expect(ok).To(BeTrue())
1144+
1145+ listenAddr = fmt.Sprintf("http://%s/", l.Addr().String())
1146+ secureListenAddr = fmt.Sprintf("https://%s/", s.tlsListener.Addr().String())
1147+ })
1148+
1149+ It("Starts the server and serves the handler on http", func() {
1150+ go func() {
1151+ defer GinkgoRecover()
1152+ Expect(srv.Start(ctx)).To(Succeed())
1153+ }()
1154+
1155+ resp, err := httpGet(ctx, listenAddr)
1156+ Expect(err).ToNot(HaveOccurred())
1157+ Expect(resp.StatusCode).To(Equal(http.StatusOK))
1158+
1159+ body, err := io.ReadAll(resp.Body)
1160+ Expect(err).ToNot(HaveOccurred())
1161+ Expect(string(body)).To(Equal(hello))
1162+ })
1163+
1164+ It("Starts the server and serves the handler on https", func() {
1165+ go func() {
1166+ defer GinkgoRecover()
1167+ Expect(srv.Start(ctx)).To(Succeed())
1168+ }()
1169+
1170+ resp, err := httpGet(ctx, secureListenAddr)
1171+ Expect(err).ToNot(HaveOccurred())
1172+ Expect(resp.StatusCode).To(Equal(http.StatusOK))
1173+
1174+ body, err := io.ReadAll(resp.Body)
1175+ Expect(err).ToNot(HaveOccurred())
1176+ Expect(string(body)).To(Equal(hello))
1177+ })
1178+
1179+ It("Stops both servers when the context is cancelled", func() {
1180+ go func() {
1181+ defer GinkgoRecover()
1182+ Expect(srv.Start(ctx)).To(Succeed())
1183+ }()
1184+
1185+ _, err := httpGet(ctx, listenAddr)
1186+ Expect(err).ToNot(HaveOccurred())
1187+ _, err = httpGet(ctx, secureListenAddr)
1188+ Expect(err).ToNot(HaveOccurred())
1189+
1190+ cancel()
1191+
1192+ Eventually(func() error {
1193+ _, err := httpGet(ctx, listenAddr)
1194+ return err
1195+ }).Should(HaveOccurred())
1196+ Eventually(func() error {
1197+ _, err := httpGet(ctx, secureListenAddr)
1198+ return err
1199+ }).Should(HaveOccurred())
1200+ })
1201+ })
1202+
8831203 Context("with both an ipv6 http and an ipv6 https server", func() {
8841204 var listenAddr, secureListenAddr string
8851205