Class: RETS::StreamHTTP

Inherits:
Object
  • Object
show all
Defined in:
lib/rets/stream_http.rb

Constant Summary collapse

ENCODABLE =
RUBY_VERSION >= "1.9.0"

Instance Method Summary collapse

Constructor Details

#initialize(response) ⇒ StreamHTTP

Initializes a new HTTP stream which can be passed to Nokogiri for SAX parsing.

Parameters:

  • response (Net::HTTPResponse)

    Unused HTTP response, no calls to any of the read_body or other methods can have been called.



12
13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/rets/stream_http.rb', line 12

def initialize(response)
  @response = response
  @left_to_read = @response.content_length
  @content_length = @response.content_length
  @chunked = @response.chunked?
  @socket = @response.instance_variable_get(:@socket)

  @digest = Digest::SHA1.new
  @total_size = 0

  if @response.header.key?("content-type") and @response["content-type"] =~ /.*charset=(.*)/i
    @encoding = $1.to_s.upcase
  end
end

Instance Method Details

#closeObject

Does nothing, only used because Nokogiri requires it in a SAX parser.



156
157
# File 'lib/rets/stream_http.rb', line 156

def close
end

#encodingObject

Detected encoding



41
42
43
# File 'lib/rets/stream_http.rb', line 41

def encoding
  @encoding
end

#hashObject

SHA1 hash of the data read from the stream



35
36
37
# File 'lib/rets/stream_http.rb', line 35

def hash
  @digest.hexdigest
end

#read(read_len) ⇒ Object

Read

Parameters:

  • read_len (Integer)

    How many bytes to read from the HTTP stream



50
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/rets/stream_http.rb', line 50

def read(read_len)
  # If we closed the connection, return nil without calling anything again to avoid EOF
  # or other errors
  return nil if @closed

  if @left_to_read
    # We hit the end of what we need to read, if this is a chunked request, then we need to check for the next chunk
    if @left_to_read <= read_len
      data = @socket.read(@left_to_read)
      @total_size += @left_to_read
      @left_to_read = nil
      @read_clfr = true
    # Reading from known buffer still
    else
      @left_to_read -= read_len
      @total_size += read_len
      data = @socket.read(read_len)
    end

  elsif @chunked
    # We finished reading the chunks, read the last 2 to get \r\n out of the way, and then find the next chunk
    if @read_clfr
      @read_clfr = nil
      @socket.read(2)
    end

    data, chunk_read = "", 0
    while true
      # Read first line to get the chunk length
      line = @socket.readline

      len = line.slice(/[0-9a-fA-F]+/) or raise Net::HTTPBadResponse.new("wrong chunk size line: #{line}")
      len = len.hex

      # Nothing left, read off the final \r\n
      if len == 0
        @socket.read(2)
        @socket.close
        @response.instance_variable_set(:@read, true)

        @closed = true
        break
      end

      # Reading this chunk will set us over the buffer amount
      # Read what we can of it (if anything), and send back what we have and queue a read for the rest
      if ( chunk_read + len ) > read_len
        can_read = len - ( ( chunk_read + len ) - read_len )

        @left_to_read = len - can_read
        @total_size += can_read

        data << @socket.read(can_read) if can_read > 0
        break
      # We can just return the chunk as-is
      else
        @total_size += len
        chunk_read += len

        data << @socket.read(len)
        @socket.read(2)
      end
    end

  # If we don't have a content length, then we need to keep reading until we run out of data
  elsif !@content_length
    data = @socket.readline

    @total_size += data.length if data
  end

  # We've finished reading, set this so Net::HTTP doesn't try and read it again
  if !data or data == ""
    @response.instance_variable_set(:@read, true)

    nil
  else
    if data.length >= @total_size and !@chunked
      @response.instance_variable_set(:@read, true)
    end

    if ENCODABLE and @encoding
      data = data.force_encoding(@encoding) if @encoding
      data = data.encode("UTF-8")
    end

    @digest.update(data)
    data
  end

# Mark as read finished, return the last bits of data (if any)
rescue EOFError
  @response.instance_variable_set(:@read, true)
  @socket.close
  @closed = true

  if data and data != ""
    @digest.update(data)
    data
  else
    nil
  end
end

#sizeObject

The total size read from the stream, can be called either while reading or at the end.



29
30
31
# File 'lib/rets/stream_http.rb', line 29

def size
  @total_size
end