Is Websocket supported in gargoylesoftware library? I want to get websocket object in webclient.
Yes, WebSocket is supported since version 2.11. However, it is always recommended to use the latest version.
Please ensure you use BrowserVersion with recent browser, e.g. CHROME, FIREFOX_38, or INTERNET_EXPLORER_11.
E.g.:
try (final WebClient webClient = new WebClient(BrowserVersion.CHROME)) {
HtmlUnit will automatically handle the JavaScript with WebSocket.
Update:
To intercept the requests and responses, you can use:
new WebConnectionWrapper(webClient) {
public WebResponse getResponse(WebRequest request) throws IOException {
WebResponse response = super.getResponse(request);
if (request.getUrl().toExternalForm().contains("my_url")) {
String content = response.getContentAsString("UTF-8");
//change content
WebResponseData data = new WebResponseData(content.getBytes("UTF-8"),
response.getStatusCode(), response.getStatusMessage(), response.getResponseHeaders());
response = new WebResponse(data, request, response.getLoadTime());
}
return response;
}
};
Related
My team and I are working on some old but quite big application using GWT 2.4 (JDK 1.6.0_45).
We am currently facing a public API using HTTP protocol. They recently switch to HTTPS which is not well consumed by Java 6 (free version).
I had multiple solution :
Upgrade to a maintenable, but not free, Java 6 version (We would like to avoid paying)
Upgrade to Java 8 (GWT 2.4 which is not compatible with Java 8, so we also have to upgrade to GWT 2.8 and this will take some time considering the size of the application)
Developping a small Api catching the response of this public API and sending it back to my application with a HTTP protocol
I started the third solution but I am facing some problems with the unmarshalling of the response (xml) received.
Here is what I did until now:
My API method calling the public API:
#Override
public ResponseEntity<WorkMetadataType> lookupWithFilter(String authorization, String filter, String id, Optional<String> accept, Optional<String> xISANAuthorization, Optional<String> idtype) {
WorkMetadataType res = isanApi.lookupWithFilter(authorization, filter, id, accept.orElse(null), xISANAuthorization.orElse(null), idtype.orElse(null));
if (res == null) {
throw new WorkNotFoundException();
}
return ResponseEntity.ok(res);
}
The method calling the public API.
public WorkMetadataType lookupWithFilter(String authorization, String filter, String id, String accept, String xISANAuthorization, String idtype) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(getHttpClient()));
try {
CustomMarshallingHttpMessageConverter converter;
converter = new CustomMarshallingHttpMessageConverter(JAXBContext.newInstance(ISANDataType.class));
converter.setDefaultCharset(StandardCharsets.UTF_8);
restTemplate.getMessageConverters().add(converter);
} catch (JAXBException e) {
logger.error("Erreur lors de la définition du marshaller", e);
}
HttpEntity<String> entity = new HttpEntity<>(null, getHeaders(authorization, accept, xISANAuthorization));
return restTemplate.exchange(getRequestUri(id, idtype, filter), HttpMethod.GET, entity, WorkMetadataType.class).getBody();
}
As you can see, I am using Spring and his RestTemplate class. Problem is, you need to specify response's nature which I would like to avoid because of my unmarshalling issues.
My question is : is it possible to transfer the response of this public API to my Application without consuming it while being received by my API ? (simply, doing a copy/paste of it)
I finaly used a HttpCliendBuilder to build my request and get an InputStream as a response.
This way I can just convert it in String and create a ResponseEntity with it.
I'm currently migrating an application from jersey 1 to 2. In the old app, we used a ClientFilter for all jersey clients that automatically refreshed expired OAuth tokens like this:
#Override
public ClientResponse handle(ClientRequest cr) {
ClientResponse resp = getNext().handle(cr);
if (resp.getStatus() == Status.UNAUTHORIZED.getStatusCode()) {
// Try to refresh the token
boolean refreshed = refreshToken(oAuthInfo);
if (refreshed) {
resp = getNext().handle(cr);
}
}
return resp;
}
It might not haven been the most elegant way, but the benefit was that rest client users did not have to care about expired tokens themselves.
With the ContainerResponseFilter for jersey 2, this does not seem to be that simple anymore. The only option I currently see is to use the ClientRequestContext and try to re-create the original request using getClient, getHeaders etc... and then update the result in ContainerResponseContext. This however seems a bit clunky so I was wondering if there is any more convenient way to refresh an OAuth token without having to deal with this wherever a jersey client is used?
It looks like there is not more convenient way than intercepting the response with a client filter, refreshing the token if needed and trying to repeat the exact same request with the new token. In fact, this approach is also used by jersey own filter classes.
Sample code for repeating the original rest call from within a filter class can be found in jerseys HttpAuthenticationFilter:
static boolean repeatRequest(ClientRequestContext request, ClientResponseContext response, String newAuthorizationHeader) {
Client client = request.getClient();
String method = request.getMethod();
MediaType mediaType = request.getMediaType();
URI lUri = request.getUri();
WebTarget resourceTarget = client.target(lUri);
Invocation.Builder builder = resourceTarget.request(mediaType);
MultivaluedMap<String, Object> newHeaders = new MultivaluedHashMap<String, Object>();
for (Map.Entry<String, List<Object>> entry : request.getHeaders().entrySet()) {
if (HttpHeaders.AUTHORIZATION.equals(entry.getKey())) {
continue;
}
newHeaders.put(entry.getKey(), entry.getValue());
}
newHeaders.add(HttpHeaders.AUTHORIZATION, newAuthorizationHeader);
builder.headers(newHeaders);
builder.property(REQUEST_PROPERTY_FILTER_REUSED, "true");
Invocation invocation;
if (request.getEntity() == null) {
invocation = builder.build(method);
} else {
invocation = builder.build(method,
Entity.entity(request.getEntity(), request.getMediaType()));
}
Response nextResponse = invocation.invoke();
if (nextResponse.hasEntity()) {
response.setEntityStream(nextResponse.readEntity(InputStream.class));
}
MultivaluedMap<String, String> headers = response.getHeaders();
headers.clear();
headers.putAll(nextResponse.getStringHeaders());
response.setStatus(nextResponse.getStatus());
return response.getStatus() != Response.Status.UNAUTHORIZED.getStatusCode();
}
This code is used for example in DigestAuthenticator or BasicAuthenticator to repeat a request with provided credentials in case an Unauthorised response is received from server.
Let's say I have 5 API endpoints and 4 of them require the #Header "Authorization"; the other one doesn't.
Is there a drawback or anything of the sort when I use an Interceptor to insert the header for all API calls even though one of the endpoints does not require it? :)
Probably would be better use a more customizable approach that's retrofit2 provides - dynamic headers. A dynamic header is passed like a parameter to the method. The provided parameter value gets mapped by Retrofit before executing the request. Example:
#GET("/yourEndpoint")
Call<List<Obj>> getSomth(#Header("Your-Header") String yourHeader);
But if you only worrying about extra header passing, I don't see any drawback in your particular case.
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request.Builder builder = chain.request().newBuilder();
Request request = chain.request();
if (!request.url().toString().contains("/tapi/login/login")) {
String cookie = SharedObj.getCookie();
builder.addHeader("Cookie", cookie);
}
return chain.proceed(builder.build());
}
This is my solution.All api need cookie except login api('/tapi/login/login') ,So I judge url to decide to wether to add cookie in request.
There is no problem using Interceptor with header for all API.
Set headers in interceptors and use to all API. If the API method is with or without auth it will be worked.
Like this - Create ones, use everywhere.
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(chain -> {
Request original = chain.request();
Request.Builder requestBuilder = original.newBuilder();
//Add headers here using requestbuilder.
String authToken =//Your Authtoken
if (authToken != null)
requestBuilder.header("Authorization", authToken);
requestBuilder.method(original.method(), original.body());
return chain.proceed(requestBuilder.build());
});
In Retrofit
Retrofit retrofit= new Retrofit.Builder().baseUrl("baseUrl").client(httpClient.build()).build();
I'm using Apache HttpComponents to GET some web pages for some crawled URLs. Many of those URLs actually redirect to different URLs (e.g. because they have been processed with a URL shortener). Additionally to downloading the content, I would like to resolve the final URLs (i.e. the URL which provided the downloaded content), or even better, all URLs in the redirect chain.
I have been looking through the API docs, but got no clue, where I could hook. Any hints would be greatly appreciated.
One way is to turn off automatic redirect handling by setting the relevant parameter, and do it yourself by checking for 3xx responses, and manually extracting the redirect location from the responses "Location" header.
Here's a full demo of how to do it using Apache HttpComponents.
Important Details
You'll need to extend DefaultRedirectStrategy like so:
class SpyStrategy extends DefaultRedirectStrategy {
public final Deque<URI> history = new LinkedList<>();
public SpyStrategy(URI uri) {
history.push(uri);
}
#Override
public HttpUriRequest getRedirect(
HttpRequest request,
HttpResponse response,
HttpContext context) throws ProtocolException {
HttpUriRequest redirect = super.getRedirect(request, response, context);
history.push(redirect.getURI());
return redirect;
}
}
expand method sends a HEAD request which causes client to collect URIs in spy.history deque as it follows redirects automatically:
public static Deque<URI> expand(String uri) {
try {
HttpHead head = new HttpHead(uri);
SpyStrategy spy = new SpyStrategy(head.getURI());
DefaultHttpClient client = new DefaultHttpClient();
client.setRedirectStrategy(spy);
// FIXME: the following completely ignores HTTP errors:
client.execute(head);
return spy.history;
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
You may want to set maximum number of redirects followed to something reasonable (instead of the default of 100) like so:
BasicHttpParams params = new BasicHttpParams();
params.setIntParameter(ClientPNames.MAX_REDIRECTS, 5);
DefaultHttpClient client = new DefaultHttpClient(params);
I am using CRest to deserialize a JSON stream on Android. My first steps looks very promising.
To get the JSON stream from the server and not the XML one I use the following construct:
(Accept: application/json)
#EndPoint("http://localhost:8080/myserver/rest")
#Param(name = "Accept", value = "application/json", dest = Destination.HEADER)
public interface VersionService {
#ConnectionTimeout(10000)
#Path("/version")
VersionTO getVersion();
}
This works but it's a bit annoying to copy the "Param thing" for every service.
Is there a better way to configure all Services at one place only to return JSON?
Well I'm afraid there isn't any simple way in the current version, but feel free to open a request on the issue tracker.
Cheers,
Laurent
I faced a similar situation where I used a custom HTTP client. In your case it could look like as follows:
DefaultHttpClient client = new DefaultHttpClient();
client.addRequestInterceptor(new HttpRequestInterceptor() {
public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
request.addHeader("Accept", "application/json");
}
});
CRestBuilder builder = new CRestBuilder();
builder.expectsJson();
builder.setRestService(new HttpClientRestService(client));
Another option is to set default parameter for ClientPNames.DEFAULT_HEADERS of the custom HttpClient instance.
Details can be found on http://hc.apache.org/httpcomponents-client-ga/tutorial/html/httpagent.html.