google-api-ruby-client/google-api-client/lib/google/apis/core/multipart.rb

135 lines
4.0 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.
module Google
module Apis
module Core
# Part of a multipart request for holding JSON data
#
# @private
class JsonPart
# @param [String] value
# JSON content
# @param [Hash] header
# Additional headers
def initialize(value, header = {})
@value = value
@header = header
end
def to_io(boundary)
part = ''
part << "--#{boundary}\r\n"
part << "Content-Type: application/json\r\n"
@header.each do |(k, v)|
part << "#{k}: #{v}\r\n"
end
part << "\r\n"
part << "#{@value}\r\n"
StringIO.new(part)
end
end
# Part of a multipart request for holding arbitrary content.
#
# @private
class FilePart
# @param [IO] io
# IO stream
# @param [Hash] header
# Additional headers
def initialize(io, header = {})
@io = io
@header = header
@length = io.respond_to?(:size) ? io.size : nil
end
def to_io(boundary)
head = ''
head << "--#{boundary}\r\n"
@header.each do |(k, v)|
head << "#{k}: #{v}\r\n"
end
head << "Content-Length: #{@length}\r\n" unless @length.nil?
head << "Content-Transfer-Encoding: binary\r\n"
head << "\r\n"
Google::Apis::Core::CompositeIO.new(StringIO.new(head), @io, StringIO.new("\r\n"))
end
end
# Helper for building multipart requests
class Multipart
MULTIPART_RELATED = 'multipart/related'
# @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 || Digest::SHA1.hexdigest(SecureRandom.random_bytes(8))
@content_type = "#{content_type}; boundary=#{@boundary}"
end
# Append JSON data part
#
# @param [String] body
# JSON text
# @param [String] content_id
# Optional unique ID of this part
# @return [self]
def add_json(body, content_id: nil)
header = {}
header['Content-ID'] = content_id unless content_id.nil?
@parts << Google::Apis::Core::JsonPart.new(body, header).to_io(@boundary)
self
end
# Append arbitrary data as a part
#
# @param [IO] upload_io
# IO stream
# @param [String] content_id
# Optional unique ID of this part
# @return [self]
def add_upload(upload_io, content_type: nil, content_id: nil)
header = {
'Content-Type' => content_type || 'application/octet-stream'
}
header['Content-Id'] = content_id unless content_id.nil?
@parts << Google::Apis::Core::FilePart.new(upload_io,
header).to_io(@boundary)
self
end
# Assemble the multipart requests
#
# @return [IO]
# IO stream
def assemble
@parts << StringIO.new("--#{@boundary}--\r\n\r\n")
Google::Apis::Core::CompositeIO.new(*@parts)
end
end
end
end
end