讀書《HttpClient 教程》html
前言java
儘管java.net包提供了基本經過HTTP訪問資源的功能,但它沒有提供全面的靈活性和其它不少應用程序須要的功能。HttpClient就是尋求彌補這項空白的組件,經過提供一個有效的,保持更新的,功能豐富的軟件包來實現客戶端最新的HTTP標準和建議。web
1. HttpClient的範圍apache
基於HttpCore[http://hc.apache.org/httpcomponents-core/index.html]的客戶端HTTP運輸實現庫json
基於經典(阻塞)I/O後端
內容無關設計模式
2. 什麼是HttpClient不能作的瀏覽器
HttpClient不是一個瀏覽器。緩存
第一章 基礎安全
1.1 執行請求
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpget = new HttpGet("http://localhost/");
CloseableHttpResponse response = httpclient.execute(httpget);
try {
<...>
} finally {
response.close();
}
1.1.1 HTTP請求
HttpClient支持全部定義在HTTP/1.1版本中的HTTP方法:GET,HEAD,POST,PUT,DELETE,TRACE和OPTIONS。對於每一個方法類型都有一個特殊的類:HttpGet,HttpHead,HttpPost,HttpPut,HttpDelete,HttpTrace和HttpOptions。
請求的URI是統一資源定位符,它標識了應用於哪一個請求之上的資源。HTTP請求URI包含一個協議模式,主機名稱,可選的端口,資源路徑,可選的查詢和可選的片斷。
URI uri = new URIBuilder()
.setScheme("http")
.setHost("www.google.com")
.setPath("/search")
.setParameter("q", "httpclient")
.setParameter("btnG", "Google Search")
.setParameter("aq", "f")
.setParameter("oq", "")
.build();
HttpGet httpget = new HttpGet(uri);
System.out.println(httpget.getURI());
1.1.2 HTTP響應
HTTP響應是由服務器在接收和解釋請求報文以後返回發送給客戶端的報文。響應報文的第一行包含了協議版本,以後是數字狀態碼和相關聯的文本段。
HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
System.out.println(response.getProtocolVersion());
System.out.println(response.getStatusLine().getStatusCode());
System.out.println(response.getStatusLine().getReasonPhrase());
System.out.println(response.getStatusLine().toString());
1.1.3 處理報文頭部
HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
response.addHeader("Set-Cookie", "c1=a; path=/; domain=localhost");
response.addHeader("Set-Cookie", "c2=b; path=\"/\", c3=c; domain=\"localhost\"");
Header h1 = response.getFirstHeader("Set-Cookie");
System.out.println(h1);
Header h2 = response.getLastHeader("Set-Cookie");
System.out.println(h2);
Header[] hs = response.getHeaders("Set-Cookie");
System.out.println(hs.length);
HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
response.addHeader("Set-Cookie", "c1=a; path=/; domain=localhost");
response.addHeader("Set-Cookie", "c2=b; path=\"/\", c3=c; domain=\"localhost\"");
HeaderIterator it = response.headerIterator("Set-Cookie");
while (it.hasNext()) {
System.out.println(it.next());
}
HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
response.addHeader("Set-Cookie", "c1=a; path=/; domain=localhost");
response.addHeader("Set-Cookie", "c2=b; path=\"/\", c3=c; domain=\"localhost\"");
HeaderElementIterator it = new BasicHeaderElementIterator(
response.headerIterator("Set-Cookie"));
while (it.hasNext()) {
HeaderElement elem = it.nextElement();
System.out.println(elem.getName() + " = " + elem.getValue());
NameValuePair[] params = elem.getParameters();
for (int i = 0; i < params.length; i++) {
System.out.println(" " + params[i]);
}
}
1.1.4 HTTP實體
HTTP報文能夠攜帶和請求或響應相關的內容實體。
streamed流式
self-contained自我包含式
wrapping包裝式
1.1.4.1. 重複的實體
一個實體能夠是可重複的,這意味着它的內容能夠被不止一次讀取。這是惟一可能與自包含的實體(如ByteArrayEntity或StringEntity)
1.1.4.2. 使用HTTP實體
由於一個實體能夠表示二進制和字符的內容,它具備用於字符編碼(支持後者,即字符內容)的支持。
該實體被執行與封閉內容或當請求成功並響應體用於將結果發送回客戶機的請求時建立。
讀取從該實體的內容,人們能夠檢索經由輸入流HttpEntity#的getContent()方法,後者返回一個的java.io.InputStream,或者一個能夠提供一個輸出流的HttpEntity#的writeTo(OutputStream的)方法中,一旦全部的內容已被寫入給定流的返回。
當實體已經接收與傳入消息,該方法HttpEntity#的getContentType()和HttpEntity#getContentLength()方法可用於讀取通用的元數據,如內容類型和內容長度報頭(若是可用)。因爲Content-Type頭能夠包含文本的MIME類型,如文本/純或text / html的字符編碼,該HttpEntity#getContentEncoding()方法來讀取這些信息。若是頭不可用,長度將返回-1,併爲NULL的內容類型。若是Content-Type頭可用,一個標題對象將被退回。
當要傳出消息建立實體,這個元數據具備由實體的建立者提供。
StringEntity myEntity = new StringEntity("important message",
ContentType.create("text/plain", "UTF-8"));
System.out.println(myEntity.getContentType());
System.out.println(myEntity.getContentLength());
System.out.println(EntityUtils.toString(myEntity));
System.out.println(EntityUtils.toByteArray(myEntity).length);
1.1.5 確保低級別資源釋放
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpget = new HttpGet("http://localhost/");
CloseableHttpResponse response = httpclient.execute(httpget);
try {
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream instream = entity.getContent();
try {
// do something useful
} finally {
instream.close();
}
}
} finally {
response.close();
}
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpget = new HttpGet("http://localhost/");
CloseableHttpResponse response = httpclient.execute(httpget);
try {
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream instream = entity.getContent();
int byteOne = instream.read();
int byteTwo = instream.read();
// Do not need the rest
}
} finally {
response.close();
}
1.1.6 消耗實體內容
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpget = new HttpGet("http://localhost/");
CloseableHttpResponse response = httpclient.execute(httpget);
try {
HttpEntity entity = response.getEntity();
if (entity != null) {
long len = entity.getContentLength();
if (len != -1 && len < 2048) {
System.out.println(EntityUtils.toString(entity));
} else {
// Stream content out
}
}
} finally {
response.close();
}
1.1.7 生成實體內容
File file = new File("somefile.txt");
FileEntity entity = new FileEntity(file,
ContentType.create("text/plain", "UTF-8"));
HttpPost httppost = new HttpPost("http://localhost/action.do");
httppost.setEntity(entity);
1.1.7.1. HTML表單
許多應用須要模擬提交的HTML形式,例如,爲了在登陸到Web應用程序或提交的輸入數據的處理。HttpClient的提供實體類UrlEncodedFormEntity以促進該過程。
List<NameValuePair> formparams = new ArrayList<NameValuePair>();
formparams.add(new BasicNameValuePair("param1", "value1"));
formparams.add(new BasicNameValuePair("param2", "value2"));
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, Consts.UTF_8);
HttpPost httppost = new HttpPost("http://localhost/handler.do");
httppost.setEntity(entity);
該UrlEncodedFormEntity實例將使用所謂的URL編碼編碼參數,併產生如下內容:
param1=value1¶m2=value2
1.1.7.2. 內容分塊
通常建議讓HttpClient的選擇是基於最合適的傳輸編碼被傳送的HTTP消息的屬性,這是可能的,可是,爲了通知HttpClient的那塊編碼是優選由HttpEntity#setChunked()設置爲真。請注意,HttpClient的將使用該標誌做爲提示而已。使用HTTP協議的版本時,該值將被忽略的不支持塊編碼,如HTTP/ 1.0。
StringEntity entity = new StringEntity("important message",
ContentType.create("plain/text", Consts.UTF_8));
entity.setChunked(true);
HttpPost httppost = new HttpPost("http://localhost/acrtion.do");
httppost.setEntity(entity);
1.1.8 響應控制器
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpget = new HttpGet("http://localhost/json");
ResponseHandler<MyJsonObject> rh = new ResponseHandler<MyJsonObject>() {
@Override
public JsonObject handleResponse(
final HttpResponse response) throws IOException {
StatusLine statusLine = response.getStatusLine();
HttpEntity entity = response.getEntity();
if (statusLine.getStatusCode() >= 300) {
throw new HttpResponseException(
statusLine.getStatusCode(),
statusLine.getReasonPhrase());
}
if (entity == null) {
throw new ClientProtocolException("Response contains no content");
}
Gson gson = new GsonBuilder().create();
ContentType contentType = ContentType.getOrDefault(entity);
Charset charset = contentType.getCharset();
Reader reader = new InputStreamReader(entity.getContent(), charset);
return gson.fromJson(reader, MyJsonObject.class);
}
};
MyJsonObject myjson = client.execute(httpget, rh);
1.2. HttpClient的接口
HttpClient的接口表示爲HTTP請求執行的最本質的合同,它規定了該請求執行處理沒有任何限制或特定細節和葉鏈接管理,狀態管理,驗證和重定向處理高達我的實現的具體細節。這應該使它更容易裝飾與附加功能的接口,如響應內容緩存。
通常HttpClient的實現做爲一個門面一些負責處理的HTTP協議如約持續鏈接永葆持續時間重定向或認證處理或做出決定的某一方面特殊用途的處理程序或策略接口實現。這使得用戶選擇性地取代的那些方面與定製,應用特定的人默認的實現。
ConnectionKeepAliveStrategy keepAliveStrat = new DefaultConnectionKeepAliveStrategy() {
@Override
public long getKeepAliveDuration(
HttpResponse response,
HttpContext context) {
long keepAlive = super.getKeepAliveDuration(response, context);
if (keepAlive == -1) {
// Keep connections alive 5 seconds if a keep-alive value
// has not be explicitly set by the server
keepAlive = 5000;
}
return keepAlive;
}
};
CloseableHttpClient httpclient = HttpClients.custom()
.setKeepAliveStrategy(keepAliveStrat)
.build();
1.2.1. HttpClient的線程安全
HttpClient的實施預計將是線程安全的。建議在這個類的同一個實例是重複使用屢次要求執行。
1.2.2. HttpClient的資源釋放
當一個實例CloseableHttpClient再也不須要和即將走出去的範圍與它相關的鏈接管理器必須經過調用CloseableHttpClient#close()方法關閉。
CloseableHttpClient httpclient = HttpClients.createDefault();
try {
<...>
} finally {
httpclient.close();
}
1.3執行上下文
HttpContext context = <...>
HttpClientContext clientContext = HttpClientContext.adapt(context);
HttpHost target = clientContext.getTargetHost();
HttpRequest request = clientContext.getRequest();
HttpResponse response = clientContext.getResponse();
RequestConfig config = clientContext.getRequestConfig();
CloseableHttpClient httpclient = HttpClients.createDefault();
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(1000)
.setConnectTimeout(1000)
.build();
HttpGet httpget1 = new HttpGet("http://localhost/1");
httpget1.setConfig(requestConfig);
CloseableHttpResponse response1 = httpclient.execute(httpget1, context);
try {
HttpEntity entity1 = response1.getEntity();
} finally {
response1.close();
}
HttpGet httpget2 = new HttpGet("http://localhost/2");
CloseableHttpResponse response2 = httpclient.execute(httpget2, context);
try {
HttpEntity entity2 = response2.getEntity();
} finally {
response2.close();
}
1.4 HTTP協議攔截器
CloseableHttpClient httpclient = HttpClients.custom()
.addInterceptorLast(new HttpRequestInterceptor() {
public void process(
final HttpRequest request,
final HttpContext context) throws HttpException, IOException {
AtomicInteger count = (AtomicInteger) context.getAttribute("count");
request.addHeader("Count", Integer.toString(count.getAndIncrement()));
}
})
.build();
AtomicInteger count = new AtomicInteger(1);
HttpClientContext localContext = HttpClientContext.create();
localContext.setAttribute("count", count);
HttpGet httpget = new HttpGet("http://localhost/");
for (int i = 0; i < 10; i++) {
CloseableHttpResponse response = httpclient.execute(httpget, localContext);
try {
HttpEntity entity = response.getEntity();
} finally {
response.close();
}
}
1.5 異常處理
HttpClient可以拋出兩種類型的異常:在I/O失敗時,如套接字鏈接超時或被重置的java.io.IOException異常,還有標誌HTTP請求失敗的信號,如違反HTTP協議的HttpException異常。
1.5.1 HTTP運輸安全
1.5.2 冪等的方法
1.5.3 異常自動恢復
1.5.4 請求重試處理
HttpRequestRetryHandler myRetryHandler = new HttpRequestRetryHandler() {
public boolean retryRequest(
IOException exception,
int executionCount,
HttpContext context) {
if (executionCount >= 5) {
// Do not retry if over max retry count
return false;
}
if (exception instanceof InterruptedIOException) {
// Timeout
return false;
}
if (exception instanceof UnknownHostException) {
// Unknown host
return false;
}
if (exception instanceof ConnectTimeoutException) {
// Connection refused
return false;
}
if (exception instanceof SSLException) {
// SSL handshake exception
return false;
}
HttpClientContext clientContext = HttpClientContext.adapt(context);
HttpRequest request = clientContext.getRequest();
boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
if (idempotent) {
// Retry if the request is considered idempotent
return true;
}
return false;
}
};
CloseableHttpClient httpclient = HttpClients.custom()
.setRetryHandler(myRetryHandler)
.build();
1.6. 停止請求
在某些狀況下,HTTP請求執行失敗的預期的時間框架內完成,因爲目標服務器或客戶端上發出太多的併發請求的高負荷。在這種狀況下,可能有必要提早終止該請求並解鎖的執行線程經過調用阻塞在一個I / O操做。HTTP請求由HttpClient的能夠在執行的任何階段停止執行HttpUriRequest#停止()方法,該方法是線程安全,而且能夠從任何被稱爲
跟帖當一個HTTP請求被停止它的執行線程 - 即便當前阻塞在I/ O操做 - 這是保證經過投擲InterruptedIOException時解除封鎖
1.7 處理重定向
HttpClient的自動處理全部類型的重定向,除了那些明確禁止的HTTP規範須要用戶干預。請參閱其餘(狀態代碼303)重定向的POST和PUT請求轉換所要求的HTTP規範GET請求。人們可使用自定義重定向策略,對relaxe由HTTP規範徵收POST方法自動重定向限制。
LaxRedirectStrategy redirectStrategy = new LaxRedirectStrategy();
CloseableHttpClient httpclient = HttpClients.custom()
.setRedirectStrategy(redirectStrategy)
.build();
HttpClient的每每具備重寫請求消息在其執行的過程當中。每默認HTTP/1.0和HTTP/ 1.1通常使用相對URI請求。一樣地,原始請求可能會從位置重定向到另外一屢次。最後解釋絕對HTTP位置可使用原始請求和上下文來創建。本實用方法URIUtils#決心能夠用於構建解釋絕對URI用於產生最終的請求。該方法包括從所述重定向的請求或原始請求的最後一個片斷標識符。
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpClientContext context = HttpClientContext.create();
HttpGet httpget = new HttpGet("http://localhost:8080/");
CloseableHttpResponse response = httpclient.execute(httpget, context);
try {
HttpHost target = context.getTargetHost();
List<URI> redirectLocations = context.getRedirectLocations();
URI location = URIUtils.resolve(httpget.getURI(), target, redirectLocations);
System.out.println("Final HTTP location: " + location.toASCIIString());
// Expected to be an absolute URI
} finally {
response.close();
}
第二章 鏈接管理
2.1持久鏈接
2.2 HTTP鏈接路由
2.2.1 路由計算
2.2.2安全HTTP鏈接
2.3 HTTP鏈接管理器
2.3.1管理鏈接和鏈接管理器
HttpClientContext context = HttpClientContext.create();
HttpClientConnectionManager connMrg = new BasicHttpClientConnectionManager();
HttpRoute route = new HttpRoute(new HttpHost("localhost", 80));
// Request new connection. This can be a long process
ConnectionRequest connRequest = connMrg.requestConnection(route, null);
// Wait for connection up to 10 sec
HttpClientConnection conn = connRequest.get(10, TimeUnit.SECONDS);
try {
// If not open
if (!conn.isOpen()) {
// establish connection based on its route info
connMrg.connect(conn, route, 1000, context);
// and mark it as route complete
connMrg.routeComplete(conn, route, context);
}
// Do useful things with the connection.
} finally {
connMrg.releaseConnection(conn, null, 1, TimeUnit.MINUTES);
}
2.3.2簡單的鏈接管理器
2.3.3鏈接池管理
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
// Increase max total connection to 200
cm.setMaxTotal(200);
// Increase default max connection per route to 20
cm.setDefaultMaxPerRoute(20);
// Increase max connections for localhost:80 to 50
HttpHost localhost = new HttpHost("locahost", 80);
cm.setMaxPerRoute(new HttpRoute(localhost), 50);
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(cm)
.build();
2.3.4 鏈接管理器關閉
CloseableHttpClient httpClient = <...>
httpClient.close();
2. 4 多線程執行請求
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(cm)
.build();
// URIs to perform GETs on
String[] urisToGet = {
"http://www.domain1.com/",
"http://www.domain2.com/",
"http://www.domain3.com/",
"http://www.domain4.com/"
};
// create a thread for each URI
GetThread[] threads = new GetThread[urisToGet.length];
for (int i = 0; i < threads.length; i++) {
HttpGet httpget = new HttpGet(urisToGet[i]);
threads[i] = new GetThread(httpClient, httpget);
}
// start the threads
for (int j = 0; j < threads.length; j++) {
threads[j].start();
}
// join the threads
for (int j = 0; j < threads.length; j++) {
threads[j].join();
}
static class GetThread extends Thread {
private final CloseableHttpClient httpClient;
private final HttpContext context;
private final HttpGet httpget;
public GetThread(CloseableHttpClient httpClient, HttpGet httpget) {
this.httpClient = httpClient;
this.context = HttpClientContext.create();
this.httpget = httpget;
}
@Override
public void run() {
try {
CloseableHttpResponse response = httpClient.execute(
httpget, context);
try {
HttpEntity entity = response.getEntity();
} finally {
response.close();
}
} catch (ClientProtocolException ex) {
// Handle protocol errors
} catch (IOException ex) {
// Handle I/O errors
}
}
}
2.5 鏈接驅逐策略
public static class IdleConnectionMonitorThread extends Thread {
private final HttpClientConnectionManager connMgr;
private volatile boolean shutdown;
public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr) {
super();
this.connMgr = connMgr;
}
@Override
public void run() {
try {
while (!shutdown) {
synchronized (this) {
wait(5000);
// Close expired connections
connMgr.closeExpiredConnections();
// Optionally, close connections
// that have been idle longer than 30 sec
connMgr.closeIdleConnections(30, TimeUnit.SECONDS);
}
}
} catch (InterruptedException ex) {
// terminate
}
}
public void shutdown() {
shutdown = true;
synchronized (this) {
notifyAll();
}
}
}
2.6 鏈接保持活動的策略
ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() {
public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
// Honor 'keep-alive' header
HeaderElementIterator it = new BasicHeaderElementIterator(
response.headerIterator(HTTP.CONN_KEEP_ALIVE));
while (it.hasNext()) {
HeaderElement he = it.nextElement();
String param = he.getName();
String value = he.getValue();
if (value != null && param.equalsIgnoreCase("timeout")) {
try {
return Long.parseLong(value) * 1000;
} catch(NumberFormatException ignore) {
}
}
}
HttpHost target = (HttpHost) context.getAttribute(
HttpClientContext.HTTP_TARGET_HOST);
if ("www.naughty-server.com".equalsIgnoreCase(target.getHostName())) {
// Keep alive for 5 seconds only
return 5 * 1000;
} else {
// otherwise keep alive for 30 seconds
return 30 * 1000;
}
}
};
CloseableHttpClient client = HttpClients.custom()
.setKeepAliveStrategy(myStrategy)
.build();
2.7 套接字工廠
HttpClientContext clientContext = HttpClientContext.create();
PlainConnectionSocketFactory sf = PlainConnectionSocketFactory.getSocketFactory();
Socket socket = sf.createSocket(clientContext);
int timeout = 1000; //ms
HttpHost target = new HttpHost("localhost");
InetSocketAddress remoteAddress = new InetSocketAddress(
InetAddress.getByAddress(new byte[] {127,0,0,1}), 80);
sf.connectSocket(timeout, socket, target, remoteAddress, null, clientContext);
2.7.1 安全套接字分層
2.7.2與鏈接管理器的集成
ConnectionSocketFactory plainsf = <...>
LayeredConnectionSocketFactory sslsf = <...>
Registry<ConnectionSocketFactory> r = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", plainsf)
.register("https", sslsf)
.build();
HttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(r);
HttpClients.custom()
.setConnectionManager(cm)
.build();
2.7.3 SSL/TLS的定製
KeyStore myTrustStore = <...>
SSLContext sslContext = SSLContexts.custom()
.useTLS()
.loadTrustMaterial(myTrustStore)
.build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
2.7.4 主機名驗證
SSLContext sslContext = SSLContexts.createSystemDefault();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslContext,
NoopHostnameVerifier.INSTANCE);
PublicSuffixMatcher publicSuffixMatcher = PublicSuffixMatcherLoader.load(
PublicSuffixMatcher.class.getResource("my-copy-effective_tld_names.dat"));
DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(publicSuffixMatcher);
2.8 HttpClient代理配置
HttpHost proxy = new HttpHost("someproxy", 8080);
DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy);
CloseableHttpClient httpclient = HttpClients.custom()
.setRoutePlanner(routePlanner)
.build();
SystemDefaultRoutePlanner routePlanner = new SystemDefaultRoutePlanner(
ProxySelector.getDefault());
CloseableHttpClient httpclient = HttpClients.custom()
.setRoutePlanner(routePlanner)
.build();
HttpRoutePlanner routePlanner = new HttpRoutePlanner() {
public HttpRoute determineRoute(
HttpHost target,
HttpRequest request,
HttpContext context) throws HttpException {
return new HttpRoute(target, null, new HttpHost("someproxy", 8080),
"https".equalsIgnoreCase(target.getSchemeName()));
}
};
CloseableHttpClient httpclient = HttpClients.custom()
.setRoutePlanner(routePlanner)
.build();
}
}
第三章 HTTP狀態管理
3.1 HTTP cookies
BasicClientCookie cookie = new BasicClientCookie("name", "value");
// Set effective domain and path attributes
cookie.setDomain(".mycompany.com");
cookie.setPath("/");
// Set attributes exactly as sent by the server
cookie.setAttribute(ClientCookie.PATH_ATTR, "/");
cookie.setAttribute(ClientCookie.DOMAIN_ATTR, ".mycompany.com");
3.2 Cookie規範
該CookieSpec接口表示cookie管理規範。該cookie管理規範有望實施:
•解析的Set-Cookie頭的規則。
•解析cookies的驗證規則。
•Cookie頭的格式對於給定的主機,端口和原籍路徑。
HttpClient的附帶幾個CookieSpec實現:
•Standard strict:國家管理政策符合語法和RFC 6265中定義的wellbehaved輪廓的語義,第4節。
•標準:國家管理政策符合由RFC 6265,用於與現有服務器的互操做性第4節定義更寬鬆的輪廓不符合很乖輪廓。
•網景草案(過期):該政策符合由Netscape Communications公佈了原草案規範。應避免使用,除非絕對必要與舊代碼的兼容性。
•RFC 2965(過期):國家管理政策符合由RFC 2965定義的請不要在新的應用程序使用過期的狀態管理規範。
•RFC 2109(過期):國家管理政策符合由RFC 2109定義的請不要在新的應用程序使用過期的狀態管理規範。
•瀏覽器兼容性(過期):該政策致力於密切模仿舊版本的瀏覽器應用程序如Microsoft Internet Explorer和Mozilla Firefox的(MIS)的行爲。
請不要在新應用中使用。
•默認:默認的Cookie政策是拿起一個綜合的政策不管是RFC 2965,RFC 2109或兼容的實現Netscape的草案基礎上,與HTTP響應發送的cookie的屬性(如版本的屬性,如今已通過時)。這一政策將再也不使用,取而代之的標準(RFC 6265標準)實施HttpClient的下一個次要版本的。
•忽略cookies:全部Cookie都將被忽略。
強烈建議使用標準或標準嚴格的政策,在新的應用程序。
過期的規範應被用於與僅遺留系統的兼容性。支持過期的規範將在HttpClient的下一個主要版本中刪除。
3.3 選擇cookie策略
RequestConfig globalConfig = RequestConfig.custom()
.setCookieSpec(CookieSpecs.DEFAULT)
.build();
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultRequestConfig(globalConfig)
.build();
RequestConfig localConfig = RequestConfig.copy(globalConfig)
.setCookieSpec(CookieSpecs.STANDARD_STRICT)
.build();
HttpGet httpGet = new HttpGet("/");
httpGet.setConfig(localConfig);
3.4 定製cookie策略
PublicSuffixMatcher publicSuffixMatcher = PublicSuffixMatcherLoader.getDefault();
Registry<CookieSpecProvider> r = RegistryBuilder.<CookieSpecProvider>create()
.register(CookieSpecs.DEFAULT,
new DefaultCookieSpecProvider(publicSuffixMatcher))
.register(CookieSpecs.STANDARD,
new RFC6265CookieSpecProvider(publicSuffixMatcher))
.register("easy", new EasySpecProvider())
.build();
RequestConfig requestConfig = RequestConfig.custom()
.setCookieSpec("easy")
.build();
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultCookieSpecRegistry(r)
.setDefaultRequestConfig(requestConfig)
.build();
3.5 Cookie持久化
// Create a local instance of cookie store
CookieStore cookieStore = new BasicCookieStore();
// Populate cookies if needed
BasicClientCookie cookie = new BasicClientCookie("name", "value");
cookie.setDomain(".mycompany.com");
cookie.setPath("/");
cookieStore.addCookie(cookie);
// Set the store
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultCookieStore(cookieStore)
.build();
3.6 HTTP狀態管理和執行上下文
CloseableHttpClient httpclient = <...>
Lookup<CookieSpecProvider> cookieSpecReg = <...>
CookieStore cookieStore = <...>
HttpClientContext context = HttpClientContext.create();
context.setCookieSpecRegistry(cookieSpecReg);
context.setCookieStore(cookieStore);
HttpGet httpget = new HttpGet("http://somehost/");
CloseableHttpResponse response1 = httpclient.execute(httpget, context);
<...>
// Cookie origin details
CookieOrigin cookieOrigin = context.getCookieOrigin();
// Cookie spec used
CookieSpec cookieSpec = context.getCookieSpec();
第四章 HTTP認證
4.1 用戶證書
UsernamePasswordCredentials creds = new UsernamePasswordCredentials("user", "pwd");
System.out.println(creds.getUserPrincipal().getName());
System.out.println(creds.getPassword());
NTCredentials creds = new NTCredentials("user", "pwd", "workstation", "domain");
System.out.println(creds.getUserPrincipal().getName());
System.out.println(creds.getPassword());
4.3 憑據提供器
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope("somehost", AuthScope.ANY_PORT),
new UsernamePasswordCredentials("u1", "p1"));
credsProvider.setCredentials(
new AuthScope("somehost", 8080),
new UsernamePasswordCredentials("u2", "p2"));
credsProvider.setCredentials(
new AuthScope("otherhost", 8080, AuthScope.ANY_REALM, "ntlm"),
new UsernamePasswordCredentials("u3", "p3"));
System.out.println(credsProvider.getCredentials(
new AuthScope("somehost", 80, "realm", "basic")));
System.out.println(credsProvider.getCredentials(
new AuthScope("somehost", 8080, "realm", "basic")));
System.out.println(credsProvider.getCredentials(
new AuthScope("otherhost", 8080, "realm", "basic")));
System.out.println(credsProvider.getCredentials(
new AuthScope("otherhost", 8080, null, "ntlm")));
4.4 HTTP認證和執行上下文
CloseableHttpClient httpclient = <...>
CredentialsProvider credsProvider = <...>
Lookup<AuthSchemeProvider> authRegistry = <...>
AuthCache authCache = <...>
HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider);
context.setAuthSchemeRegistry(authRegistry);
context.setAuthCache(authCache);
HttpGet httpget = new HttpGet("http://somehost/");
CloseableHttpResponse response1 = httpclient.execute(httpget, context);
<...>
AuthState proxyAuthState = context.getProxyAuthState();
System.out.println("Proxy auth state: " + proxyAuthState.getState());
System.out.println("Proxy auth scheme: " + proxyAuthState.getAuthScheme());
System.out.println("Proxy auth credentials: " + proxyAuthState.getCredentials());
AuthState targetAuthState = context.getTargetAuthState();
System.out.println("Target auth state: " + targetAuthState.getState());
System.out.println("Target auth scheme: " + targetAuthState.getAuthScheme());
System.out.println("Target auth credentials: " + targetAuthState.getCredentials());
4.5 認證數據的高速緩存
隨着4.1版本的HttpClient自動緩存有關主機它已成功地經過身份驗證。請注意,必須使用相同的執行上下文來執行邏輯相關的請求,以便緩存認證數據從一個請求到另外一個傳播。
認證數據將被儘快執行上下文超出範圍丟失。
4.6 搶先認證
CloseableHttpClient httpclient = <...>
HttpHost targetHost = new HttpHost("localhost", 80, "http");
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope(targetHost.getHostName(), targetHost.getPort()),
new UsernamePasswordCredentials("username", "password"));
// Create AuthCache instance
AuthCache authCache = new BasicAuthCache();
// Generate BASIC scheme object and add it to the local auth cache
BasicScheme basicAuth = new BasicScheme();
authCache.put(targetHost, basicAuth);
// Add AuthCache to the execution context
HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider);
context.setAuthCache(authCache);
HttpGet httpget = new HttpGet("/");
for (int i = 0; i < 3; i++) {
CloseableHttpResponse response = httpclient.execute(
targetHost, httpget, context);
try {
HttpEntity entity = response.getEntity();
} finally {
response.close();
}
}
4.7 NTLM 認證
4.7.1 NTLM鏈接持久化
CloseableHttpClient httpclient = <...>
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY,
new NTCredentials("user", "pwd", "myworkstation", "microsoft.com"));
HttpHost target = new HttpHost("www.microsoft.com", 80, "http");
// Make sure the same context is used to execute logically related requests
HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider);
// Execute a cheap method first. This will trigger NTLM authentication
HttpGet httpget = new HttpGet("/ntlm-protected/info");
CloseableHttpResponse response1 = httpclient.execute(target, httpget, context);
try {
HttpEntity entity1 = response1.getEntity();
} finally {
response1.close();
}
// Execute an expensive method next reusing the same context (and connection)
HttpPost httppost = new HttpPost("/ntlm-protected/form");
httppost.setEntity(new StringEntity("lots and lots of data"));
CloseableHttpResponse response2 = httpclient.execute(target, httppost, context);
try {
HttpEntity entity2 = response2.getEntity();
} finally {
response2.close();
}
4.8. SPNEGO / Kerberos身份驗證
該SPNEGO(簡單和受保護GSSAPI協商機制)被設計爲容許身份驗證服務時,既不知道到底對方可使用/提供什麼。它是最經常使用的作Kerberos認證。它能夠包裝等機制,但目前的版本中HttpClient的設計只使用Kerberos的初衷。
1.客戶端的Web瀏覽器的HTTP GET資源。
2. Web服務器返回的HTTP 401狀態和標題:WWW身份驗證:協商
3.客戶端生成NegTokenInit,BASE64編碼它,並從新提交GET與
標題受權:受權:協商<base64編碼>。
4.服務器解碼NegTokenInit,提取支持MechTypes(僅Kerberos V5在咱們的狀況下),可確保它是所預計的一個,而後提取MechToken(Kerberos令牌),並驗證它。
若是更多的處理是必需的另外一個HTTP 401被返回到客戶端,在所述WWW-Authenticate頭更多的數據。客戶須要的信息,並生成另外一個令牌傳遞這回在Authorization頭,直到完成。
5.當客戶端已經認證了Web服務器應返回的HTTP 200狀態,最終WWW-Authenticate頭和網頁內容。
4.8.1. SPNEGO支持的HttpClient
在SPNEGO認證方案與Sun的Java版本1.5及更高版本兼容。然而,使用Java> = 1.6,強烈推薦,由於它支持更全面SPNEGO認證。
太陽JRE提供了支持類作幾乎全部的Kerberos和SPNEGO令牌處理。
這意味着大量的設置是對於GSS類。該SPNegoScheme是一個簡單的類來處理編組令牌和讀取和寫入正確的頭。
以最好的方式開始是搶例子KerberosHttpClient.java文件和嘗試,並獲得它的工做。有不少可能發生的問題,但若是幸運的話它會工做沒有太多的問題。
它還應提供一些輸出與調試。
在Windows中,應該默認使用登陸的憑證;這能夠經過使用「的kinit」例如被覆蓋$ JAVA_HOME \ BIN \的kinit testuser@AD.EXAMPLE.NET,這對於測試和調試問題很是有幫助。經過刪除建立的kinit緩存文件恢復到Windows Kerberos緩存。
確保列出domain_realms在krb5.conf文件。這是問題的主要根源。
4.8.2. GSS / Java的Kerberos設置
本文假設您使用的是Windows,但大部分的信息適用於Unix的爲好。
該org.ietf.jgss中類有不少可能的配置參數,主要是在krb5.conf / krb5.ini文件。在http://web.mit.edu/kerberos/krb5-1.4/ krb5-1.4.1 / DOC / KRB5管理員/ krb5.conf.html的格式一些更多的信息。
4.8.3. login.conf的文件
下面的配置是一個基本的設置是工做在Windows XP中對IIS和JBoss的談判模塊。
系統屬性java.security.auth.login.config能夠用來指向login.conf的文件。
login.conf的內容可能看起來像下面這樣:
com.sun.security.jgss.login {
com.sun.security.auth.module.Krb5LoginModule所需的客戶端= TRUE useTicketCache = TRUE;
};
com.sun.security.jgss.initiate {
com.sun.security.auth.module.Krb5LoginModule所需的客戶端= TRUE useTicketCache = TRUE;
};
com.sun.security.jgss.accept {
com.sun.security.auth.module.Krb5LoginModule所需的客戶端= TRUE useTicketCache = TRUE;
};
4.8.4. 的krb5.conf / krb5.ini文件
若是不指定,系統默認的將被使用。若是須要經過設置系統屬性java.security.krb5.conf指向自定義krb5.conf文件覆蓋。
krb5.conf的內容可能看起來像下面這樣:
[libdefaults]
default_realm = AD.EXAMPLE.NET
udp_preference_limit = 1
[realms域]
AD.EXAMPLE.NET = {
KDC = KDC.AD.EXAMPLE.NET
}
[domain_realms]
.ad.example.net = AD.EXAMPLE.NET
ad.example.net = AD.EXAMPLE.NET
4.8.5. Windows特定配置
爲了讓Windows使用當前用戶的車票,系統屬性javax.security.auth.useSubjectCredsOnly必須設置爲false和Windows註冊表鍵allowtgtsessionkey應補充並正確設置容許會話密鑰在Kerberos票據受權發送票。
在Windows Server 2003和Windows 2000 SP4,這裏是所需的註冊表設置:
HKEY_LOCAL_MACHINE \系統\ CurrentControlSet \控制\ LSA \ Kerberos的\參數
值名稱:allowtgtsessionkey
數值類型:REG_DWORD
值:0x01
下面是在Windows XP SP2中的註冊表設置的位置:
HKEY_LOCAL_MACHINE \系統\ CurrentControlSet \控制\ LSA \ Kerberos的\
值名稱:allowtgtsessionkey
數值類型:REG_DWORD
值:0x01
第五章流利的API
5.1. 易於使用的API門面
因爲版本4.2的HttpClient配備了一個易於使用的API門面基於一個流暢的界面的概念。流利的門面API暴露的HttpClient只有最基本的功能,適用於簡單的用例不須要的HttpClient的充分的靈活性。舉例來講,流暢的外觀API免除了用戶沒必要處理鏈接管理和資源釋放。
如下是經過慧聰流利的API執行HTTP請求的幾個例子
// Execute a GET with timeout settings and return response content as String.
Request.Get("http://somehost/")
.connectTimeout(1000)
.socketTimeout(1000)
.execute().returnContent().asString();
// Execute a POST with the 'expect-continue' handshake, using HTTP/1.1,
// containing a request body as String and return response content as byte array.
Request.Post("http://somehost/do-stuff")
.useExpectContinue()
.version(HttpVersion.HTTP_1_1)
.bodyString("Important stuff", ContentType.DEFAULT_TEXT)
.execute().returnContent().asBytes();
// Execute a POST with a custom header through the proxy containing a request body
// as an HTML form and save the result to the file
Request.Post("http://somehost/some-form")
.addHeader("X-Custom-header", "stuff")
.viaProxy(new HttpHost("myproxy", 8080))
.bodyForm(Form.form().add("username", "vip").add("password", "secret").build())
.execute().saveContent(new File("result.dump"));
人們也能夠直接使用執行程序,以便在特定的安全上下文中執行的請求,由此認證細節被緩存並再用於後續請求。
Executor executor = Executor.newInstance()
.auth(new HttpHost("somehost"), "username", "password")
.auth(new HttpHost("myproxy", 8080), "username", "password")
.authPreemptive(new HttpHost("myproxy", 8080));
executor.execute(Request.Get("http://somehost/"))
.returnContent().asString();
executor.execute(Request.Post("http://somehost/do-stuff")
.useExpectContinue()
.bodyString("Important stuff", ContentType.DEFAULT_TEXT))
.returnContent().asString();
5.1.1. 響應處理
流暢的外觀通常API減輕了用戶沒必要處理鏈接管理和資源釋放。在大多數狀況下,雖然,這是以具備以在內存中緩衝的響應消息的內容的價格。強烈建議使用ResponseHandler所進行的HTTP響應處理,以免在內存中緩衝的內容。
Document result = Request.Get("http://somehost/content")
.execute().handleResponse(new ResponseHandler<Document>() {
public Document handleResponse(final HttpResponse response) throws IOException {
StatusLine statusLine = response.getStatusLine();
HttpEntity entity = response.getEntity();
if (statusLine.getStatusCode() >= 300) {
throw new HttpResponseException(
statusLine.getStatusCode(),
statusLine.getReasonPhrase());
}
if (entity == null) {
throw new ClientProtocolException("Response contains no content");
}
DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
ContentType contentType = ContentType.getOrDefault(entity);
if (!contentType.equals(ContentType.APPLICATION_XML)) {
throw new ClientProtocolException("Unexpected content type:" +
contentType);
}
String charset = contentType.getCharset();
if (charset == null) {
charset = HTTP.DEFAULT_CONTENT_CHARSET;
}
return docBuilder.parse(entity.getContent(), charset);
} catch (ParserConfigurationException ex) {
throw new IllegalStateException(ex);
} catch (SAXException ex) {
throw new ClientProtocolException("Malformed XML document", ex);
}
}
});
第六章HTTP緩存
6.1. 通常概念
HttpClient的緩存提供了一個HTTP / 1.1兼容的緩存層要與HttpClient的使用 - 在Java至關於一個瀏覽器的緩存。落實責任遵循設計模式的鏈,其中的緩存HttpClient的實施能夠成爲一個簡易替換默認非高速緩存HttpClient的執行狀況;能夠徹底從緩存來知足的請求將不會致使實際原點的請求。陳舊的緩存條目都與原點在可能的狀況自動進行驗證,使用條件GET和了If-Modified-Since和/或若是 - 無 - 匹配請求頭。
HTTP / 1.1的緩存通常被設計爲語義透明;即,高速緩衝不該該改變的客戶端和服務器之間的請求 - 響應交換的含義。所以,它應該是安全的降低緩存的HttpClient到現有標準的客戶端 - 服務器關係。雖然緩存模塊是從視圖的HTTP協議點客戶端的一部分,執行目標是與放置在透明緩存代理的要求相兼容。
最後,緩存HttpClient的包括支持由RFC 5861規定的緩存控制擴展(陳舊,若是錯誤和過期的,同時,從新驗證)。
當緩存HttpClient的執行請求時,它會經過如下流程:
1.檢查基本符合HTTP 1.1協議的請求,並試圖更正請求。
2.沖洗將由這一請求是無效的任何緩存條目。
3.肯定若是當前的請求將是從高速緩存servable。若是沒有,直接經過請求到原始服務器並返回響應,若是合適將其高速緩存以後。
4.若是是AA緩存servable請求時,它會嘗試從緩存中讀取它。若是不是在高速緩存中,調用源服務器和緩存響應,若是合適。
5.若是緩存的響應是適合於擔任響應,構建一個包含一個ByteArrayEntity和BasicHttpResponse返回它。不然,試圖從新驗證對源服務器的高速緩存條目。
6.在一個緩存的響應不能被從新驗證的狀況下,調用源服務器和緩存響應,若是合適的話。
當HttpClient的緩存收到響應,它會經過如下流程:
1.檢查爲協議一致性響應
2.肯定響應是否緩存
3.若是它是可緩存,試圖讀取到在配置所容許的最大尺寸,並將其存儲在緩存中。
4.若是反應過大的緩存,重建部分消耗和響應的狀況下直接將其高速緩存返回。
要注意的是緩存HttpClient的不是它自己,一個不一樣的實施HttpClient的,但它的工做原理是插入自己做爲產生額外處理組件對請求執行管線是很重要的。
6.2. RFC-2616符合
咱們相信HttpClient的緩存是無條件地符合RFC-2616[RFC http://www.ietf.org// rfc2616.txt]。也就是說,只要規範指示必須不絕,應該或不該該用於HTTP緩存,緩存層試圖表現得知足這些要求的方式。這意味着緩存模塊不會產生不正確的行爲,當你把它研究。
6.3. 用法示例
這是如何創建一個基本的緩存HttpClient的一個簡單的例子。如配置,它將存儲最多1000緩存對象,其每個可具備8192字節的最大致尺寸。
此處所選的數字僅僅是做爲示例而且不旨在是說明性的或視爲建議。
CacheConfig cacheConfig = CacheConfig.custom()
.setMaxCacheEntries(1000)
.setMaxObjectSize(8192)
.build();
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(30000)
.setSocketTimeout(30000)
.build();
CloseableHttpClient cachingClient = CachingHttpClients.custom()
.setCacheConfig(cacheConfig)
.setDefaultRequestConfig(requestConfig)
.build();
HttpCacheContext context = HttpCacheContext.create();
HttpGet httpget = new HttpGet("http://www.mydomain.com/content/");
CloseableHttpResponse response = cachingClient.execute(httpget, context);
try {
CacheResponseStatus responseStatus = context.getCacheResponseStatus();
switch (responseStatus) {
case CACHE_HIT:
System.out.println("A response was generated from the cache with " +
"no requests sent upstream");
break;
case CACHE_MODULE_RESPONSE:
System.out.println("The response was generated directly by the " +
"caching module");
break;
case CACHE_MISS:
System.out.println("The response came from an upstream server");
break;
case VALIDATED:
System.out.println("The response was generated from the cache " +
"after validating the entry with the origin server");
break;
}
} finally {
response.close();
}
6.4. 組態
緩存HttpClient的繼承了全部的配置選項,默認的非緩存實現的參數(這包括像設置超時和鏈接池大小選項)。對於cachingspecific配置,能夠提供一個CacheConfig實例跨越如下幾個方面定製行爲:
緩存大小。若是後端存儲支持這些限制,您能夠指定緩存條目的最大數量,以及最大緩存響應的車身尺寸。
公共/私有緩存。默認狀況下,緩存模塊認爲本身是一個共享(公共)高速緩存,而且不會,例如,高速緩存的響應與標有「緩存控制:私人」受權頭或響應請求。可是,若是緩存只會用在一個邏輯上的「用戶」(行爲相似於瀏覽器緩存),那麼你將要關閉共享的高速緩存設置。
啓發式caching.Per RFC2616,高速緩存能夠緩存,即便沒有明確的緩存控制報頭被設置原點必定的緩存條目。此行爲是默認關閉的,可是您可能但願把這個上,若是您正在使用一個原點,不設置適當的標題,但在這裏,你仍然想緩存響應。您將要啓用啓發式緩存,而後指定一個默認的新鮮感壽命和/或時間的一小部分,由於資源的最後修改。爲啓發式緩存的更多詳細信息,請參見13.2.2節和HTTP的13.2.4 / 1.1 RFC。
背景驗證。高速緩存模塊支持RFC5861的陳舊,同時,從新驗證指令,這使得某些緩存條目從新確認發生在後臺。您可能須要調整設置爲最小和最大數量的背景工做者線程,以及它們能夠被回收以前閒置的最長時間。還能夠控制用於從新確認隊列的大小時,有不足夠的工人以跟上需求。
6.5. 存儲後端
HttpClient的緩存的缺省實現存儲在內存中的應用程序的JVM緩存條目和緩存響應機構。雖然這提供了高性能,它可能不適合你的應用,因爲尺寸上的限制,或由於緩存條目是短暫的,沒有生存從新啓動應用程序。當前版本包括用於存儲使用的Ehcache和memcached的實現,它容許溢出緩存項到磁盤或者將它們存儲在外部進程緩存條目的支持。
若是沒有這些選項是適合你的應用,能夠經過實現HttpCacheStorage接口,而後供應,要在施工時間緩存HttpClient的提供本身的存儲後端。在這種狀況下,高速緩存條目將使用方案來存儲,但你會獲得重用周圍全部的HTTP / 1.1合規性和緩存處理的邏輯。通常來講,它應該有可能創造一個HttpCacheStorage實施選自任何支持一鍵/值存儲(相似於Java Map接口)與應用原子更新的能力。
最後,有一些額外的努力,這是徹底有可能創建一個多層次的高速緩存層次結構;例如,包裝在內存中緩存的HttpClient圍繞一個存儲在磁盤上或在遠程memcached的緩存項,如下相似虛擬內存,L1 / L2處理器緩存等模式
第七章 高級主題
7.1 自定義客戶端鏈接
提供一個自定義LineParser/LineFormatter接口實現
class MyLineParser extends BasicLineParser {
@Override
public Header parseHeader(
CharArrayBuffer buffer) throws ParseException {
try {
return super.parseHeader(buffer);
} catch (ParseException ex) {
// Suppress ParseException exception
return new BasicHeader(buffer.toString(), null);
}
}
}
提過一個自定義的OperatedClientConnection實現
HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory =
new ManagedHttpClientConnectionFactory(
new DefaultHttpRequestWriterFactory(),
new DefaultHttpResponseParserFactory(
new MyLineParser(), new DefaultHttpResponseFactory()));
爲了建立新類的鏈接,提供一個自定義的ClientConnectionOperator接口實現
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(
connFactory);
CloseableHttpClient httpclient = HttpClients.custom()
.setConnectionManager(cm)
.build();
7.2 有狀態的HTTP鏈接
7.2.1 用戶令牌處理器
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpClientContext context = HttpClientContext.create();
HttpGet httpget = new HttpGet("http://localhost:8080/");
CloseableHttpResponse response = httpclient.execute(httpget, context);
try {
Principal principal = context.getUserToken(Principal.class);
System.out.println(principal);
} finally {
response.close();
}
UserTokenHandler userTokenHandler = new UserTokenHandler() {
public Object getUserToken(HttpContext context) {
return context.getAttribute("my-token");
}
};
CloseableHttpClient httpclient = HttpClients.custom()
.setUserTokenHandler(userTokenHandler)
.build();
7.2.2 持續的鏈接狀態
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpClientContext context1 = HttpClientContext.create();
HttpGet httpget1 = new HttpGet("http://localhost:8080/");
CloseableHttpResponse response1 = httpclient.execute(httpget1, context1);
try {
HttpEntity entity1 = response1.getEntity();
} finally {
response1.close();
}
Principal principal = context1.getUserToken(Principal.class);
HttpClientContext context2 = HttpClientContext.create();
context2.setUserToken(principal);
HttpGet httpget2 = new HttpGet("http://localhost:8080/");
CloseableHttpResponse response2 = httpclient.execute(httpget2, context2);
try {
HttpEntity entity2 = response2.getEntity();
} finally {
response2.close();
}
7.3. 使用FutureRequestExecutionService
使用FutureRequestExecutionService,你能夠安排HTTP調用和治療的反應做爲將來。這是有用的,當如進行屢次調用Web服務。使用FutureRequestExecutionService的好處是,你可使用多個線程併發調度請求,在任務設置超時或取消時,他們的響應再也不是必要的。
FutureRequestExecutionService包裝用HttpRequestFutureTask,它擴展FutureTask提供的請求。這個類可讓你取消任務,以及跟蹤各類指標,如要求時間。
7.3.1. 建立FutureRequestExecutionService
的構造futureRequestExecutionService採起任何現有的HttpClient的實例和ExecutorService的實例。當配置兩個,它對準的最大鏈接數與您將要使用的線程的數量是很是重要的。當有比鏈接多個線程,所述鏈接能夠啓動超時,由於沒有可用的鏈接。當有比多線程鏈接,futureRequestExecutionService不會使用全部的人
HttpClient httpClient = HttpClientBuilder.create().setMaxConnPerRoute(5).build();
ExecutorService executorService = Executors.newFixedThreadPool(5);
FutureRequestExecutionService futureRequestExecutionService =
new FutureRequestExecutionService(httpClient, executorService);
7.3.2. 調度請求
要安排一個請求,只是提供一個HttpUriRequest,HttpContext的,和ResponseHandler的。
由於該請求由執行服務處理,一個ResponseHandler的是強制性的。
private final class OkidokiHandler implements ResponseHandler<Boolean> {
public Boolean handleResponse(
final HttpResponse response) throws ClientProtocolException, IOException {
return response.getStatusLine().getStatusCode() == 200;
}
}
HttpRequestFutureTask<Boolean> task = futureRequestExecutionService.execute(
new HttpGet("http://www.google.com"), HttpClientContext.create(),
new OkidokiHandler());
// blocks until the request complete and then returns true if you can connect to Google
boolean ok=task.get();
7.3.3。取消任務
計劃任務可能會被取消。若是任務還沒有執行,而只是排隊等待執行,它只是將永遠不執行。若是正在執行和mayInterruptIfRunning參數設置爲true,停止()將被調用的請求;不然,響應將被忽略,但該請求將被容許正常完成。任何後續調用task.get()將一個IllegalStateException失敗。應當注意到,取消任務僅能夠釋放客戶端的資源。該請求實際上能夠在服務器端正常處理。
task.cancel(true)
task.get() // throws an Exception
7.3.4. 回調
不用手動調用task.get(),你也可使用得到的回調請求完成時FutureCallback實例。這是相同的接口被用於在HttpAsyncClient
private final class MyCallback implements FutureCallback<Boolean> {
public void failed(final Exception ex) {
// do something
}
public void completed(final Boolean result) {
// do something
}
public void cancelled() {
// do something
}
}
HttpRequestFutureTask<Boolean> task = futureRequestExecutionService.execute(
new HttpGet("http://www.google.com"), HttpClientContext.create(),
new OkidokiHandler(), new MyCallback());
7.3.5. 度量
FutureRequestExecutionService一般用於在應用程序,使大量的web服務調用。爲了便於例如監測或配置調整,在FutureRequestExecutionService跟蹤的幾個指標。
每一個HttpRequestFutureTask提供的方法來獲取任務安排的時間,開始和結束。此外,請求和任務持續時間可用。這些指標彙總到FutureRequestExecutionService在FutureRequestExecutionMetrics實例能夠經過FutureRequestExecutionService.metrics訪問()。
task.scheduledTime() // returns the timestamp the task was scheduled
task.startedTime() // returns the timestamp when the task was started
task.endedTime() // returns the timestamp when the task was done executing
task.requestDuration // returns the duration of the http request
task.taskDuration // returns the duration of the task from the moment it was scheduled
FutureRequestExecutionMetrics metrics = futureRequestExecutionService.metrics()
metrics.getActiveConnectionCount() // currently active connections
metrics.getScheduledConnectionCount(); // currently scheduled connections
metrics.getSuccessfulConnectionCount(); // total number of successful requests
metrics.getSuccessfulConnectionAverageDuration(); // average request duration
metrics.getFailedConnectionCount(); // total number of failed tasks
metrics.getFailedConnectionAverageDuration(); // average duration of failed tasks
metrics.getTaskCount(); // total number of tasks scheduled
metrics.getRequestCount(); // total number of requests
metrics.getRequestAverageDuration(); // average request duration
metrics.getTaskAverageDuration(); // average task duration
(完)