Added multipart plugin, to handle multipart requests

This logic was extracted from the vanilla httpx build to a plugin to
make the gem "leaner", by removing "http_form_data" as a hard
dependency.

The multipart plugin still requires one to install it though, but if you
don't need to upload files, you don't have to install the gem anymore
This commit is contained in:
HoneyryderChuck 2018-11-23 16:06:25 +00:00
parent 82ef579047
commit ce674ff4e2
7 changed files with 121 additions and 52 deletions

View File

@ -20,8 +20,7 @@ Gem::Specification.new do |gem|
gem.require_paths = ["lib"]
gem.version = HTTPX::VERSION
gem.add_runtime_dependency "http-2", ">= 0.9.0"
gem.add_runtime_dependency "http-form_data", ">= 2.0.0", "< 3"
gem.add_development_dependency "http-cookie", "~> 1.0"
gem.add_runtime_dependency "http-2", ">= 0.9.0"
gem.add_development_dependency "http-form_data", ">= 2.0.0", "< 3"
gem.add_development_dependency "http-cookie", "~> 1.0"
end

View File

@ -0,0 +1,50 @@
# frozen_string_literal: true
module HTTPX
module Plugins
module Multipart
module FormTranscoder
module_function
class Encoder
extend Forwardable
def_delegator :@raw, :content_type
def_delegator :@raw, :to_s
def_delegator :@raw, :read
def initialize(form)
@raw = HTTP::FormData.create(form)
end
def bytesize
@raw.content_length
end
def force_encoding(*args)
@raw.to_s.force_encoding(*args)
end
def to_str
@raw.to_s
end
end
def encode(form)
Encoder.new(form)
end
end
def self.load_dependencies(*)
require "http/form_data"
end
def self.configure(*)
Transcoder.register("form", FormTranscoder)
end
end
register_plugin :multipart, Multipart
end
end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
require "forwardable"
require "http/form_data"
require "uri"
module HTTPX::Transcoder
module Form
@ -10,22 +10,18 @@ module HTTPX::Transcoder
class Encoder
extend Forwardable
def_delegator :@raw, :content_type
def_delegator :@raw, :to_s
def_delegator :@raw, :read
def_delegator :@raw, :bytesize
def_delegator :@raw, :force_encoding
def initialize(form)
@raw = HTTP::FormData.create(form)
@raw = URI.encode_www_form(form)
end
def bytesize
@raw.content_length
end
def force_encoding(*args)
@raw.to_s.force_encoding(*args)
def content_type
"application/x-www-form-urlencoded"
end
def to_str

View File

@ -23,6 +23,7 @@ class HTTPTest < Minitest::Test
include Plugins::Compression
include Plugins::H2C
include Plugins::Retries
include Plugins::Multipart
private

View File

@ -21,6 +21,7 @@ class HTTPSTest < Minitest::Test
include Plugins::Compression
include Plugins::PushPromise if OpenSSL::SSL::SSLContext.instance_methods.include?(:alpn_protocols)
include Plugins::Retries
include Plugins::Multipart
private

View File

@ -0,0 +1,48 @@
# frozen_string_literal: true
require "http/form_data"
module Requests
module Plugins
module Multipart
%w[post put patch delete].each do |meth|
define_method :"test_plugin_multipart_urlencoded_#{meth}" do
uri = build_uri("/#{meth}")
response = HTTPX.plugin(:multipart)
.send(meth, uri, form: { "foo" => "bar" })
verify_status(response, 200)
body = json_body(response)
verify_header(body["headers"], "Content-Type", "application/x-www-form-urlencoded")
verify_uploaded(body, "form", "foo" => "bar")
end
define_method :"test_plugin_multipart_formdata_#{meth}" do
uri = build_uri("/#{meth}")
response = HTTPX.plugin(:multipart)
.send(meth, uri, form: { image: HTTP::FormData::File.new(fixture_file_path) })
verify_status(response, 200)
body = json_body(response)
verify_header(body["headers"], "Content-Type", "multipart/form-data")
verify_uploaded_image(body)
end
end
def fixture
File.read(fixture_file_path, encoding: Encoding::BINARY)
end
def fixture_name
File.basename(fixture_file_path)
end
def fixture_file_path
File.join("test", "support", "fixtures", "image.jpg")
end
def verify_uploaded_image(body)
assert body.key?("files"), "there were no files uploaded"
assert body["files"].key?("image"), "there is no image in the file"
end
end
end
end

View File

@ -21,6 +21,17 @@ module Requests
verify_uploaded(body, "form", "foo" => "bar")
end
define_method :"test_#{meth}_expect_100_form_params" do
uri = build_uri("/#{meth}")
response = HTTPX.headers("expect" => "100-continue")
.send(meth, uri, form: { "foo" => "bar" })
verify_status(response, 200)
body = json_body(response)
verify_header(body["headers"], "Content-Type", "application/x-www-form-urlencoded")
verify_header(body["headers"], "Expect", "100-continue")
verify_uploaded(body, "form", "foo" => "bar")
end
define_method :"test_#{meth}_json_params" do
uri = build_uri("/#{meth}")
response = HTTPX.send(meth, uri, json: { "foo" => "bar" })
@ -73,50 +84,13 @@ module Requests
verify_header(body["headers"], "Content-Type", "application/octet-stream")
verify_uploaded(body, "data", "data")
end
define_method :"test_#{meth}_form_file_params" do
uri = build_uri("/#{meth}")
response = HTTPX.send(meth, uri, form: { image: HTTP::FormData::File.new(fixture_file_path) })
verify_status(response, 200)
body = json_body(response)
verify_header(body["headers"], "Content-Type", "multipart/form-data")
verify_uploaded_image(body)
end
define_method :"test_#{meth}_expect_100_form_file_params" do
uri = build_uri("/#{meth}")
response = HTTPX.headers("expect" => "100-continue")
.send(meth, uri, form: { image: HTTP::FormData::File.new(fixture_file_path) })
verify_status(response, 200)
body = json_body(response)
verify_header(body["headers"], "Content-Type", "multipart/form-data")
verify_header(body["headers"], "Expect", "100-continue")
verify_uploaded_image(body)
end
end
private
def fixture
File.read(fixture_file_path, encoding: Encoding::BINARY)
end
def fixture_name
File.basename(fixture_file_path)
end
def fixture_file_path
File.join("test", "support", "fixtures", "image.jpg")
end
def verify_uploaded(body, type, expect)
assert body.key?(type), "there is no #{type} available"
assert body[type] == expect, "#{type} is unexpected: #{body[type]} (expected: #{expect})"
end
def verify_uploaded_image(body)
assert body.key?("files"), "there were no files uploaded"
assert body["files"].key?("image"), "there is no image in the file"
end
end
end