174 lines
4.9 KiB
Ruby
174 lines
4.9 KiB
Ruby
|
# Copyright 2015 Google Inc.
|
||
|
#
|
||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
# you may not use this file except in compliance with the License.
|
||
|
# You may obtain a copy of the License at
|
||
|
#
|
||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||
|
#
|
||
|
# Unless required by applicable law or agreed to in writing, software
|
||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
# See the License for the specific language governing permissions and
|
||
|
# limitations under the License.
|
||
|
|
||
|
require 'hurley'
|
||
|
|
||
|
module Google
|
||
|
module Apis
|
||
|
module Core
|
||
|
# Part of a multipart request for holding JSON data
|
||
|
#
|
||
|
# @private
|
||
|
class JsonPart
|
||
|
include Hurley::Multipart::Part
|
||
|
|
||
|
# @return [Fixnum]
|
||
|
# Length of part
|
||
|
attr_reader :length
|
||
|
|
||
|
# @param [String] boundary
|
||
|
# Multipart boundary
|
||
|
# @param [String] value
|
||
|
# JSON content
|
||
|
def initialize(boundary, value)
|
||
|
@part = build_part(boundary, value)
|
||
|
@length = @part.bytesize
|
||
|
@io = StringIO.new(@part)
|
||
|
end
|
||
|
|
||
|
private
|
||
|
|
||
|
# Format the part
|
||
|
#
|
||
|
# @param [String] boundary
|
||
|
# Multipart boundary
|
||
|
# @param [String] value
|
||
|
# JSON content
|
||
|
# @return [String]
|
||
|
def build_part(boundary, value)
|
||
|
part = ''
|
||
|
part << "--#{boundary}\r\n"
|
||
|
part << "Content-Type: application/json\r\n"
|
||
|
part << "\r\n"
|
||
|
part << "#{value}\r\n"
|
||
|
end
|
||
|
end
|
||
|
|
||
|
# Part of a multipart request for holding arbitrary content. Modified
|
||
|
# from Hurley::Multipart::FilePart to remove Content-Disposition
|
||
|
#
|
||
|
# @private
|
||
|
class FilePart
|
||
|
include Hurley::Multipart::Part
|
||
|
|
||
|
# @return [Fixnum]
|
||
|
# Length of part
|
||
|
attr_reader :length
|
||
|
|
||
|
# @param [String] boundary
|
||
|
# Multipart boundary
|
||
|
# @param [Google::Apis::Core::UploadIO] io
|
||
|
# IO stream
|
||
|
# @param [Hash] header
|
||
|
# Additional headers
|
||
|
def initialize(boundary, io, header = {})
|
||
|
file_length = io.respond_to?(:length) ? io.length : File.size(io.local_path)
|
||
|
|
||
|
@head = build_head(boundary, io.content_type, file_length,
|
||
|
io.respond_to?(:opts) ? io.opts.merge(header) : header)
|
||
|
|
||
|
@length = @head.bytesize + file_length + FOOT.length
|
||
|
@io = Hurley::CompositeReadIO.new(@length, StringIO.new(@head), io, StringIO.new(FOOT))
|
||
|
end
|
||
|
|
||
|
private
|
||
|
|
||
|
# Construct the header for the part
|
||
|
#
|
||
|
# @param [String] boundary
|
||
|
# Multipart boundary
|
||
|
# @param [String] type
|
||
|
# Content type for the part
|
||
|
# @param [Fixnum] content_len
|
||
|
# Length of the part
|
||
|
# @param [Hash] header
|
||
|
# Headers for the part
|
||
|
def build_head(boundary, type, content_len, header)
|
||
|
sprintf(HEAD_FORMAT,
|
||
|
boundary,
|
||
|
content_len.to_i,
|
||
|
header[:content_type] || type,
|
||
|
header[:content_transfer_encoding] || DEFAULT_TR_ENCODING)
|
||
|
end
|
||
|
|
||
|
DEFAULT_TR_ENCODING = 'binary'.freeze
|
||
|
FOOT = "\r\n".freeze
|
||
|
HEAD_FORMAT = <<-END
|
||
|
--%s\r
|
||
|
Content-Length: %d\r
|
||
|
Content-Type: %s\r
|
||
|
Content-Transfer-Encoding: %s\r
|
||
|
\r
|
||
|
END
|
||
|
end
|
||
|
|
||
|
# Helper for building multipart requests
|
||
|
class Multipart
|
||
|
MULTIPART_RELATED = 'multipart/related'
|
||
|
DEFAULT_BOUNDARY = 'RubyApiClientMultiPart'
|
||
|
|
||
|
# @return [String]
|
||
|
# Content type header
|
||
|
attr_reader :content_type
|
||
|
|
||
|
# @param [String] content_type
|
||
|
# Content type for the multipart request
|
||
|
# @param [String] boundary
|
||
|
# Part delimiter
|
||
|
|
||
|
def initialize(content_type: MULTIPART_RELATED, boundary: nil)
|
||
|
@parts = []
|
||
|
@boundary = boundary || DEFAULT_BOUNDARY
|
||
|
@content_type = "#{content_type}; boundary=#{boundary}"
|
||
|
end
|
||
|
|
||
|
# Append JSON data part
|
||
|
#
|
||
|
# @param [String] body
|
||
|
# JSON text
|
||
|
# @return [self]
|
||
|
def add_json(body)
|
||
|
@parts << Google::Apis::Core::JsonPart.new(@boundary, body)
|
||
|
self
|
||
|
end
|
||
|
|
||
|
# Append arbitrary data as a part
|
||
|
#
|
||
|
# @param [Google::Apis::Core::UploadIO] upload_io
|
||
|
# IO stream
|
||
|
# @return [self]
|
||
|
def add_upload(upload_io)
|
||
|
@parts << Google::Apis::Core::FilePart.new(@boundary, upload_io)
|
||
|
self
|
||
|
end
|
||
|
|
||
|
# Assemble the multipart requests
|
||
|
#
|
||
|
# @return [IO]
|
||
|
# IO stream
|
||
|
def assemble
|
||
|
@parts << Hurley::Multipart::EpiloguePart.new(@boundary)
|
||
|
ios = []
|
||
|
len = 0
|
||
|
@parts.each do |part|
|
||
|
len += part.length
|
||
|
ios << part.to_io
|
||
|
end
|
||
|
Hurley::CompositeReadIO.new(len, *ios)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|