Comments (8)
I've been working with @juicetiger1 on this issue.
Here are my testing notes if it helps address the issue:
Issue: Resource Proxy (Java) fails to Renew Token after it Expires
Notes:
- Client is running resource proxy 1.1.2
https://github.com/Esri/resource-proxy/releases/tag/v1.1.2 - The proxy is configured to allow access to allow access to a secured Geocode service via tokenServiceUri, Username, Password combination
<serverUrl url="https://citymaps.peterborough.ca/arcgis/rest/services/PtboLocatorLocateApp/GeocodeServer"
matchAll="true"
tokenServiceUri="https://citymaps.peterborough.ca/portal/sharing/rest/generateToken"
username="XXXXX"
password="XXXXX"
/>
-
ArcGIS Server is federated with Portal. Both Server and Portal are at 10.6.1
-
The resource proxy is hosted on Tomcat web server.
-
The proxy is able to successfully generate a token and use it without a problem.
-
After an hour passes, the token expires, and the resource proxy fails to recognize that the token is expired, and doesn't attempt to generate a new token.
-
The proxy log shows no errors
-
We inspected the Java code responsible for renewing the token
-
After adding a line to print log of the response from Server that includes any error messages, we found that the proxy prints some incoherent text instead of a proper string response.
-
Following the logic of the code in line 212, this string won't match any of the conditions of the If statement and won't be able to recognize the error returned from the server/portal
https://github.com/Esri/resource-proxy/blob/master/Java/proxy.jsp#L212 -
The issue seems limited to this one installation of the resource proxy
-
Client has other applications setup with their own resource proxy (also, Java on Tomcat) and they all work fine with similar configuration.
-
I've attempted to reproduce the issue, but couldn't. The proxy works fine.
-
We're looking for help identifying what's causing the resource proxy to behave this way.
from resource-proxy.
I've run into what may be the same issue. If so, I've managed to fix/workaround it. I was getting the same "wing-ding" response body when one particular service sent along an authentication error that looked like this when written to the log:
I actually found two issues when handling the authentication error, but the root cause is that the authentication error is gzip encoded. Now, I didn't dig deep enough to determine if this encoding issue is with the proxy code, something else in the call stack (like a tomcat, or connection issue), or if the problem was with the server itself sending an ill-formed response (it would be nice to know why other responses aren't gzipped, or if they are, why they get properly decoded and this one doesn't). But either way I was able to put in a work-around to gzip decode the response if other means didn't catch the authentication error, like so:
//proxy gets the response back from server
private boolean fetchAndPassBackToClient(HttpURLConnection con, HttpServletResponse clientResponse, boolean ignoreAuthenticationErrors) throws IOException{
if (con!=null){
//copy the response content to the response to the client
InputStream byteStream;
if (con.getResponseCode() >= 400 && con.getErrorStream() != null){
if (ignoreAuthenticationErrors && (con.getResponseCode() == 498 || con.getResponseCode() == 499)){
log.info("Authentication error found in response code.");
return true;
}
byteStream = con.getErrorStream();
}else{
byteStream = con.getInputStream();
}
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int length = 5000;
byte[] bytes = new byte[length];
int bytesRead;
while ((bytesRead = byteStream.read(bytes, 0, length)) > 0) {
buffer.write(bytes, 0, bytesRead);
}
//if the content of the HttpURLConnection contains error message, it means the token expired, so let proxy try again
//only check small responses (< 1KB). It's probably expensive to convert and parse large responses every time.
log.info("RESPONSE BUFFER SIZE: " + buffer.size());
if(buffer.size() <= 1024 && !ignoreAuthenticationErrors){
String strResponse = buffer.toString();
log.info("RESPONSE: " + strResponse);
if (hasResponseBodyAuthenticationError(strResponse)) {
log.info("Authentication error found in response body.");
return true;
}
//Do a gzip decode and check those results as well
if("gzip".equals(con.getHeaderField("Content-Encoding"))) {
GZIPInputStream gzin = new GZIPInputStream(new ByteArrayInputStream(buffer.toByteArray()));
ByteArrayOutputStream outputBytes = new ByteArrayOutputStream();
int read = 0;
byte gzBytes[] = new byte[1024];
while ((read = gzin.read(gzBytes, 0, gzBytes.length)) > 0) {
outputBytes.write(gzBytes, 0, read);
}
String strGzipResponse = outputBytes.toString();
log.info("CONTAINS GZIP HEADER. UN-GZIPPED RESPONSE: " + strGzipResponse);
if (hasResponseBodyAuthenticationError(strGzipResponse)) {
log.info("Authentication error found in gzipped response body.");
return true;
}
}
}
copyHeadersToResponse(con, clientResponse);
clientResponse.setStatus(con.getResponseCode());
byte[] byteResponse = buffer.toByteArray();
OutputStream ostream = clientResponse.getOutputStream();
ostream.write(byteResponse);
}
return false;
}
Since I'm unable to determine exactly what conditions cause the decoding issue, I just check all responses for errors that aren't caught via the first check by gzip decoding them, and optimize to only check small-ish responses to save some cycles.
You also may notice that I moved the code that sets the response headers to occur after the authentication error check. I found that when it's before the check, it adds duplicate headers that cause issues at the client since that section of code would be called twice if an authentication error occurred.
Finally, you may notice I removed all the flush() and close() calls, as those are unnecessary in Java 8, so be sure to leave those in if using something lower.
Anywho, I hope this helps you (or anybody) out. Apologies, I can't submit a pull request for a fix because I've refactored this project into a Spring Boot project to make it more easily deployable in a Kubernetes cluster.
from resource-proxy.
On another, slightly related note, I've also found that some error responses have been missed because they contain a space before and after the colon and should be checked, like so:
private boolean hasResponseBodyAuthenticationError(String responseBody){
return responseBody.contains("error")
&& (responseBody.contains("\"code\": 498")
|| responseBody.contains("\"code\": 499")
|| responseBody.contains("\"code\":498")
|| responseBody.contains("\"code\":499")
|| responseBody.contains("\"code\" : 498") //add this check
|| responseBody.contains("\"code\" : 499")); // and this check
}
from resource-proxy.
Are you able to send the copyHeadersToResponse function as well?
Thanks
from resource-proxy.
Are you able to send the copyHeadersToResponse function as well?
Thanks
I've made no changes here, other than re-arranging the code. I simply extracted all of the header-setting code at the top of the method into its own method and moved it below the authentication checking code.
from resource-proxy.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you need additional assistance please contact Esri Technical Support. Thank you for your contributions.
from resource-proxy.
This issue has been automatically closed due to inactivity. If you need additional assistance please contact Esri Technical Support.
from resource-proxy.
Are you able to send the copyHeadersToResponse function as well?
Thanks
private void copyHeadersToResponse(HttpURLConnection con, HttpServletResponse clientResponse){
Map<String, List<String>> headerFields = con.getHeaderFields();
Set<String> headerFieldsSet = headerFields.keySet();
//copy the response header to the response to the client
for (String headerFieldKey : headerFieldsSet){
//prevent request for partial content
if (headerFieldKey != null && headerFieldKey.toLowerCase().equals("accept-ranges")){
continue;
}
List<String> headerFieldValue = headerFields.get(headerFieldKey);
StringBuilder sb = new StringBuilder();
for (String value : headerFieldValue) {
// Reset the content-type for OGC WMS - issue #367
// Note: this might not be what everyone expects, but it helps some users
// TODO: make this configurable
if (headerFieldKey != null && headerFieldKey.toLowerCase().equals("content-type")){
if (value != null && value.toLowerCase().contains("application/vnd.ogc.wms_xml")){
_log(Level.FINE, "Adjusting Content-Type for WMS OGC: " + value);
value = "text/xml";
}
}
// remove Transfer-Encoding/chunked to the client
// StackOverflow http://stackoverflow.com/questions/31452074/how-to-proxy-http-requests-in-spring-mvc
if (headerFieldKey != null && headerFieldKey.toLowerCase().equals("transfer-encoding")) {
if (value != null && value.toLowerCase().equals("chunked")) {
continue;
}
}
sb.append(value);
sb.append("");
}
if (headerFieldKey != null){
clientResponse.addHeader(headerFieldKey, DataValidUtil.removeCRLF(sb.toString()));
}
}
}
from resource-proxy.
Related Issues (20)
- The proxy server application .Net version does not work for ArcGIS server 10.61 HOT 9
- The proxy server application php version does not work with php version 7 and higher HOT 4
- Getting Invalid token error when trying to access ArcGIS World Geocoding Service on non-public Web Map HOT 2
- Problema con proxy DotNet HOT 2
- wildcard in allowedReferer not working HOT 2
- 304 Not modified errors created in log files for all request. HOT 5
- PHP7 ErrorException count(): Parameter must be an array or an object that implements Countable HOT 2
- Proxy has not been set up for this URL HOT 5
- _legends information lost on secured services HOT 2
- The dotnet version has an unused variable
- Multiple Accounts HOT 3
- time to archive? HOT 1
- Mention NGINX as an alternative in README HOT 3
- preliminary calls not using proxy HOT 5
- How to use esri-resource-proxy in an application running on a different port? HOT 4
- Impossible de créer un canal sécurisé SSL/TLS
- How to install the PHP proxy on EC2 (Amazon Linux 2) HOT 1
- PHP Proxy Doesn't work HOT 1
- Directory Traversal Vulnerability HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from resource-proxy.