Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions pkg/api/http_client.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package api

import (
"context"
"fmt"
"io"
"net"
Expand Down Expand Up @@ -235,13 +236,14 @@ func (hrt headerRoundTripper) RoundTrip(req *http.Request) (*http.Response, erro
}

func newUnixDomainSocketRoundTripper(socketPath string) http.RoundTripper {
dial := func(network, addr string) (net.Conn, error) {
return net.Dial("unix", socketPath)
dial := func(ctx context.Context, network, addr string) (net.Conn, error) {
var d net.Dialer
return d.DialContext(ctx, "unix", socketPath)
}

return &http.Transport{
Dial: dial,
DialTLS: dial,
DialContext: dial,
DialTLSContext: dial,
DisableKeepAlives: true,
}
}
Expand Down
70 changes: 70 additions & 0 deletions pkg/api/http_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ import (
"bytes"
"fmt"
"io"
"net"
"net/http"
"os"
"path/filepath"
"strings"
"testing"

"github.com/cli/go-gh/v2/internal/testutils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/h2non/gock.v1"
)

Expand Down Expand Up @@ -321,6 +325,72 @@ func TestNewHTTPClientCheckRedirect(t *testing.T) {
})
}

func TestNewHTTPClientSendsHTTPRequestsThroughUnixDomainSocket(t *testing.T) {
t.Parallel()

// Given a server listening on a unix socket and a client configured to use it
socketPath := serveOnUnixSocket(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "socket server received %s%s", r.Host, r.URL.Path)
}))
client, err := NewHTTPClient(ClientOptions{
Host: "github.com",
AuthToken: "oauth_token",
UnixDomainSocket: socketPath,
})
require.NoError(t, err)

// When the client requests an http URL whose host cannot resolve
res, err := client.Get("http://unresolvable.invalid/user")

// Then the request is delivered to the socket server
require.NoError(t, err)
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
require.NoError(t, err)
assert.Equal(t, "socket server received unresolvable.invalid/user", string(body))
}

func TestNewHTTPClientSendsHTTPSRequestsThroughUnixDomainSocketWithoutTLS(t *testing.T) {
t.Parallel()

// Given a plain HTTP server listening on a unix socket and a client configured to use it
socketPath := serveOnUnixSocket(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "socket server received %s%s", r.Host, r.URL.Path)
}))
client, err := NewHTTPClient(ClientOptions{
Host: "github.com",
AuthToken: "oauth_token",
UnixDomainSocket: socketPath,
})
require.NoError(t, err)

// When the client requests an https URL whose host cannot resolve
res, err := client.Get("https://unresolvable.invalid/user")

// Then the request is delivered to the socket server as plain HTTP
require.NoError(t, err)
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
require.NoError(t, err)
assert.Equal(t, "socket server received unresolvable.invalid/user", string(body))
}

// serveOnUnixSocket serves handler on a new unix socket and returns its path.
func serveOnUnixSocket(t *testing.T, handler http.Handler) string {
t.Helper()
// t.TempDir() can exceed the 104-byte socket path limit on macOS.
dir, err := os.MkdirTemp("", "gh")
require.NoError(t, err)
t.Cleanup(func() { _ = os.RemoveAll(dir) })
socketPath := filepath.Join(dir, "gh.sock")
listener, err := net.Listen("unix", socketPath)
require.NoError(t, err)
server := &http.Server{Handler: handler}
go func() { _ = server.Serve(listener) }()
t.Cleanup(func() { _ = server.Close() })
return socketPath
}

type tripper struct {
roundTrip func(*http.Request) (*http.Response, error)
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/x/markdown/accessibility_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ func TestAccessibleLightStyleConfigIs4Bit(t *testing.T) {
// that are downsampled to 4-bit colors unlike Glamour, which are 8-bit colors.
// For more information, https://github.com/alecthomas/chroma/blob/0bf0e9f9ae2a81d463afe769cce01ff821bee3ba/formatters/tty_indexed.go#L32-L44
func validateColors(t *testing.T, v reflect.Value, path string) {
if v.Kind() == reflect.Ptr {
if v.Kind() == reflect.Pointer {
if v.IsNil() {
return
}
Expand All @@ -212,7 +212,7 @@ func validateColors(t *testing.T, v reflect.Value, path string) {
if fieldType.Name == "Chroma" {
continue
} else if (fieldType.Name == "Color" || fieldType.Name == "BackgroundColor") &&
fieldType.Type.Kind() == reflect.Ptr && fieldType.Type.Elem().Kind() == reflect.String {
fieldType.Type.Kind() == reflect.Pointer && fieldType.Type.Elem().Kind() == reflect.String {

if field.IsNil() {
continue
Expand Down
Loading