How do I process multipart http responses in Ruby Net:HTTP? - ruby

There is so much information out there on how to generate multipart responses or do multipart file uploads. I can't seem to find any information on how to process a multipart http response. Here is some IRB output from a multipart http response I am working with.
>> response.http.content_type
=> "multipart/related"
>> response.http.body[0..2048]
=> "\r\n------=_Part_3_806633756.1271797659309\r\nContent-Type: text/xml; charset=UTF-8\r\nContent-Transfer-Encoding: binary\r\nContent-Id: <A0FCC4333C6D0FCA346B97FAB6B61818>\r\n\r\n<?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Body><ns1:runReportResponse soapenv:encodingStyle="http://www.w3.org/2003/05/soap-encoding" xmlns:ns1="http://192.168.1.200:8080/jasperserver/services/repository"><ns2:result xmlns:ns2="http://www.w3.org/2003/05/soap-rpc">runReportReturn</ns2:result><runReportReturn xsi:type="xsd:string"><?xml version="1.0" encoding="UTF-8"?>\n<operationResult version="2.0.1">\n\t<returnCode><![CDATA[0]]></returnCode>\n</operationResult>\n</runReportReturn></ns1:runReportResponse></soapenv:Body></soapenv:Envelope>\r\n------=_Part_3_806633756.1271797659309\r\nContent-Type: application/pdf\r\nContent-Transfer-Encoding: binary\r\nContent-Id: <report>\r\n\r\n%PDF-1.4\n%\342\343\317\323\n3 0 obj

You can use Rack to do that for you, here's the utility function that does it: Rack::Utils::parse_multipart. Obviously you'll have to make your response object look like a request object Rack would accept (the env object).

Related

How can I read this binary XML response with Ruby?

