3 require 'mogilefs/backend'
4 require 'mogilefs/util'
7 # HTTPFile wraps up the new file operations for storing files onto an HTTP
10 # You really don't want to create an HTTPFile by hand. Instead you want to
11 # create a new file using MogileFS::MogileFS.new_file.
14 # TODO dup'd content in MogileFS::NFSFile
16 class MogileFS::HTTPFile < StringIO
17 include MogileFS::Util
19 class EmptyResponseError < MogileFS::Error; end
20 class BadResponseError < MogileFS::Error; end
21 class UnparseableResponseError < MogileFS::Error; end
22 class NoStorageNodesError < MogileFS::Error
23 def message; 'Unable to open socket to storage node'; end
27 # The URI this file will be stored to.
32 # The key for this file. This key won't represent a real file until you've
38 # The class of this file.
43 # The big_io name in case we have file > 256M
48 # Works like File.open. Use MogileFS::MogileFS#new_file instead of this
54 return fp unless block_given?
64 # Creates a new HTTPFile with MogileFS-specific data. Use
65 # MogileFS::MogileFS#new_file instead of this method.
67 def initialize(mg, fid, klass, key, dests, content_length)
83 # Writes an HTTP PUT request to +sock+ to upload the file and
84 # returns file size if the socket finished writing
85 def upload(devid, uri)
87 sock = Socket.mogilefs_new(uri.host, uri.port)
88 sock.mogilefs_tcp_cork = true
91 # Don't try to run out of memory
92 File.open(@big_io) do |fp|
93 file_size = fp.stat.size
95 syswrite_full(sock, "PUT #{uri.request_uri} HTTP/1.0\r\n" \
96 "Content-Length: #{file_size}\r\n\r\n")
100 syswrite_full(sock, "PUT #{uri.request_uri} HTTP/1.0\r\n" \
101 "Content-Length: #{length}\r\n\r\n#{string}")
103 sock.mogilefs_tcp_cork = false
106 raise EmptyResponseError, 'Unable to read response line from server'
108 if line =~ %r%^HTTP/\d+\.\d+\s+(\d+)% then
110 when 200..299 then # success!
112 raise BadResponseError, "HTTP response status from upload: #{$1}"
115 raise UnparseableResponseError, "Response line not understood: #{line}"
118 @mg.backend.create_close(:fid => @fid, :devid => devid,
119 :domain => @mg.domain, :key => @key,
120 :path => uri.to_s, :size => file_size)
125 try_dests = @dests.dup
129 devid, url = try_dests.shift
130 devid && url or break
134 bytes = upload(devid, uri)
135 @devid, @uri = devid, uri
137 rescue SystemCallError, Errno::ECONNREFUSED, MogileFS::Timeout,
138 EmptyResponseError, BadResponseError,
139 UnparseableResponseError => err
140 last_err = @tried[url] = err
144 raise last_err ? last_err : NoStorageNodesError