response: Revert to using fields instead of methods

This commit is contained in:
Adnan Maolood 2021-02-24 18:50:39 -05:00
parent 867074d81b
commit 1bc5c68c3f
4 changed files with 38 additions and 56 deletions

2
doc.go
View File

@ -9,7 +9,7 @@ Client is a Gemini client.
if err != nil { if err != nil {
// handle error // handle error
} }
defer resp.Close() defer resp.Body.Close()
// ... // ...
Server is a Gemini server. Server is a Gemini server.

View File

@ -103,9 +103,9 @@ func do(req *gemini.Request, via []*gemini.Request) (*gemini.Response, error) {
return resp, err return resp, err
} }
switch resp.Status().Class() { switch resp.Status.Class() {
case gemini.StatusInput: case gemini.StatusInput:
input, ok := getInput(resp.Meta(), resp.Status() == gemini.StatusSensitiveInput) input, ok := getInput(resp.Meta, resp.Status == gemini.StatusSensitiveInput)
if !ok { if !ok {
break break
} }
@ -119,7 +119,7 @@ func do(req *gemini.Request, via []*gemini.Request) (*gemini.Response, error) {
return resp, errors.New("too many redirects") return resp, errors.New("too many redirects")
} }
target, err := url.Parse(resp.Meta()) target, err := url.Parse(resp.Meta)
if err != nil { if err != nil {
return resp, err return resp, err
} }
@ -156,13 +156,13 @@ func main() {
defer resp.Close() defer resp.Close()
// Handle response // Handle response
if resp.Status().Class() == gemini.StatusSuccess { if resp.Status.Class() == gemini.StatusSuccess {
_, err := io.Copy(os.Stdout, resp) _, err := io.Copy(os.Stdout, resp)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
} else { } else {
fmt.Printf("%d %s\n", resp.Status(), resp.Meta()) fmt.Printf("%d %s\n", resp.Status, resp.Meta)
os.Exit(1) os.Exit(1)
} }
} }

View File

@ -15,24 +15,29 @@ const defaultMediaType = "text/gemini; charset=utf-8"
// //
// The Client returns Responses from servers once the response // The Client returns Responses from servers once the response
// header has been received. The response body is streamed on demand // header has been received. The response body is streamed on demand
// as the response is read. If the network connection fails or the server // as the Body field is read.
// terminates the response, Read calls return an error.
//
// It is the caller's responsibility to close the response.
type Response struct { type Response struct {
status Status // Status is the response status code.
meta string Status Status
body io.ReadCloser
conn net.Conn
}
// NewResponse returns a new response with the provided status, meta, and body. // Meta returns the response meta.
func NewResponse(status Status, meta string, body io.ReadCloser) *Response { // For successful responses, the meta should contain the media type of the response.
return &Response{ // For failure responses, the meta should contain a short description of the failure.
status: status, Meta string
meta: meta,
body: body, // 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. // ReadResponse reads a Gemini response from the provided io.ReadCloser.
@ -49,7 +54,7 @@ func ReadResponse(r io.ReadCloser) (*Response, error) {
if err != nil { if err != nil {
return nil, ErrInvalidResponse return nil, ErrInvalidResponse
} }
resp.status = Status(status) resp.Status = Status(status)
// Read one space // Read one space
if b, err := br.ReadByte(); err != nil { if b, err := br.ReadByte(); err != nil {
@ -69,11 +74,11 @@ func ReadResponse(r io.ReadCloser) (*Response, error) {
if len(meta) > 1024 { if len(meta) > 1024 {
return nil, ErrInvalidResponse return nil, ErrInvalidResponse
} }
if resp.status.Class() == StatusSuccess && meta == "" { if resp.Status.Class() == StatusSuccess && meta == "" {
// Use default media type // Use default media type
meta = defaultMediaType meta = defaultMediaType
} }
resp.meta = meta resp.Meta = meta
// Read terminating newline // Read terminating newline
if b, err := br.ReadByte(); err != nil { if b, err := br.ReadByte(); err != nil {
@ -82,38 +87,15 @@ func ReadResponse(r io.ReadCloser) (*Response, error) {
return nil, ErrInvalidResponse return nil, ErrInvalidResponse
} }
if resp.status.Class() == StatusSuccess { if resp.Status.Class() == StatusSuccess {
resp.body = newBufReadCloser(br, r) resp.Body = newBufReadCloser(br, r)
} else { } else {
resp.body = nopReadCloser{} resp.Body = nopReadCloser{}
r.Close() r.Close()
} }
return resp, nil 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. // Conn returns the network connection on which the response was received.
func (r *Response) Conn() net.Conn { func (r *Response) Conn() net.Conn {
return r.conn return r.conn

View File

@ -91,13 +91,13 @@ func TestReadWriteResponse(t *testing.T) {
// No response // No response
continue continue
} }
if resp.status != test.Status { if resp.Status != test.Status {
t.Errorf("expected status = %d, got %d", test.Status, resp.status) t.Errorf("expected status = %d, got %d", test.Status, resp.Status)
} }
if resp.meta != test.Meta { if resp.Meta != test.Meta {
t.Errorf("expected meta = %s, got %s", test.Meta, resp.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) body := string(b)
if body != test.Body { if body != test.Body {
t.Errorf("expected body = %#v, got %#v", test.Body, body) t.Errorf("expected body = %#v, got %#v", test.Body, body)