/*
* The MIT License
*
* Copyright 2017 Intuit Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.intuit.karate.http.jersey;
import com.intuit.karate.ScriptContext;
import com.intuit.karate.ScriptValue;
import com.intuit.karate.XmlUtils;
import static com.intuit.karate.http.Cookie.*;
import com.intuit.karate.http.HttpClient;
import com.intuit.karate.http.HttpConfig;
import com.intuit.karate.http.HttpResponse;
import com.intuit.karate.http.HttpUtils;
import com.intuit.karate.http.MultiPartItem;
import com.intuit.karate.http.MultiValuedMap;
import com.jayway.jsonpath.DocumentContext;
import java.io.InputStream;
import java.util.List;
import java.util.Map.Entry;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation.Builder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.NewCookie;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.client.HttpUrlConnectorProvider;
import org.glassfish.jersey.media.multipart.BodyPart;
import org.glassfish.jersey.media.multipart.FormDataBodyPart;
import org.glassfish.jersey.media.multipart.MultiPart;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.media.multipart.file.StreamDataBodyPart;
import org.w3c.dom.Document;
/**
*
* @author pthomas3
*/
public class JerseyHttpClient extends HttpClient<Entity> {
private Client client;
private WebTarget target;
private Builder builder;
@Override
public void configure(HttpConfig config, ScriptContext context) {
ClientBuilder clientBuilder = ClientBuilder.newBuilder()
.register(new LoggingInterceptor(context.logger)) // must be first
.register(MultiPartFeature.class);
if (config.isSslEnabled()) {
String sslAlgorithm = config.getSslAlgorithm();
SSLContext ssl = HttpUtils.getSslContext(sslAlgorithm);
HttpsURLConnection.setDefaultSSLSocketFactory(ssl.getSocketFactory());
clientBuilder.sslContext(ssl);
clientBuilder.hostnameVerifier((host, session) -> true);
}
client = clientBuilder.build();
client.property(ClientProperties.CONNECT_TIMEOUT, config.getConnectTimeout());
client.property(ClientProperties.READ_TIMEOUT, config.getReadTimeout());
if (config.getProxyUri() != null) {
client.property(ClientProperties.PROXY_URI, config.getProxyUri());
if (config.getProxyUsername() != null && config.getProxyPassword() != null) {
client.property(ClientProperties.PROXY_USERNAME, config.getProxyUsername());
client.property(ClientProperties.PROXY_PASSWORD, config.getProxyPassword());
}
}
}
@Override
public String getRequestUri() {
return target.getUri().toString();
}
@Override
public void buildUrl(String url) {
target = client.target(url);
builder = target.request();
}
@Override
public void buildPath(String path) {
target = target.path(path);
builder = target.request();
}
@Override
public void buildParam(String name, Object... values) {
target = target.queryParam(name, values);
builder = target.request();
}
@Override
public void buildHeader(String name, Object value, boolean replace) {
if (replace) {
builder.header(name, null);
}
builder.header(name, value);
}
@Override
public void buildCookie(com.intuit.karate.http.Cookie c) {
Cookie cookie = new Cookie(c.getName(), c.getValue());
builder.cookie(cookie);
}
@Override
public Entity getEntity(MultiValuedMap fields, String mediaType) {
MultivaluedHashMap<String, Object> map = new MultivaluedHashMap<>();
for (Entry<String, List> entry : fields.entrySet()) {
map.put(entry.getKey(), entry.getValue());
}
return Entity.entity(map, mediaType);
}
@Override
public Entity getEntity(List<MultiPartItem> items, String mediaType) {
MultiPart multiPart = new MultiPart();
for (MultiPartItem item : items) {
if (item.getValue() == null || item.getValue().isNull()) {
continue;
}
String name = item.getName();
ScriptValue sv = item.getValue();
if (name == null) {
BodyPart bp;
switch (sv.getType()) {
case JSON:
DocumentContext dc = sv.getValue(DocumentContext.class);
bp = new BodyPart().entity(dc.jsonString()).type(MediaType.APPLICATION_JSON_TYPE);
break;
case XML:
Document doc = sv.getValue(Document.class);
bp = new BodyPart().entity(XmlUtils.toString(doc)).type(MediaType.APPLICATION_XML_TYPE);
break;
default:
bp = new BodyPart().entity(sv.getValue());
}
multiPart.bodyPart(bp);
} else if (sv.getType() == ScriptValue.Type.INPUT_STREAM) {
InputStream is = (InputStream) sv.getValue();
StreamDataBodyPart part = new StreamDataBodyPart(name, is);
multiPart.bodyPart(part);
} else {
multiPart.bodyPart(new FormDataBodyPart(name, sv.getAsString()));
}
}
return Entity.entity(multiPart, mediaType);
}
@Override
public Entity getEntity(String value, String mediaType) {
return Entity.entity(value, mediaType);
}
@Override
public Entity getEntity(InputStream value, String mediaType) {
return Entity.entity(value, mediaType);
}
@Override
public HttpResponse makeHttpRequest(Entity entity, long startTime) {
String method = request.getMethod();
if ("PATCH".equals(method)) { // http://danofhisword.com/dev/2015/09/04/Jersey-Client-Http-Patch.html
builder.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);
}
Response resp;
if (entity != null) {
resp = builder.method(method, entity);
} else {
resp = builder.method(method);
}
byte[] bytes = resp.readEntity(byte[].class);
long responseTime = getResponseTime(startTime);
HttpResponse response = new HttpResponse();
response.setTime(responseTime);
response.setUri(getRequestUri());
response.setBody(bytes);
response.setStatus(resp.getStatus());
for (NewCookie c : resp.getCookies().values()) {
com.intuit.karate.http.Cookie cookie = new com.intuit.karate.http.Cookie(c.getName(), c.getValue());
cookie.put(DOMAIN, c.getDomain());
cookie.put(PATH, c.getPath());
if (c.getExpiry() != null) {
cookie.put(EXPIRES, c.getExpiry().getTime() + "");
}
cookie.put(SECURE, c.isSecure() + "");
cookie.put(HTTP_ONLY, c.isHttpOnly() + "");
cookie.put(MAX_AGE, c.getMaxAge() + "");
response.addCookie(cookie);
}
for (Entry<String, List<Object>> entry : resp.getHeaders().entrySet()) {
response.addHeader(entry.getKey(), entry.getValue());
}
return response;
}
}