JAX-RS How to choose image mimetype? - jersey

Is there any way to select the very best Accept mimetype for image manipulation?
I have a resource looks like this.
#GET
#Produces({"image/jpeg", "image/png"})
public Response readResizedImage(
#Context Request request,
#Context HttpHeaders httpHeaders,
#QueryParam("width") final int width,
#QueryParam("height") final int height) {
final List<Variant> variants = Variant.mediaTypes(
new MediaType("image", "jpeg"), new MediaType("image", "png")).build();
// Why on earth variants is empty?
if (!variants.isEmpty()) {
final Variant variant = request.selectVariant(variants);
LOGGER.log(Level.INFO, "{0}", variant.getMediaType().toString());
}
final List<MediaType> acceptableMediaTypes =
httpHeaders.getAcceptableMediaTypes();
for (MediaType acceptableMediaType : acceptableMediaTypes) {
LOGGER.log(Level.INFO, "acceptableMediaType:{0}/{1}",
new Object[]{acceptableMediaType.getType(),
acceptableMediaType.getSubtype()});
}
return null;
}
I tried this resource with following command.
$ curl -v -H "Accept: */*" \
-H "Accept: image/*;q=0.2" \
-H "Accept: image/jpeg;q=0.5" \
-H "Accept: image/png;q=1.0" \
http://.............
And server prints
acceptableMediaType:image/png
acceptableMediaType:*/*
acceptableMediaType:image/jpeg
acceptableMediaType:image/*
QUESTION:
How can I select a proper (not wildcarded) mime type?
I must have one for manipulating image bytes for re-sizing.

It is easiest if you leave this up to Jersey - i.e. have 2 methods, one producing image/png, other producing image/jpeg. Jersey will call the right one depending on the quality parameter of individual media types in the accept header of the incoming request.

Related

Coinbase Sandbox API /accounts call - Invalid Signature with curl

Greets,
Looks like I'm having a similar problem to others (here and here), but seem to be missing something obvious. Trying to call Coinbase Sandbox API /accounts to get a list of accounts. Literally the most basic call to get into this...
Following the SIGN docs at docs.cloud.coinbase.com
To understand the problem, I'm using stock standard BASH script with a curl call:
#!/usr/bin/env bash
TS=$(date +%s)
API_KEY=fbb28bed4617217f482d878770b8c9b7
PASSPHRASE="passphrase87867"
SECRET="apcep9z66jyW3koh5uHhnq0hKQ5q59EBgTtpZ/GsvN9aigrFbxMpuz+YP7xXo/ev+OBZpqmv4OpCk7OKx6qGbw=="
URL="https://api-public.sandbox.exchange.coinbase.com/accounts"
#https://api.exchange.coinbase.com/accounts \
#https://api-public.sandbox.pro.coinbase.com/accounts \
SIG=$(echo "${TS}GET/accounts" | hmac256 --binary $API_KEY | base64)
#SIG=$(echo "${TS}GET/accounts" | hmac256 --binary $SECRET | base64)
#also tried with PASSPHRASE & SECRET and without base64:
#SIG=$(echo "${TS}GET/accounts" | hmac256 $PASSPHRASE)
#SIG=$(echo "${TS}GET/accounts" | hmac256 $SECRET)
curl --request GET \
--url $URL \
--header 'Accept: application/json' \
--header "cb-access-key: $API_KEY" \
--header "cb-access-passphrase: $PASSPHRASE" \
--header "cb-access-sign: $SIG" \
--header "cb-access-timestamp: $TS"
#comments indicate various settings I've tried.
I just keep getting the {"message":"invalid signature"} error.
I'd appreciate any pointers.
/update:
This page contains a way to calculate the signature (binance, yes, I get the irony): https://binance-docs.github.io/apidocs/spot/en/#signed-trade-user_data-and-margin-endpoint-security :
echo -n "symbol=LTCBTC&side=BUY&type=LIMIT&timeInForce=GTC&quantity=1&price=0.1&recvWindow=5000&timestamp=1499827319559" | openssl dgst -sha256 -hmac "NhqPtmdSJYdKjVHjA7PZj4Mge3R5YNiP1e3UZjInClVN65XAbvqqM6A7H5fATj0j"
Thanks
For completeness sake, here's the C# solution:
using RestSharp;
using System.Text;
using System.Security.Cryptography;
using System.Globalization;
string computeSignature(
HttpMethod httpMethod,
string secret,
double timestamp,
string requestUri,
string contentBody = "")
{
var convertedString = System.Convert.FromBase64String(secret);
var prehash = timestamp.ToString("F0", CultureInfo.InvariantCulture) + httpMethod.ToString().ToUpper() + requestUri + contentBody;
return hashString(prehash, convertedString);
}
string hashString(string str, byte[] secret)
{
var bytes = Encoding.UTF8.GetBytes(str);
using (var hmaccsha = new HMACSHA256(secret))
{
return System.Convert.ToBase64String(hmaccsha.ComputeHash(bytes));
}
}
var timeStamp = DateTimeOffset.Now.ToUnixTimeSeconds();
var apiKey = "fbb28bed4617217f482d878770b8c9b7";
var passPhrase = "passphrase87867";
var secret = "apcep9z66jyW3koh5uHhnq0hKQ5q59EBgTtpZ/GsvN9aigrFbxMpuz+YP7xXo/ev+OBZpqmv4OpCk7OKx6qGbw==";
var URL = "https://api-public.sandbox.exchange.coinbase.com/accounts";
var client = new RestClient(URL);
var request = new RestRequest();
request.AddHeader("Accept", "application/json");
request.AddHeader("cb-access-key", apiKey);
request.AddHeader("cb-access-passphrase", passPhrase);
request.AddHeader("cb-access-sign", computeSignature(HttpMethod.Get, secret, timeStamp, "/accounts"));
request.AddHeader("cb-access-timestamp", timeStamp);
RestResponse response = client.Execute(request);
// ...

How to send file with curl to a spring-boot-starter-webflux app?

I'm trying to send a file to a spring-boot-starter-webflux application.
This is my controller:
#RestController
#RequestMapping("/fplan")
public class FplanController {
private final FplanService fplanService;
public FplanController(FplanService fplanService) {
this.fplanService = fplanService;
}
#PostMapping(value = "/file")
public Flux<Boolean> handleFileUpload(#RequestPart("file") MultipartFile file) throws IOException {
LOGGER.info(file.getOriginalFilename());
return fplanService.newFplan(file.getInputStream());
}
}
And this is my curl command line:
curl -v -F 'file=#fplan.txt' 'http://localhost:8082/fplan/file'
And this is the error output:
* Trying 127.0.0.1:8082...
* Connected to localhost (127.0.0.1) port 8082 (#0)
> POST /fplan/file HTTP/1.1
> Host: localhost:8082
> User-Agent: curl/7.79.1
> Accept: */*
> Content-Length: 16985001
> Content-Type: multipart/form-data; boundary=------------------------76d46a224dfc0ceb
> Expect: 100-continue
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 100 Continue
* We are completely uploaded and fine
* Mark bundle as not supporting multiuse
< HTTP/1.1 415 Unsupported Media Type
< Content-Type: application/json
< Content-Length: 137
<
{"timestamp":"2022-03-07T15:30:53.056+00:00","path":"/fplan/file","status":415,"error":"Unsupported Media Type","requestId":"936a38c5-5"}
I already tried:
#PostMapping(value = "/file", consumes = MediaType.ALL_VALUE)
or
#PostMapping(value = "/file", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
or
curl -v -X POST -H "Content-Type: multipart/form-data" -F 'file=#./fplandb_fv.txt;type=text/plain' "http://localhost:8082/fplan/file"
without success.
Any ideas whats missing?
After all I found this post POST binary file with cmd line curl using headers contained in the file.
If I change the controller method like this
#PostMapping(value = "/file", consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public Flux<Boolean> handleFileUpload(#RequestBody byte[] bytes) throws IOException {
LOGGER.info("Received {} bytes", bytes.length);
return fplanService.newFplan(new ByteArrayInputStream(bytes));
}
then this curl line sends the file:
curl -v --header "Content-Type:application/octet-stream" --data-binary #fplandb_fv.txt 'http://localhost:8082/fplan/file'
Feel free to post an answer that explains how to send a file for the controller implementation in the question.

