package finger import ( "bytes" "context" "errors" "io" "net" "strings" "tildegit.org/tjp/sliderule/internal/types" ) // Client is used for sending finger requests and reading responses. // // It carries no state and is reusable simultaneously by multiple goroutines. // // The zero value is immediately usable. type Client struct{} // RoundTrip sends a single finger request and returns its response. func (c Client) RoundTrip(ctx context.Context, request *types.Request) (*types.Response, error) { if request.Scheme != "finger" && request.Scheme != "" { return nil, errors.New("non-finger protocols not supported") } host := request.Host if _, port, _ := net.SplitHostPort(host); port == "" { host = net.JoinHostPort(host, "79") } conn, err := (&net.Dialer{}).DialContext(ctx, "tcp", host) if err != nil { return nil, err } defer conn.Close() request.RemoteAddr = conn.RemoteAddr() request.TLSState = nil username := request.User.String() if username == "" { username = strings.TrimPrefix(request.Path, "/") } if _, err := conn.Write([]byte(username + "\r\n")); err != nil { return nil, err } response, err := io.ReadAll(conn) if err != nil { return nil, err } return &types.Response{Body: bytes.NewBuffer(response), Request: request}, nil } // Fetch resolves a finger query. func (c Client) Fetch(ctx context.Context, query string) (*types.Response, error) { req, err := ParseRequest(bytes.NewBufferString(query + "\r\n")) if err != nil { return nil, err } return c.RoundTrip(ctx, req) } func (c Client) IsRedirect(_ *types.Response) bool { return false }