Class: RFuzz::PushBackIO

Inherits:
Object
  • Object
show all
Defined in:
lib/rfuzz/pushbackio.rb

Overview

A simple class that using a StringIO object internally to allow for faster and simpler “push back” semantics. It basically lets you read a random amount from a secondary IO object, parse what is needed, and then anything remaining can be quickly pushed back in one chunk for the next read.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(secondary) ⇒ PushBackIO

Returns a new instance of PushBackIO.



11
12
13
14
15
# File 'lib/rfuzz/pushbackio.rb', line 11

def initialize(secondary)
  @secondary = secondary
  @buffer = StringIO.new
  @die_after = rand($io_death_count) if $io_death_count
end

Instance Attribute Details

#secondaryObject

Returns the value of attribute secondary.



9
10
11
# File 'lib/rfuzz/pushbackio.rb', line 9

def secondary
  @secondary
end

Instance Method Details

#closeObject



92
93
94
# File 'lib/rfuzz/pushbackio.rb', line 92

def close
  @secondary.close rescue nil
end

#flushObject



82
83
84
85
# File 'lib/rfuzz/pushbackio.rb', line 82

def flush
  random_death
  protect { @secondary.flush }
end

#pop(n) ⇒ Object



33
34
35
36
37
# File 'lib/rfuzz/pushbackio.rb', line 33

def pop(n)
  random_death
  @buffer.rewind
  @buffer.read(n) || ""
end

#protectObject



96
97
98
99
100
101
102
# File 'lib/rfuzz/pushbackio.rb', line 96

def protect
  if !@secondary.closed?
    yield
  else
    raise HttpClientError.new("Socket closed.")
  end
end

#push(content) ⇒ Object

Pushes the given string content back onto the stream for the next read to handle.



26
27
28
29
30
31
# File 'lib/rfuzz/pushbackio.rb', line 26

def push(content)
  random_death
  if content.length > 0
    @buffer.write(content)
  end
end

#random_deathObject



17
18
19
20
21
22
# File 'lib/rfuzz/pushbackio.rb', line 17

def random_death
  if $io_death_count
    @die_after -= 1
    close if @die_after <= 0 
  end
end

#read(n, partial = false) ⇒ Object

First does a read from the internal buffer, and then appends anything needed from the secondary IO to complete the request. The return value is guaranteed to be a String, and never nil. If it returns a string of length 0 then there is nothing to read from the buffer (most likely closed). It will also avoid reading from a secondary that’s closed.

If partial==true then readpartial is used instead.



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/rfuzz/pushbackio.rb', line 51

def read(n, partial=false)
  random_death
  r = pop(n)
  needs = n - r.length

  if needs > 0
    sec = ""
    if partial
      begin
        protect do
          sec = @secondary.readpartial(needs) 
        end
      rescue EOFError
        close
      end
    else
      protect { sec = @secondary.read(needs)}
    end

    r << (sec || "")

    # finally, if there's nothing at all returned then this is bad
    if r.length == 0
      raise HttpClientError.new("Server returned empty response.")
    end
  end

  reset
  return r
end

#resetObject



39
40
41
42
# File 'lib/rfuzz/pushbackio.rb', line 39

def reset
  random_death
  @buffer.string = @buffer.read  # reset out internal buffer
end

#write(content) ⇒ Object



87
88
89
90
# File 'lib/rfuzz/pushbackio.rb', line 87

def write(content)
  random_death
  protect { @secondary.write(content) }
end