本人最近通过自己动手处理http请求,对http协议、Jetty以及HttpClient有了更深刻的理解,特在此与大家分享。
此图是http协议的请求格式。根据请求方法,有get和post之分。get和post的区别在于参数传递的方式:post的参数就是请求包体那一行;get的参数跟在URL后面,也就没有请求包体。
get方式的请求格式
GET /search?hl=zh-CN&source=hp&q=domety&aq=f&oq= HTTP/1.1 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint,
application/msword, application/x-silverlight, application/x-shockwave-flash, */* Referer: <a href="http://www.google.cn/">http://www.google.cn/</a> Accept-Language: zh-cn Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; TheWorld) Host: <a href="http://www.google.cn">www.google.cn</a> Connection: Keep-Alive Cookie: PREF=ID=80a06da87be9ae3c:U=f7167333e2c3b714:NW=1:TM=1261551909:LM=1261551917:S=ybYcq2wpfefs4V9g;
NID=31=ojj8d-IygaEtSxLgaJmqSjVhCspkviJrB6omjamNrSm8lZhKy_yMfO2M4QMRKcH1g0iQv9u-2hfBW7bUFwVh7pGaRUb0RnHcJU37y-
FxlRugatx63JLv7CWMD6UB_O_r
post方式的请求格式
POST /search HTTP/1.1 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint,
application/msword, application/x-silverlight, application/x-shockwave-flash, */* Referer: <a href="http://www.google.cn/">http://www.google.cn/</a> Accept-Language: zh-cn Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; TheWorld) Host: <a href="http://www.google.cn">www.google.cn</a> Connection: Keep-Alive Cookie: PREF=ID=80a06da87be9ae3c:U=f7167333e2c3b714:NW=1:TM=1261551909:LM=1261551917:S=ybYcq2wpfefs4V9g;
NID=31=ojj8d-IygaEtSxLgaJmqSjVhCspkviJrB6omjamNrSm8lZhKy_yMfO2M4QMRKcH1g0iQv9u-2hfBW7bUFwVh7pGaRUb0RnHcJU37y-
FxlRugatx63JLv7CWMD6UB_O_r hl=zh-CN&source=hp&q=domety
所以,对请求进行解析的时候需要区分。
1、用Jetty捕获请求
直接上代码
1 package test; 2 import org.eclipse.jetty.server.Request; 3 import org.eclipse.jetty.server.Server; 4 import org.eclipse.jetty.server.handler.AbstractHandler; 5 6 import javax.servlet.ServletException; 7 import javax.servlet.http.HttpServletRequest; 8 import javax.servlet.http.HttpServletResponse; 9 10 public class JettyServer { 11 public static void main(String[] args) { 12 try { 13 // 进行服务器配置 14 Server server = new Server(8888); 15 server.setHandler(new MyHandler()); 16 // 启动服务器 17 server.start(); 18 server.join(); 19 } catch (Exception e) { 20 e.printStackTrace(); 21 } 22 } 23 } 24 25 class MyHandler extends AbstractHandler { 26 27 @Override 28 public void handle(String target, Request baseRequest, 29 HttpServletRequest request, HttpServletResponse response) 30 throws IOException, ServletException { 31 //写自己的处理 32 } 33 34 }
Jetty类似于tomcat,但是Jetty比较轻量级,特别适合内嵌到自己写的程序中,比如Hadoop的内置Server就是Jetty。
Jetty将请求进行了封装,比如Jetty自己的Request类,以及我们熟悉的HttpServletRequest类;并且把对象传递给handler。我们拿到HttpServletRequest的对象,就可以对请求进行解析、修改和拼装
Enumeration params = request.getParameterNames(); while(params.hasMoreElements()){ String paraName = (String)params.nextElement(); String paraValue = request.getParameter(paraName); paramList.add(new BasicNameValuePair(paraName, paraValue)); } Enumeration headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()) { String headerName = (String) headerNames.nextElement(); //HttpClient会自动加上这个请求头 if(headerName.equals("Content-Length")) { continue; } String headerValue = request.getHeader(headerName); Header header = new BasicHeader(headerName, headerValue); headers.add(header); }
2、用HttpClient转发请求
把捕获的请求进行重新组装之后,就可以通过HttpClient转发出去。
HttpClient 是 Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包。多说无益,简单语法请自行百度。
下面是一个完整的加入了HttpClient进行请求转发的类
1 package service; 2 import java.io.BufferedReader; 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.io.InputStreamReader; 6 import java.io.UnsupportedEncodingException; 7 import java.util.ArrayList; 8 import java.util.Enumeration; 9 import java.util.List; 10 11 import javax.servlet.http.HttpServletRequest; 12 13 import org.apache.http.Header; 14 import org.apache.http.HttpEntity; 15 import org.apache.http.HttpResponse; 16 import org.apache.http.NameValuePair; 17 import org.apache.http.client.ClientProtocolException; 18 import org.apache.http.client.HttpClient; 19 import org.apache.http.client.entity.UrlEncodedFormEntity; 20 import org.apache.http.client.methods.HttpGet; 21 import org.apache.http.client.methods.HttpPost; 22 import org.apache.http.client.methods.HttpUriRequest; 23 import org.apache.http.impl.client.DefaultHttpClient; 24 import org.apache.http.message.BasicHeader; 25 import org.apache.http.message.BasicNameValuePair; 26 27 public class RequestSender implements Runnable { 28 private HttpClient httpclient = null; 29 private RequestAdaptor requestAdaptor = null;//封装之后的请求 30 private String ip = null; 31 private String port = null; 32 33 public RequestSender(RequestAdaptor requestAdaptor, String ip, String port) { 34 httpclient = new DefaultHttpClient(); 35 this.requestAdaptor = requestAdaptor; 36 this.ip = ip; 37 this.port = port; 38 } 39 40 @Override 41 public void run() { 42 send(); 43 } 44 45 public void send() { 46 if(requestAdaptor == null) { 47 return; 48 } 49 String method = requestAdaptor.getMethod(); 50 String uri = requestAdaptor.getUri(); 51 String host = ip + ":" + port; 52 String url = "http://" + host + uri; 53 if(method.equals("GET")) { 54 get(url); 55 }else if(method.equals("POST")) { 56 post(url); 57 } 58 } 59 60 public void get(String url) { 61 url = url + "?" + requestAdaptor.getQueryString(); 62 HttpGet httpGet = new HttpGet(url); 63 addHeaders(httpGet); 64 try{ 65 66 HttpResponse resp = httpclient.execute(httpGet); 67 System.out.println(resp.getStatusLine().getStatusCode()); 68 }catch(ClientProtocolException cp) { 69 cp.printStackTrace(); 70 }catch(IOException io) { 71 io.printStackTrace(); 72 } 73 //return showResponse(httpGet); 74 } 75 76 public void post(String url) { 77 System.out.println(url); 78 HttpPost httpPost = new HttpPost(url); 79 addHeaders(httpPost); 80 UrlEncodedFormEntity uefEntity = null; 81 try { 82 uefEntity = new UrlEncodedFormEntity(requestAdaptor.getParamList(), "UTF-8"); 83 } catch (UnsupportedEncodingException e) { 84 e.printStackTrace(); 85 } 86 httpPost.setEntity(uefEntity); 87 try{ 88 HttpResponse resp = httpclient.execute(httpPost); 89 System.out.println(resp.getStatusLine().getStatusCode()); 90 }catch(ClientProtocolException cp) { 91 cp.printStackTrace(); 92 }catch(IOException io) { 93 io.printStackTrace(); 94 } 95 //return showResponse(httpPost); 96 } 97 98 public void addHeaders(HttpUriRequest httpUriRequest) { 99 List<Header> headers = requestAdaptor.getHeaders(); 100 for(int i = 0 ; i< headers.size();i++){ 101 httpUriRequest.addHeader(headers.get(i)); 102 } 103 } 104 105 public String showResponse(HttpUriRequest httpUriRequest) { 106 HttpResponse response = null; 107 try { 108 response = httpclient.execute(httpUriRequest); 109 } catch (ClientProtocolException e) { 110 e.printStackTrace(); 111 } catch (IOException e) { 112 e.printStackTrace(); 113 } 114 HttpEntity entity = response.getEntity(); 115 if (entity != null) { 116 InputStream instreams = null; 117 try { 118 instreams = entity.getContent(); 119 } catch (UnsupportedOperationException e) { 120 e.printStackTrace(); 121 } catch (IOException e) { 122 e.printStackTrace(); 123 } 124 String str = convertStreamToString(instreams); 125 //System.out.println(str); 126 // Do not need the rest 127 httpUriRequest.abort(); 128 return str; 129 } 130 return null; 131 } 132 133 134 public static String convertStreamToString(InputStream is) { 135 BufferedReader reader = new BufferedReader(new InputStreamReader(is)); 136 StringBuilder sb = new StringBuilder(); 137 String line = null; 138 try { 139 while ((line = reader.readLine()) != null) { 140 sb.append(line + " "); 141 } 142 } catch (IOException e) { 143 e.printStackTrace(); 144 } finally { 145 try { 146 is.close(); 147 } catch (IOException e) { 148 e.printStackTrace(); 149 } 150 } 151 return sb.toString(); 152 } 153 }
转发的核心是httpclient.execute(httpGet)和httpclient.execute(httpPost)
转发post请求的时候,需要把参数一个一个的组装进去;而转发get请求的时候就不用,应为参数已经跟在URL后面。但是,无论get还是post,都需要把请求头原封不动的组装进去。
基于以上两点,我们可以做的事情就有很多了。