Skip to content
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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

weshatheleopard
Copy link

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.

@bblimke
Copy link
Owner

bblimke commented Dec 27, 2018

@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.

@weshatheleopard
Copy link
Author

@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.

@weshatheleopard
Copy link
Author

@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 Mechanize) generates new random boundary with every call, so it is not possible to create one persistent signature; the only way to handle that seems to replace all the changing stuff to static values upon entering Webmock code, which is exactly what I do here.

@bblimke
Copy link
Owner

bblimke commented Dec 27, 2018

@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.

@kwstannard
Copy link

@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

@bblimke
Copy link
Owner

bblimke commented Aug 19, 2023

@kwstannard thank you. I will revisit support for multipart and your changes when I find some time

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants