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, 14:45 UTC
Revert "listeners: Add support for named socket activation (#7243)"
This reverts commit 156ce99d3a46be8cefe8502b2c30b757e4deb79f.
Matthew Holt committed
Feb 19, 2026 at 11:32 UTC
6772ffb805d688475369a697e3962fa48cbf6bd2
2 files changed
+1
-365
listeners.go
+1
-81
@@ -38,10 +38,6 @@ import (
38
"github.com/caddyserver/caddy/v2/internal"
39
)
40
41
-// listenFdsStart is the first file descriptor number for systemd socket activation.
42
-// File descriptors 0, 1, 2 are reserved for stdin, stdout, stderr.
43
-const listenFdsStart = 3
44
-
41
// NetworkAddress represents one or more network addresses.
42
// It contains the individual components for a parsed network
43
// address of the form accepted by ParseNetworkAddress().
@@ -309,64 +305,6 @@ func IsFdNetwork(netw string) bool {
305
return strings.HasPrefix(netw, "fd")
306
}
307
312
-// getFdByName returns the file descriptor number for the given
313
-// socket name from systemd's LISTEN_FDNAMES environment variable.
314
-// Socket names are provided by systemd via socket activation.
315
-//
316
-// The name can optionally include an index to handle multiple sockets
317
-// with the same name: "web:0" for first, "web:1" for second, etc.
318
-// If no index is specified, defaults to index 0 (first occurrence).
319
-func getFdByName(nameWithIndex string) (int, error) {
320
- if nameWithIndex == "" {
321
- return 0, fmt.Errorf("socket name cannot be empty")
322
- }
323
-
324
- fdNamesStr := os.Getenv("LISTEN_FDNAMES")
325
- if fdNamesStr == "" {
326
- return 0, fmt.Errorf("LISTEN_FDNAMES environment variable not set")
327
- }
328
-
329
- // Parse name and optional index
330
- parts := strings.Split(nameWithIndex, ":")
331
- if len(parts) > 2 {
332
- return 0, fmt.Errorf("invalid socket name format '%s': too many colons", nameWithIndex)
333
- }
334
-
335
- name := parts[0]
336
- targetIndex := 0
337
-
338
- if len(parts) > 1 {
339
- var err error
340
- targetIndex, err = strconv.Atoi(parts[1])
341
- if err != nil {
342
- return 0, fmt.Errorf("invalid socket index '%s': %v", parts[1], err)
343
- }
344
- if targetIndex < 0 {
345
- return 0, fmt.Errorf("socket index cannot be negative: %d", targetIndex)
346
- }
347
- }
348
-
349
- // Parse the socket names
350
- names := strings.Split(fdNamesStr, ":")
351
-
352
- // Find the Nth occurrence of the requested name
353
- matchCount := 0
354
- for i, fdName := range names {
355
- if fdName == name {
356
- if matchCount == targetIndex {
357
- return listenFdsStart + i, nil
358
- }
359
- matchCount++
360
- }
361
- }
362
-
363
- if matchCount == 0 {
364
- return 0, fmt.Errorf("socket name '%s' not found in LISTEN_FDNAMES", name)
365
- }
366
-
367
- return 0, fmt.Errorf("socket name '%s' found %d times, but index %d requested", name, matchCount, targetIndex)
368
-}
369
-
308
// ParseNetworkAddress parses addr into its individual
309
// components. The input string is expected to be of
310
// the form "network/host:port-range" where any part is
@@ -398,27 +336,9 @@ func ParseNetworkAddressWithDefaults(addr, defaultNetwork string, defaultPort ui
336
}, err
337
}
338
if IsFdNetwork(network) {
401
- fdAddr := host
402
-
403
- // Handle named socket activation (fdname/name, fdgramname/name)
404
- if strings.HasPrefix(network, "fdname") || strings.HasPrefix(network, "fdgramname") {
405
- fdNum, err := getFdByName(host)
406
- if err != nil {
407
- return NetworkAddress{}, fmt.Errorf("named socket activation: %v", err)
408
- }
409
- fdAddr = strconv.Itoa(fdNum)
410
-
411
- // Normalize network to standard fd/fdgram
412
- if strings.HasPrefix(network, "fdname") {
413
- network = "fd"
414
- } else {
415
- network = "fdgram"
416
- }
417
- }
418
-
339
return NetworkAddress{
340
Network: network,
421
- Host: fdAddr,
341
+ Host: host,
342
}, nil
343
}
344
var start, end uint64
listeners_test.go
-284
@@ -15,7 +15,6 @@
15
package caddy
16
17
import (
18
- "os"
18
"reflect"
19
"testing"
20
@@ -653,286 +652,3 @@ func TestSplitUnixSocketPermissionsBits(t *testing.T) {
652
}
653
}
654
}
656
-
657
-// TestGetFdByName tests the getFdByName function for systemd socket activation.
658
-func TestGetFdByName(t *testing.T) {
659
- // Save original environment
660
- originalFdNames := os.Getenv("LISTEN_FDNAMES")
661
-
662
- // Restore environment after test
663
- defer func() {
664
- if originalFdNames != "" {
665
- os.Setenv("LISTEN_FDNAMES", originalFdNames)
666
- } else {
667
- os.Unsetenv("LISTEN_FDNAMES")
668
- }
669
- }()
670
-
671
- tests := []struct {
672
- name string
673
- fdNames string
674
- socketName string
675
- expectedFd int
676
- expectError bool
677
- }{
678
- {
679
- name: "simple http socket",
680
- fdNames: "http",
681
- socketName: "http",
682
- expectedFd: 3,
683
- },
684
- {
685
- name: "multiple different sockets - first",
686
- fdNames: "http:https:dns",
687
- socketName: "http",
688
- expectedFd: 3,
689
- },
690
- {
691
- name: "multiple different sockets - second",
692
- fdNames: "http:https:dns",
693
- socketName: "https",
694
- expectedFd: 4,
695
- },
696
- {
697
- name: "multiple different sockets - third",
698
- fdNames: "http:https:dns",
699
- socketName: "dns",
700
- expectedFd: 5,
701
- },
702
- {
703
- name: "duplicate names - first occurrence (no index)",
704
- fdNames: "web:web:api",
705
- socketName: "web",
706
- expectedFd: 3,
707
- },
708
- {
709
- name: "duplicate names - first occurrence (explicit index 0)",
710
- fdNames: "web:web:api",
711
- socketName: "web:0",
712
- expectedFd: 3,
713
- },
714
- {
715
- name: "duplicate names - second occurrence (index 1)",
716
- fdNames: "web:web:api",
717
- socketName: "web:1",
718
- expectedFd: 4,
719
- },
720
- {
721
- name: "complex duplicates - first api",
722
- fdNames: "web:api:web:api:dns",
723
- socketName: "api:0",
724
- expectedFd: 4,
725
- },
726
- {
727
- name: "complex duplicates - second api",
728
- fdNames: "web:api:web:api:dns",
729
- socketName: "api:1",
730
- expectedFd: 6,
731
- },
732
- {
733
- name: "complex duplicates - first web",
734
- fdNames: "web:api:web:api:dns",
735
- socketName: "web:0",
736
- expectedFd: 3,
737
- },
738
- {
739
- name: "complex duplicates - second web",
740
- fdNames: "web:api:web:api:dns",
741
- socketName: "web:1",
742
- expectedFd: 5,
743
- },
744
- {
745
- name: "socket not found",
746
- fdNames: "http:https",
747
- socketName: "missing",
748
- expectError: true,
749
- },
750
- {
751
- name: "empty socket name",
752
- fdNames: "http",
753
- socketName: "",
754
- expectError: true,
755
- },
756
- {
757
- name: "missing LISTEN_FDNAMES",
758
- fdNames: "",
759
- socketName: "http",
760
- expectError: true,
761
- },
762
- {
763
- name: "index out of range",
764
- fdNames: "web:web",
765
- socketName: "web:2",
766
- expectError: true,
767
- },
768
- {
769
- name: "negative index",
770
- fdNames: "web",
771
- socketName: "web:-1",
772
- expectError: true,
773
- },
774
- {
775
- name: "invalid index format",
776
- fdNames: "web",
777
- socketName: "web:abc",
778
- expectError: true,
779
- },
780
- {
781
- name: "too many colons",
782
- fdNames: "web",
783
- socketName: "web:0:extra",
784
- expectError: true,
785
- },
786
- }
787
-
788
- for _, tc := range tests {
789
- t.Run(tc.name, func(t *testing.T) {
790
- // Set up environment
791
- if tc.fdNames != "" {
792
- os.Setenv("LISTEN_FDNAMES", tc.fdNames)
793
- } else {
794
- os.Unsetenv("LISTEN_FDNAMES")
795
- }
796
-
797
- // Test the function
798
- fd, err := getFdByName(tc.socketName)
799
-
800
- if tc.expectError {
801
- if err == nil {
802
- t.Errorf("Expected error but got none")
803
- }
804
- } else {
805
- if err != nil {
806
- t.Errorf("Expected no error but got: %v", err)
807
- }
808
- if fd != tc.expectedFd {
809
- t.Errorf("Expected FD %d but got %d", tc.expectedFd, fd)
810
- }
811
- }
812
- })
813
- }
814
-}
815
-
816
-// TestParseNetworkAddressFdName tests parsing of fdname and fdgramname addresses.
817
-func TestParseNetworkAddressFdName(t *testing.T) {
818
- // Save and restore environment
819
- originalFdNames := os.Getenv("LISTEN_FDNAMES")
820
- defer func() {
821
- if originalFdNames != "" {
822
- os.Setenv("LISTEN_FDNAMES", originalFdNames)
823
- } else {
824
- os.Unsetenv("LISTEN_FDNAMES")
825
- }
826
- }()
827
-
828
- // Set up test environment
829
- os.Setenv("LISTEN_FDNAMES", "http:https:dns")
830
-
831
- tests := []struct {
832
- input string
833
- expectAddr NetworkAddress
834
- expectErr bool
835
- }{
836
- {
837
- input: "fdname/http",
838
- expectAddr: NetworkAddress{
839
- Network: "fd",
840
- Host: "3",
841
- },
842
- },
843
- {
844
- input: "fdname/https",
845
- expectAddr: NetworkAddress{
846
- Network: "fd",
847
- Host: "4",
848
- },
849
- },
850
- {
851
- input: "fdname/dns",
852
- expectAddr: NetworkAddress{
853
- Network: "fd",
854
- Host: "5",
855
- },
856
- },
857
- {
858
- input: "fdname/http:0",
859
- expectAddr: NetworkAddress{
860
- Network: "fd",
861
- Host: "3",
862
- },
863
- },
864
- {
865
- input: "fdname/https:0",
866
- expectAddr: NetworkAddress{
867
- Network: "fd",
868
- Host: "4",
869
- },
870
- },
871
- {
872
- input: "fdgramname/http",
873
- expectAddr: NetworkAddress{
874
- Network: "fdgram",
875
- Host: "3",
876
- },
877
- },
878
- {
879
- input: "fdgramname/https",
880
- expectAddr: NetworkAddress{
881
- Network: "fdgram",
882
- Host: "4",
883
- },
884
- },
885
- {
886
- input: "fdgramname/http:0",
887
- expectAddr: NetworkAddress{
888
- Network: "fdgram",
889
- Host: "3",
890
- },
891
- },
892
- {
893
- input: "fdname/nonexistent",
894
- expectErr: true,
895
- },
896
- {
897
- input: "fdgramname/nonexistent",
898
- expectErr: true,
899
- },
900
- {
901
- input: "fdname/http:99",
902
- expectErr: true,
903
- },
904
- {
905
- input: "fdname/invalid:abc",
906
- expectErr: true,
907
- },
908
- // Test that old fd/N syntax still works
909
- {
910
- input: "fd/7",
911
- expectAddr: NetworkAddress{
912
- Network: "fd",
913
- Host: "7",
914
- },
915
- },
916
- {
917
- input: "fdgram/8",
918
- expectAddr: NetworkAddress{
919
- Network: "fdgram",
920
- Host: "8",
921
- },
922
- },
923
- }
924
-
925
- for i, tc := range tests {
926
- actualAddr, err := ParseNetworkAddress(tc.input)
927
-
928
- if tc.expectErr && err == nil {
929
- t.Errorf("Test %d (%s): Expected error but got none", i, tc.input)
930
- }
931
- if !tc.expectErr && err != nil {
932
- t.Errorf("Test %d (%s): Expected no error but got: %v", i, tc.input, err)
933
- }
934
- if !tc.expectErr && !reflect.DeepEqual(tc.expectAddr, actualAddr) {
935
- t.Errorf("Test %d (%s): Expected %+v but got %+v", i, tc.input, tc.expectAddr, actualAddr)
936
- }
937
- }
938
-}