Class: HTTP_Spew::ContentMD5

Inherits:
Object
  • Object
show all
Defined in:
lib/http_spew/content_md5.rb

Overview

this uses a pipe internally so it can use IO.tee in the “io_splice” RubyGem

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(env, input = env["rack.input"]) ⇒ ContentMD5

Returns a new instance of ContentMD5.



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/http_spew/content_md5.rb', line 10

def initialize(env, input = env["rack.input"])
  if trailer = env["HTTP_TRAILER"]
    unless trailer.split(/\s*,\s*/).grep(/\AContent-MD5\z/i)[0]
      trailer << (trailer.empty? ? "Content-MD5".freeze
                                 : ",Content-MD5".freeze)
    end
  else
    env["HTTP_TRAILER"] = "Content-MD5".freeze
  end
  env["HTTP_TRANSFER_ENCODING"] = "chunked".freeze
  @to_io, wr = HTTP_Spew::ChunkyPipe.new
  expect_md5 = env.delete("HTTP_CONTENT_MD5")
  expect_len = env.delete("CONTENT_LENGTH")
  start_write_driver(input, wr, expect_md5, expect_len)
end

Instance Attribute Details

#bytes_digestedObject (readonly)

Returns the value of attribute bytes_digested.



8
9
10
# File 'lib/http_spew/content_md5.rb', line 8

def bytes_digested
  @bytes_digested
end

#content_md5Object (readonly)

Returns the value of attribute content_md5.



7
8
9
# File 'lib/http_spew/content_md5.rb', line 7

def content_md5
  @content_md5
end

#to_ioObject (readonly)

Returns the value of attribute to_io.



6
7
8
# File 'lib/http_spew/content_md5.rb', line 6

def to_io
  @to_io
end

Instance Method Details

#read(length, buffer = "") ⇒ Object

compatible with IO#read and Rack::InputWrapper#read



27
28
29
30
# File 'lib/http_spew/content_md5.rb', line 27

def read(length, buffer = "")
  # calls HTTP_Spew::ChunkyPipe#read
  @to_io.read(length, buffer)
end

#start_write_driver(input, wr, expect_md5, expect_len) ⇒ Object

:nodoc:



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/http_spew/content_md5.rb', line 32

def start_write_driver(input, wr, expect_md5, expect_len) # :nodoc:
  Thread.new do
    begin
      digest = Digest::MD5.new
      @bytes_digested = 0
      if buf = input.read(0x4000)
        begin
          n = buf.size
          @bytes_digested += n
          wr.write("#{n.to_s(16)}\r\n")
          digest.update(buf)
          wr.write(buf << "\r\n".freeze)
        end while input.read(0x4000, buf)
        buf.clear
      end
      if expect_len && expect_len.to_i != @bytes_digested
        raise HTTP_Spew::LengthError,
             "expect=#{expect_len} != got=#@bytes_digested", []
      end
      md5 = [ digest.digest ].pack("m0")
      if expect_md5 && expect_md5.strip != md5
        raise HTTP_Spew::ChecksumError,
              "expect=#{expect_md5} != got=#{md5}", []
      end
      wr.write "0\r\nContent-MD5: #{md5}\r\n\r\n"
      @content_md5 = md5
    rescue => e
      @to_io.error = e
    ensure
      wr.close
    end
  end
end