Created
January 18, 2012 05:52
-
-
Save mpage/1631333 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'fileutils' | |
require 'net/http' | |
require 'uri' | |
require 'vcap/common' | |
module VCAP | |
module Stager | |
end | |
end | |
module VCAP::Stager::Util | |
class HttpStatusError < StandardError; end | |
class IOBuffer | |
attr_reader :size | |
def initialize(*ios) | |
@ios = ios.dup | |
@io_off = 0 | |
@stream_off = 0 | |
@size = @ios.reduce(0) {|sum, io| sum + io.size } | |
end | |
def read(nbytes_left=nil, dst=nil) | |
ret = nil | |
nbytes_left ||= @size - @stream_off | |
while (@io_off < @ios.length) && (nbytes_left > 0) | |
cur_io = @ios[@io_off] | |
tbuf = cur_io.read(nbytes_left) | |
if tbuf == nil | |
# EOF encountered, no bytes read. This can happen | |
# if a previous read read exactly the number of bytes remaining for the io. | |
@io_off += 1 | |
else | |
if ret | |
ret += tbuf | |
else | |
ret = tbuf | |
end | |
# EOF encountered after reading > 0 bytes. | |
@io_off += 1 if tbuf.length < nbytes_left | |
@stream_off += tbuf.length | |
nbytes_left -= tbuf.length | |
end | |
end | |
dst += ret if dst | |
ret | |
end | |
def rewind | |
@io_off = 0 | |
@stream_off = 0 | |
@ios.each {|io| io.rewind } | |
end | |
end | |
class MultipartFileStream | |
attr_reader :boundary, :size, :header, :footer | |
def initialize(fieldname, file) | |
@boundary = VCAP.secure_uuid | |
@fieldname = fieldname | |
@header = make_header(@boundary, fieldname, File.basename(file.path)) | |
@footer = make_footer(@boundary) | |
@io_buf = VCAP::Stager::Util::IOBuffer.new(@header, file, @footer) | |
@size = @io_buf.size | |
end | |
def read(*args) | |
@io_buf.read(*args) | |
end | |
def size | |
@io_buf.size | |
end | |
private | |
def make_header(boundary, fieldname, filename) | |
hdr = "--#{boundary}\r\n" | |
hdr += "Content-Disposition: form-data; name=\"#{fieldname}\"; filename=\"#{filename}\"\r\n" | |
hdr += "Content-Type: application/octet-stream\r\n" | |
hdr += "\r\n" | |
StringIO.new(hdr) | |
end | |
def make_footer(boundary) | |
StringIO.new("\r\n--#{boundary}--\r\n") | |
end | |
end | |
class << self | |
# Uploads the file living at _droplet_path_ to the uri using a PUT request. | |
# | |
# NB: This streams the file off of disk. | |
# | |
# @param uri String Where the app should be uploaded to | |
# @param droplet_path String The location on disk of the droplet to be uploaded | |
def upload_droplet(droplet_uri, droplet_path) | |
uri = URI.parse(droplet_uri) | |
req = make_request(Net::HTTP::Post, uri) | |
droplet = File.open(droplet_path) | |
mpfs = MultipartFileStream.new('upload[droplet]', droplet) | |
req.content_length = mpfs.size | |
req.set_content_type('multipart/form-data', :boundary => mpfs.boundary) | |
req.body_stream = mpfs | |
ret = Net::HTTP.start(uri.host, uri.port) do |http| | |
http.request(req) | |
end | |
# Throws if non-200 | |
ret.value | |
ensure | |
droplet.close if droplet | |
end | |
private | |
# Creates an instance of a class derived from Net::HTTPRequest | |
# and sets basic auth info | |
# | |
# @param klass Net::HTTPRequest | |
# @param uri URI::HTTP | |
# | |
# @return Net::HTTPRequest | |
def make_request(klass, uri) | |
req = klass.new(uri.request_uri) | |
if uri.user | |
req.basic_auth(uri.user, uri.password) | |
end | |
req | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment