1 # -*- encoding: binary -*-
2 # here are internal implementation details, do not use them in your code
5 require 'mogilefs/backend'
6 require 'mogilefs/util'
9 # HTTPFile wraps up the new file operations for storing files onto an HTTP
12 # You really don't want to create an HTTPFile by hand. Instead you want to
13 # create a new file using MogileFS::MogileFS.new_file.
15 class MogileFS::HTTPFile < StringIO
16 include MogileFS::Util
18 class EmptyResponseError < MogileFS::Error; end
19 class BadResponseError < MogileFS::Error; end
20 class UnparseableResponseError < MogileFS::Error; end
21 class NoStorageNodesError < MogileFS::Error
22 def message; 'Unable to open socket to storage node'; end
26 # The URI this file will be stored to.
33 # The big_io name in case we have file > 256M
37 attr_accessor :streaming_io
40 # Creates a new HTTPFile with MogileFS-specific data. Use
41 # MogileFS::MogileFS#new_file instead of this method.
43 def initialize(dests, content_length)
45 @streaming_io = @big_io = @uri = @devid = nil
51 # Writes an HTTP PUT request to +sock+ to upload the file and
52 # returns file size if the socket finished writing
53 def upload(devid, uri) # :nodoc:
55 sock = MogileFS::Socket.tcp(uri.host, uri.port)
58 file_size = @streaming_io.length
59 sock.write("PUT #{uri.request_uri} HTTP/1.0\r\n" \
60 "Content-Length: #{file_size}\r\n\r\n")
61 @streaming_io.call(Proc.new do |data_to_write|
62 sock.write(data_to_write)
65 # Don't try to run out of memory
66 File.open(@big_io, "rb") do |fp|
67 file_size = fp.stat.size
68 sock.write("PUT #{uri.request_uri} HTTP/1.0\r\n" \
69 "Content-Length: #{file_size}\r\n\r\n")
73 sock.write("PUT #{uri.request_uri} HTTP/1.0\r\n" \
74 "Content-Length: #{length}\r\n\r\n#{string}")
77 case line = sock.timed_read(23, "")
78 when %r{^HTTP/\d\.\d\s+(2\d\d)\s} # success!
81 raise EmptyResponseError, 'Unable to read response line from server'
82 when %r{^HTTP/\d\.\d\s+(\d+)}
83 raise BadResponseError, "HTTP response status from upload: #$1"
85 raise UnparseableResponseError, "Response line not understood: #{line}"
88 sock.close if sock && ! sock.closed?
93 @dests.each do |devid, path|
96 bytes_uploaded = upload(devid, uri)
97 @devid, @uri = devid, uri
101 errors << "#{path} failed with #{e.message} (#{e.class})"
105 raise NoStorageNodesError,
106 "all paths failed with PUT: #{errors.join(', ')}", []