diff options
author | Eric Wong <normalperson@yhbt.net> | 2011-11-05 01:59:27 +0000 |
---|---|---|
committer | Eric Wong <normalperson@yhbt.net> | 2011-11-05 01:59:27 +0000 |
commit | d6b2624ed1f4502d499dad93ee44a716b5c56f08 (patch) | |
tree | fda544feac7b7188809baa5f08145ed4ae63a25e /lib/mogilefs/http_file.rb | |
parent | 37c6ee5d64d2cbdc739b4f4afcf55b3762234392 (diff) | |
download | mogilefs-client-d6b2624ed1f4502d499dad93ee44a716b5c56f08.tar.gz |
rename httpfile => http_file
Should be easier to read this way
Diffstat (limited to 'lib/mogilefs/http_file.rb')
-rw-r--r-- | lib/mogilefs/http_file.rb | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/lib/mogilefs/http_file.rb b/lib/mogilefs/http_file.rb new file mode 100644 index 0000000..22692e7 --- /dev/null +++ b/lib/mogilefs/http_file.rb @@ -0,0 +1,108 @@ +# -*- encoding: binary -*- +# here are internal implementation details, do not use them in your code +require 'stringio' +require 'uri' +require 'mogilefs/backend' +require 'mogilefs/util' + +## +# HTTPFile wraps up the new file operations for storing files onto an HTTP +# storage node. +# +# You really don't want to create an HTTPFile by hand. Instead you want to +# create a new file using MogileFS::MogileFS.new_file. +# +class MogileFS::HTTPFile < StringIO + include MogileFS::Util + + class EmptyResponseError < MogileFS::Error; end + class BadResponseError < MogileFS::Error; end + class UnparseableResponseError < MogileFS::Error; end + class NoStorageNodesError < MogileFS::Error + def message; 'Unable to open socket to storage node'; end + end + + ## + # The URI this file will be stored to. + + attr_reader :uri + + attr_reader :devid + + ## + # The big_io name in case we have file > 256M + + attr_accessor :big_io + + attr_accessor :streaming_io + + ## + # Creates a new HTTPFile with MogileFS-specific data. Use + # MogileFS::MogileFS#new_file instead of this method. + + def initialize(dests, content_length) + super "" + @streaming_io = @big_io = @uri = @devid = nil + @dests = dests + @tried = {} + end + + ## + # Writes an HTTP PUT request to +sock+ to upload the file and + # returns file size if the socket finished writing + def upload(devid, uri) # :nodoc: + file_size = length + sock = MogileFS::Socket.tcp(uri.host, uri.port) + + if @streaming_io + file_size = @streaming_io.length + sock.write("PUT #{uri.request_uri} HTTP/1.0\r\n" \ + "Content-Length: #{file_size}\r\n\r\n") + @streaming_io.call(Proc.new do |data_to_write| + sock.write(data_to_write) + end) + elsif @big_io + # Don't try to run out of memory + File.open(@big_io, "rb") do |fp| + file_size = fp.stat.size + sock.write("PUT #{uri.request_uri} HTTP/1.0\r\n" \ + "Content-Length: #{file_size}\r\n\r\n") + copy_stream(fp, sock) + end + else + sock.write("PUT #{uri.request_uri} HTTP/1.0\r\n" \ + "Content-Length: #{length}\r\n\r\n#{string}") + end + + case line = sock.timed_read(23, "") + when %r{^HTTP/\d\.\d\s+(2\d\d)\s} # success! + file_size + when nil + raise EmptyResponseError, 'Unable to read response line from server' + when %r{^HTTP/\d\.\d\s+(\d+)} + raise BadResponseError, "HTTP response status from upload: #$1" + else + raise UnparseableResponseError, "Response line not understood: #{line}" + end + ensure + sock.close if sock && ! sock.closed? + end + + def commit + errors = nil + @dests.each do |devid, path| + begin + uri = URI.parse(path) + bytes_uploaded = upload(devid, uri) + @devid, @uri = devid, uri + return bytes_uploaded + rescue => e + errors ||= [] + errors << "#{path} failed with #{e.message} (#{e.class})" + end + end + + raise NoStorageNodesError, + "all paths failed with PUT: #{errors.join(', ')}", [] + end +end |