# 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