Documentation
¶
Overview ¶
Package re provides tools for reading an io.Reader more than once. It offers a re-readable Reader type that buffers content in memory, plus helpers for reading (and re-reading) HTTP request and response bodies.
Example (ReReader) ¶
Example_reReader shows the core feature: a re.Reader can be read to completion more than once. After each read reaches EOF, the cursor rewinds so the next read starts again from the beginning.
package main
import (
"fmt"
"io"
"strings"
"github.com/benpate/re"
)
func main() {
// A standard io.Reader can only be read once.
singleReader := strings.NewReader("Hello World.")
// Wrap it so it can be read repeatedly.
multipleReader, err := re.NewReader(singleReader)
if err != nil {
panic(err)
}
for i := 0; i < 3; i++ {
body, _ := io.ReadAll(multipleReader)
fmt.Println(string(body))
}
}
Output: Hello World. Hello World. Hello World.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CloneResponse ¶ added in v0.3.0
CloneResponse makes an exact copy of a response WITHOUT closing the original response body. A nil original yields a zero-value response.
func ReadRequestBody ¶ added in v0.2.0
ReadRequestBody reads the request.Body then replaces it with a new reader that can be read again by another process.
Example ¶
ExampleReadRequestBody shows that after reading a request body, the body is still readable by a later handler, because ReadRequestBody replaces it with a fresh reader.
package main
import (
"fmt"
"io"
"net/http/httptest"
"strings"
"github.com/benpate/re"
)
func main() {
request := httptest.NewRequest("POST", "https://example.com", strings.NewReader("request payload"))
// One layer reads the body...
body, _ := re.ReadRequestBody(request)
fmt.Printf("first reader: %s\n", body)
// ...and a later layer can still read the same body.
again, _ := io.ReadAll(request.Body)
fmt.Printf("second reader: %s\n", again)
}
Output: first reader: request payload second reader: request payload
Types ¶
type Reader ¶
type Reader struct {
// contains filtered or unexported fields
}
Reader is a simple, re-usable io.ReadCloser. It stores the entire contents of another io.Reader in memory, so it should not be used with large files.
Unlike a one-shot reader, a Reader can be read repeatedly: each Read advances an internal cursor and returns io.EOF when the content is exhausted, then the cursor rewinds so the next Read cycle starts again from the beginning. This lets one consumer read the content, then a later consumer read it again.
A Reader is NOT safe for concurrent use: the cursor is shared mutable state, so the re-reads must happen in sequence, not in parallel. To fan out to concurrent consumers, give each its own Reader over the same bytes (for example, NewReaderFromBytes(r.Bytes())).
func NewReaderFromBytes ¶
NewReaderFromBytes creates a new Reader from the given slice of bytes. The slice is retained, not copied, so callers must not mutate it afterward.
func (*Reader) Close ¶
Close implements the io.Closer interface. It is a no-op (the content is held in memory) and never returns an error.
func (*Reader) Read ¶
Read implements the io.Reader interface. It copies the next unread bytes into p, advancing the cursor. When the content is exhausted it returns 0, io.EOF and rewinds the cursor, so the Reader can be read again from the start.
Example ¶
ExampleReader_Read shows that draining a Reader through a buffer smaller than its contents returns the full content, in order, across multiple Read calls.
package main
import (
"fmt"
"io"
"github.com/benpate/re"
)
func main() {
// Read through a small (4-byte) buffer until EOF. The buffer is allocated
// once in the loop's init clause and reused across iterations.
for reader, buffer := re.NewReaderFromBytes([]byte("Hello World.")), make([]byte, 4); ; {
n, err := reader.Read(buffer)
fmt.Printf("%q\n", buffer[:n])
if err == io.EOF {
break
}
}
}
Output: "Hell" "o Wo" "rld." ""
func (*Reader) Reset ¶ added in v0.4.0
func (r *Reader) Reset()
Reset rewinds the cursor to the start of the content, so the next Read returns the content from the beginning.
Example ¶
ExampleReader_Reset shows how Reset rewinds the cursor mid-stream, so a Reader that has only been partially consumed can be read again from the start.
package main
import (
"fmt"
"io"
"github.com/benpate/re"
)
func main() {
reader := re.NewReaderFromBytes([]byte("Hello World."))
// Consume the first five bytes.
first := make([]byte, 5)
if _, err := reader.Read(first); err != nil {
panic(err)
}
fmt.Printf("first read: %s\n", first)
// Rewind, then read the whole thing.
reader.Reset()
all, _ := io.ReadAll(reader)
fmt.Printf("after reset: %s\n", all)
}
Output: first read: Hello after reset: Hello World.