peek.go 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. package util
  2. import (
  3. "bytes"
  4. "io"
  5. "strings"
  6. )
  7. // PeekedReadCloser is a ReadCloser that allows peeking into a stream and buffering it in memory.
  8. // It can be instantiated using the Peek function. After a stream has been peeked, it can still be fully
  9. // read by reading the PeekedReadCloser. It first drained from the memory buffer, and then from the remaining
  10. // underlying reader.
  11. type PeekedReadCloser struct {
  12. PeekedBytes []byte
  13. LimitReached bool
  14. peeked io.Reader
  15. underlying io.ReadCloser
  16. closed bool
  17. }
  18. // Peek reads the underlying ReadCloser into memory up until the limit and returns a PeekedReadCloser.
  19. // It does not return an error if limit is reached. Instead, LimitReached will be set to true.
  20. func Peek(underlying io.ReadCloser, limit int) (*PeekedReadCloser, error) {
  21. if underlying == nil {
  22. underlying = io.NopCloser(strings.NewReader(""))
  23. }
  24. peeked := make([]byte, limit)
  25. read, err := io.ReadFull(underlying, peeked)
  26. if err != nil && err != io.ErrUnexpectedEOF && err != io.EOF {
  27. return nil, err
  28. }
  29. return &PeekedReadCloser{
  30. PeekedBytes: peeked[:read],
  31. LimitReached: read == limit,
  32. underlying: underlying,
  33. peeked: bytes.NewReader(peeked[:read]),
  34. closed: false,
  35. }, nil
  36. }
  37. func PeekLimit(underlying io.ReadCloser, limit int) (*PeekedReadCloser, error) {
  38. rc, err := Peek(underlying, limit)
  39. if err != nil {
  40. return nil, err
  41. } else if rc.LimitReached {
  42. return nil, ErrLimitReached
  43. }
  44. return rc, nil
  45. }
  46. // Read reads from the peeked bytes and then from the underlying stream
  47. func (r *PeekedReadCloser) Read(p []byte) (n int, err error) {
  48. if r.closed {
  49. return 0, io.EOF
  50. }
  51. n, err = r.peeked.Read(p)
  52. if err == io.EOF {
  53. return r.underlying.Read(p)
  54. } else if err != nil {
  55. return 0, err
  56. }
  57. return
  58. }
  59. // Close closes the underlying stream
  60. func (r *PeekedReadCloser) Close() error {
  61. if r.closed {
  62. return io.EOF
  63. }
  64. r.closed = true
  65. return r.underlying.Close()
  66. }