Sending .gz file via CURL to RESTful put creating ZipException in GZIPInputStream

The application I am creating takes a gzipped file sent to a RESTful PUT, unzips the file and then does further processing like so:
public class Service {
#PUT
#Path("/{filename}")
Response doPut(#Context HttpServletRequest request,
#PathParam("filename") String filename,
InputStream inputStream) {
try {
GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream);
// Do Stuff with GZIPInputStream
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
I am able to successfully send a gzipped file in a unit test like so:
InputStream inputStream = new FileInputStream("src/main/resources/testFile.gz);
Service service = new Service();
service.doPut(mockHttpServletRequest, "testFile.gz", inputStream);
// Verify processing stuff happens
But when I build the application and attempt to CURL the same file from the src/main/resources dir with the following I get a ZipException:
curl -v -k -X PUT --user USER:Password -H "Content-Type: application/gzip" --data-binary #testFile.gz https://myapp.dev.com/testFile.gz
The exception is:
java.util.zip.ZipException: Not in GZIP format
at java.util.zip.GZIPInputStream.readHeader(GZIPInputStream.java:165)
at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:79)
at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:91)
at Service.doPut(Service.java:23)
// etc.
So does anyone have any idea why sending the file via CURL causes the ZipException?
Update:
I ended up taking a look at the actual bytes being sent via the InputStream and figured out where the ZipException: Not in GZIP format error was coming from. The first two bytes of a GZIP file are required to be 1F and 8B respectively in order for GZIPInputStream to recognize the data as being in GZIP format. Instead the 8B byte, along with every other byte in the steam that doesn't correspond to a valid UTF-8 character, was transformed into the bytes EF, BF, BD which are the UTF-8 unknown character replacement bytes. Thus the server is reading the GZIP data as UTF-8 characters rather than as binary and is corrupting the data.
The issue I am having now is I can't figure out where I need to change the configuration in order to get the server to treat the compressed data as binary vs UTF-8. The application uses Jax-rs on a Jersey server using Spring-Boot that is deployed in a Kubernetes pod and ran as a service, so something in the setup of one of those technologies needs to be tweaked to prevent improper encoding from being used on the data.
I have tried adding -H "Content-Encoding: gzip" to the curl command, registering the EncodingFilter.class and GZipEncoder.class in jersey ResourceConfig class, adding application/gzip to the server.compression.mime-types in application.propertes, adding the #Consumes("application/gzip") annotation to the doPut method above, and several other things I can't remember off the top of my head but nothing seems to have any effect.
I am seeing the following in the verbose CURL logs:
> PUT /src/main/resources/testFile.gz
> HOST: my.host.com
> Authorization: Basic <authorization stuff>
> User-Agent: curl/7.54.1
> Accept: */*
> Content-Encoding: gzip
> Content-Type: application/gzip
> Content-Length: 31
>
} [31 bytes data]
* upload completely sent off: 31 out of 31 bytes
< HTTP/1.1 500
< X-Application-Context: application
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: <date stuff>
...etc
Nothing I have done has affected the receiving side
Content-Type: application/json;charset=UTF-8
portion, which I suspect is the issue.
I met the same problem and finally solved it by using -H 'Content-Type:application/json;charset=UTF-8'
Use Charles to find the difference
I can successfully send the gzipped file using Postman. So I used Charles to catch two packages sent by curl and postman respectively. After I compared these two packages, I found that Postman used application/json as Content Type while curl used text/plain.
Spring docs: Content Type and Transformation
According to Spring docs, if the content type is text/plain and the source payload is byte[], Spring will convert the payload to string using charset specified in the content-type header. That's why ZipException occurred. Since the original byte data had already been decoded and not in gzip format anymore.
Spring source code
#Override
protected Object convertFromInternal(Message<?> message, Class<?> targetClass, #Nullable Object conversionHint) {
Charset charset = getContentTypeCharset(getMimeType(message.getHeaders()));
Object payload = message.getPayload();
return (payload instanceof String ? payload : new String((byte[]) payload, charset));
}

parse.com cloud code GET function with parameters?

I'm writing a cloud code function in parse and I'm trying to figure out how to handle parameters in the GET url.
So I have a simple function like this:
Parse.Cloud.define("someFunction", function(request, response) {
// how can I use GET parameters here??
});
How to I rename the "someFunction" to handle GET parameters so I can use them in my cloud code function logic?
so for example I want to be able to pass in a name string: "myName" in the GET
https://api.parse.com/1/functions/someFunction?name=myName
Any simple example? I searched for a while I couldn't find one.
Thank you
EDIT:
So I modified my function to look like this:
Parse.Cloud.define("someFunction", function(request, response) {
// how can I use GET parameters here??
var name = request.params.name
response.success("the name = " + name)
});
then I call it like this:
https://api.parse.com/1/functions/someFunction?name=someName
what I get back is this:
{"result":"the name = **undefined**"}
Cloud Functions are called with a POST request, not a GET request. Here is a simple example for cURL I took from the documentation [1].
curl -X POST \
-H "X-Parse-Application-Id: YOUR_APP_ID" \
-H "X-Parse-REST-API-Key: YOUR_REST_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"John Doe"}' \
https://api.parse.com/1/functions/someFunction
[1] https://www.parse.com/docs/cloud_code_guide#functions
try calling the Cloud from JS layer...
Parse.initialize(appId, jsId);
p = Parse.Cloud.run('someFunc', {"name":refToName}).then(function(result) {

How to filter invalid parameters in the curl command data using Jersey framework

My resource class:
#POST
#Path("/add")
#Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
#Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public Object add(Flow flow){
Object obj = fun(flow.getParam1(),flow.getParam2());
return obj;
}
Flow is a JAXB class, all params are its member variables.
Commands used:
curl -X POST -HContent-type:application/xml -k --data "<rootElementName param1=\"val1\" param2=\"val2\"/>" https://<ip>:8443/xxxx/add
curl -X POST -HContent-type:application/json -k --data "{\"#param1\":\"val1\",\"#param2\":\"val2\"}" https://<ip>:8443/xxxx/add
My requirement is to identify invalid parameter name and through error.
Ex:
curl -X POST -HContent-type:application/xml -k --data "<rootElementName invalidParam1=\"val1\" param2=\"val2\"/>" https://<ip>:8443/xxxx/add
Resource class has to validate this data and return message Invalid param: invalidParam1
For HTTP GET method, I have done this by iterating the multi valued map uriInfo.getQueryParameters()
Please suggest better way to do this invalid param validation for POST method (if possible by reusing the multi valued map validation code)

Resources