Perhaps someone here has run into this working with Payara 5.2022.3 and/or Payara 4/Jersey-2.25.1.
I've got a REST endpoint protected with BASIC auth. This was working, and then it stopped. I'm not sure what happened here, as the code didn't change. Perhaps a system update to Java?
It looks like there is a pre-flight that's not sending the BASIC auth header. If I run the same request with curl, it works fine. So, this is a Jersey client issue.
The following code is a slimmed down version, for brevity, just to give you an idea of what I'm going:
Server code:
#BasicAuthenticationMechanismDefinition(
realmName = "appSearch"
)
#DatabaseIdentityStoreDefinition(
dataSourceLookup = "jdbc/MyDB",
callerQuery = "select passhash from User where username = ?",
groupsQuery = "select groupname from Group where username = ?",
hashAlgorithm = javax.security.enterprise.identitystore.Pbkdf2PasswordHash.class,
priority = 30)
#DeclareRoles({"SOME_GROUP"})
#Singleton
#Startup
public class ApplicationSettingsBean implements ApplicationSettingsBeanLocal {
#RolesAllowed({"SOME_GROUP"})
#Path("search")
public class SearchRequestFacade {
#GET
#Path("file-number/{uuid: [a-zA-F0-9]{8}-([a-zA-F0-9]{4}-){3}[a-zA-F0-9]{12}}")
#Produces({MediaType.TEXT_PLAIN})
public Response request(
#PathParam("uuid") String uuid
) {
return Response.ok(search.fileNumber(uuid)).build();
}
}
Jersey Client (from Payara 4 application):
ClientConfig clientConfig = new ClientConfig();
clientConfig.register(GsonProvider.GsonFeature.class);
clientConfig.property("jersey.config.server.disableMoxyJson", Boolean.TRUE);
clientConfig.property("jersey.config.client.disableMoxyJson", Boolean.TRUE);
clientConfig.property("com.sun.jersey.api.json.POJOMappingFeature", Boolean.TRUE);
clientConfig.register(HttpAuthenticationFeature.basicBuilder().nonPreemptive().credentials(username, password).build());
clientConfig.register(new LoggingFeature(Logger.getLogger(getClass().getName()), Level.INFO, null, null));
clientConfig.register(MultiPartFeature.class);
int timeout = Math.toIntExact(TimeUnit.SECONDS.toMillis(10));
clientConfig.property("jersey.config.client.connectTimeout", timeout);
clientConfig.property("jersey.config.client.readTimeout", timeout);
Client client = ClientBuilder.newClient(clientConfig);
WebTarget webTarget = client.target(url).path(urlPath);
Invocation.Builder invocationBuilder = webTarget.request(MediaType.TEXT_PLAIN_TYPE);
Response response = invocationBuilder.get();
String fileNumber = response.readEntity(String.class)
Jersey Client logging (notice the two requests/responses that occur as part of my single GET request - first one fails, second one succeeds. This leads me to believe it's a pre-flight happening with Jersey client, but not with curl):
INFO: 1 * Sending client request on thread http-thread-pool::http-listener-1(2)
1 > GET http://127.0.0.1:8080/app/api/v1/search/file-number/0c6c437e-e459-43d3-a8e6-942e128f2b80
1 > Accept: text/plain
INFO: 1 * Client response received on thread http-thread-pool::http-listener-1(2)
1 < 401
1 < Content-Language:
1 < Content-Length: 1076
1 < Content-Type: text/html
1 < Server: Payara Server 5.2022.3 #badassfish
1 < WWW-Authenticate: Basic realm="appSearch"
1 < X-Frame-Options: SAMEORIGIN
1 < X-Powered-By: Servlet/4.0 JSP/2.3 (Payara Server 5.2022.3 #badassfish Java/Ubuntu/11)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>Payara Server 5.2022.3 #badassfish - Error report</title><style type="text/css"><!--H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}HR {color : #525D76;}--></style> </head><body><h1>HTTP Status 401 - Unauthorized</h1><hr/><p><b>type</b> Status report</p><p><b>message</b>Unauthorized</p><p><b>description</b>This request requires HTTP authentication.</p><hr/><h3>Payara Server 5.2022.3 #badassfish</h3></body></html>
INFO: 2 * Sending client request on thread http-thread-pool::http-listener-1(2)
2 > GET http://127.0.0.1:8080/app/api/v1/search/file-number/0c6c437e-e459-43d3-a8e6-942e128f2b80
2 > Accept: text/plain
2 > Authorization: Basic SOMEBASE64USERPASS
2 > User-Agent: Jersey/2.25.1 (HttpUrlConnection 1.8.0_352)
INFO: 2 * Client response received on thread http-thread-pool::http-listener-1(2)
2 < 200
2 < Content-Length: 0
2 < Content-Type: text/plain
2 < Server: Payara Server 5.2022.3 #badassfish
2 < X-Frame-Options: SAMEORIGIN
2 < X-Powered-By: Servlet/4.0 JSP/2.3 (Payara Server 5.2022.3 #badassfish Java/Ubuntu/11)
1234567890
Using curl:
$ curl -k --ssl --basic -i http://127.0.0.1:8080/app/api/v1/search/file-number/FD36C98C-6A37-47FA-BBDD-3388CB79010B?user-id=test -u username:password
HTTP/1.1 200 OK
Server: Payara Server 5.2022.3 #badassfish
X-Powered-By: Servlet/4.0 JSP/2.3 (Payara Server 5.2022.3 #badassfish Java/Ubuntu/11)
Content-Type: text/plain
Content-Length: 0
X-Frame-Options: SAMEORIGIN
1234567890
It appears that the problem was how the HttpAuthenticationFeature was constructed. Adding .nonPreemptive() caused a two-part request, where the BASIC auth header was only sent when it failed with a 401 on the first attempt. Although removing this fixed the request issue, leaving it in place should have worked, too, as the second attempt succeeded in the single request. Perhaps this is a bug with Jersey client.
Old:
HttpAuthenticationFeature.basicBuilder().nonPreemptive().credentials(username, password).build();
New:
HttpAuthenticationFeature.basicBuilder().credentials(username, password).build();
Sauce: https://web.archive.org/web/20230204034859/https://howtodoinjava.com/jersey/jersey-rest-client-authentication/
Related
Been running myself crazy trying to solve this, very short this is the code I use to execute a call towards my REST endpoint:
String url = "http://localhost:5000/getMyObject";
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
RestTemplate restTemplate = new RestTemplate(factory);
String result = restTemplate.getForObject(url, String.class);
But no matter what headers I add it always ends up with connection refused.
Snippet of Stacktrace:
Caused by: java.net.ConnectException: Connection refused: connect
at java.base/java.net.PlainSocketImpl.waitForConnect(Native Method)
...
org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:776)
... 74 more
Setup: Test running on Windows, Application running in WSL
It works through Curl (in console and through my test)
It works if the test itself starts the application
It works through web browser
Curl -v:
localhost:5000/getMyObject
* Trying 127.0.0.1:5000
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 5000 (#0)
> GET /getMyObject HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.68.0
> Accept: */*
* Mark bundle as not supporting multiuse
< HTTP/1.1 200
< Vary: Origin
< Vary: Access-Control-Request-Method
< Vary: Access-Control-Request-Headers
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Sat, 22 Jan 2022 10:05:31 GMT
<
* Connection #0 to host localhost left intact
{<Data>}
Something with SimpleClientHttpRequestFactory just didn't play nicely, I switched it to HttpComponentsClientHttpRequestFactory instead and now it works just fine.
String url = "http://localhost:5000/getMyObject";
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory ();
RestTemplate restTemplate = new RestTemplate(factory);
String result = restTemplate.getForObject(url, String.class);
I'm am developing an application with the Jersey framework that acts as a proxy to the docker HTTP API. I'm able to upload images successfully to Docker but I am unable to forward the response from Docker to the client without an error. I believe it has something to do with the Transfer-Encoding=[chunked] header in the response.
Response
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> POST /proxy/images/load HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 127.0.0.1:8080
> Accept: */*
> Content-Type: application/octet-stream
> Content-Length: 477637632
> Expect: 100-continue
>
< HTTP/1.1 100
< HTTP/1.1 200
< Transfer-Encoding: chunked
< Docker-Experimental: false
< Api-Version: 1.26
< Server: Docker/1.13.1 (linux)
< Date: Mon, 23 Jul 2018 11:20:14 GMT
< Content-Type: application/json
< Content-Length: 54
<
* Problem (2) in the Chunked-Encoded data
* Closing connection 0
curl: (56) Problem (2) in the Chunked-Encoded data
code
#POST
#Path("/load2")
//#Consumes(MediaType.APPLICATION_OCTET_STREAM)
public Response loadimages(byte[] input, #Context UriInfo uriInfo) {
WebTarget webTarget = ClientBuilder.newClient().target("http://127.0.0.1:5555/images").path("load");
Response response = webTarget.request(MediaType.APPLICATION_JSON).post(Entity.entity(input, MediaType.APPLICATION_OCTET_STREAM));
return response;
}
I have a small demo to play with customized status code.
The interesting part is the request will always hang there if the status is below 200, like 105, 199, etc. But works for any status greater than 200, like 209, 789 etc.
Http status code registry, refer to https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
Spring boot: 1.5.4.RELEASE with embedded tomcat
Java: 8
Controller:
#RestController
public class DemoController {
#GetMapping("/hello")
public ResponseEntity get() {
return ResponseEntity.status(105).build();
}
}
Can anyone give me a clear explanation?
I create a gist at here: https://gist.github.com/pengisgood/dbea1fcdc45c2bb5809871c7f020b800
Update:
I also create a small demo to reproduce it at here:
https://github.com/pengisgood/springboot-customize-status-code
Update:
After I run curl -v localhost:8080/hello, I can see the status, but the response doesn't finish. Refer to the gif below:
I also ran into this issue and found that it is not Spring that creates this behavior. It is Tomcat.
curl -v --header "Expect: 100-continue" http://localhost:8080
Calling any configured endpoint like this will return an extra response code that doesn't terminate the request.
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0*
Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.56.1
> Accept: */*
> Expect: 100-continue
>
< HTTP/1.1 100
< HTTP/1.1 200
< Set-Cookie: JSESSIONID=9355141A10CF546E9A9A43F5A5C0B1A4; Path=/; HttpOnly
< Content-Type: text/html;charset=ISO-8859-1
< Content-Length: 58
< Date: Tue, 31 Jul 2018 17:27:52 GMT
<
{ [58 bytes data]
100 58 100 58 0 0 58 0 0:00:01 --:--:-- 0:00:01 82<html>
<body>
<h2>Hello Heroku!</h2>
</body>
</html>
* Connection #0 to host localhost left intact
note the HTTP/1.1 100
This response came from this project https://devcenter.heroku.com/articles/create-a-java-web-application-using-embedded-tomcat which doesn't have spring. If I modify the HelloServlet to include a response code of 100 it just hangs.
Looking deeper:
https://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.2.3
The specification makes it clear that the 100 response is supposed to happen in the same request. The reason it hangs is because it is expecting the client to respond with the content of the request.
Looking at the wiki for other 1XX response codes, it appears to also be true that some information is returned without closing the request. My guess is that Tomcat expects all 1xx response codes to act in this manner.
As far as I can tell the Spring DispacherServlet is handling the different return codes in exactly the same way. I think what's happening is curl is just leaving the connection open because the response is in the 1xx range.
This article provides a good primer on status codes. This sentence in particular is relevant:
100–199 Codes in the 100s are informational, indicating that the client should respond with some other action.
If you run curl with --trace you'll see that the 105 response does actually arrive:
curl -v -trace http://localhost:8080/hello
Trying ::1...
TCP_NODELAY set
Connected to localhost (::1) port 8080 (#0)
> GET /hello HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 105
< Date: Tue, 19 Sep 2017 18:07:04 GMT
^C
So what I think is happening is the response is returned, the client should respond with some other action (but doesn't) so it looks like the thing has hung.
Probably the real question here is why are you trying to return a 105 status and what do you expect to happen?
I have the following method implemented using JAX-RS 2.0 (Jersey):
#PUT
#Path("container/{containername}/catalog")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response updateCatalog(#PathParam("containername") String containerName,
#FormDataParam("catalog") SIRFCatalog catalog) throws IOException, URISyntaxException {
log.info("Unmarshalling config...");
SIRFConfiguration config = new SIRFConfigurationUnmarshaller().
unmarshalConfig(new String(Files.readAllBytes(Paths.get(
SIRFConfiguration.SIRF_DEFAULT_DIRECTORY + "conf.json"))));
log.info("Creating strategy...");
StorageContainerStrategy strat = AbstractStrategyFactory.createStrategy(config);
log.info("Pushing catalog...");
strat.pushCatalog(catalog, containerName);
log.info("Sending response...");
return Response.ok(new URI("sirf/container/" + containerName + "/catalog")).build();
}
I am using Eclipse MOXy and the War file has the jaxb.properties file in the same directory as the compiled class. The contents of the properties file are:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
When I sent an XML file containing a SIRFCatalog everything runs as expected:
curl -i -X PUT -H "Content-Type:multipart/form-data" -F catalog=#a.xml http://$OPENSIRF_IP:$OPENSIRF_PORT/sirf/container/philContainer/catalog
HTTP/1.1 100 Continue
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/json
Content-Length: 48
Date: Sat, 10 Sep 2016 18:30:30 GMT
{"value":"sirf/container/philContainer/catalog"}
However, when I send the very same contents in JSON (and I know they are the same because I marshalled the same object to XML and JSON and saved to a.xml and a.json) I get an HTTP 400:
curl -i -X PUT -H "Content-Type:multipart/form-data" -F catalog=#a.json http://$OPENSIRF_IP:$OPENSIRF_PORT/sirf/container/philContainer/catalog
HTTP/1.1 100 Continue
HTTP/1.1 400 Bad Request
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=utf-8
Content-Language: en
Content-Length: 1033
Date: Sat, 10 Sep 2016 18:30:38 GMT
Connection: close
<!DOCTYPE html><html><head><title>Apache Tomcat/8.0.36 - Error report</title><style type="text/css">H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}A.name {color : black;}.line {height: 1px; background-color: #525D76; border: none;}</style> </head><body><h1>HTTP Status 400 - Bad Request</h1><div class="line"></div><p><b>type</b> Status report</p><p><b>message</b> <u>Bad Request</u></p><p><b>description</b> <u>The request sent by the client was syntactically incorrect.</u></p><hr class="line"><h3>Apache Tomcat/8.0.36</h3></body></html>
I suspect that my server is actually using Vanilla JAXB instead of MOXy, but have no idea how to debug that! The tomcat8 logs say Content not allowed in prolog:
10-Sep-2016 18:30:36.902 INFO [http-nio-8080-exec-21] org.glassfish.jersey.filter.LoggingFilter.log 2 * Server has received a request on thread http-nio-8080-exec-21
2 > PUT http://200.144.189.109:8088/sirf/container/philContainer/catalog
2 > accept: */*
2 > content-length: 1755
2 > content-type: multipart/form-data; boundary=----------------------------bb3a9e312931
2 > expect: 100-continue
2 > host: 200.144.189.109:8088
2 > user-agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.19.1 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2
[Fatal Error] :1:1: Content is not allowed in prolog.
10-Sep-2016 18:30:38.386 INFO [http-nio-8080-exec-21] org.glassfish.jersey.filter.LoggingFilter.log 2 * Server responded with a response on thread http-nio-8080-exec-21
2 < 400
Any thoughts?
However, when I send the very same contents in JSON (and I know they
are the same because I marshalled the same object to XML and JSON and
saved to a.xml and a.json) I get an HTTP 400:
I suspect the generated json has root node (like in XML) and so you will have to instruct MOxy to not use root node. Please check this link and link as explained by Blaise.
UPDATE:
And yes you will need to specify the type of data in the cURL command as below:
-F "catalog=#a.json;type=application/json"
I've tried using Retrofits "Rest Builder" with interface methods containing Java "Collections" or "Array List" as arguments, but always end up with 400 or 500 type errors.
Method in interface SvcApi
#POST(PATIENT_MEDSTAKEN + "/{patientID}")
public boolean submitMedsTaken(#Body ArrayList<MedicationTaken> medstaken, #Path("patientID") long id);
API build by:
private SvcApi symptomManagementService = new RestAdapter.Builder()
.setEndpoint(TEST_URL)
.setLogLevel(LogLevel.FULL).build()
.create(SvcApi.class);
RESULT:
JUnit Failure trace: retrofit.RetrofitError:500 Server Error
Eclipse Console:
---> HTTP POST http://10.0.0.5:8080/patient/medicationstaken/1
Content-Type: application/json; charset=UTF-8
Content-Length: 63
[{"Id":0,"taken":true,"timeTaken":1415132592031,"checkinId":2}]
---> END HTTP (63-byte body)
<--- HTTP 500 http://localhost:8080/patient/medicationstaken/1 (35ms)
: HTTP/1.1 500 Server Error
Content-Language: en-US
Content-Length: 501
Content-Type: text/html;charset=ISO-8859-1
X-Application-Context: application
Server: Jetty(8.1.14.v20131031)