I'm trying to read a response from a marketplace web service. Every other response from this web service is returned in XML format. However, this particular call is requesting a file download. I'm unfamiliar with the way in which it's returned. After looking at the contents, there is XML as well as encoded binary data which is in there as some sort of attachment.
The request I make looks like this. The request is a simple XML request:
begin
response = Net::HTTP.start(url.host, url.port, :use_ssl => url.scheme == 'https') do |http|
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
http.request(request)
end
rescue Errno::ECONNRESET => e
count += 1
retry unless count > 10
puts "Tried 10 times and couldn't get #{url.host}: #{e}"
end
Here is what the response.body looks like:
--MIMEBoundaryurn_uuid_AF2837F4196B2631EC15070889135182607126
Content-Type: application/xop+xml; charset=utf-8; type="text/xml"
Content-Transfer-Encoding: binary
Content-ID: <0.urn:uuid:AF2837F4196B2631EC15070889135182607127>
<?xml version='1.0' encoding='UTF-8'?>
<downloadFileResponse xmlns="http://www.marketplace.com/marketplace/services">
<ack>Success</ack>
<version>1.1.0</version>
<timestamp>2017-10-04T03:48:33.518Z</timestamp>
<fileAttachment>
<Size>25895</Size>
<Data><xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:urn:uuid:E3A8215C82DBC51E6D1507090865513"/></Data>
</fileAttachment>
</downloadFileResponse>
--MIMEBoundaryurn_uuid_AF2837F4196B2631EC15070889135182607126
Content-Type: application/zip
Content-Transfer-Encoding: binary
Content-ID: <urn:uuid:E3A8215C82DBC51E6D1507090865513>
PK&úCKÃEK¬gdi∂6509805153_report.xmlUT hH‘YhH‘Yux00ÏùÎs‚8∂¿ø˜_°€üÓ≠=-â Ù6ÚÍ< ›”≥µïr# NåÕ⁄&è˘ÎØÑÅêàçÕ‡…¶+ùnåd=|Œ—œÁ˱ۜáæÓT:æ˜Îg¥?Âu¸Æ„]ˇ˙y]ïƒÁ~˘¥≥;tokvd◊:=€ªVM|/T!–˘ΩP'
º≤∫¥Àˆ¿ Àj˜x◊U’ÔÎT ã¬œ˙-flÌ6’¿¢/ü>Ìú]‘Td;n¯e¸Ò∞ˆ L%‰åqÀ*!é, òdB±≥=Ic™Û®ÇÛpÙ©ÁªÆ≥ŸA∏≥=˚≈8ŸûÑ—©›W_v˛Á_’Z•]˘W€$Ó‹˛˚fl_∆9û“å3€/Ûòbº)œ4…8KΩØõÚî>äÀË≈Ÿ˛Ít\ÿ›Í¯˝ß{ƒy∆7hÙt_=›ÄC$·Ä3åürƒâtgˆúASuúÅ£ªwnοlç_'èo—ä•"ÙîAÇ)–ÆÔ{˜ v£ÿuÔ∫”õL2Ãf«œæ√„Ô™NÙ¯ºb{∂\Ÿ”{MSLnfGÍ,h˛ù„ufÚ}ØÃˇ<Mú≥·áëÌV˝ÓL&åuKJÿBhöy&Ÿ∏䲖ãǵ<o=UpÊ˚8«#ÎEKwŒl˝Œ-∞Ë¥O›4õÓ”N√~ÏÎ~Ø∫ T∑ÌË€aàx ¡$mƒ 
...
--MIMEBoundaryurn_uuid_AF2837F4196B2631EC15070889135182607126--
Obviously I can see there is zip data here. But I've read every other response with Hash.from_xml and obviously that's not going to work here.
Update
If I write the string to a file test.zip, I can unzip it at the Linux command line, and it creates a readable XML file after flashing this warning:
Archive: test.zip
warning [test.zip]: 822 extra bytes at beginning or within zipfile
(attempting to process anyway)
inflating: 6509805153_report.xml
Not sure what the extra bytes it's complaining about are.
Update 2
That's definitely the MIME header and the XML envelope. I can confirm that if I strip those characters out by hand, and the MIME footer, then the test file unzips without warning.
So this appears to be a zipped XML envelope containing a zip file.
This may be a bit more complicated than just using a library. Closest I could find was an implementation in the savon SOAP library that handles xop in multipart responses. You might be able to analyze the code there and come up with a solution that fits your need, or if this is a SOAP service, leverage the savon gem.
https://github.com/savonrb/savon-multipart/blob/master/lib/savon/multipart/response.rb#L63-L80

How do I access multipart form data in Sinatra?

I am using Sinatra with Thin.
When trying to get access the contents of a POST request with multipart data, I don't see any convenient way to access this, though it seems I can get to the raw body.
Is there something I am missing? Is there a convenient way to do this, or must I parse the data myself?
request.env['rack.request.form_hash'] gives an empty hash.
When dumping the body (request.body.read()) to stdout, it looks like this:
--KoZIhvcNAQcB
Content-Disposition: form-data; filename="1"
X-compId: 1
Content-Length: 1024
QeQL4k8RPBQVrP2u8Zf6Ie82labdA_
d7s FgvwSQvkP6e0TwaWWCfoHWbaP6
... (some lines omitted)
--KoZIhvcNAQcB--
I also see that it is not a chunked transfer encoding issue (I read that Thin has an issue with that); the Transfer-Encoding header is not set.
But as for my question, does Sinatra or Rack supply something in the API for accessing multipart details?
UPDATE: After doing some reading, I see the multipart data is supposed to be visible in the request parameters. As a result, I have logged an issue on GitHub. If I get it resolved, I will post an answer here. https://github.com/rack/rack/issues/695

No Payload Detected when using JMeter with a REST Webservice and XML body

I am using JMeter to do load-testing. My test consists of a simple POST request. When I perform this request, the server, in its error logs, says No Payload Detected. I have verified that the server is receiving a Post request, and I have tried different texts in the Raw Post Body.
Next, to make sure it wasn't a problem with the server, I used a different Http Request program (Wiztools rest client) to send off a post with the same content. It works, and no No Payload Detected error is thrown.
My current theory is that both JMeter and the server do parsing. I know the server does, as it is looking for a small piece of data, and that JMeter's parsing ruins that small piece of data.
So, in conclusion, my question is "What parsing does JMeter do?".
I've looked for this answer, and the only thing I know that it does parse is ${VARNAME}.
The text I am sending is below:
<IpReport xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="">
<ProductReviewId>12345</ProductReviewId>
<DepartmentCode>ABC</DepartmentCode>
<ProductTitle>Test of a teaser</ProductTitle>
<ProjectId>2</ProjectId>
<ProductCodePD>ICS-1139150</ProductCodePD>
<Priority>Pre-Approved</Priority>
<Status>Approved</Status>
<IpStatus>Submitted</IpStatus>
<EvalStatus>Approved</EvalStatus>
<EvalNotes>No Limitations - message from eval</EvalNotes>
<Notes>Notes test</Notes>
<IsInClarity>true</IsInClarity>
<IsPreRelease>true</IsPreRelease>
<IsApproved>true</IsApproved>
<IsCOREvalIP>false</IsCOREvalIP>
<Elements>
<IpReportElement>
<IpReportElementLineItemId>21774</IpReportElementLineItemId>
<ElementType>ReportingStatement</ElementType>
<Contributor>Sample Contributor</Contributor>
<Description>test</Description>
<LocationInProduct>test</LocationInProduct>
<ReportingStatementId>7</ReportingStatementId>
<IpCodeId>3</IpCodeId>
<Links>
<IpReportLink>
<IpReportLinkId>25982</IpReportLinkId>
<Name>Link</Name>
<Url>https://example.org/</Url>
</IpReportLink>
</Links>
</IpReportElement>
</Elements>
<CustomData><site>ldsorg</site>
<site-root>/preview/ldsorg/</site-root>
<correlation-document-id>123456-eng</correlation-document-id>
<post-to-server>l12773:9239</post-to-server>
</CustomData>
<ApprovalNumber>2013-1139150-I</ApprovalNumber>
<EvalApprovalNumber>2013-1139150-E</EvalApprovalNumber>
</IpReport>
Here is an screenshot of JMeter. This is the only request, with no other config elements:
You may also need to add an HTTP Header Manager as a child of HTTP Request to set some header like content-type:
First of all. In your question above, both in screenshot and in request text part of request has symbols < and > encoded and part - not. And you are using Content Encoding = UTF-8 in addition. Seems that in this situation request will be sent malformed.
You can add e.g. View Results Tree listener and check how the request is sent from jmeter.
You can try dedicated REST Sampler as well.

Parse body of POST reqest in self-made server in Ruby

I am writing mock Ruby servers to test components of API. I send a POST request with a body, and I'd like my mock server to return a body of that POST request. Currently i have this code:
require 'socket'
webserver = TCPServer.new('127.0.0.1', 7125)
loop do
session = webserver.accept
session.print "HTTP/1.1 200/OK\r\nContent-type:text/html\r\n\r\n"
request = session.gets
session.puts request
session.close
end
A POST request with body FOO returns a response with body that contains only POST / HTTP/1.1 How to fix it?
Writing your own HTTP server is really going to get you into trouble because the specification, while superficially simple, has a number of subtle nuances that can trip you up. In this case, you're reading one line with gets and ignoring the bulk of the submission. You're going to have to address that by reading in and properly decoding the posted data.
For something with a familiar interface you might start with Net::HTTP::Server instead.

Parsing SOAP response using libxml in Ruby

I am trying to parse following SOAP response coming from Savon SOAP api
<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<ns:getConnectionResponse xmlns:ns="http://webservice.jchem.chemaxon">
<ns:return>
<ConnectionHandlerId>connectionHandlerID-283854719</ConnectionHandlerId>
</ns:return>
</ns:getConnectionResponse>
</soapenv:Body>
</soapenv:Envelope>
I am trying to use libxml-ruby without any success. Basically I want to extract anything inside tag and the connectionHandlerID value.
As you are using Savon you can convert the response to a hash. The conversion method response.to_hash does some other useful things for you as well.
You would then be able to get the value you want using code similar to the following
hres = soap_response.to_hash
conn_handler_id = hres[:get_connection_response][:return][:connection_handler_id]
Check out the documentation
I'd recommend nokogiri.
Assuming your XML response is in an object named response.
require 'nokogiri'
doc = Nokogiri::XML::parse response
doc.at_xpath("//ConnectionHandlerId").text

Resources