-
Notifications
You must be signed in to change notification settings - Fork 553
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enable handling of multipart/form-data content #791
base: master
Are you sure you want to change the base?
Conversation
@weshatheleopard thanks for sharing this solution. I'm not sure if this should be added to WebMock by default. This modifies request body and might be confusing. E.g. VCR gem uses request signatures created by WebMock. I consider adding that as a configuration though. |
@bblimke As I said, we desperately needed this fixed so this is what I was able to throw together in the limited time frame. I might be able to revisit this issue later and modify the comparison routine to ignore the differences in boundary strings, but it will take much bigger intrusion in the code. |
@bblimke I studied the code some more and I'm afraid I need your expertise here. I am not sure how this can be handled on the VCR level since the external code (such as |
@weshatheleopard that's the reason why multipart has not been supported by WebMock. I think your solution is clever and can work as a WebMock feature enabled with a configuration setting, after some small refactoring and specs coverage. |
@bblimke Sorry for the format here, but I managed a solution that seems to work with RestClient's implementation. Here are the relevant monkey patches: require 'webmock/request_signature'
require 'webmock/request_pattern'
WebMock::RequestPattern.define_method(:matches?) do |request_signature|
content_type = request_signature.headers['Content-Type'] if request_signature.headers
@method_pattern.matches?(request_signature.method) &&
@uri_pattern.matches?(request_signature.uri) &&
(@body_pattern.nil? || @body_pattern.matches?(request_signature.body, content_type || "")) &&
(@headers_pattern.nil? || @headers_pattern.matches?(request_signature.headers)) &&
(@with_block.nil? || @with_block.call(request_signature))
end
WebMock::BodyPattern::BODY_FORMATS["multipart/form-data"] = "multipart/form-data"
WebMock::BodyPattern.define_method(:assert_non_multipart_body) do |content_type|
#noop
end
WebMock::BodyPattern.define_method(:body_as_hash) do |body, content_type|
ct, boundary = content_type.split(/; boundary=/)
case body_format(ct)
when :json then
WebMock::Util::JSON.parse(body)
when :xml then
Crack::XML.parse(body)
when 'multipart/form-data' then
body.gsub("\r", "").sub(/--\n\z/, "").split("--"+boundary).reduce({}) do |hash, part|
next hash unless part.present?
lines = part.strip.lines
key = lines.shift.match(/; name="(\w*)"/) {|m| m[1].to_s }
if part_content_type = lines.shift.match(/\AContent-Type: (\S+)/) {|m| m[1] }
lines.shift
value = body_as_hash(lines.join("\n"), part_content_type)
else
value = part.lines[2..-1].join("\n").strip
end
hash[key] = value
hash
end
else
WebMock::Util::QueryMapper.query_to_values(body, notation: Config.instance.query_values_notation)
end
end |
@kwstannard thank you. I will revisit support for multipart and your changes when I find some time |
We desperately needed that since our project uses some external websites (that we cannot change) that utilize this kind of forms, and we needed to write tests for those. Since HTTP clients use automatically generated content delimeters, I replace them with constant strings to enable creation of tests that do not change.