Make Response an io.ReadCloser
This commit is contained in:
parent
ae3fc2fc73
commit
e1c04ee605
48
response.go
48
response.go
@ -3,7 +3,6 @@ package gemini
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -16,7 +15,10 @@ 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 Body field is read.
|
// 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.
|
||||||
type Response struct {
|
type Response struct {
|
||||||
// Status contains the response status code.
|
// Status contains the response status code.
|
||||||
Status Status
|
Status Status
|
||||||
@ -27,18 +29,7 @@ type Response struct {
|
|||||||
// Meta should not be longer than 1024 bytes.
|
// Meta should not be longer than 1024 bytes.
|
||||||
Meta string
|
Meta string
|
||||||
|
|
||||||
// Body represents the response body.
|
body io.ReadCloser
|
||||||
//
|
|
||||||
// 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
|
conn net.Conn
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,9 +81,9 @@ func ReadResponse(rc io.ReadCloser) (*Response, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if resp.Status.Class() == StatusSuccess {
|
if resp.Status.Class() == StatusSuccess {
|
||||||
resp.Body = newReadCloserBody(br, rc)
|
resp.body = newReadCloserBody(br, rc)
|
||||||
} else {
|
} else {
|
||||||
resp.Body = nopReadCloser{}
|
resp.body = nopReadCloser{}
|
||||||
rc.Close()
|
rc.Close()
|
||||||
}
|
}
|
||||||
return resp, nil
|
return resp, nil
|
||||||
@ -135,22 +126,15 @@ func (b *readCloserBody) Read(p []byte) (n int, err error) {
|
|||||||
return b.ReadCloser.Read(p)
|
return b.ReadCloser.Read(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write writes r to w in the Gemini response format, including the
|
// Read reads data from the response body.
|
||||||
// header and body.
|
// The response body is streamed on demand as Read is called.
|
||||||
//
|
func (r *Response) Read(p []byte) (n int, err error) {
|
||||||
// This method consults the Status, Meta, and Body fields of the response.
|
return r.body.Read(p)
|
||||||
// The Response Body is closed after it is sent.
|
}
|
||||||
func (r *Response) Write(w io.Writer) error {
|
|
||||||
if _, err := fmt.Fprintf(w, "%02d %s\r\n", r.Status, r.Meta); err != nil {
|
// Close closes the response body.
|
||||||
return err
|
func (r *Response) Close() error {
|
||||||
}
|
return r.body.Close()
|
||||||
if r.Body != nil {
|
|
||||||
defer r.Body.Close()
|
|
||||||
if _, err := io.Copy(w, r.Body); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Conn returns the network connection on which the response was received.
|
// Conn returns the network connection on which the response was received.
|
||||||
|
@ -96,7 +96,7 @@ func TestReadWriteResponse(t *testing.T) {
|
|||||||
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, _ := io.ReadAll(resp.Body)
|
b, _ := io.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)
|
||||||
@ -107,14 +107,12 @@ func TestReadWriteResponse(t *testing.T) {
|
|||||||
if test.Err != nil || test.SkipWrite {
|
if test.Err != nil || test.SkipWrite {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
resp := &Response{
|
|
||||||
Status: test.Status,
|
|
||||||
Meta: test.Meta,
|
|
||||||
Body: io.NopCloser(strings.NewReader(test.Body)),
|
|
||||||
}
|
|
||||||
|
|
||||||
var b strings.Builder
|
var b strings.Builder
|
||||||
if err := resp.Write(&b); err != nil {
|
w := NewResponseWriter(nopCloser{&b})
|
||||||
|
w.WriteHeader(test.Status, test.Meta)
|
||||||
|
io.Copy(w, strings.NewReader(test.Body))
|
||||||
|
if err := w.Flush(); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -125,3 +123,15 @@ func TestReadWriteResponse(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type nopCloser struct {
|
||||||
|
io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w nopCloser) Write(b []byte) (int, error) {
|
||||||
|
return w.Writer.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nopCloser) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user