package com.cycore.dbs.common.demo.hc; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.InterruptedIOException; import java.io.Reader; import java.net.URI; import java.net.URISyntaxException; import java.net.UnknownHostException; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import javax.net.ssl.SSLException; import org.apache.http.Consts; import org.apache.http.Header; import org.apache.http.HeaderElement; import org.apache.http.HeaderElementIterator; import org.apache.http.HeaderIterator; import org.apache.http.HttpEntity; import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpException; import org.apache.http.HttpHost; import org.apache.http.HttpRequest; import org.apache.http.HttpRequestInterceptor; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.HttpVersion; import org.apache.http.NameValuePair; import org.apache.http.ParseException; import org.apache.http.StatusLine; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpRequestRetryHandler; import org.apache.http.client.HttpResponseException; import org.apache.http.client.ResponseHandler; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.client.utils.URIBuilder; import org.apache.http.client.utils.URIUtils; import org.apache.http.conn.ConnectTimeoutException; import org.apache.http.conn.ConnectionKeepAliveStrategy; import org.apache.http.entity.BufferedHttpEntity; import org.apache.http.entity.ContentType; import org.apache.http.entity.FileEntity; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.client.LaxRedirectStrategy; import org.apache.http.message.BasicHeaderElementIterator; import org.apache.http.message.BasicHttpResponse; import org.apache.http.message.BasicNameValuePair; import org.apache.http.protocol.BasicHttpContext; import org.apache.http.protocol.HttpContext; import org.apache.http.util.EntityUtils; import com.cycore.dbs.vo.RoleVo; import com.google.gson.Gson; import com.google.gson.GsonBuilder; public class HC_Fundamentals { public static void m1(){ try { CloseableHttpClient client=HttpClients.createDefault(); URI uri=new URIBuilder() .setScheme("http") .setHost("www.google.com") .setPath("/search") .setParameter("q", "httpclient") .setParameter("aq", "f") .build(); HttpGet httpGet=new HttpGet(uri); System.out.println(httpGet.getURI()); CloseableHttpResponse response=client.execute(httpGet); HttpEntity entity=response.getEntity(); if(entity!=null){ InputStream is=entity.getContent(); //... } response.close(); } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (URISyntaxException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public static void m2(){ HttpResponse httpResponse=new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); System.out.println(httpResponse.getProtocolVersion());//HTTP/1.1 System.out.println(httpResponse.getStatusLine().getStatusCode());//200 System.out.println(httpResponse.getStatusLine().getReasonPhrase());//OK System.out.println(httpResponse.getStatusLine().toString());//HTTP/1.1 200 OK } /** * header的使用 */ public static void m3(){ 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 firstHeader=response.getFirstHeader("Set-Cookie"); System.out.println(firstHeader); Header lastHeader=response.getLastHeader("Set-Cookie"); System.out.println(lastHeader); Header[] headers=response.getHeaders("Set-Cookie"); System.out.println(headers.length); //获取所有header最有效率的方式 HeaderIterator hi=response.headerIterator("Set-Cookie"); while(hi.hasNext()){ System.out.println(hi.next()); } //将http消息转换成独立的头部元素 HeaderElementIterator hei=new BasicHeaderElementIterator( response.headerIterator("Set-Cookie")); while(hei.hasNext()){ HeaderElement ele=hei.nextElement(); System.out.println(ele.getName()+" = "+ele.getValue()); NameValuePair[] params=ele.getParameters(); for (int i = 0; i < params.length; i++) { System.out.println(" "+params[i]); } } } /** * StringEntity * @throws ParseException * @throws IOException */ public static void m4() throws ParseException, IOException{ StringEntity stringEntity=new StringEntity("important message", ContentType.create("text/plain","UTF-8")); System.out.println(stringEntity.getContentType()); System.out.println(stringEntity.getContentLength()); System.out.println(EntityUtils.toString(stringEntity)); System.out.println(EntityUtils.toByteArray(stringEntity).length); } /** * 1.关闭和entity内容相关的流 或 2.HttpResponse对象本身,以释放系统资源。 * 前者会通过消费实体内容尝试保持潜在连接,而后者则是立马关闭连接。 * @throws ClientProtocolException * @throws IOException */ public static void m5() throws ClientProtocolException, IOException{ CloseableHttpClient client=HttpClients.createDefault(); HttpGet httpGet=new HttpGet("http://localhost"); CloseableHttpResponse response=client.execute(httpGet); try { HttpEntity entity=response.getEntity(); if(entity!=null){ InputStream inputStream=entity.getContent(); try { //do something } finally{ inputStream.close();//1 } } } finally{ response.close();//2 } } /** * 当响应中一部分内容需要被获取,那么消费剩下的消息内容和保持连接可重用的性能消耗会很高。 * 可以通过关闭response终止内容流,连接将不会被重用,但该连接所拥有的所有层次的资源都得到合理的分配。 * @throws ClientProtocolException * @throws IOException */ public static void m6() throws ClientProtocolException, IOException{ CloseableHttpClient client=HttpClients.createDefault(); HttpGet httpGet=new HttpGet("http://localhost"); CloseableHttpResponse response=client.execute(httpGet); try { HttpEntity entity=response.getEntity(); if(entity!=null){ InputStream inputStream=entity.getContent(); int byte1=inputStream.read(); int byte2=inputStream.read(); //Do not need the rest } } finally{ response.close(); } } /** * 推荐使用 HttpEntity#getContent()方法或HttpEntity#writeTo(OutputStream)方法来消费实体内容。 * 虽然使用EntityUtils的各种静态暴露方法很方便的获取实体内容的字节数组或字符串序列, * 但是不推荐,除非实体内容来自一个可信任的HTTP服务器且限长可知。 * @throws ClientProtocolException * @throws IOException */ public static void m7() throws ClientProtocolException, IOException{ CloseableHttpClient client=HttpClients.createDefault(); HttpGet httpGet=new HttpGet("http://localhost"); CloseableHttpResponse response=client.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));//EntityUtils获取字符序列 }else{ //Stream content out } } } finally{ response.close(); } } /** * 在有些情况下可能需要多次读取实体内容,这就需要被缓冲进内存或硬盘中。 * 最简单的办法就是用BufferedHttpEntity类包装最初的entity,这样会把最初的entity读进内存缓冲区。 * @throws ClientProtocolException * @throws IOException */ public static void m8() throws ClientProtocolException, IOException{ CloseableHttpClient client=HttpClients.createDefault(); HttpGet httpGet=new HttpGet("http://localhost"); CloseableHttpResponse response=client.execute(httpGet); try { HttpEntity entity=response.getEntity(); if(entity!=null){ entity=new BufferedHttpEntity(entity);//缓冲封装 } } finally{ response.close(); } } /** * hc为大多数共同的数据容器比如string , byte array , input stream和file的提供了若干类。 * 这些类有StringEntity , ByteArrayEntity , InputStreamEntity , 和 FileEntity 。 * 这些类可以通过POST和PUT方式,与闭合请求相关联高效输出内容。 * 注意: InputStreamEntity只能从潜在数据中读取一次,不可重复读。推荐使用自定义HttpEntity实现类 * 替代使用普通的 InputStreamEntity。FileEntity可以成为一个很高的开端。 */ public static void m9(){ 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); } /** * UrlEncodedFormEntity-简化了HTML表单提交的过程,使用URL编码编码参数。 * 编码之后的参数看起来像这样: param1=value1¶m2=value2 */ public static void m10(){ 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/action.do"); httpPost.setEntity(entity); } /** * 在基于被传输的HTTP消息中属性,一般来说会推荐让HttpClient选择最合适的传输编码方式。 * entity.setChunked(true)即可通知HttpClient设置块编码。 * 注意:当使用HTTP协议版本时如HTTP/1.0,将忽略该块编码设置。 */ public static void m11(){ StringEntity entity=new StringEntity("important message", ContentType.create("plain/text", Consts.UTF_8)); entity.setChunked(true); HttpPost httpPost=new HttpPost("http://localhost/action.do"); httpPost.setEntity(entity); } /** * ResponseHandler-处理响应,其中有个方法handleResponse方法可以保证连接资源的自动释放, * 不需要担心连接的管理,不管请求执行成功还是失败. * @throws ClientProtocolException * @throws IOException */ public static void m12() throws ClientProtocolException, IOException{ CloseableHttpClient client=HttpClients.createDefault(); HttpGet httpGet=new HttpGet("http://localhost/json"); ResponseHandler<RoleVo> rh = new ResponseHandler<RoleVo>() { public RoleVo handleResponse(HttpResponse response) throws ClientProtocolException, 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.get(entity); Charset charset=contentType.getCharset(); Reader reader=new InputStreamReader(entity.getContent(), charset); return gson.fromJson(reader, RoleVo.class); } }; RoleVo roleVo=client.execute(httpGet, rh); } /** * HttpClient接口,仅表示HTTP请求执行的最基础的契约. * 没有对请求执行强加限制或特殊细节。正是如此, * 可以让诸如状态处理、认证、和重定向处理独立实现它们自身特征。 */ public static void m13(){ //定制连接空闲时间管理策略 ConnectionKeepAliveStrategy keepAliveStrategy=new DefaultConnectionKeepAliveStrategy(){ public long getKeepAliveDuration(HttpResponse response, HttpContext context) { long keepAlive = super.getKeepAliveDuration(response, context); if(keepAlive == -1){ //keep connection 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(keepAliveStrategy) .build(); } /** * HttpClient线程安全和资源重分配 * 1.HttpClient实现被期望是线程安全的,最好该类的同一个实例可被多个请求执行重用; * 2.当一个CloseableHttpClient实例不再使用或超出了同其相关的连接管理器范围,需要被关闭。 * @throws IOException */ public static void m14() throws IOException{ CloseableHttpClient client=HttpClients.createDefault(); try { //... } finally{ client.close(); } } /** * HttpContext可能含有任意对象所以在多线程中共享时是不安全的。 * 所以为了保证线程安全,需要每一个执行线程都有自己的上下文。 * * 多个请求的会话上下文和状态信息可以在同一个HttpContext实例中自动传播。 * @throws ClientProtocolException * @throws IOException */ public static void m15() throws ClientProtocolException, IOException{ HttpContext context=new BasicHttpContext(); HttpClientContext clientContext=HttpClientContext.adapt(context); // HttpHost target=clientContext.getTargetHost(); // HttpRequest request=clientContext.getRequest(); // HttpResponse response=clientContext.getResponse(); // RequestConfig config=clientContext.getRequestConfig(); CloseableHttpClient client=HttpClients.createDefault(); RequestConfig requestConfig=RequestConfig .custom() .setSocketTimeout(1000) .setConnectTimeout(1000) .build(); HttpGet httpGet1=new HttpGet("http://localhost/1"); httpGet1.setConfig(requestConfig); CloseableHttpResponse response1=client.execute(httpGet1, clientContext); try { HttpEntity entity1=response1.getEntity(); } finally { response1.close(); } HttpGet httpGet2=new HttpGet("http://localhost/2"); httpGet2.setConfig(requestConfig); CloseableHttpResponse response2=client.execute(httpGet2, clientContext); try { HttpEntity entity2=response2.getEntity(); } finally { response2.close(); } } /** * HTTP协议拦截器:是实现HTTP协议一个特性方面的例行程序。 * 1.通常,协议拦截器会被期望作用于进入的消息的一个特殊的头部header或一组相关联的头部,或者用一个特殊的头部或一组相关联的头部填充输出消息。 * 2.协议拦截器可以通过HTTP执行上下文合作共享信息如进程状态。协议拦截器可以利用上下文为一个请求或多个连续请求存储一个进程状态。 * 3.通常拦截器的执行顺序影响不是很大只要他们不依赖于执行上下文的一个特殊状态。如果协议拦截器之间有相关性那么就一定要以一个特殊顺序执行, * 他们应该以被期望的顺序添加进相同的协议进程序列。 * 4.协议拦截器要以线程安全的方式实现。和servlet类似,协议拦截器不应该使用实例变量,除非对变量的访问是线程同步的。 * @throws ClientProtocolException * @throws IOException */ public static void m16() throws ClientProtocolException, IOException{ CloseableHttpClient httpClient=HttpClients.custom().addInterceptorLast(new HttpRequestInterceptor() { public void process(HttpRequest request, 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(); } } } /** * HttpRequestRetryHandler-请求重试处理程序,自定义实现可以启用异常回复机制。 * 默认的,HttpClient假定非实体附入的方法如GET和HEAD是幂等的,而实体附入的方法如POST和PUT * 由于兼容性的原因不是幂等的。幂等方法多次请求执行跟一次请求执行的副作用是一样的。 * 在应用层保证HTTP方法的幂等性可以保证HTTP传输层的安全性。 */ public static void m17(){ 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=new HttpClientContext(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(); } /** * 重定向处理程序 */ public static void m18(){ LaxRedirectStrategy redirectStrategy=new LaxRedirectStrategy(); CloseableHttpClient client=HttpClients.custom() .setRedirectStrategy(redirectStrategy) .build(); } /** * HttpClient经常不得不重写执行进程中的请求消息。每个默认的HTTP/1.0和HTTP/1.1广泛使用相对请求统一资源识别符。 * 同样地,最初的请求可能会被从一个位置多次重定向到另一个位置。使用最初的请求和上下文可以生成最后的绝对的经过解释的绝对的HTTP位置。 * 可以使用工具类方法 URIUtils#resolve去达成以产生最后的请求。该方法包含了来自重定向请求或最初请求的最后片段识别符。 * @throws ClientProtocolException * @throws IOException * @throws URISyntaxException */ public static void m19() throws ClientProtocolException, IOException, URISyntaxException{ 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(); } } public static void main(String[] args) throws ParseException, IOException { // m1(); // m2(); // m3(); m4(); } }