package restservices.consume;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.auth.AuthPolicy;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.httpclient.cookie.CookieSpec;
import org.apache.commons.httpclient.methods.DeleteMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.PutMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.httpclient.methods.multipart.ByteArrayPartSource;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.io.IOUtils;
import com.mendix.thirdparty.org.json.JSONArray;
import com.mendix.thirdparty.org.json.JSONObject;
import com.mendix.thirdparty.org.json.JSONTokener;
import restservices.RestServices;
import restservices.proxies.Cookie;
import restservices.proxies.HttpMethod;
import restservices.proxies.ReferableObject;
import restservices.proxies.RequestResult;
import restservices.proxies.ResponseCode;
import restservices.proxies.RestServiceError;
import restservices.util.JsonDeserializer;
import restservices.util.JsonSerializer;
import restservices.util.UriTemplate;
import restservices.util.Utils;
import system.proxies.FileDocument;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import com.mendix.core.Core;
import com.mendix.core.CoreException;
import com.mendix.m2ee.api.IMxRuntimeResponse;
import com.mendix.systemwideinterfaces.core.IContext;
import com.mendix.systemwideinterfaces.core.IMendixIdentifier;
import com.mendix.systemwideinterfaces.core.IMendixObject;
import com.mendix.systemwideinterfaces.core.meta.IMetaAssociation;
import com.mendix.systemwideinterfaces.core.meta.IMetaAssociation.AssociationType;
import com.mendix.systemwideinterfaces.core.meta.IMetaObject;
import communitycommons.StringUtils;
public class RestConsumer {
private static ThreadLocal<HttpResponseData> lastConsumeError = new ThreadLocal<HttpResponseData>();
private static MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
static HttpClient client = new HttpClient(connectionManager);
static {
connectionManager.getParams().setMaxConnectionsPerHost(HostConfiguration.ANY_HOST_CONFIGURATION, 10);
}
public static class HttpResponseData{
private int status;
private String body = null;
private String eTag;
private String url;
private String method;
private Header[] headers;
HttpResponseData(String method, String url, int status, String eTag, Header[] headers) {
this.url = url;
this.status = status;
this.eTag = eTag;
this.method = method;
this.headers = headers;
}
public void setBody(String body) {
this.body = body;
}
public boolean isOk() {
return status == HttpStatus.SC_NOT_MODIFIED || (status >= 200 && status < 300);
}
public RequestResult asRequestResult(IContext context) {
JSONObject jsonHeaders = getResponseHeadersAsJson();
RequestResult rr = new RequestResult(context);
rr.setRequestUrl(url);
rr.setETag(getETag());
rr.setRawResponseCode(status);
rr.setResponseBody(getBody());
rr.setResponseCode(asResponseCode());
rr.set_ResponseHeaders(jsonHeaders.toString());
if (
status >= 400 && status < 600
&& jsonHeaders.has(RestServices.HEADER_CONTENTTYPE)
&& jsonHeaders.getJSONArray(RestServices.HEADER_CONTENTTYPE).getString(0).contains("json")
) {
RestServiceError rse = new RestServiceError(context);
try {
JsonDeserializer.readJsonDataIntoMendixObject(context, new JSONObject(getBody()), rse.getMendixObject(), false);
rr.setErrorDetails(rse);
} catch (Exception e) {
RestServices.LOGCONSUME.warn("Failed to parse error message to JSON: " + getBody());
}
}
return rr;
}
public ResponseCode asResponseCode() {
if (status == IMxRuntimeResponse.NOT_MODIFIED)
return ResponseCode.NotModified;
else if (status >= 400 || status <= 0) //-1 is used if making the connection failed
return ResponseCode.Error;
else
return ResponseCode.OK; //We consider all other responses 'OK-ish', even redirects and such.. Users can check the actual response code with the RawResponse field
}
@Override public String toString() {
return String.format("[HTTP Request: %s '%s' --> Response status: %d %s, ETag: %s, body: '%s']",
method, url,
status, status < 0 ? "CONNECTION FAILED" : HttpStatus.getStatusText(status),
eTag,
RestServices.LOGCONSUME.isDebugEnabled() || status != 200 ? body : "(omitted)");
}
public String getETag() {
return eTag;
}
public int getStatus() {
return status;
}
public String getBody() {
return body;
}
private JSONObject getResponseHeadersAsJson() {
JSONObject res = new JSONObject();
if (headers != null) for(Header header : headers) {
if (!res.has(header.getName()))
res.put(header.getName(), new JSONArray());
res.getJSONArray(header.getName()).put(header.getValue());
}
return res;
}
}
static ThreadLocal<Map<String, String>> nextHeaders = new ThreadLocal<Map<String, String>>();
private static Map<String, String> prepareNextHeadersMap() {
Map<String, String> headers = nextHeaders.get();
if (headers == null) {
headers = new HashMap<String, String>();
nextHeaders.set(headers);
}
return headers;
}
public static synchronized void setGlobalRequestSettings(Long maxConcurrentRequests, Long timeout) {
if (timeout != null) {
client.getParams().setConnectionManagerTimeout(timeout);
client.getParams().setSoTimeout(timeout.intValue());
}
if (maxConcurrentRequests != null) {
connectionManager.getParams().setMaxConnectionsPerHost(HostConfiguration.ANY_HOST_CONFIGURATION, maxConcurrentRequests.intValue());
connectionManager.getParams().setMaxTotalConnections(maxConcurrentRequests.intValue() * 2);
}
}
public static void addHeaderToNextRequest(String header, String value) {
prepareNextHeadersMap().put(header, value);
}
public static void addCookieToNextRequest(Cookie cookie) {
if (cookie == null || cookie.getName().isEmpty())
throw new IllegalArgumentException();
Map<String, String> headers = prepareNextHeadersMap();
org.apache.commons.httpclient.Cookie rq = new org.apache.commons.httpclient.Cookie("", cookie.getName(), cookie.getValue());
String cookiestr = CookiePolicy.getDefaultSpec().formatCookie(rq);
if (!headers.containsKey("Cookie"))
headers.put("Cookie", cookiestr);
else
headers.put("Cookie", headers.get("Cookie") + "; " + cookiestr);
}
static void includeHeaders(HttpMethodBase request) {
Map<String, String> headers = nextHeaders.get();
nextHeaders.set(null);
includeHeaders(request, headers);
}
static void includeHeaders(HttpMethodBase request, Map<String, String> headers) {
if (headers != null) {
for(Entry<String,String> e : headers.entrySet())
request.addRequestHeader(e.getKey(), e.getValue());
}
}
/**
* Retreives a url. Returns if statuscode is 200 OK, 304 NOT MODIFIED or 404 NOT FOUND. Exception otherwise.
* @throws IOException
* @throws HttpException
*/
private static HttpResponseData doRequest(String method, String url, Map<String, String> requestHeaders,
Map<String, String> params, RequestEntity requestEntity, Predicate<InputStream> onSuccess) throws HttpException, IOException {
if (RestServices.LOGCONSUME.isDebugEnabled())
RestServices.LOGCONSUME.debug("Fetching '" + url + "'..");
HttpMethodBase request = null;
try {
if (params != null && !"POST".equals(method)) {
//append params to url. Do *not* use request.setQueryString; that will override any args already in there
for(Entry<String, String> e : params.entrySet())
url = Utils.appendParamToUrl(url, e.getKey(), e.getValue());
}
if ("GET".equals(method))
request = new GetMethod(url);
else if ("DELETE".equals(method))
request = new DeleteMethod(url);
else if ("POST".equals(method))
request = new PostMethod(url);
else if ("PUT".equals(method))
request = new PutMethod(url);
else
throw new IllegalStateException("Unsupported method: " + method);
request.getParams().setCookiePolicy(CookiePolicy.IGNORE_COOKIES);
request.setRequestHeader(RestServices.HEADER_ACCEPT, RestServices.CONTENTTYPE_APPLICATIONJSON);
if (requestHeaders != null) for(Entry<String, String> e : requestHeaders.entrySet())
request.addRequestHeader(e.getKey(), e.getValue());
includeHeaders(request);
if (params != null && request instanceof PostMethod)
((PostMethod)request).addParameters(mapToNameValuePairs(params));
if (request instanceof PostMethod && requestEntity != null)
((PostMethod)request).setRequestEntity(requestEntity);
else if (request instanceof PutMethod && requestEntity != null)
((PutMethod)request).setRequestEntity(requestEntity);
int status = client.executeMethod(request);
Header responseEtag = request.getResponseHeader(RestServices.HEADER_ETAG);
HttpResponseData response = new HttpResponseData(method, url, status, responseEtag == null ? null : responseEtag.getValue(), request.getResponseHeaders());
InputStream instream = request.getResponseBodyAsStream();
if (onSuccess != null && status >= 200 && status < 300 && instream != null) //NO CONENT doesnt yield a stream..
onSuccess.apply(instream);
else if (instream != null)
response.setBody(IOUtils.toString(instream));
if (RestServices.LOGCONSUME.isDebugEnabled())
{
RestServices.LOGCONSUME.debug(response);
}
return response;
}
catch(Exception e) {
HttpResponseData response = new HttpResponseData(method, url, -1, null, null);
response.setBody(e.getClass().getName() + ": " + e.getMessage());
RestServices.LOGCONSUME.error("Failed to connect to " + url + ": " + e.getMessage(), e);
return response;
}
finally {
if (request != null)
request.releaseConnection();
}
}
private static NameValuePair[] mapToNameValuePairs(Map<String, String> params) {
NameValuePair[] res = new NameValuePair[params.size()];
int i = 0;
for(Entry<String, String> e : params.entrySet()) {
res[i] = new NameValuePair(e.getKey(), e.getValue());
i++;
}
return res;
}
public static void readJsonObjectStream(String url, final Predicate<Object> onObject) throws Exception, IOException {
lastConsumeError.set(null);
HttpResponseData response = doRequest("GET", url, null, null, null, new Predicate<InputStream>() {
@Override
public boolean apply(InputStream stream) {
JSONTokener x = new JSONTokener(stream);
//Based on: https://github.com/douglascrockford/JSON-java/blob/master/JSONArray.java
if (x.nextClean() != '[')
throw x.syntaxError("A JSONArray text must start with '['");
for (;;) {
switch(x.nextClean()) {
case ',':
break;
case ']':
return true;
case '{':
x.back();
onObject.apply(new JSONObject(x));
break;
case '[':
x.back();
onObject.apply(new JSONArray(x));
break;
default:
x.back();
onObject.apply(x.nextValue());
}
}
}
});
if (response.getStatus() != HttpStatus.SC_OK) {
lastConsumeError.set(response);
throw new RestConsumeException(response.getStatus(), "Failed to start request stream on '" + url + "', expected status to be 200 OK");
}
}
public static void registerCredentials(String urlBasePath, String username, String password) throws MalformedURLException
{
client.getParams().setAuthenticationPreemptive(true);
Credentials defaultcreds = new UsernamePasswordCredentials(username, password);
URL url = new URL(urlBasePath);
client.getState().setCredentials(new AuthScope(url.getHost(), url.getPort(), AuthScope.ANY_REALM), defaultcreds);
}
public static void registerNTCredentials(String urlBasePath, String username, String password, String domain) throws MalformedURLException
{
client.getParams().setAuthenticationPreemptive(true);
URL url = new URL(urlBasePath);
Core.getLogger("NTLM").info(url.getHost());
Credentials defaultcreds = new NTCredentials(username, password, url.getHost(), domain);
AuthPolicy.registerAuthScheme(AuthPolicy.NTLM, restservices.util.JCIFS_NTLMScheme.class);
List<String> authpref = new ArrayList<String>();
authpref.add(AuthPolicy.NTLM);
client.getParams().setParameter("http.auth.target-scheme-pref", authpref);
client.getState().setCredentials(new AuthScope(AuthScope.ANY), defaultcreds);
}
private static void getCollectionHelper(final IContext context, String collectionUrl, final Function<IContext, IMendixObject> objectFactory, final Function<IMendixObject, Boolean> callback) throws Exception
{
RestConsumer.readJsonObjectStream(collectionUrl, new Predicate<Object>() {
@Override
public boolean apply(Object data) {
IMendixObject item = objectFactory.apply(context);
try {
JsonDeserializer.readJsonDataIntoMendixObject(context, data, item, true);
} catch (Exception e) {
throw new RuntimeException(e);
}
callback.apply(item);
return true;
}
});
}
public static void getCollection(final IContext context, String collectionUrl, final List<IMendixObject> resultList, final IMendixObject firstResult) throws Exception {
if (resultList == null || resultList.size() > 0)
throw new RuntimeException("Expected stub collection to have size 0");
getCollectionHelper(context, collectionUrl, new Function<IContext, IMendixObject>() {
@Override
public IMendixObject apply(IContext arg0) {
if (resultList.size() == 0)
return firstResult;
return Core.instantiate(context, firstResult.getType());
}
}, new Function<IMendixObject, Boolean>() {
@Override
public Boolean apply(IMendixObject obj) {
resultList.add(obj);
return true;
}
});
}
public static void getCollectionAsync(String collectionUrl, final String callbackMicroflow) throws Exception {
//args check
Map<String, String> argTypes = Utils.getArgumentTypes(callbackMicroflow);
if (argTypes.size() != 1)
throw new IllegalArgumentException("Microflow '" + callbackMicroflow + "' should have exactly one argument");
final String entityType = argTypes.values().iterator().next();
if (Core.getMetaObject(entityType) == null)
throw new IllegalArgumentException("Microflow '" + callbackMicroflow + "' should expect an entity as argument");
final IContext context = Core.createSystemContext();
getCollectionHelper(context, collectionUrl, new Function<IContext, IMendixObject>() {
@Override
public IMendixObject apply(IContext arg0) {
return Core.instantiate(arg0, entityType);
}
}, new Function<IMendixObject, Boolean>() {
@Override
public Boolean apply(IMendixObject item) {
try {
Core.execute(context, callbackMicroflow, item);
} catch (CoreException e) {
throw new RuntimeException(e);
}
return true;
}
});
}
public static RequestResult request(final IContext context, HttpMethod method, String url,
final IMendixObject source, final IMendixObject target, final boolean asFormData) throws Exception {
lastConsumeError.set(null);
if (context == null)
throw new IllegalArgumentException("Context should not be null");
if (method == null)
method = HttpMethod.GET;
final boolean isFileSource = source != null && Core.isSubClassOf(FileDocument.entityName, source.getType());
final boolean isFileTarget = target != null && Core.isSubClassOf(FileDocument.entityName, target.getType());
final boolean hasFileParts = source != null && hasFileParts(source.getMetaObject());
if ((isFileSource || hasFileParts) && !(method == HttpMethod.POST || method == HttpMethod.PUT))
throw new IllegalArgumentException("Files can only be send with method is POST or PUT");
Map<String, String> requestHeaders = new HashMap<String, String>();
Map<String, String> params = new HashMap<String, String>();
RequestEntity requestEntity = null;
final JSONObject data = source == null ? null : JsonSerializer.writeMendixObjectToJson(context, source, false);
boolean appendDataToUrl = source != null && (asFormData || method == HttpMethod.GET || method == HttpMethod.DELETE);
url = updateUrlPathComponentsWithParams(url, appendDataToUrl, isFileSource, data, params);
//Setup request entity for file
if (!asFormData && isFileSource) {
requestEntity = new InputStreamRequestEntity(Core.getFileDocumentContent(context, source));
}
else if (source != null && asFormData && (isFileSource || hasFileParts)) {
requestEntity = buildMultiPartEntity(context, source, params);
}
else if (asFormData && !isFileSource)
requestHeaders.put(RestServices.HEADER_CONTENTTYPE, RestServices.CONTENTTYPE_FORMENCODED);
else if (data != null && data.length() != 0) {
requestEntity = new StringRequestEntity(data.toString(4), RestServices.CONTENTTYPE_APPLICATIONJSON, RestServices.UTF8);
if (RestServices.LOGCONSUME.isDebugEnabled()) {
RestServices.LOGCONSUME.debug("[Body JSON Data] " + data.toString());
}
}
final StringBuilder bodyBuffer = new StringBuilder();
HttpResponseData response = doRequest(method.toString(), url, requestHeaders, params, requestEntity, new Predicate<InputStream>() {
@Override
public boolean apply(InputStream stream) {
try {
if (isFileTarget)
Core.storeFileDocumentContent(context, target, stream);
else {
String body = IOUtils.toString(stream);
bodyBuffer.append(body);
if (target != null){
if (!body.matches("^\\s*\\{[\\s\\S]*"))
throw new IllegalArgumentException("Response body does not seem to be a valid JSON Object. A JSON object starts with '{' but found: " + body);
JsonDeserializer.readJsonDataIntoMendixObject(context, new JSONTokener(body).nextValue(), target, true);
}
}
return true;
}
catch(Exception e) {
throw new RuntimeException(e);
}
}
});
//wrap up
if (!response.isOk()) {
lastConsumeError.set(response);
throw new RestConsumeException(response);
}
response.setBody(bodyBuffer.toString());
if (target != null && Core.isSubClassOf(ReferableObject.entityName, target.getType())) {
target.setValue(context, ReferableObject.MemberNames.URL.toString(), url);
target.setValue(context, ReferableObject.MemberNames.ETag.toString(), response.getETag());
}
return response.asRequestResult(context);
}
private static String updateUrlPathComponentsWithParams(String url, boolean appendDataToUrl, final boolean isFileSource, final JSONObject data, Map<String, String> params) {
//substitute template variable in the uri, and make sure they are not send along as body / params data
UriTemplate uriTemplate = new UriTemplate(url);
Map<String, String> keyMapping = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
if (data != null) for (Iterator<String> iterator = data.keys(); iterator.hasNext();) {
String key = iterator.next();
keyMapping.put(key, key);
}
Map<String, String> values = new HashMap<String, String>();
if (data != null) for(String templateVar : uriTemplate.getTemplateVariables()) {
if (keyMapping.containsKey(templateVar)) {
String realkey = (String) keyMapping.get(templateVar);
Object value = data.get(realkey);
if (!(value instanceof JSONObject) && !(value instanceof JSONArray)) {
data.remove(realkey);
values.put(templateVar, value == null || value == JSONObject.NULL ? "" : String.valueOf(value));
}
}
}
url = uriTemplate.createURI(values);
//register params, if its a GET request or formData format is used
if (data != null && data.length() > 0 && appendDataToUrl) {
for(String key : JSONObject.getNames(data)) {
if (isFileSource && isFileDocAttr(key))
continue; //Do not pick up default filedoc attrs!
if (Utils.isSystemAttribute(key))
continue;
Object value = data.get(key);
if (value != null && !(value instanceof JSONObject) && !(value instanceof JSONArray))
params.put(key, String.valueOf(value));
}
}
return url;
}
private static RequestEntity buildMultiPartEntity(final IContext context,
final IMendixObject source, Map<String, String> params)
throws IOException, CoreException {
// MWE: don't set contenttype to CONTENTTYPE_MULTIPART; this will be
// done by the request entity and add the boundaries
List<Part> parts = Lists.newArrayList();
// This object self could be a filedocument
if (Core.isSubClassOf(FileDocument.entityName, source.getType())) {
String partName = getFileDocumentFileName(context, source);
if (partName == null || partName.isEmpty())
throw new IllegalArgumentException("The filename of a System.FileDocument in a multipart request should reflect the part name and cannot be empty");
addFilePart(context, partName, source, parts);
}
// .. or one of its children could be a filedocument. This way multiple
// file parts, or specifically named file parts can be send
for (String name : getAssociationsReferingToFileDocs(source
.getMetaObject())) {
IMendixIdentifier subObject = (IMendixIdentifier) source.getValue(
context, name);
params.remove(Utils.getShortMemberName(name));
if (subObject != null) {
addFilePart(context, Utils.getShortMemberName(name),
Core.retrieveId(context, subObject), parts);
}
}
// serialize any other members as 'normal' key value pairs
for (Entry<String, String> e : params.entrySet()) {
parts.add(new StringPart(e.getKey(), e.getValue(),
RestServices.UTF8));
}
params.clear();
return new MultipartRequestEntity(parts.toArray(new Part[0]),
new HttpMethodParams());
}
private static Set<String> getAssociationsReferingToFileDocs(
IMetaObject meta) {
Set<String> names = new HashSet<String>();
for (IMetaAssociation assoc : meta.getMetaAssociationsParent()) {
if (assoc.getType() == AssociationType.REFERENCE && Core.isSubClassOf(FileDocument.entityName, assoc.getChild().getName()))
names.add(assoc.getName());
}
return names;
}
private static boolean hasFileParts(IMetaObject metaObject) {
return getAssociationsReferingToFileDocs(metaObject).size() > 0;
}
private static void addFilePart(final IContext context, String partname,
final IMendixObject source, List<Part> parts) throws IOException {
ByteArrayPartSource p = new ByteArrayPartSource(
getFileDocumentFileName(context, source),
IOUtils.toByteArray(Core.getFileDocumentContent(context, source)));
parts.add(new FilePart(partname, p));
}
private static String getFileDocumentFileName(final IContext context,
final IMendixObject source) {
return (String) source.getValue(context, FileDocument.MemberNames.Name.toString());
}
private static boolean isFileDocAttr(String key) {
try {
FileDocument.MemberNames.valueOf(key);
return true;
}
catch (IllegalArgumentException e) {
//Ok, this is a non filedoc attr
return false;
}
}
public static void addCredentialsToNextRequest(String username, String password) {
addHeaderToNextRequest(RestServices.HEADER_AUTHORIZATION, RestServices.BASIC_AUTHENTICATION + " " + StringUtils.base64Encode(username + ":" + password));
}
public static RequestResult deleteObject(IContext context, String resourceUrl,
String optEtag) throws Exception {
useETagInNextRequest(optEtag);
return request(context, HttpMethod.DELETE, resourceUrl, null, null, false);
}
public static RequestResult getObject(IContext context, String url,
String optEtag, IMendixObject target) throws Exception {
useETagInNextRequest(optEtag);
return request(context, HttpMethod.GET, url, null, target, false);
}
public static RequestResult getObject(IContext context, String url, IMendixObject target) throws Exception {
return request(context, HttpMethod.GET, url, null, target, false);
}
public static RequestResult getObject(IContext context, String url, IMendixObject source, IMendixObject target) throws Exception {
return request(context, HttpMethod.GET, url, source, target, false);
}
public static RequestResult putObject(IContext context, String url,
IMendixObject dataObject, String optEtag) throws Exception {
useETagInNextRequest(optEtag);
return request(context, HttpMethod.PUT, url, dataObject, null, false);
}
public static RequestResult postObject(IContext context, String collectionUrl,
IMendixObject dataObject, Boolean asFormData) throws Exception {
return request(context, HttpMethod.POST, collectionUrl, dataObject, null, asFormData);
}
public static RequestResult postObject(IContext context, String collectionUrl,
IMendixObject dataObject, IMendixObject targetObject) throws Exception {
return request(context, HttpMethod.POST, collectionUrl, dataObject, targetObject, false);
}
public static void useETagInNextRequest(String eTag) {
if (eTag != null)
addHeaderToNextRequest(RestServices.HEADER_IFNONEMATCH, eTag);
}
public static String getResponseHeaderFromRequestResult(
RequestResult requestResult, String headerName) {
if (requestResult == null)
throw new IllegalArgumentException("No request result provided");
JSONObject headers = new JSONObject(requestResult.get_ResponseHeaders());
if (headers.has(headerName))
return headers.getJSONArray(headerName).getString(0);
return null;
}
public static List<Cookie> getResponseCookiesFromRequestResult(IContext context, RequestResult requestResult) throws MalformedURLException {
if (requestResult == null)
throw new IllegalArgumentException("No request result provided");
List<Cookie> res = new ArrayList<Cookie>();
JSONObject headers = new JSONObject(requestResult.get_ResponseHeaders());
URL requestUrl = new URL(requestResult.getRequestUrl());
CookieSpec spec = CookiePolicy.getDefaultSpec();
if (headers.has("Set-Cookie")) {
JSONArray cookies = headers.getJSONArray("Set-Cookie");
for(int i = 0; i < cookies.length(); i++) {
try {
org.apache.commons.httpclient.Cookie[] innercookies = spec.parse(requestUrl.getHost(), requestUrl.getPort(), requestUrl.getPath(), "https".equals(requestUrl.getProtocol()), cookies.getString(i));
for(org.apache.commons.httpclient.Cookie innercookie : innercookies) {
Cookie cookie = new Cookie(context);
cookie.setName(innercookie.getName());
cookie.setValue(innercookie.getValue());
cookie.setDomain(innercookie.getDomain());
cookie.setPath(innercookie.getPath());
cookie.setMaxAgeSeconds(innercookie.getExpiryDate() == null ? -1 : Math.round((innercookie.getExpiryDate().getTime() - System.currentTimeMillis()) / 1000L));
res.add(cookie);
}
} catch (Exception e) {
RestServices.LOGCONSUME.warn("Failed to parse cookie: " + e.getMessage(), e);
}
}
}
return res;
}
public static RequestResult getLastConsumeError(IContext context) {
HttpResponseData res = lastConsumeError.get();
if (res == null)
return null;
lastConsumeError.set(null);
return res.asRequestResult(context);
}
}