Make (*Response).Body an io.ReadCloser

This commit is contained in:
Adnan Maolood 2020-10-27 19:16:55 -04:00
parent 860a33f5a2
commit 12a9deb1a6
3 changed files with 46 additions and 19 deletions

View File

@ -66,7 +66,6 @@ func (c *Client) Send(req *Request) (*Response, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer conn.Close()
// Write the request // Write the request
w := bufio.NewWriter(conn) w := bufio.NewWriter(conn)
@ -77,8 +76,7 @@ func (c *Client) Send(req *Request) (*Response, error) {
// Read the response // Read the response
resp := &Response{} resp := &Response{}
r := bufio.NewReader(conn) if err := resp.read(conn); err != nil {
if err := resp.read(r); err != nil {
return nil, err return nil, err
} }
// Store connection information // Store connection information

View File

@ -7,6 +7,7 @@ import (
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"fmt" "fmt"
"io/ioutil"
"net/url" "net/url"
"os" "os"
"time" "time"
@ -80,7 +81,12 @@ func sendRequest(req *gmi.Request) error {
req.URL.RawQuery = url.QueryEscape(scanner.Text()) req.URL.RawQuery = url.QueryEscape(scanner.Text())
return sendRequest(req) return sendRequest(req)
case gmi.StatusClassSuccess: case gmi.StatusClassSuccess:
fmt.Print(string(resp.Body)) defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
fmt.Print(string(body))
return nil return nil
case gmi.StatusClassRedirect: case gmi.StatusClassRedirect:
fmt.Println("Redirecting to", resp.Meta) fmt.Println("Redirecting to", resp.Meta)

View File

@ -3,7 +3,7 @@ package gemini
import ( import (
"bufio" "bufio"
"crypto/tls" "crypto/tls"
"io/ioutil" "io"
"strconv" "strconv"
) )
@ -18,19 +18,20 @@ type Response struct {
// Meta should not be longer than 1024 bytes. // Meta should not be longer than 1024 bytes.
Meta string Meta string
// Body contains the response body. // Body contains the response body for successful responses.
Body []byte Body io.ReadCloser
// TLS contains information about the TLS connection on which the response // TLS contains information about the TLS connection on which the response
// was received. // was received.
TLS tls.ConnectionState TLS tls.ConnectionState
} }
// read reads a Gemini response from the provided buffered reader. // read reads a Gemini response from the provided io.ReadCloser.
func (resp *Response) read(r *bufio.Reader) error { func (resp *Response) read(rc io.ReadCloser) error {
br := bufio.NewReader(rc)
// Read the status // Read the status
statusB := make([]byte, 2) statusB := make([]byte, 2)
if _, err := r.Read(statusB); err != nil { if _, err := br.Read(statusB); err != nil {
return err return err
} }
status, err := strconv.Atoi(string(statusB)) status, err := strconv.Atoi(string(statusB))
@ -47,14 +48,14 @@ func (resp *Response) read(r *bufio.Reader) error {
} }
// Read one space // Read one space
if b, err := r.ReadByte(); err != nil { if b, err := br.ReadByte(); err != nil {
return err return err
} else if b != ' ' { } else if b != ' ' {
return ErrInvalidResponse return ErrInvalidResponse
} }
// Read the meta // Read the meta
meta, err := r.ReadString('\r') meta, err := br.ReadString('\r')
if err != nil { if err != nil {
return err return err
} }
@ -67,19 +68,41 @@ func (resp *Response) read(r *bufio.Reader) error {
resp.Meta = meta resp.Meta = meta
// Read terminating newline // Read terminating newline
if b, err := r.ReadByte(); err != nil { if b, err := br.ReadByte(); err != nil {
return err return err
} else if b != '\n' { } else if b != '\n' {
return ErrInvalidResponse return ErrInvalidResponse
} }
// Read response body
if resp.Status.Class() == StatusClassSuccess { if resp.Status.Class() == StatusClassSuccess {
var err error resp.Body = newReadCloserBody(br, rc)
resp.Body, err = ioutil.ReadAll(r)
if err != nil {
return err
}
} }
return nil return nil
} }
type readCloserBody struct {
br *bufio.Reader // used until empty
io.ReadCloser
}
func newReadCloserBody(br *bufio.Reader, rc io.ReadCloser) io.ReadCloser {
body := &readCloserBody{ReadCloser: rc}
if br.Buffered() != 0 {
body.br = br
}
return body
}
func (b *readCloserBody) Read(p []byte) (n int, err error) {
if b.br != nil {
if n := b.br.Buffered(); len(p) > n {
p = p[:n]
}
n, err = b.br.Read(p)
if b.br.Buffered() == 0 {
b.br = nil
}
return n, err
}
return b.ReadCloser.Read(p)
}