1 # -*- encoding: binary -*-
4 require 'mogilefs/backend'
5 require 'mogilefs/util'
8 # HTTPFile wraps up the new file operations for storing files onto an HTTP
11 # You really don't want to create an HTTPFile by hand. Instead you want to
12 # create a new file using MogileFS::MogileFS.new_file.
14 class MogileFS::HTTPFile < StringIO
15 include MogileFS::Util
17 class EmptyResponseError < MogileFS::Error; end
18 class BadResponseError < MogileFS::Error; end
19 class UnparseableResponseError < MogileFS::Error; end
20 class NoStorageNodesError < MogileFS::Error
21 def message; 'Unable to open socket to storage node'; end
25 # The URI this file will be stored to.
30 # The key for this file. This key won't represent a real file until you've
36 # The class of this file.
41 # The big_io name in case we have file > 256M
45 attr_accessor :streaming_io
48 # Works like File.open. Use MogileFS::MogileFS#new_file instead of this
53 fp.set_encoding(Encoding::BINARY) if fp.respond_to?(:set_encoding)
55 return fp unless block_given?
65 # Creates a new HTTPFile with MogileFS-specific data. Use
66 # MogileFS::MogileFS#new_file instead of this method.
68 def initialize(mg, fid, klass, key, dests, content_length)
85 # Writes an HTTP PUT request to +sock+ to upload the file and
86 # returns file size if the socket finished writing
87 def upload(devid, uri)
89 sock = MogileFS::Socket.tcp(uri.host, uri.port)
92 file_size = @streaming_io.length
93 sock.write("PUT #{uri.request_uri} HTTP/1.0\r\n" \
94 "Content-Length: #{file_size}\r\n\r\n")
95 @streaming_io.call(Proc.new do |data_to_write|
96 sock.write(data_to_write)
99 # Don't try to run out of memory
100 File.open(@big_io, "rb") do |fp|
101 file_size = fp.stat.size
102 sock.write("PUT #{uri.request_uri} HTTP/1.0\r\n" \
103 "Content-Length: #{file_size}\r\n\r\n")
107 sock.write("PUT #{uri.request_uri} HTTP/1.0\r\n" \
108 "Content-Length: #{length}\r\n\r\n#{string}")
112 raise EmptyResponseError, 'Unable to read response line from server'
114 if line =~ %r%^HTTP/\d+\.\d+\s+(\d+)% then
116 when 200..299 then # success!
118 raise BadResponseError, "HTTP response status from upload: #{$1}"
121 raise UnparseableResponseError, "Response line not understood: #{line}"
124 @mg.backend.create_close(:fid => @fid, :devid => devid,
125 :domain => @mg.domain, :key => @key,
126 :path => uri.to_s, :size => file_size)
131 try_dests = @dests.dup
135 devid, url = try_dests.shift
136 devid && url or break
140 bytes = upload(devid, uri)
141 @devid, @uri = devid, uri
143 rescue SystemCallError, Errno::ECONNREFUSED, MogileFS::Timeout,
144 EmptyResponseError, BadResponseError,
145 UnparseableResponseError => err
146 last_err = @tried[url] = err
150 raise last_err ? last_err : NoStorageNodesError