@marcel91200 / caddy Mirror · read-only
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 11, 01:00 UTC

master
go 236 lines 5.85 KB
Raw
1 // Copyright 2015 Matthew Holt and The Caddy Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 //go:build linux
16
17 package integration
18
19 import (
20 "context"
21 "crypto/tls"
22 "fmt"
23 "io"
24 "net"
25 "os"
26 "os/exec"
27 "strconv"
28 "strings"
29 "testing"
30 "time"
31
32 "github.com/caddyserver/caddy/v2"
33 )
34
35 const systemdListenHelper = "CADDY_SYSTEMD_LISTEN_HELPER"
36
37 func TestSystemdListenFDIntegration(t *testing.T) {
38 if os.Getenv(systemdListenHelper) == "1" {
39 runSystemdListenFDHelper(t)
40 return
41 }
42
43 tcpListener, err := net.Listen("tcp4", "127.0.0.1:0")
44 if err != nil {
45 t.Fatal(err)
46 }
47 tcpFile, err := tcpListener.(*net.TCPListener).File()
48 if err != nil {
49 t.Fatal(err)
50 }
51 _ = tcpListener.Close()
52 defer tcpFile.Close()
53
54 udpConn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)})
55 if err != nil {
56 t.Fatal(err)
57 }
58 udpFile, err := udpConn.File()
59 if err != nil {
60 t.Fatal(err)
61 }
62 _ = udpConn.Close()
63 defer udpFile.Close()
64
65 h3Conn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)})
66 if err != nil {
67 t.Fatal(err)
68 }
69 h3File, err := h3Conn.File()
70 if err != nil {
71 t.Fatal(err)
72 }
73 _ = h3Conn.Close()
74 defer h3File.Close()
75
76 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
77 defer cancel()
78 cmd := exec.CommandContext(ctx, os.Args[0], "-test.run=^TestSystemdListenFDIntegration$", "-test.v")
79 cmd.ExtraFiles = []*os.File{tcpFile, udpFile, h3File}
80 cmd.Env = append(withoutSystemdListenEnv(os.Environ()),
81 systemdListenHelper+"=1",
82 "LISTEN_FDS=3",
83 "LISTEN_FDNAMES=web:dns:h3",
84 )
85 output, err := cmd.CombinedOutput()
86 if err != nil {
87 t.Fatalf("helper failed: %v\n%s", err, output)
88 }
89 }
90
91 func runSystemdListenFDHelper(t *testing.T) {
92 t.Setenv("LISTEN_PID", strconv.Itoa(os.Getpid()))
93 repl := caddy.NewReplacer()
94
95 stream := resolveSystemdListenAddress(t, repl, "fd/{systemd.listen.web}", "fd/3")
96 lnAny, err := stream.Listen(context.Background(), 0, net.ListenConfig{})
97 if err != nil {
98 t.Fatal(err)
99 }
100 ln, ok := lnAny.(net.Listener)
101 if !ok {
102 t.Fatalf("stream listener has type %T", lnAny)
103 }
104 defer ln.Close()
105 checkInheritedStream(t, ln)
106 if _, err := repl.ReplaceOrErr("fd/{systemd.listen.missing}", true, true); err == nil {
107 t.Fatal("unknown descriptor name did not fail replacement")
108 }
109
110 // The inherited descriptor mapping belongs to the process. Keep using the
111 // first valid snapshot even if later code changes the environment.
112 t.Setenv("LISTEN_FDNAMES", "changed")
113
114 datagram := resolveSystemdListenAddress(t, repl, "fdgram/{systemd.listen.dns}", "fdgram/4")
115 pcAny, err := datagram.Listen(context.Background(), 0, net.ListenConfig{})
116 if err != nil {
117 t.Fatal(err)
118 }
119 pc, ok := pcAny.(net.PacketConn)
120 if !ok {
121 t.Fatalf("datagram listener has type %T", pcAny)
122 }
123 defer pc.Close()
124 checkInheritedDatagram(t, pc)
125
126 h3 := resolveSystemdListenAddress(t, repl, "fdgram/{systemd.listen.h3}", "fdgram/5")
127 var tlsConfig *tls.Config
128 tlsConfig = &tls.Config{
129 GetConfigForClient: func(*tls.ClientHelloInfo) (*tls.Config, error) {
130 return tlsConfig, nil
131 },
132 }
133 quicListener, err := h3.ListenQUIC(context.Background(), 0, net.ListenConfig{}, caddy.ListenQUICOptions{
134 TLSConfig: tlsConfig,
135 })
136 if err != nil {
137 t.Fatal(err)
138 }
139 if err := quicListener.Close(); err != nil {
140 t.Fatal(err)
141 }
142 if err := quicListener.Close(); err != nil {
143 t.Fatal(err)
144 }
145 }
146
147 func resolveSystemdListenAddress(t *testing.T, repl *caddy.Replacer, input, want string) caddy.NetworkAddress {
148 t.Helper()
149 resolved, err := repl.ReplaceOrErr(input, true, true)
150 if err != nil {
151 t.Fatal(err)
152 }
153 if resolved != want {
154 t.Fatalf("resolved address = %q; want %q", resolved, want)
155 }
156 address, err := caddy.ParseNetworkAddress(resolved)
157 if err != nil {
158 t.Fatal(err)
159 }
160 return address
161 }
162
163 func checkInheritedStream(t *testing.T, ln net.Listener) {
164 t.Helper()
165 done := make(chan error, 1)
166 go func() {
167 conn, err := ln.Accept()
168 if err != nil {
169 done <- err
170 return
171 }
172 defer conn.Close()
173 buf := make([]byte, 4)
174 _, err = io.ReadFull(conn, buf)
175 if err == nil && string(buf) != "ping" {
176 err = fmt.Errorf("received %q; want ping", buf)
177 }
178 done <- err
179 }()
180
181 conn, err := net.DialTimeout("tcp", ln.Addr().String(), time.Second)
182 if err != nil {
183 t.Fatal(err)
184 }
185 _, err = conn.Write([]byte("ping"))
186 _ = conn.Close()
187 if err != nil {
188 t.Fatal(err)
189 }
190 select {
191 case err := <-done:
192 if err != nil {
193 t.Fatal(err)
194 }
195 case <-time.After(time.Second):
196 t.Fatal("timed out waiting for inherited listener")
197 }
198 }
199
200 func checkInheritedDatagram(t *testing.T, pc net.PacketConn) {
201 t.Helper()
202 conn, err := net.Dial("udp", pc.LocalAddr().String())
203 if err != nil {
204 t.Fatal(err)
205 }
206 defer conn.Close()
207 if _, err := conn.Write([]byte("ping")); err != nil {
208 t.Fatal(err)
209 }
210 if err := pc.SetReadDeadline(time.Now().Add(time.Second)); err != nil {
211 t.Fatal(err)
212 }
213 buf := make([]byte, 4)
214 n, _, err := pc.ReadFrom(buf)
215 if err != nil {
216 t.Fatal(err)
217 }
218 if string(buf[:n]) != "ping" {
219 t.Fatalf("received %q; want ping", buf[:n])
220 }
221 }
222
223 func withoutSystemdListenEnv(env []string) []string {
224 filtered := make([]string, 0, len(env))
225 for _, entry := range env {
226 if strings.HasPrefix(entry, "LISTEN_PID=") ||
227 strings.HasPrefix(entry, "LISTEN_PIDFDID=") ||
228 strings.HasPrefix(entry, "LISTEN_FDS=") ||
229 strings.HasPrefix(entry, "LISTEN_FDNAMES=") ||
230 strings.HasPrefix(entry, systemdListenHelper+"=") {
231 continue
232 }
233 filtered = append(filtered, entry)
234 }
235 return filtered
236 }