Class: Datadog::AppSec::Contrib::Rack::BufferedInput

Inherits:
Object
  • Object
show all
Defined in:
lib/datadog/appsec/contrib/rack/buffered_input.rb

Overview

Wraps a rack.input stream with a buffer placed in front of it. Every read drains the buffer first, then continues from the stream

NOTE: Forward-only: no rewind, no seek

NOTE: Rack 3 dropped the rewind requirement from the input stream contract

Constant Summary collapse

READ_BUFSIZE_BYTES =

NOTE: Rack's multipart parser reads in 1 MiB chunks, used to bound #each the same way

1_048_576

Instance Method Summary collapse

Constructor Details

#initialize(stream, buffer:) ⇒ BufferedInput

Returns a new instance of BufferedInput.



20
21
22
23
# File 'lib/datadog/appsec/contrib/rack/buffered_input.rb', line 20

def initialize(stream, buffer:)
  @stream = stream
  @buffer = buffer
end

Instance Method Details

#close ⇒ Object



74
75
76
77
78
# File 'lib/datadog/appsec/contrib/rack/buffered_input.rb', line 74

def close
  @buffer.close
ensure
  @stream.close
end

#each ⇒ Object



66
67
68
69
70
71
72
# File 'lib/datadog/appsec/contrib/rack/buffered_input.rb', line 66

def each
  while (chunk = read(READ_BUFSIZE_BYTES))
    yield chunk
  end

  self
end

#gets ⇒ Object



56
57
58
59
60
61
62
63
64
# File 'lib/datadog/appsec/contrib/rack/buffered_input.rb', line 56

def gets
  line = @buffer.gets

  return @stream.gets if line.nil?
  return line if line.end_with?("\n")

  more = @stream.gets
  more ? (line << more) : line
end

#read(length = nil, outbuf = nil) ⇒ Object



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/datadog/appsec/contrib/rack/buffered_input.rb', line 25

def read(length = nil, outbuf = nil)
  if length.nil?
    data = @buffer.read(nil, outbuf) || +""
    more = @stream.read

    data << more if more

    return data
  end

  data = @buffer.read(length, outbuf)

  if data.nil?
    more = @stream.read(length, outbuf)
    return more if more && !more.empty?

    # NOTE: Match `IO#read(length, outbuf)` at EOF. Return nil and clear
    #       the caller's buffer so stale bytes are not mistaken for data
    outbuf&.clear
    return
  end

  remaining = length - data.bytesize
  return data if remaining <= 0

  more = @stream.read(remaining)
  data << more if more

  data
end