// *************************************************************************************************************************** | |
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * | |
// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * | |
// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * | |
// * with the License. You may obtain a copy of the License at * | |
// * * | |
// * http://www.apache.org/licenses/LICENSE-2.0 * | |
// * * | |
// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * | |
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * | |
// * specific language governing permissions and limitations under the License. * | |
// *************************************************************************************************************************** | |
package org.apache.juneau.rest.client; | |
import org.apache.juneau.parser.*; | |
import org.apache.juneau.rest.client.assertion.*; | |
import static org.apache.juneau.httppart.HttpPartType.*; | |
import java.lang.reflect.*; | |
import java.text.*; | |
import java.util.*; | |
import java.util.logging.*; | |
import org.apache.http.*; | |
import org.apache.http.message.*; | |
import org.apache.http.params.*; | |
import org.apache.http.util.*; | |
import org.apache.juneau.*; | |
import org.apache.juneau.assertions.*; | |
import org.apache.juneau.common.internal.*; | |
import org.apache.juneau.http.header.*; | |
import org.apache.juneau.httppart.*; | |
import org.apache.juneau.httppart.bean.*; | |
import org.apache.juneau.internal.*; | |
/** | |
* Represents a response from a remote REST resource. | |
* | |
* <p> | |
* Instances of this class are created by calling the {@link RestRequest#run()} method. | |
* | |
* <h5 class='section'>See Also:</h5><ul> | |
* <li class='link'><a class="doclink" href="../../../../../index.html#juneau-rest-client">juneau-rest-client</a> | |
* </ul> | |
*/ | |
@FluentSetters | |
public class RestResponse implements HttpResponse { | |
private final RestClient client; | |
private final RestRequest request; | |
private final HttpResponse response; | |
private final Parser parser; | |
private ResponseContent responseContent; | |
private boolean isClosed; | |
private HeaderList headers; | |
private Map<HttpPartParser,HttpPartParserSession> partParserSessions = new IdentityHashMap<>(); | |
private HttpPartParserSession partParserSession; | |
/** | |
* Constructor. | |
* | |
* @param client The RestClient that created this response. | |
* @param request The REST request. | |
* @param response The HTTP response. Can be <jk>null</jk>. | |
* @param parser The overridden parser passed into {@link RestRequest#parser(Parser)}. | |
*/ | |
protected RestResponse(RestClient client, RestRequest request, HttpResponse response, Parser parser) { | |
this.client = client; | |
this.request = request; | |
this.parser = parser; | |
this.response = response == null ? new BasicHttpResponse(null, 0, null) : response; | |
this.responseContent = new ResponseContent(client, request, this, parser); | |
this.headers = HeaderList.of(this.response.getAllHeaders()); | |
} | |
/** | |
* Returns the request object that created this response object. | |
* | |
* @return The request object that created this response object. | |
*/ | |
public RestRequest getRequest() { | |
return request; | |
} | |
//------------------------------------------------------------------------------------------------------------------ | |
// Setters | |
//------------------------------------------------------------------------------------------------------------------ | |
/** | |
* Consumes the response body. | |
* | |
* <p> | |
* This is equivalent to closing the input stream. | |
* | |
* @return This object. | |
* @throws RestCallException If one of the {@link RestCallInterceptor RestCallInterceptors} threw an exception. | |
*/ | |
@FluentSetter | |
public RestResponse consume() throws RestCallException { | |
close(); | |
return this; | |
} | |
//------------------------------------------------------------------------------------------------------------------ | |
// Status line | |
//------------------------------------------------------------------------------------------------------------------ | |
/** | |
* Returns the status code of the response. | |
* | |
* Shortcut for calling <code>getStatusLine().getStatusCode()</code>. | |
* | |
* @return The status code of the response. | |
*/ | |
public int getStatusCode() { | |
return getStatusLine().getStatusCode(); | |
} | |
/** | |
* Returns the status line reason phrase of the response. | |
* | |
* Shortcut for calling <code>getStatusLine().getReasonPhrase()</code>. | |
* | |
* @return The status line reason phrase of the response. | |
*/ | |
public String getReasonPhrase() { | |
return getStatusLine().getReasonPhrase(); | |
} | |
//------------------------------------------------------------------------------------------------------------------ | |
// Status line assertions | |
//------------------------------------------------------------------------------------------------------------------ | |
/** | |
* Provides the ability to perform fluent-style assertions on the response {@link StatusLine} object. | |
* | |
* <h5 class='section'>Examples:</h5> | |
* <p class='bjava'> | |
* MyBean <jv>bean</jv> = <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .run() | |
* .assertStatus().asCode().is(200) | |
* .getContent().as(MyBean.<jk>class</jk>); | |
* </p> | |
* | |
* @return A new fluent assertion object. | |
*/ | |
public FluentResponseStatusLineAssertion<RestResponse> assertStatus() { | |
return new FluentResponseStatusLineAssertion<>(getStatusLine(), this); | |
} | |
/** | |
* Provides the ability to perform fluent-style assertions on the response status code. | |
* | |
* <h5 class='section'>Examples:</h5> | |
* <p class='bjava'> | |
* MyBean <jv>bean</jv> = <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .run() | |
* .assertStatus(200) | |
* .getContent().as(MyBean.<jk>class</jk>); | |
* </p> | |
* | |
* @param value The value to assert. | |
* @return A new fluent assertion object. | |
*/ | |
public RestResponse assertStatus(int value) { | |
assertStatus().asCode().is(value); | |
return this; | |
} | |
//------------------------------------------------------------------------------------------------------------------ | |
// Headers | |
//------------------------------------------------------------------------------------------------------------------ | |
/** | |
* Shortcut for calling <code>getHeader(name).asString()</code>. | |
* | |
* @param name The header name. | |
* @return The header value, never <jk>null</jk> | |
*/ | |
public Optional<String> getStringHeader(String name) { | |
return getHeader(name).asString(); | |
} | |
/** | |
* Shortcut for retrieving the response charset from the <l>Content-Type</l> header. | |
* | |
* @return The response charset. | |
* @throws RestCallException If REST call failed. | |
*/ | |
public String getCharacterEncoding() throws RestCallException { | |
Optional<ContentType> ct = getContentType(); | |
String s = null; | |
if (ct.isPresent()) | |
s = getContentType().get().getParameter("charset"); | |
return StringUtils.isEmpty(s) ? "utf-8" : s; | |
} | |
/** | |
* Shortcut for retrieving the response content type from the <l>Content-Type</l> header. | |
* | |
* <p> | |
* This is equivalent to calling <c>getHeader(<js>"Content-Type"</js>).as(ContentType.<jk>class</jk>)</c>. | |
* | |
* @return The response charset. | |
* @throws RestCallException If REST call failed. | |
*/ | |
public Optional<ContentType> getContentType() throws RestCallException { | |
return getHeader("Content-Type").as(ContentType.class); | |
} | |
/** | |
* Provides the ability to perform fluent-style assertions on the response character encoding. | |
* | |
* <h5 class='section'>Examples:</h5> | |
* <p class='bjava'> | |
* <jc>// Validates that the response content charset is UTF-8.</jc> | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .run() | |
* .assertCharset().is(<js>"utf-8"</js>); | |
* </p> | |
* | |
* @return A new fluent assertion object. | |
* @throws RestCallException If REST call failed. | |
*/ | |
public FluentStringAssertion<RestResponse> assertCharset() throws RestCallException { | |
return new FluentStringAssertion<>(getCharacterEncoding(), this); | |
} | |
/** | |
* Provides the ability to perform fluent-style assertions on a response header. | |
* | |
* <h5 class='section'>Examples:</h5> | |
* <p class='bjava'> | |
* <jc>// Validates the content type header is provided.</jc> | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .run() | |
* .assertHeader(<js>"Content-Type"</js>).exists(); | |
* | |
* <jc>// Validates the content type is JSON.</jc> | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .run() | |
* .assertHeader(<js>"Content-Type"</js>).is(<js>"application/json"</js>); | |
* | |
* <jc>// Validates the content type is JSON using test predicate.</jc> | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .run() | |
* .assertHeader(<js>"Content-Type"</js>).is(<jv>x</jv> -> <jv>x</jv>.equals(<js>"application/json"</js>)); | |
* | |
* <jc>// Validates the content type is JSON by just checking for substring.</jc> | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .run() | |
* .assertHeader(<js>"Content-Type"</js>).contains(<js>"json"</js>); | |
* | |
* <jc>// Validates the content type is JSON using regular expression.</jc> | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .run() | |
* .assertHeader(<js>"Content-Type"</js>).isPattern(<js>".*json.*"</js>); | |
* | |
* <jc>// Validates the content type is JSON using case-insensitive regular expression.</jc> | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .run() | |
* .assertHeader(<js>"Content-Type"</js>).isPattern(<js>".*json.*"</js>, <jsf>CASE_INSENSITIVE</jsf>); | |
* </p> | |
* | |
* <p> | |
* The assertion test returns the original response object allowing you to chain multiple requests like so: | |
* <p class='bjava'> | |
* <jc>// Validates the header and converts it to a bean.</jc> | |
* MediaType <jv>mediaType</jv> = <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .run() | |
* .assertHeader(<js>"Content-Type"</js>).isNotEmpty() | |
* .assertHeader(<js>"Content-Type"</js>).isPattern(<js>".*json.*"</js>) | |
* .getHeader(<js>"Content-Type"</js>).as(MediaType.<jk>class</jk>); | |
* </p> | |
* | |
* @param name The header name. | |
* @return A new fluent assertion object. | |
*/ | |
public FluentResponseHeaderAssertion<RestResponse> assertHeader(String name) { | |
return new FluentResponseHeaderAssertion<>(getHeader(name), this); | |
} | |
//------------------------------------------------------------------------------------------------------------------ | |
// Body | |
//------------------------------------------------------------------------------------------------------------------ | |
/** | |
* Returns the body of the response. | |
* | |
* This method can be called multiple times returning the same response body each time. | |
* | |
* @return The body of the response. | |
*/ | |
public ResponseContent getContent() { | |
return responseContent; | |
} | |
/** | |
* Provides the ability to perform fluent-style assertions on this response body. | |
* | |
* <h5 class='section'>Examples:</h5> | |
* <p class='bjava'> | |
* <jc>// Validates the response body equals the text "OK".</jc> | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .run() | |
* .assertContent().is(<js>"OK"</js>); | |
* | |
* <jc>// Validates the response body contains the text "OK".</jc> | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .run() | |
* .assertContent().isContains(<js>"OK"</js>); | |
* | |
* <jc>// Validates the response body passes a predicate test.</jc> | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .run() | |
* .assertContent().is(<jv>x</jv> -> <jv>x</jv>.contains(<js>"OK"</js>)); | |
* | |
* <jc>// Validates the response body matches a regular expression.</jc> | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .run() | |
* .assertContent().isPattern(<js>".*OK.*"</js>); | |
* | |
* <jc>// Validates the response body matches a regular expression using regex flags.</jc> | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .run() | |
* .assertContent().isPattern(<js>".*OK.*"</js>, <jsf>MULTILINE</jsf> & <jsf>CASE_INSENSITIVE</jsf>); | |
* | |
* <jc>// Validates the response body matches a regular expression in the form of an existing Pattern.</jc> | |
* Pattern <jv>pattern</jv> = Pattern.<jsm>compile</jsm>(<js>".*OK.*"</js>); | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .run() | |
* .assertContent().isPattern(<jv>pattern</jv>); | |
* </p> | |
* | |
* <p> | |
* The assertion test returns the original response object allowing you to chain multiple requests like so: | |
* <p class='bjava'> | |
* <jc>// Validates the response body matches a regular expression.</jc> | |
* MyBean <jv>bean</jv> = <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .run() | |
* .assertContent().isPattern(<js>".*OK.*"</js>); | |
* .assertContent().isNotPattern(<js>".*ERROR.*"</js>) | |
* .getContent().as(MyBean.<jk>class</jk>); | |
* </p> | |
* | |
* <h5 class='section'>Notes:</h5><ul> | |
* <li class='note'> | |
* If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed. | |
* <li class='note'> | |
* When using this method, the body is automatically cached by calling the {@link ResponseContent#cache()}. | |
* <li class='note'> | |
* The input stream is automatically closed after this call. | |
* </ul> | |
* | |
* @return A new fluent assertion object. | |
*/ | |
public FluentResponseBodyAssertion<RestResponse> assertContent() { | |
return new FluentResponseBodyAssertion<>(responseContent, this); | |
} | |
/** | |
* Provides the ability to perform fluent-style assertions on this response body. | |
* | |
* <p> | |
* A shortcut for calling <c>assertContent().is(<jv>value</jv>)</c>. | |
* | |
* <h5 class='section'>Examples:</h5> | |
* <p class='bjava'> | |
* <jc>// Validates the response body equals the text "OK".</jc> | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .run() | |
* .assertContent(<js>"OK"</js>); | |
* </p> | |
* | |
* @param value The value to assert. | |
* @return This object. | |
*/ | |
public RestResponse assertContent(String value) { | |
assertContent().is(value); | |
return this; | |
} | |
/** | |
* Provides the ability to perform fluent-style assertions on this response body. | |
* | |
* <p> | |
* A shortcut for calling <c>assertContent().asString().isMatches(<jv>value</jv>)</c>. | |
* | |
* @see FluentStringAssertion#isMatches(String) | |
* @param value The value to assert. | |
* @return This object. | |
*/ | |
public RestResponse assertContentMatches(String value) { | |
assertContent().asString().isMatches(value); | |
return this; | |
} | |
/** | |
* Caches the response body so that it can be read as a stream multiple times. | |
* | |
* This is equivalent to calling the following: | |
* <p class='bjava'> | |
* getContent().cache(); | |
* </p> | |
* | |
* @return The body of the response. | |
*/ | |
@FluentSetter | |
public RestResponse cacheContent() { | |
responseContent.cache(); | |
return this; | |
} | |
@SuppressWarnings("unchecked") | |
<T> T as(ResponseBeanMeta rbm) { | |
Class<T> c = (Class<T>)rbm.getClassMeta().getInnerClass(); | |
final RestClient rc = this.client; | |
return (T)Proxy.newProxyInstance( | |
c.getClassLoader(), | |
new Class[] { c }, | |
new InvocationHandler() { | |
@Override /* InvocationHandler */ | |
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { | |
ResponseBeanPropertyMeta pm = rbm.getProperty(method.getName()); | |
HttpPartParserSession pp = getPartParserSession(pm.getParser().orElse(rc.getPartParser())); | |
HttpPartSchema schema = pm.getSchema(); | |
HttpPartType pt = pm.getPartType(); | |
String name = pm.getPartName().orElse(null); | |
ClassMeta<?> type = rc.getBeanContext().getClassMeta(method.getGenericReturnType()); | |
if (pt == RESPONSE_HEADER) | |
return getHeader(name).parser(pp).schema(schema).as(type).orElse(null); | |
if (pt == RESPONSE_STATUS) | |
return getStatusCode(); | |
return getContent().schema(schema).as(type); | |
} | |
}); | |
} | |
/** | |
* Logs a message. | |
* | |
* @param level The log level. | |
* @param t The throwable cause. | |
* @param msg The message with {@link MessageFormat}-style arguments. | |
* @param args The arguments. | |
* @return This object. | |
*/ | |
public RestResponse log(Level level, Throwable t, String msg, Object...args) { | |
client.log(level, t, msg, args); | |
return this; | |
} | |
/** | |
* Logs a message. | |
* | |
* @param level The log level. | |
* @param msg The message with {@link MessageFormat}-style arguments. | |
* @param args The arguments. | |
* @return This object. | |
*/ | |
public RestResponse log(Level level, String msg, Object...args) { | |
client.log(level, msg, args); | |
return this; | |
} | |
// ----------------------------------------------------------------------------------------------------------------- | |
// HttpResponse pass-through methods. | |
// ----------------------------------------------------------------------------------------------------------------- | |
/** | |
* Obtains the status line of this response. | |
* | |
* The status line can be set using one of the setStatusLine methods, or it can be initialized in a constructor. | |
* | |
* @return The status line, or <jk>null</jk> if not yet set. | |
*/ | |
@Override /* HttpResponse */ | |
public ResponseStatusLine getStatusLine() { | |
return new ResponseStatusLine(this, response.getStatusLine()); | |
} | |
/** | |
* Sets the status line of this response. | |
* | |
* @param statusline The status line of this response | |
*/ | |
@Override /* HttpResponse */ | |
public void setStatusLine(StatusLine statusline) { | |
response.setStatusLine(statusline); | |
} | |
/** | |
* Sets the status line of this response. | |
* | |
* <p> | |
* The reason phrase will be determined based on the current locale. | |
* | |
* @param ver The HTTP version. | |
* @param code The status code. | |
*/ | |
@Override /* HttpResponse */ | |
public void setStatusLine(ProtocolVersion ver, int code) { | |
response.setStatusLine(ver, code); | |
} | |
/** | |
* Sets the status line of this response with a reason phrase. | |
* | |
* @param ver The HTTP version. | |
* @param code The status code. | |
* @param reason The reason phrase, or <jk>null</jk> to omit. | |
*/ | |
@Override /* HttpResponse */ | |
public void setStatusLine(ProtocolVersion ver, int code, String reason) { | |
response.setStatusLine(ver, code, reason); | |
} | |
/** | |
* Updates the status line of this response with a new status code. | |
* | |
* @param code The HTTP status code. | |
* @throws IllegalStateException If the status line has not be set. | |
*/ | |
@Override /* HttpResponse */ | |
public void setStatusCode(int code) { | |
response.setStatusCode(code); | |
} | |
/** | |
* Updates the status line of this response with a new reason phrase. | |
* | |
* @param reason The new reason phrase as a single-line string, or <jk>null</jk> to unset the reason phrase. | |
* @throws IllegalStateException If the status line has not be set. | |
*/ | |
@Override /* HttpResponse */ | |
public void setReasonPhrase(String reason) { | |
response.setReasonPhrase(reason); | |
} | |
/** | |
* Obtains the message entity of this response. | |
* | |
* <p> | |
* The entity is provided by calling setEntity. | |
* | |
* <h5 class='section'>Notes:</h5><ul> | |
* <li class='note'>Unlike the {@link HttpResponse#getEntity()} method, this method never returns a <jk>null</jk> response. | |
* Instead, <c>getContent().isPresent()</c> can be used to determine whether the response has a body. | |
* </ul> | |
* | |
* @return The response entity. Never <jk>null</jk>. | |
*/ | |
@Override /* HttpResponse */ | |
public ResponseContent getEntity() { | |
return responseContent; | |
} | |
/** | |
* Associates a response entity with this response. | |
* | |
* <h5 class='section'>Notes:</h5><ul> | |
* <li class='note'>If an entity has already been set for this response and it depends on an input stream | |
* ({@link HttpEntity#isStreaming()} returns <jk>true</jk>), it must be fully consumed in order to ensure | |
* release of resources. | |
* </ul> | |
* | |
* @param entity The entity to associate with this response, or <jk>null</jk> to unset. | |
*/ | |
@Override /* HttpResponse */ | |
public void setEntity(HttpEntity entity) { | |
response.setEntity(entity); | |
this.responseContent = new ResponseContent(client, request, this, parser); | |
} | |
/** | |
* Obtains the locale of this response. | |
* | |
* The locale is used to determine the reason phrase for the status code. | |
* It can be changed using {@link #setLocale(Locale)}. | |
* | |
* @return The locale of this response, never <jk>null</jk>. | |
*/ | |
@Override /* HttpResponse */ | |
public Locale getLocale() { | |
return response.getLocale(); | |
} | |
/** | |
* Changes the locale of this response. | |
* | |
* @param loc The new locale. | |
*/ | |
@Override /* HttpResponse */ | |
public void setLocale(Locale loc) { | |
response.setLocale(loc); | |
} | |
/** | |
* Returns the protocol version this message is compatible with. | |
* | |
* @return The protocol version this message is compatible with. | |
*/ | |
@Override /* HttpMessage */ | |
public ProtocolVersion getProtocolVersion() { | |
return response.getProtocolVersion(); | |
} | |
/** | |
* Checks if a certain header is present in this message. | |
* | |
* <p> | |
* Header values are ignored. | |
* | |
* @param name The header name to check for. | |
* @return <jk>true</jk> if at least one header with this name is present. | |
*/ | |
@Override /* HttpMessage */ | |
public boolean containsHeader(String name) { | |
return response.containsHeader(name); | |
} | |
/** | |
* Returns all the headers with a specified name of this message. | |
* | |
* Header values are ignored. | |
* <br>Headers are ordered in the sequence they were sent over a connection. | |
* | |
* @param name The name of the headers to return. | |
* @return All the headers with a specified name of this message. | |
*/ | |
@Override /* HttpMessage */ | |
public ResponseHeader[] getHeaders(String name) { | |
return headers.stream(name).map(x -> new ResponseHeader(name, request, this, x).parser(getPartParserSession())).toArray(ResponseHeader[]::new); | |
} | |
/** | |
* Returns the first header with a specified name of this message. | |
* | |
* <p> | |
* If there is more than one matching header in the message the first element of {@link #getHeaders(String)} is returned. | |
* <p> | |
* This method always returns a value so that you can perform assertions on the result. | |
* | |
* @param name The name of the header to return. | |
* @return The header, never <jk>null</jk>. | |
*/ | |
@Override /* HttpMessage */ | |
public ResponseHeader getFirstHeader(String name) { | |
return new ResponseHeader(name, request, this, headers.getFirst(name).orElse(null)).parser(getPartParserSession()); | |
} | |
/** | |
* Returns the last header with a specified name of this message. | |
* | |
* <p> | |
* If there is more than one matching header in the message the last element of {@link #getHeaders(String)} is returned. | |
* <p> | |
* This method always returns a value so that you can perform assertions on the result. | |
* | |
* @param name The name of the header to return. | |
* @return The header, never <jk>null</jk>. | |
*/ | |
@Override /* HttpMessage */ | |
public ResponseHeader getLastHeader(String name) { | |
return new ResponseHeader(name, request, this, headers.getLast(name).orElse(null)).parser(getPartParserSession()); | |
} | |
/** | |
* Returns the response header with the specified name. | |
* | |
* <p> | |
* If more that one header with the given name exists the values will be combined with <js>", "</js> as per <a href='https://tools.ietf.org/html/rfc2616#section-4.2'>RFC 2616 Section 4.2</a>. | |
* | |
* @param name The name of the header to return. | |
* @return The header, never <jk>null</jk>. | |
*/ | |
public ResponseHeader getHeader(String name) { | |
return new ResponseHeader(name, request, this, headers.get(name).orElse(null)).parser(getPartParserSession()); | |
} | |
/** | |
* Returns all the headers of this message. | |
* | |
* Headers are ordered in the sequence they were sent over a connection. | |
* | |
* @return All the headers of this message. | |
*/ | |
@Override /* HttpMessage */ | |
public ResponseHeader[] getAllHeaders() { | |
return headers.stream().map(x -> new ResponseHeader(x.getName(), request, this, x).parser(getPartParserSession())).toArray(ResponseHeader[]::new); | |
} | |
/** | |
* Adds a header to this message. | |
* | |
* The header will be appended to the end of the list. | |
* | |
* @param header The header to append. | |
*/ | |
@Override /* HttpMessage */ | |
public void addHeader(Header header) { | |
headers.append(header); | |
} | |
/** | |
* Adds a header to this message. | |
* | |
* The header will be appended to the end of the list. | |
* | |
* @param name The name of the header. | |
* @param value The value of the header. | |
*/ | |
@Override /* HttpMessage */ | |
public void addHeader(String name, String value) { | |
headers.append(name, value); | |
} | |
/** | |
* Overwrites the first header with the same name. | |
* | |
* The new header will be appended to the end of the list, if no header with the given name can be found. | |
* | |
* @param header The header to set. | |
*/ | |
@Override /* HttpMessage */ | |
public void setHeader(Header header) { | |
headers.set(header); | |
} | |
/** | |
* Overwrites the first header with the same name. | |
* | |
* The new header will be appended to the end of the list, if no header with the given name can be found. | |
* | |
* @param name The name of the header. | |
* @param value The value of the header. | |
*/ | |
@Override /* HttpMessage */ | |
public void setHeader(String name, String value) { | |
headers.set(name, value); | |
} | |
/** | |
* Overwrites all the headers in the message. | |
* | |
* @param headers The array of headers to set. | |
*/ | |
@Override /* HttpMessage */ | |
public void setHeaders(Header[] headers) { | |
this.headers = HeaderList.of(headers); | |
} | |
/** | |
* Removes a header from this message. | |
* | |
* @param header The header to remove. | |
*/ | |
@Override /* HttpMessage */ | |
public void removeHeader(Header header) { | |
headers.remove(header); | |
} | |
/** | |
* Removes all headers with a certain name from this message. | |
* | |
* @param name The name of the headers to remove. | |
*/ | |
@Override /* HttpMessage */ | |
public void removeHeaders(String name) { | |
headers.remove(name); | |
} | |
/** | |
* Returns an iterator of all the headers. | |
* | |
* @return {@link Iterator} that returns {@link Header} objects in the sequence they are sent over a connection. | |
*/ | |
@Override /* HttpMessage */ | |
public HeaderIterator headerIterator() { | |
return headers.headerIterator(); | |
} | |
/** | |
* Returns an iterator of the headers with a given name. | |
* | |
* @param name The name of the headers over which to iterate, or <jk>null</jk> for all headers. | |
* @return {@link Iterator} that returns {@link Header} objects with the argument name in the sequence they are sent over a connection. | |
*/ | |
@Override /* HttpMessage */ | |
public HeaderIterator headerIterator(String name) { | |
return headers.headerIterator(name); | |
} | |
/** | |
* Returns the parameters effective for this message as set by {@link #setParams(HttpParams)}. | |
* | |
* @return The parameters effective for this message as set by {@link #setParams(HttpParams)}. | |
* @deprecated Use configuration classes provided <jk>org.apache.http.config</jk> and <jk>org.apache.http.client.config</jk>. | |
*/ | |
@Override /* HttpMessage */ | |
@Deprecated | |
public HttpParams getParams() { | |
return response.getParams(); | |
} | |
/** | |
* Provides parameters to be used for the processing of this message. | |
* | |
* @param params The parameters. | |
* @deprecated Use configuration classes provided <jk>org.apache.http.config</jk> and <jk>org.apache.http.client.config</jk>. | |
*/ | |
@Override /* HttpMessage */ | |
@Deprecated | |
public void setParams(HttpParams params) { | |
response.setParams(params); | |
} | |
void close() throws RestCallException { | |
if (isClosed) | |
return; | |
isClosed = true; | |
EntityUtils.consumeQuietly(response.getEntity()); | |
if (!request.isLoggingSuppressed() && (request.isDebug() || client.logRequestsPredicate.test(request, this))) { | |
if (client.logRequests == DetailLevel.SIMPLE) { | |
client.log(client.logRequestsLevel, "HTTP {0} {1}, {2}", request.getMethod(), request.getURI(), this.getStatusLine()); | |
} else if (request.isDebug() || client.logRequests == DetailLevel.FULL) { | |
String output = getContent().asString(); | |
StringBuilder sb = new StringBuilder(); | |
sb.append("\n=== HTTP Call (outgoing) ======================================================"); | |
sb.append("\n=== REQUEST ===\n"); | |
sb.append(request.getMethod()).append(" ").append(request.getURI()); | |
sb.append("\n---request headers---"); | |
request.getHeaders().forEach(x -> sb.append("\n\t").append(x)); | |
if (request.hasHttpEntity()) { | |
sb.append("\n---request entity---"); | |
HttpEntity e = request.getHttpEntity(); | |
if (e.getContentType() != null) | |
sb.append("\n\t").append(e.getContentType()); | |
if (e.isRepeatable()) { | |
try { | |
sb.append("\n---request content---\n").append(EntityUtils.toString(e)); | |
} catch (Exception ex) { | |
sb.append("\n---request content exception---\n").append(ex.getMessage()); | |
} | |
} | |
} | |
sb.append("\n=== RESPONSE ===\n").append(getStatusLine()); | |
sb.append("\n---response headers---"); | |
for (Header h : getAllHeaders()) | |
sb.append("\n\t").append(h); | |
sb.append("\n---response content---\n").append(output); | |
sb.append("\n=== END ======================================================================="); | |
client.log(client.logRequestsLevel, sb.toString()); | |
} | |
} | |
for (RestCallInterceptor r : request.interceptors) { | |
try { | |
r.onClose(request, this); | |
} catch (RuntimeException | RestCallException e) { | |
throw e; | |
} catch (Exception e) { | |
throw new RestCallException(this, e, "Interceptor throw exception on close"); | |
} | |
} | |
client.onCallClose(request, this); | |
} | |
//------------------------------------------------------------------------------------------------------------------ | |
// Other methods | |
//------------------------------------------------------------------------------------------------------------------ | |
/** | |
* Creates a session of the specified part parser. | |
* | |
* @param parser The parser to create a session for. | |
* @return A session of the specified parser. | |
*/ | |
protected HttpPartParserSession getPartParserSession(HttpPartParser parser) { | |
HttpPartParserSession s = partParserSessions.get(parser); | |
if (s == null) { | |
s = parser.getPartSession(); | |
partParserSessions.put(parser, s); | |
} | |
return s; | |
} | |
/** | |
* Creates a session of the client-default parat parser. | |
* | |
* @return A session of the specified parser. | |
*/ | |
protected HttpPartParserSession getPartParserSession() { | |
if (partParserSession == null) | |
partParserSession = client.getPartParser().getPartSession(); | |
return partParserSession; | |
} | |
HttpResponse asHttpResponse() { | |
return response; | |
} | |
//----------------------------------------------------------------------------------------------------------------- | |
// Fluent setters | |
//----------------------------------------------------------------------------------------------------------------- | |
// <FluentSetters> | |
// </FluentSetters> | |
} |