diff --git a/doc.go b/doc.go index 19e50fc..227180a 100644 --- a/doc.go +++ b/doc.go @@ -9,7 +9,7 @@ Client is a Gemini client. if err != nil { // handle error } - defer resp.Close() + defer resp.Body.Close() // ... Server is a Gemini server. diff --git a/examples/client.go b/examples/client.go index adf3a6f..267af7c 100644 --- a/examples/client.go +++ b/examples/client.go @@ -103,9 +103,9 @@ func do(req *gemini.Request, via []*gemini.Request) (*gemini.Response, error) { return resp, err } - switch resp.Status().Class() { + switch resp.Status.Class() { case gemini.StatusInput: - input, ok := getInput(resp.Meta(), resp.Status() == gemini.StatusSensitiveInput) + input, ok := getInput(resp.Meta, resp.Status == gemini.StatusSensitiveInput) if !ok { break } @@ -119,7 +119,7 @@ func do(req *gemini.Request, via []*gemini.Request) (*gemini.Response, error) { return resp, errors.New("too many redirects") } - target, err := url.Parse(resp.Meta()) + target, err := url.Parse(resp.Meta) if err != nil { return resp, err } @@ -156,13 +156,13 @@ func main() { defer resp.Close() // Handle response - if resp.Status().Class() == gemini.StatusSuccess { + if resp.Status.Class() == gemini.StatusSuccess { _, err := io.Copy(os.Stdout, resp) if err != nil { log.Fatal(err) } } else { - fmt.Printf("%d %s\n", resp.Status(), resp.Meta()) + fmt.Printf("%d %s\n", resp.Status, resp.Meta) os.Exit(1) } } diff --git a/response.go b/response.go index d89fcb7..076b3fa 100644 --- a/response.go +++ b/response.go @@ -15,24 +15,29 @@ const defaultMediaType = "text/gemini; charset=utf-8" // // The Client returns Responses from servers once the response // header has been received. The response body is streamed on demand -// as the response is read. If the network connection fails or the server -// terminates the response, Read calls return an error. -// -// It is the caller's responsibility to close the response. +// as the Body field is read. type Response struct { - status Status - meta string - body io.ReadCloser - conn net.Conn -} + // Status is the response status code. + Status Status -// NewResponse returns a new response with the provided status, meta, and body. -func NewResponse(status Status, meta string, body io.ReadCloser) *Response { - return &Response{ - status: status, - meta: meta, - body: body, - } + // Meta returns the response meta. + // For successful responses, the meta should contain the media type of the response. + // For failure responses, the meta should contain a short description of the failure. + Meta string + + // Body represents the response body. + // + // The response body is streamed on demand as the Body field + // is read. If the network connection fails or the server + // terminates the response, Body.Read calls return an error. + // + // The Gemini client guarantees that Body is always + // non-nil, even on responses without a body or responses with + // a zero-length body. It is the caller's responsibility to + // close Body. + Body io.ReadCloser + + conn net.Conn } // ReadResponse reads a Gemini response from the provided io.ReadCloser. @@ -49,7 +54,7 @@ func ReadResponse(r io.ReadCloser) (*Response, error) { if err != nil { return nil, ErrInvalidResponse } - resp.status = Status(status) + resp.Status = Status(status) // Read one space if b, err := br.ReadByte(); err != nil { @@ -69,11 +74,11 @@ func ReadResponse(r io.ReadCloser) (*Response, error) { if len(meta) > 1024 { return nil, ErrInvalidResponse } - if resp.status.Class() == StatusSuccess && meta == "" { + if resp.Status.Class() == StatusSuccess && meta == "" { // Use default media type meta = defaultMediaType } - resp.meta = meta + resp.Meta = meta // Read terminating newline if b, err := br.ReadByte(); err != nil { @@ -82,38 +87,15 @@ func ReadResponse(r io.ReadCloser) (*Response, error) { return nil, ErrInvalidResponse } - if resp.status.Class() == StatusSuccess { - resp.body = newBufReadCloser(br, r) + if resp.Status.Class() == StatusSuccess { + resp.Body = newBufReadCloser(br, r) } else { - resp.body = nopReadCloser{} + resp.Body = nopReadCloser{} r.Close() } return resp, nil } -// Status returns the response status code. -func (r *Response) Status() Status { - return r.status -} - -// Meta returns the response meta. -// For successful responses, the meta should contain the media type of the response. -// For failure responses, the meta should contain a short description of the failure. -func (r *Response) Meta() string { - return r.meta -} - -// Read reads data from the response body. -// The response body is streamed on demand as Read is called. -func (r *Response) Read(p []byte) (n int, err error) { - return r.body.Read(p) -} - -// Close closes the response body. -func (r *Response) Close() error { - return r.body.Close() -} - // Conn returns the network connection on which the response was received. func (r *Response) Conn() net.Conn { return r.conn diff --git a/response_test.go b/response_test.go index 5e3e4d2..2de050f 100644 --- a/response_test.go +++ b/response_test.go @@ -91,13 +91,13 @@ func TestReadWriteResponse(t *testing.T) { // No response continue } - if resp.status != test.Status { - t.Errorf("expected status = %d, got %d", test.Status, resp.status) + if resp.Status != test.Status { + t.Errorf("expected status = %d, got %d", test.Status, resp.Status) } - if resp.meta != test.Meta { - t.Errorf("expected meta = %s, got %s", test.Meta, resp.meta) + if resp.Meta != test.Meta { + t.Errorf("expected meta = %s, got %s", test.Meta, resp.Meta) } - b, _ := ioutil.ReadAll(resp.body) + b, _ := ioutil.ReadAll(resp.Body) body := string(b) if body != test.Body { t.Errorf("expected body = %#v, got %#v", test.Body, body)