AdminAPIRequest makes an API request according to the CLI flags given, with the given HTTP method and request URI. If body is non-nil, it will be assumed to be Content-Type application/json. The caller should close the response body. Should only be used by Caddy CLI commands which need to interact w
(adminAddr, method, uri string, headers http.Header, body io.Reader)
| 772 | // the response body. Should only be used by Caddy CLI commands which |
| 773 | // need to interact with a running instance of Caddy via the admin API. |
| 774 | func AdminAPIRequest(adminAddr, method, uri string, headers http.Header, body io.Reader) (*http.Response, error) { |
| 775 | parsedAddr, err := caddy.ParseNetworkAddress(adminAddr) |
| 776 | if err != nil || parsedAddr.PortRangeSize() > 1 { |
| 777 | return nil, fmt.Errorf("invalid admin address %s: %v", adminAddr, err) |
| 778 | } |
| 779 | origin := "http://" + parsedAddr.JoinHostPort(0) |
| 780 | if parsedAddr.IsUnixNetwork() { |
| 781 | origin = "http://127.0.0.1" // bogus host is a hack so that http.NewRequest() is happy |
| 782 | |
| 783 | // the unix address at this point might still contain the optional |
| 784 | // unix socket permissions, which are part of the address/host. |
| 785 | // those need to be removed first, as they aren't part of the |
| 786 | // resulting unix file path |
| 787 | addr, _, err := internal.SplitUnixSocketPermissionsBits(parsedAddr.Host) |
| 788 | if err != nil { |
| 789 | return nil, err |
| 790 | } |
| 791 | parsedAddr.Host = addr |
| 792 | } else if parsedAddr.IsFdNetwork() { |
| 793 | origin = "http://127.0.0.1" |
| 794 | } |
| 795 | |
| 796 | // form the request |
| 797 | req, err := http.NewRequest(method, origin+uri, body) |
| 798 | if err != nil { |
| 799 | return nil, fmt.Errorf("making request: %v", err) |
| 800 | } |
| 801 | if parsedAddr.IsUnixNetwork() || parsedAddr.IsFdNetwork() { |
| 802 | // We used to conform to RFC 2616 Section 14.26 which requires |
| 803 | // an empty host header when there is no host, as is the case |
| 804 | // with unix sockets and socket fds. However, Go required a |
| 805 | // Host value so we used a hack of a space character as the host |
| 806 | // (it would see the Host was non-empty, then trim the space later). |
| 807 | // As of Go 1.20.6 (July 2023), this hack no longer works. See: |
| 808 | // https://github.com/golang/go/issues/60374 |
| 809 | // See also the discussion here: |
| 810 | // https://github.com/golang/go/issues/61431 |
| 811 | // |
| 812 | // After that, we now require a Host value of either 127.0.0.1 |
| 813 | // or ::1 if one is set. Above I choose to use 127.0.0.1. Even |
| 814 | // though the value should be completely irrelevant (it could be |
| 815 | // "srldkjfsd"), if for some reason the Host *is* used, at least |
| 816 | // we can have some reasonable assurance it will stay on the local |
| 817 | // machine and that browsers, if they ever allow access to unix |
| 818 | // sockets, can still enforce CORS, ensuring it is still coming |
| 819 | // from the local machine. |
| 820 | } else { |
| 821 | req.Header.Set("Origin", origin) |
| 822 | } |
| 823 | if body != nil { |
| 824 | req.Header.Set("Content-Type", "application/json") |
| 825 | } |
| 826 | maps.Copy(req.Header, headers) |
| 827 | |
| 828 | // make an HTTP client that dials our network type, since admin |
| 829 | // endpoints aren't always TCP, which is what the default transport |
| 830 | // expects; reuse is not of particular concern here |
| 831 | client := http.Client{ |
no test coverage detected