Sha256: f67d148700dde180b5653675a9970d00a5248983d2c44d6b122e4e02b91cb71c

Contents?: true

Size: 1.91 KB

Versions: 3

Compression:

Stored size: 1.91 KB

Contents

# -*- encoding: binary -*-
require "digest/md5"

# this uses a pipe internally so it can use IO.tee in the "io_splice" RubyGem
class HTTP_Spew::ContentMD5
  attr_reader :to_io
  attr_reader :content_md5
  attr_reader :bytes_digested

  CRLF = "\r\n" # :nodoc:

  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" : ",Content-MD5")
      end
    else
      env["HTTP_TRAILER"] = "Content-MD5"
    end
    env["HTTP_TRANSFER_ENCODING"] = "chunked"
    @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

  # compatible with IO#read and Rack::InputWrapper#read
  def read(length, buffer = "")
    # calls HTTP_Spew::ChunkyPipe#read
    @to_io.read(length, buffer)
  end

  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 << CRLF)
          end while input.read(0x4000, buf)
        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
end

Version data entries

3 entries across 3 versions & 1 rubygems

Version Path
http_spew-0.4.1 lib/http_spew/content_md5.rb
http_spew-0.4.0 lib/http_spew/content_md5.rb
http_spew-0.3.0 lib/http_spew/content_md5.rb