I'm doing a POST to a restlet and need to return a zip file. But although the created file is zip, the method returns gibberish.
I tried wrapping the FileRepresentation as was suggested here:
new org.restlet.engine.application.EncodeRepresentation(org.restlet.data.Encoding.ZIP, representation);
And also tried adding a Produces annotation like this:
#Produces({"application/x-zip-compressed"})
But neither works. The representation returns as gibberish string, and the Content-Type header stays application/octet-stream. What am I missing?
These are the request headers. Note the Accept-Encoding: gzip, deflate:
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36
Origin: chrome-extension://hgmloofddffdnphfgcellkdfbfbjeloo
Content-Type: application/json
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8,he;q=0.6
The response headers:
Vary: Accept-Encoding
Last-Modified: Wed, 06 May 2015 14:49:03 GMT
Content-Disposition: attachment; filename=_backup_20150506.zip; size=162191
Date: Wed, 06 May 2015 14:49:03 GMT
Accept-Ranges: bytes
Server: Restlet-Framework/2.2.1
Vary: Accept-Charset, Accept-Encoding, Accept-Language, Accept
Set-Cookie: JSESSIONID=5F10BBBDC58D5C3D6C0474FA12C44FB9; Path=/AppName/; Domain=localhost
Content-Encoding: gzip
Content-Type: application/octet-stream
Transfer-Encoding: chunked
EDIT: I also tried changing the media type when creating the representation:
MediaType mt = MediaType.APPLICATION_ZIP;
FileRepresentation fr = new FileRepresentation(file, mt);
The response content type changed to Content-Type: application/zip but the returned value is still a gibberish string.
The right way to do that is what you used:
public class MyServerResource extends ServerResource {
#Post
public Representation test(Representation repr) {
FileRepresentation outputRepresentation
= new FileRepresentation(new File("(...)"),
MediaType.APPLICATION_ZIP);
return outputRepresentation;
}
}
So this should work.
Using curl with such code, here is what I have:
$ curl -X POST http://localhost:8182/test > mycontent.zip
$ unzip mycontent.zip
Archive: mycontent.zip
extracting: test.txt
In addition, here is what I have with the verbose mode of curl:
curl -X POST --verbose http://localhost:8182/test
* Hostname was NOT found in DNS cache
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8182 (#0)
> POST /test HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:8182
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-type: application/zip
< Last-modified: Thu, 07 May 2015 08:08:59 GMT
< Content-length: 134
* Server Restlet-Framework/2.3.1 is not blacklisted
< Server: Restlet-Framework/2.3.1
< Accept-ranges: bytes
< Vary: Accept-Charset, Accept-Encoding, Accept-Language, Accept
< Date: Thu, 07 May 2015 08:19:26 GMT
<
Notice that you can use the header Disposition if you want to configure hints within the download dialog of your browser.
Otherwise, "enable GZIP compression of the JSON response entity on Resltet" corresponds to automatic compression of the whole response content by Restlet. Browsers support this and can directly uncompress the content before displaying it. I don't think that isn't really what you need / expect. If it's the case, you could be interested in this link: https://templth.wordpress.com/2015/02/23/optimizing-restlet-server-applications/.
Hope it helps you,
Thierry
Related
I'm trying to use JMeter to test a restful endpoint for uploading files, but I'm getting a 400 error. The one thing that jumps out at me is the boundary value; it's not the same I provided as the one shown in the request. I'm able to use the endpoint in the browser without issue, and I've replicated the headers revealed in the developer tools in FF.
Here is the relevant info from JMeter:
Result Tab:
Thread Name: Asdf - Load Test 1-1
Sample Start: 2017-06-05 08:47:46 EDT
Load time: 159
Connect Time: 28
Latency: 159
Size in bytes: 438
Sent bytes:821003
Headers size in bytes: 438
Body size in bytes: 0
Sample Count: 1
Error Count: 1
Data type ("text"|"bin"|""):
Response code: 400
Response message: Bad Request
Response headers:
HTTP/1.1 400 Bad Request
Date: Mon, 05 Jun 2017 12:47:46 GMT
Server: Apache/2.4.25 (Win64) OpenSSL/1.0.2k
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
X-XSS-Protection: 1; mode=block
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Cache-Control: no-cache, must-revalidate
Content-Length: 0
Connection: close
HTTPSampleResult fields:
ContentType:
DataEncoding: null
Request Tab:
POST https://localhost/my/rest/endpoint
POST data:
--9amm365-gMmimP70lvs9jIvlIxOfkocUN
Content-Disposition: form-data; name="parseable"; filename="asdf.docx"
Content-Type: application/vnd.openxmlformats-
officedocument.wordprocessingml.document
Content-Transfer-Encoding: binary
--9amm365-gMmimP70lvs9jIvlIxOfkocUN--
[no cookies]
Request Headers:
Connection: keep-alive
Content-Type: multipart/form-data; boundary=--Uc_2uLvcVgc7SqvzIJxR3encUKw-
f7w9
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.5
Accept: application/json, text/plain, /
X-Request-OnBehalfOf: some-user-uuid
X-Requested-With: XMLHttpRequest
Content-Length: 820532
Host: localhost
User-Agent: Apache-HttpClient/4.5.3 (Java/1.8.0_121)
Response Data Tab is blank.
Server side implementation:
#POST
#Override
#EnhancedDetail
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Path("my/rest/endpoint")
public Response uploadProduct(#PathParam("id") final String id, MultiPart multipart) throws IOException {
return processMultiFileUpload(id, multipart, MultiPartUploadType.DRAFT, false);
}
If you are building your request manually like it is described in Testing REST API File Uploads in JMeter article - make sure you have Use multipart/form-data for POST box of the HTTP Request sampler is not checked.
And vice versa, if you tick this box, you won't need to override the Content-Type header
I believe the easiest way would be just recording your file upload event using HTTP(S) Test Script Recorder (just make sure your asdf.docx file is copied to the "bin" folder of your JMeter installation)
I figured it out. I changed the Client Implementation on the HTTP Request to Java. Everything I've read said use HttpClient4, but I tried Java and it worked.
I've created a simple server Java application using Gradle. As an embedded server , I am using Jetty . I am also using a Gretty plugin as it supports the latest Jetty version.
The project runs just fine . And I have tried to stress test it. As a part of my test I need to check the response time and therefore I am sending "Connection:Close" header via curl.
My response is a long JSON string , and I see only part of it , after which the connection hangs . I would like to know Why is it happening , and how can I work around it.
NOTE :
When sending Connection:Keep-alive header , everything is fine
When response from the server is not a long string , but smaller . It works just fine (doesn't hang)
Tried the standard Jetty plugin from gradle , the result was the same.
HOW TO TEST :
Build and run my project from console ./gradlew appRun
From bash console run curl -H "Connection:Close" -i "http://localhost:8080/Environment/example"
See the partial response and the connection still alive...
Seems like you are confusing the persistent connection modes between HTTP/1.0 and HTTP/1.1.
Either that, or you are using a really old version of curl that still defaults to HTTP/1.0.
HTTP/1.0 has no persistent connections by default, so to use persistent connections we send Connection: keep-alive.
HTTP/1.1 uses persistent connections by default, so to disable it we can send Connection: close
Using HTTP/1.0, with Connection: close is like sending this ...
GET /Environment/example HTTP/1.0
Host: localhost:8080
Connection: close
... which produces an invalid header value for Connection per the HTTP/1.0 spec
Lets use the verbose features of curl to see what's really going on Connection wise...
Example: HTTP/1.1 with normal operation:
$ curl --verbose --http1.1 http://apache.org/ -so /dev/null
* Trying 88.198.26.2...
* Connected to apache.org (88.198.26.2) port 80 (#0)
> GET / HTTP/1.1
> Host: apache.org
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Fri, 06 May 2016 12:05:39 GMT
< Server: Apache/2.4.7 (Ubuntu)
< Last-Modified: Fri, 06 May 2016 11:10:20 GMT
< ETag: "cf64-5322a812896a8"
< Accept-Ranges: bytes
< Content-Length: 53092
< Vary: Accept-Encoding
< Cache-Control: max-age=3600
< Expires: Fri, 06 May 2016 13:05:39 GMT
< Content-Type: text/html
<
{ [1125 bytes data]
* Connection #0 to host apache.org left intact
Notice that it says it kept the connection intact?
Example: HTTP/1.1 with manual Connection: close operation:
$ curl --verbose --http1.1 --header "Connection: close" http://apache.org/ -so /dev/null
* Trying 140.211.11.105...
* Connected to apache.org (140.211.11.105) port 80 (#0)
> GET / HTTP/1.1
> Host: apache.org
> User-Agent: curl/7.43.0
> Accept: */*
> Connection: close
>
< HTTP/1.1 200 OK
< Date: Fri, 06 May 2016 12:06:35 GMT
< Server: Apache/2.4.7 (Ubuntu)
< Last-Modified: Fri, 06 May 2016 11:10:20 GMT
< ETag: "cf64-5322a812896a8"
< Accept-Ranges: bytes
< Content-Length: 53092
< Vary: Accept-Encoding
< Cache-Control: max-age=3600
< Expires: Fri, 06 May 2016 13:06:35 GMT
< Connection: close
< Content-Type: text/html
<
{ [1106 bytes data]
* Closing connection 0
Ah, the HTTP response headers say that the server will close, and curl saw the connection being closed. What we wanted.
Example: HTTP/1.0 with normal operation:
$ curl --verbose --http1.0 http://apache.org/ -so /dev/null
* Trying 140.211.11.105...
* Connected to apache.org (140.211.11.105) port 80 (#0)
> GET / HTTP/1.0
> Host: apache.org
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Fri, 06 May 2016 12:08:27 GMT
< Server: Apache/2.4.7 (Ubuntu)
< Last-Modified: Fri, 06 May 2016 11:10:20 GMT
< ETag: "cf64-5322a812896a8"
< Accept-Ranges: bytes
< Content-Length: 53092
< Vary: Accept-Encoding
< Cache-Control: max-age=3600
< Expires: Fri, 06 May 2016 13:08:27 GMT
< Connection: close
< Content-Type: text/html
<
{ [4002 bytes data]
* Closing connection 0
See how the HTTP response headers say that the server will close?
Curl also saw the connection being closed.
That's what we expect with normal HTTP/1.0 operation.
Example: HTTP/1.0 with persistent connection:
$ curl --verbose --http1.0 --header "Connection: keep-alive" http://apache.org/ -so /dev/null
* Trying 88.198.26.2...
* Connected to apache.org (88.198.26.2) port 80 (#0)
> GET / HTTP/1.0
> Host: apache.org
> User-Agent: curl/7.43.0
> Accept: */*
> Connection: keep-alive
>
< HTTP/1.1 200 OK
< Date: Fri, 06 May 2016 12:08:37 GMT
< Server: Apache/2.4.7 (Ubuntu)
< Last-Modified: Fri, 06 May 2016 11:10:20 GMT
< ETag: "cf64-5322a812896a8"
< Accept-Ranges: bytes
< Content-Length: 53092
< Vary: Accept-Encoding
< Cache-Control: max-age=3600
< Expires: Fri, 06 May 2016 13:08:37 GMT
< Keep-Alive: timeout=30, max=100
< Connection: Keep-Alive
< Content-Type: text/html
<
{ [3964 bytes data]
* Connection #0 to host apache.org left intact
Yup, the server indicates that it will use Keep-Alive too (per HTTP/1.0 spec), and curl even concurs and says the connection is left intact.
I've got this URL (http://vignette4.wikia.nocookie.net/fantendo/images/6/6e/Small-mario.png/revision/latest?cb=20120718024112)
how determinate the file extension if it isn't at the end of the url?
You'll need to read the response headers. The MIME Type if known is stored in the Content-Type header.
HTTP/1.1 200 OK
Content-Disposition: inline; filename="Small-mario.png"
X-Thumbnailer: Vignette
Content-Type: image/png
Cache-Control: public, max-age=31536000
X-Surrogate-Key: ad1f82ba0cbe38fa60f83c036993a71e05dae492
Server: Jetty(9.2.z-SNAPSHOT)
X-Cacheable: YES
Content-Length: 58457
Accept-Ranges: bytes
Date: Mon, 06 Jul 2015 16:12:31 GMT
Age: 65
Connection: keep-alive
X-Served-By: thumbnailer-s1, cache-wk-sjc3160-WIKIA, cache-lhr6322-LHR
X-Cache: ORIGIN, MISS, HIT
X-Cache-Hits: ORIGIN, 0, 5
X-Timer: S1436199151.564330,VS0,VE0
Vary: Accept-Encoding
Timing-Allow-Origin: *
You're looking for the Content-Type header, which the server ought to send in the HTTP response to tell you this.
Note that it is not guaranteed to be accurate, or present at all.
I'm using java sockets to communicate with my server. I'm getting http response from the server in a byte[] format. Now I need to parse the headers and content of the byte[]. The sample response I'm getting from server is :
HTTP/1.1 200 OK
alternate-protocol: 443:quic,p=0.01
cache-control: no-cache, must-revalidate
content-disposition: attachment; filename="f.txt"
content-encoding: gzip
content-length: 341
content-type: text/javascript; charset=UTF-8
date: Sat, 15 Nov 2014 18:32:55 GMT
expires: -1
pragma: no-cache
server: gws
status: 200 OK
version: HTTP/1.1
x-frame-options: SAMEORIGIN
x-xss-protection: 1; mode=block
1. Either I need an easiest way to parse this in Java (May be a library)
2. Or a way to convert it into HttpResponse object.
Apache HttpComponents is your friend in this case.
Please take a look at http://hc.apache.org/httpclient-3.x/apidocs/org/apache/commons/httpclient/HttpParser.html
when I get the following url with curl
curl -D headers.http "http://www.springerlink.com/index/10.1007/s00453-007-9157-8"
the file headers.http contains a "Location" header:
HTTP/1.1 302 Found
Date: Tue, 27 Oct 2009 17:00:20 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
X-AspNet-Version: 2.0.50727
Location: http://www.springerlink.com/link.asp?id=c104731297q64224
Set-Cookie: CookiesSupported=True; expires=Wed, 27-Oct-2010 17:00:20 GMT; path=/
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 173
but when I used the apache httpclient library this "Location:" header was missing (?).
int status = httpClient.executeMethod(method);
if(status!=HttpStatus.SC_OK &&
status!=HttpStatus.SC_MOVED_TEMPORARILY &&
status!=HttpStatus.SC_MOVED_PERMANENTLY
)
{
throw new IOException("connection failure for "+url+" status:"+status);
}
Header header=method.getResponseHeader("Location");
if(header==null )
{
for(Header h:method.getResponseHeaders())
{
LOG.info(h.toString());
}
throw new IOException(
"Expected a redirect for "+url
);
}
I've listed the headers below:
INFO: Date: Tue, 27 Oct 2009 17:05:13 GMT
INFO: Server: Microsoft-IIS/6.0
INFO: X-Powered-By: ASP.NET
INFO: X-AspNet-Version: 2.0.50727
INFO: Set-Cookie: ASP.NET_SessionId=js1o5wqnuhuh24islnvkyr45; path=/; HttpOnly
INFO: Cache-Control: private
INFO: Content-Type: text/html; charset=utf-8
INFO: Content-Length: 17245
uhh ???
What's going on is that with curl , you are getting a 302 which is actually a redirect, to the URL in the location header.
With the Apache httpclient it is doing the redirect for you, and returning the headers from the request to the redirected-to location.
To demonstrate this try
curl -D headers.http "http://www.springerlink.com/link.asp?id=c104731297q64224"
and compare the response.
edit: There are actually about 4 redirects in there if you follow each location header through with curl.
http://www.springerlink.com/index/10.1007/s00453-007-9157-8 is actually a redirect. Since the -D option means "headers only", the first one is not redirecting to the specified Location: ..., while the second one is. Take a look at the Content-Length, it's much different.
What happens when you leave out the -D?
Add this
method.setFollowRedirects(false);
Before you execute the method.
HttpClient follows the redirect automatically by default but Curl doesn't.