@marcel91200 / caddy Mirror · read-only / commits / 6772ffb8
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, 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 -}