proxy端是一个简单的servlet,实现代理的功能,目的是接收从手机发送过来的数据,再转发给CMA接收工程,工程源码如下:
1
2 package com.freelynet.meta.j2me;
3
4 import java.io.BufferedInputStream;
5 import java.io.BufferedOutputStream;
6 import java.io.DataOutputStream;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.io.PrintWriter;
10 import java.net.HttpURLConnection;
11 import java.net.MalformedURLException;
12 import java.net.URL;
13 import java.util.Date;
14 import java.util.Enumeration;
15 import java.util.Map;
16 import java.util.StringTokenizer;
17
18 import javax.servlet.ServletException;
19 import javax.servlet.ServletRequest;
20 import javax.servlet.ServletResponse;
21 import javax.servlet.http.HttpServlet;
22 import javax.servlet.http.HttpServletRequest;
23 import javax.servlet.http.HttpServletResponse;
24
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27 import org.apache.log4j.BasicConfigurator;
28
29 /**
30 * 实现代理功能
31 * <p>
32 * 请求方只需将URL放在请求数据发送到proxy,然后由proxy去访问URL,并将数据返回给请求方。
33 * </p>
34 * <p>
35 * ---- added at 2006.12.21 ----<br/>有几点是需要注意的:<br/>
36 * <li>1.现在的J2ME客户端访问的时候都是带ACTION的,程序中以此作为区别普通客户端和J2ME客户端,在J2ME客户端访问的时候,proxy会将文本数据用writeUTF来输出.</li>
37 * <li>2.服务器端的数据输出有可能是writeUTF,也有可能是直接用out.print,所以当是J2ME客户端时,会忽略到<zhml>前面的所有数据,这主要是考虑到兼容性问题</li>
38 * <li>3.本proxy现只支持HTTP,如果后期有需要的话,还可以加上FTP等,可以根据url来判断然后再加上相应的实现
39 * </p>
40 * <p>
41 * ---- added at 2009.01.14 ----<br/>上传图片的补充说明:<br/>
42 * <li>1.程序新增图片上传功能的处理。由于图片较大,转发时需用post方式发送数据</li>
43 * <li>2.首先对传来的二进制数据解析,判断是否有图片,如果有分离出url和图片,二者在终端程序中以&#标志分开。</li>
44 * <li>3.如果数据中包含图片,用新增的post方式流程转发数据(post方式只处理图片上传),否则,所有提交走原来proxy的处理流程。
45 * </p>
46 *
47 * @version 1.0 Dec 20, 2006
48 * @author Xiang.Xian.Jing (xiangxj@gmail.com)
49 *
50 * @version 2.0 Jan 14, 2009
51 * @author Du Changfeng (duchangfeng@163.com)
52 */
53 public class J2meProxy extends HttpServlet {
54
55 static {
56 BasicConfigurator.configure();
57 }
58
59 private static final long serialVersionUID = 1L;
60
61 //private static Log log = LogFactory.getLog(J2meProxy.class);
62
63 /**
64 * 实现代理功能
65 *
66 * @param req
67 * @param resp
68 * @throws ServletException
69 * @throws IOException
70 */
71 public void service(ServletRequest req, ServletResponse resp)
72 throws ServletException, IOException {
73 Date serviceBegintime=new Date();
74 Date serviceEndime=new Date();
75 HttpServletRequest request = (HttpServletRequest) req;
76 HttpServletResponse response = (HttpServletResponse) resp;
77 String action = request.getHeader("ACTION");
78 // 1.获取请求内容,并将其解析为代理需访问的http协议的url
79 byte[] reqData = getRequestData(request);
80
81 //----------------------add by dcf begin_1-----------------------
82 //对传来的二进制解析,分离出url和图片,二者在终端程序中以&#标志分开。
83 byte[] urlData = null; //url字节数组
84 byte[] picData = null; //图片字节数组
85 byte[] mark = new byte[2]; //标志位字节数组
86 boolean pictureUpload = false; //是否上传图片
87
88 //把标志位 “&#” 的字节数组位置找到,还原成字符串,以此分开url数组(抛离&#)和图片数组
89 //如: http://127.0.0.1/group.action?id=123&name=jackmyPicData
90 // url = http://127.0.0.1/group.action?id=123&name=jack;
91 // byte[] picData = myPicData;
92 for (int i=0; i< reqData.length; i++){
93 System.arraycopy(reqData, i, mark, 0, 1);
94 String word = new String(mark, "UTF-8");
95
96 //问号出现的位置
97 if (word.trim().equals("&")){
98 //再判断后面是不是#号
99 System.arraycopy(reqData, i+1, mark, 0, 1);
100 String wordNext = new String(mark, "UTF-8");
101 if (wordNext.trim().equals("#")){
102 //分开url数组和图片数组
103 urlData = new byte[i];//url字节数组
104 picData = new byte[reqData.length - (i+2)];//图片字节数组
105 pictureUpload = true;
106
107 System.arraycopy(reqData, 0, urlData, 0, i);
108 System.arraycopy(reqData, (i+2), picData, 0, reqData.length - (i+2) );
109 break;
110 }
111 }
112 }
113 //----------------------add by dcf end_1--------------------------
114
115 //----------------------modified by dcf begin_2-------------------
116 //如果上传图片,走这个单独供上传图片专用的流程,post方式; 否则,一切提交走原来proxy的流程,全部不变。
117 if(pictureUpload)
118 {
119 boolean isRequestOK = true; // 请求体内容是否合适
120 String url = null; // 请求体内容被解析为代理需访问的url
121 String urlParameter = null; // 请求体内容被解析为代理需访问的url
122 try {
123 if (urlData == null || urlData.length == 0) {
124 throw new IllegalArgumentException();
125 }
126
127 // 把带参数的url以?号分开为纯url和参数值(开头不带问号的),如:
128 //把 http://127.0.0.1:80/cma/kjava/group.action?groupid=123456&name=jack
129 // url = "http://127.0.0.1:80/cma/kjava/group.action";
130 // urlParameter = "groupid=123456&&name=jack";
131 String aUrl = new String(urlData, "UTF-8");//带参数的完整url
132 int beginIndex = aUrl.indexOf("http://");
133 int beginIndexWenhao = aUrl.indexOf("?");
134
135 if (beginIndex < 0) {
136 throw new IllegalArgumentException("No \"http://\" Found!");
137 }
138 if (beginIndexWenhao < 0) {
139 //如果没有参数。大部分都是测试之用吧
140 url = aUrl.substring(beginIndex);
141 }else
142 {
143 if (beginIndex >= 0)
144 {
145 url = aUrl.substring(beginIndex, beginIndexWenhao);
146 }
147 if (beginIndex >= 0)
148 {
149 urlParameter = aUrl.substring(beginIndexWenhao+1);
150 }
151 }
152
153
154 } catch (Exception e) {
155 isRequestOK = false;
156 }
157 // 2.打印请求信息
158 //dumpRequestInfo(request, isRequestOK, reqData, url);
159 // 3.如果访问是有问题的,马上返回
160 if (!isRequestOK) {
161 prepareResponse(request, response,
162 HttpURLConnection.HTTP_BAD_REQUEST);
163 return;
164 }
165 // 4.访问url所指向的内容,返回一个字节数组
166 byte[] respData = null;
167 String h_ACTION = request.getHeader("ACTION");
168 HttpURLConnection connection = null;
169 Date getReponseDataBegintime=new Date();
170 Date getReponseDataEndtime=new Date();
171 try {
172 //以Post方式转发。
173 connection = (HttpURLConnection) (new URL(url).openConnection());
174 connection.setDoOutput(true);
175 connection.setRequestMethod("POST");
176 HttpURLConnection.setFollowRedirects(true);// 支持重定向
177 transferHeaderInfo1(request, response, connection);// 请求头信息转移
178 connection.connect();
179
180 //写参数
181 if(urlParameter != null){
182 //开头不带问号的参数,如:urlParameter = "groupid=123456&name=jack";
183 connection.getOutputStream().write( (urlParameter).getBytes() );
184 }
185 //写图片
186 if(picData != null && picData.length != 0 ){
187 //如果单单是图片转发出错,可用分次发送测试
188 connection.getOutputStream().write("".getBytes() );
189 connection.getOutputStream().write(picData);
190 }
191 connection.getOutputStream().close();
192
193 int respCode = connection.getResponseCode();
194 if (respCode != HttpURLConnection.HTTP_OK) { // 返回不正常
195 //log.warn("Not HTTP_OK:\r\n" + url + "\r\n" + connection.getResponseMessage());
196 System.out.println("Not HTTP_OK:\r\n" + url + "\r\n" + connection.getResponseMessage());
197 return;
198 } else {
199 respData = getReponseData(request, response, connection);
200 getReponseDataEndtime=new Date();
201 if (respData == null || respData.length == 0) {
202 // 没有获取到响应数据
203 return;
204 }
205
206 }
207 } catch (MalformedURLException e) {
208 //log.error("Unable to parse URL!", e);
209 System.out.println("Unable to parse URL!"+ e);
210 return;
211 } catch (IOException e) {
212 //log.error("I/O Error!", e);
213 System.out.println("I/O Error!"+ e);
214 return;
215 } finally {
216 prepareResponse(request, response, connection);
217 }
218 // 响应内容根据connection的返回头信息来决定
219 Date setReponseDataBegintime=new Date();
220 Date setReponseDataEndtime=new Date();
221 String respContentType1 = connection.getContentType();
222 respContentType1 = (respContentType1 == null) ? "" : respContentType1;
223 String respContentType2 = respContentType1; // 返回给客户端的ContentType
224 String respCharacterEncoding = "UTF-8";
225 transferHeaderInfo2(request, response, connection);// 响应头信息转移
226 boolean isResponseText = true;
227 if(action != null)
228 {
229 if(action.equals("IMG"))
230 {
231 respContentType1="image/png";
232 respContentType2="image/png";
233 }
234 }
235 String respString = null;
236 if (respContentType1.indexOf("text/") >= 0) { // 文本类输入
237 isResponseText = true;
238 String str = tokenOfCharacterEncoding(respContentType1);
239 if (str != null) {
240 respCharacterEncoding = str;
241 respContentType2.replaceFirst(respCharacterEncoding, "UTF-8");
242 }
243 respString = new String(respData, respCharacterEncoding);
244 if (h_ACTION == null || h_ACTION.trim().equals("")) { // 标准的HTTP访问
245 response.setContentType(respContentType2); // 设置响应内容类型
246 response.setCharacterEncoding("UTF-8");
247 transferHeaderInfo2(request, response, connection);
248 PrintWriter out = response.getWriter();
249 out.print(respString); // 输出
250 out.close();
251 } else { // j2me客户端的做法,兼容性问题 <xiangxj>
252 response.setContentType(respContentType2); // 设置响应内容类型
253 // int beginIndex = respString.indexOf("<");
254 int beginIndex = respString.indexOf("<zhml>");
255 if (beginIndex < 0) {
256 // nothing to do
257 } else if (beginIndex == 0) {
258 // good
259 } else {
260 respString = respString.substring(beginIndex);
261 }
262 DataOutputStream out = new DataOutputStream(response
263 .getOutputStream());
264 out.writeUTF(respString);// 输出UTF数据格式
265 out.close();
266 }
267 } else {
268 isResponseText = false;
269 response.setContentType(respContentType2); // 设置响应内容类型
270 transferHeaderInfo2(request, response, connection);
271 BufferedOutputStream out = new BufferedOutputStream(response
272 .getOutputStream());
273 out.write(respData); // 输出
274 out.close();
275 }
276 setReponseDataEndtime=new Date();
277 // 打印响应信息
278 dumpResponseInfo(response, respData, respString, isResponseText);
279 serviceEndime=new Date();
280 dumpTimeinfo(serviceBegintime,serviceEndime,getReponseDataBegintime,getReponseDataEndtime,setReponseDataBegintime,setReponseDataEndtime);
281
282 //走单独上传图片的流程结束;
283 //----------------------modified by dcf end_2--------------------------
284
285 }else
286 {
287 //当没有图片上传时,全部走此处原来proxy的流程,get方式,代码与原来proxy完全相同。
288
289 boolean isRequestOK = true; // 请求体内容是否合适
290 String url = null; // 请求体内容被解析为代理需访问的url
291 try {
292 if (reqData == null || reqData.length == 0) {
293 throw new IllegalArgumentException();
294 }
295 url = new String(reqData, "UTF-8");
296 // 对于有些用writeUTF函数过来的数据
297 int beginIndex = url.indexOf("http://");
298 if (beginIndex < 0) {
299 throw new IllegalArgumentException("No \"http://\" Found!");
300 }
301 if (beginIndex > 0) {
302 url = url.substring(beginIndex);
303 }
304 } catch (Exception e) {
305 isRequestOK = false;
306 }
307 // 2.打印请求信息
308 //dumpRequestInfo(request, isRequestOK, reqData, url);
309 // 3.如果访问是有问题的,马上返回
310 if (!isRequestOK) {
311 prepareResponse(request, response,
312 HttpURLConnection.HTTP_BAD_REQUEST);
313 return;
314 }
315 // 4.访问url所指向的内容,返回一个字节数组
316 byte[] respData = null;
317 String h_ACTION = request.getHeader("ACTION");
318 HttpURLConnection connection = null;
319 Date getReponseDataBegintime=new Date();
320 Date getReponseDataEndtime=new Date();
321 try {
322 connection = (HttpURLConnection) (new URL(url).openConnection());
323 HttpURLConnection.setFollowRedirects(true);// 支持重定向
324 transferHeaderInfo1(request, response, connection);// 请求头信息转移
325 connection.connect();
326 int respCode = connection.getResponseCode();
327 if (respCode != HttpURLConnection.HTTP_OK) { // 返回不正常
328 //log.warn("Not HTTP_OK:\r\n" + url + "\r\n" + connection.getResponseMessage());
329 System.out.println("Not HTTP_OK:\r\n" + url + "\r\n" + connection.getResponseMessage());
330 return;
331 } else {
332 respData = getReponseData(request, response, connection);
333 getReponseDataEndtime=new Date();
334 if (respData == null || respData.length == 0) {
335 // 没有获取到响应数据
336 return;
337 }
338
339 }
340 } catch (MalformedURLException e) {
341 //log.error("Unable to parse URL!", e);
342 System.out.println("Unable to parse URL!"+ e);
343 return;
344 } catch (IOException e) {
345 //log.error("I/O Error!", e);
346 System.out.println("I/O Error!"+ e);
347 return;
348 } finally {
349 prepareResponse(request, response, connection);
350 }
351 // 响应内容根据connection的返回头信息来决定
352 Date setReponseDataBegintime=new Date();
353 Date setReponseDataEndtime=new Date();
354 String respContentType1 = connection.getContentType();
355 respContentType1 = (respContentType1 == null) ? "" : respContentType1;
356 String respContentType2 = respContentType1; // 返回给客户端的ContentType
357 String respCharacterEncoding = "UTF-8";
358 transferHeaderInfo2(request, response, connection);// 响应头信息转移
359 boolean isResponseText = true;
360 if(action != null)
361 {
362 if(action.equals("IMG"))
363 {
364 respContentType1="image/png";
365 respContentType2="image/png";
366 }
367 }
368 String respString = null;
369 if (respContentType1.indexOf("text/") >= 0) { // 文本类输入
370 isResponseText = true;
371 String str = tokenOfCharacterEncoding(respContentType1);
372 if (str != null) {
373 respCharacterEncoding = str;
374 respContentType2.replaceFirst(respCharacterEncoding, "UTF-8");
375 }
376 respString = new String(respData, respCharacterEncoding);
377 if (h_ACTION == null || h_ACTION.trim().equals("")) { // 标准的HTTP访问
378 response.setContentType(respContentType2); // 设置响应内容类型
379 response.setCharacterEncoding("UTF-8");
380 transferHeaderInfo2(request, response, connection);
381 PrintWriter out = response.getWriter();
382 out.print(respString); // 输出
383 out.close();
384 } else { // j2me客户端的做法,兼容性问题 <xiangxj>
385 response.setContentType(respContentType2); // 设置响应内容类型
386 // int beginIndex = respString.indexOf("<");
387 int beginIndex = respString.indexOf("<zhml>");
388 if (beginIndex < 0) {
389 // nothing to do
390 } else if (beginIndex == 0) {
391 // good
392 } else {
393 respString = respString.substring(beginIndex);
394 }
395 DataOutputStream out = new DataOutputStream(response
396 .getOutputStream());
397 out.writeUTF(respString);// 输出UTF数据格式
398 out.close();
399 }
400 } else {
401 isResponseText = false;
402 response.setContentType(respContentType2); // 设置响应内容类型
403 transferHeaderInfo2(request, response, connection);
404 BufferedOutputStream out = new BufferedOutputStream(response
405 .getOutputStream());
406 out.write(respData); // 输出
407 out.close();
408 }
409 setReponseDataEndtime=new Date();
410 // 打印响应信息
411 dumpResponseInfo(response, respData, respString, isResponseText);
412 serviceEndime=new Date();
413 dumpTimeinfo(serviceBegintime,serviceEndime,getReponseDataBegintime,getReponseDataEndtime,setReponseDataBegintime,setReponseDataEndtime);
414
415 }
416 }
417
418 private void dumpTimeinfo(Date serviceBegintime, Date serviceEndime,
419 Date getReponseDataBegintime, Date getReponseDataEndtime,
420 Date setReponseDataBegintime, Date setReponseDataEndtime) {
421 StringBuilder sb=new StringBuilder();
422 long serviceLong=serviceEndime.getTime()-serviceBegintime.getTime();
423 long getReponseDataLong=getReponseDataEndtime.getTime()-getReponseDataBegintime.getTime();
424 long setReponseDataLong=setReponseDataEndtime.getTime()-setReponseDataBegintime.getTime();
425 sb.append("serviceLong="+serviceLong);
426 sb.append(";getReponseDataLong="+getReponseDataLong);
427 sb.append(";setReponseDataLong="+setReponseDataLong);
428 System.out.println(sb.toString());
429
430 }
431
432 /**
433 * 打印请求信息
434 *
435 * @param request
436 * @param isRequestOK
437 * @param reqData
438 * @param url
439 */
440 private void dumpRequestInfo(HttpServletRequest request,
441 boolean isRequestOK, byte[] reqData, String url) {
442 //if (isRequestOK && log.isInfoEnabled()) { // 正常
443 if (isRequestOK) { // 正常
444 StringBuffer sb = new StringBuffer();
445 sb.append("request data\r\n");
446 sb.append("---- header information ----\r\n");
447 sb.append(request.getMethod());
448 sb.append(' ');
449 sb.append(request.getRequestURL());
450 sb.append(' ');
451 request.getProtocol();
452 sb.append("\r\n");
453 Enumeration headerNames = request.getHeaderNames();
454 while (headerNames.hasMoreElements()) {
455 String headerName = (String) headerNames.nextElement();
456 String headerValue = request.getHeader(headerName);
457 sb.append(headerName);
458 sb.append(": ");
459 sb.append(headerValue);
460 sb.append("\r\n");
461 }
462 sb.append("---- target url ----\r\n");
463 sb.append(url);
464 sb.append("\r\n");
465 //log.info(sb);
466 System.out.println(sb);
467 }
468 //if (!isRequestOK && log.isWarnEnabled()) { // 有问题的访问
469 if (!isRequestOK) { // 有问题的访问
470 StringBuffer sb = new StringBuffer();
471 sb.append("---- header information ----\r\n");
472 sb.append(request.getMethod());
473 sb.append(' ');
474 sb.append(request.getRequestURL());
475 sb.append(' ');
476 request.getProtocol();
477 sb.append("\r\n");
478 Enumeration headerNames = request.getHeaderNames();
479 while (headerNames.hasMoreElements()) {
480 String headerName = (String) headerNames.nextElement();
481 String headerValue = request.getHeader(headerName);
482 sb.append(headerName);
483 sb.append(": ");
484 sb.append(headerValue);
485 sb.append("\r\n");
486 }
487 sb.append("---- request data ----\r\n");
488 appendHexData(sb, reqData);
489 sb.append("\r\n");
490 //log.warn(sb);
491 System.out.println(sb);
492 }
493 }
494
495 /**
496 * 打印响应内容
497 *
498 * @param response
499 * @param respData
500 */
501 private void dumpResponseInfo(HttpServletResponse response,
502 byte[] respData, String respString, boolean isText) {
503 //if (isText && log.isInfoEnabled()) { // text
504 if (isText) { // text
505 StringBuffer sb = new StringBuffer();
506 sb.append("response data\r\n");
507 sb.append("Content-Type: ");
508 sb.append(response.getContentType());
509 sb.append("\r\n");
510 sb.append("Content-Length: ");
511 sb.append(respData.length);
512 sb.append("\r\n");
513 sb.append(respString);
514 sb.append("\r\n");
515 //log.info(sb.toString());
516 System.out.println(sb);
517 }
518 //if (!isText && log.isInfoEnabled()) { // binary
519 if (!isText) { // binary
520 StringBuffer sb = new StringBuffer();
521 sb.append("Content-Type: ");
522 sb.append(response.getContentType());
523 sb.append("\r\n");
524 sb.append("Content-Length: ");
525 sb.append(respData.length);
526 sb.append("\r\n");
527 appendHexData(sb, respData);
528 sb.append("\r\n");
529 //log.info(sb.toString());
530 System.out.println(sb);
531 }
532 }
533
534 /**
535 * 获取请求数据
536 *
537 * @param request
538 * @return
539 */
540 private byte[] getRequestData(HttpServletRequest request) {
541 try {
542 BufferedInputStream in = new BufferedInputStream(request
543 .getInputStream());
544 byte[] buffer = new byte[200 * 1024]; // 请求内容最大200K
545 int pos = 0;
546 int total = 0;
547 // 获取请求头长度
548 while (true) {
549 int n = in.read();
550 if (n == -1) {
551 break;
552 }
553 buffer[pos] = (byte) n;
554 ++pos;
555 ++total;
556 }
557 if (total > 0) {
558 byte[] data = new byte[total];
559 for (int i = 0; i < data.length; ++i) {
560 data[i] = buffer[i];
561 }
562 return data;
563 } else {
564 return null;
565 }
566 } catch (Exception e) {
567 //log.error("error occurs when fetching the request data!", e);
568 System.out.println("error occurs when fetching the request data!" + e);
569 return null;
570 }
571 }
572
573 /**
574 * 在访问Target URL之前先设置请求头信息
575 *
576 * @param request
577 * @param response
578 * @param connection
579 */
580 private void transferHeaderInfo1(HttpServletRequest request,
581 HttpServletResponse response, HttpURLConnection connection) {
582 Enumeration headerNames = request.getHeaderNames();
583 // List oldCookies = new ArrayList();
584 // String oldUserAgent = "";
585 while (headerNames.hasMoreElements()) {
586 String headerName = (String) (headerNames.nextElement());
587 if ("cookie".equalsIgnoreCase(headerName)) {
588 // oldCookies.add(request.getHeader(headerName));
589 continue;
590 }
591 if ("user-agent".equalsIgnoreCase(headerName)) {
592 // oldUserAgent = request.getHeader(headerName);
593 continue;
594 }
595 // 将原值赋予心的请求头中。如果上传图片,终端分次发送,程序会默认设置头部transfer-encoding,这里要去掉。
596 // modified by duchangfeng.
597 String headerValue = request.getHeader(headerName);
598 if(!headerName.equals("transfer-encoding")){
599 connection.setRequestProperty(headerName, headerValue);
600 //System.out.println("connection.setRequestProperty( “"+ headerName+ "”, “" + headerValue+"” )" );
601 }
602 }
603 // cookie策略
604 // String h_ID = request.getHeader("ID");
605 // if (h_ID == null || h_ID.lastIndexOf('~') < 0) {
606 // // 没有cookie
607 //
608 // }
609 // connection.addRequestProperty("cookie", "");
610 // user-agent
611 connection.setRequestProperty("user-agent",
612 "XiangXianJing/1.0 J2meProxy.Client/2006.12");
613 connection.setRequestProperty("connection","keep-alive");
614 }
615
616 /**
617 * 在返回数据给Client之前先设置响应头信息
618 *
619 * @param request
620 * @param response
621 * @param connection
622 */
623 private void transferHeaderInfo2(HttpServletRequest request,
624 HttpServletResponse response, HttpURLConnection connection) {
625 try {
626 Map headerFields = connection.getHeaderFields();
627 Object[] headerNames = headerFields.keySet().toArray();
628 for (int i = 0; i < headerNames.length; ++i) {
629 String headerName = (String) headerNames[i];
630 if (headerName == null) {
631 // HTTP/1.1 200 OK这一行是没有HeaderName的
632 continue;
633 }
634 if ("Server".equals(headerName)
635 || "Content-Type".equalsIgnoreCase(headerName)
636 || "Content-Length".equalsIgnoreCase(headerName)
637 || "Cookie".equalsIgnoreCase(headerName)
638 || "Transfer-Encoding".equalsIgnoreCase(headerName)) {
639 continue; // 过滤掉
640 }
641 String headerValue = connection.getHeaderField(headerName);
642 response.setHeader(headerName, headerValue);
643 }
644 } catch (Exception e) {
645 }
646 }
647
648 /**
649 * 返回给j2me客户端之前做一些操作
650 *
651 * @param request
652 * @param response
653 * @param respCode
654 */
655 private void prepareResponse(HttpServletRequest request,
656 HttpServletResponse response, int respCode) {
657 response.setStatus(respCode);
658 response.setHeader("Server",
659 "XiangXianJing/1.0 J2meProxy.Server/2006.12");
660 return;
661 }
662
663 /**
664 * 返回给j2me客户端之前做一些操作
665 *
666 * @param request
667 * @param response
668 * @param connection
669 */
670 private void prepareResponse(HttpServletRequest request,
671 HttpServletResponse response, HttpURLConnection connection) {
672 int respCode = HttpURLConnection.HTTP_OK;
673 try {
674 respCode = connection.getResponseCode();
675 } catch (Exception e) {
676 respCode = HttpURLConnection.HTTP_NO_CONTENT;
677 }
678 prepareResponse(request, response, respCode);
679 }
680
681 /**
682 * 获取URL的服务器端输出
683 *
684 * @param request
685 * @param response
686 * @param connection
687 * @return
688 * @throws IOException
689 */
690 private byte[] getReponseData(HttpServletRequest request,
691 HttpServletResponse response, HttpURLConnection connection)
692 throws IOException {
693 BufferedInputStream bis = new BufferedInputStream(connection
694 .getInputStream());
695
696 int total = 0;
697 int max = 1024 * 1024; // 支持最大1M的数据
698 byte[] buffer = new byte[max];
699
700 long end5 = System.currentTimeMillis();
701 long end6 ;
702 int n = 0;
703
704 while (true) {
705 // int len = bis.read(buffer, total, max-total );
706 int len = bis.read(buffer, total, max-total );
707
708 n++;
709 end6 = System.currentTimeMillis();
710 System.out.println("=====================>"+n+";"+len+";<=========;"+(end6 - end5)+";cl="+connection.getContentLength());
711 end5 = end6;
712
713 if(len != -1)
714 {
715 total += len;
716 }
717 else
718 {
719 break;
720 }
721 }
722
723 byte[] ba = new byte[total];
724 System.arraycopy(buffer, 0, ba, 0, total);
725 buffer = null;
726 // for (int i = 0; i < total; ++i) {
727 // ba[i] = buffer[i];
728 // }
729
730 if(bis != null)
731 {
732 try
733 {
734 bis.close();
735 }
736 catch(Exception e)
737 {
738 e.printStackTrace();
739 }
740
741 bis = null;
742 }
743
744 return ba;
745 }
746
747 /**
748 * 从Content-Type里面读取编码方式
749 *
750 * @param ContentType
751 * @return
752 */
753 private String tokenOfCharacterEncoding(String contentType) {
754 if (contentType == null || contentType.length() < 10) {
755 return null;
756 }
757 StringTokenizer tokenizer = new StringTokenizer(contentType, ";=");
758 while (tokenizer.hasMoreTokens()) {
759 String token = tokenizer.nextToken();
760 if (token.equalsIgnoreCase("charset")) {
761 if (tokenizer.hasMoreTokens()) {
762 return tokenizer.nextToken();
763 }
764 }
765 }
766 return null;
767 }
768
769 /**
770 * 将二进制数组追加到字符串中
771 *
772 * @param sb
773 * @param ba
774 */
775 private void appendHexData(StringBuffer sb, byte[] ba) {
776 if (ba == null || ba.length == 0) {
777 sb.append("null");
778 } else {
779 for (int i = 0; i < ba.length; ++i) {
780 int n = ba[i];
781 if (n < 0x00) {
782 n += 0x100;
783 }
784 if (n < 0x10) {
785 sb.append('0');
786 }
787 sb.append(Integer.toHexString(n));
788 if ((i % 32) == (32 - 1)) {
789 sb.append("\r\n");
790 } else {
791 sb.append(' ');
792 }
793 }
794 }
795 }
796 }
797
附不用上传的代码:
二者主要区别是接收图片时的不同,在proxy中,当有图片时,要用post方式转发给CMA工程,没有图片时用get即可。另外,有图片时,头文件设置要去掉一个属性,就是手机端发送大数据时默认加上去的transfer_encoding属性。
1
2 package com.freelynet.meta.j2me;
3
4 import java.io.BufferedInputStream;
5 import java.io.BufferedOutputStream;
6 import java.io.DataOutputStream;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.io.PrintWriter;
10 import java.net.HttpURLConnection;
11 import java.net.MalformedURLException;
12 import java.net.URL;
13 import java.util.Date;
14 import java.util.Enumeration;
15 import java.util.Map;
16 import java.util.StringTokenizer;
17
18 import javax.servlet.ServletException;
19 import javax.servlet.ServletRequest;
20 import javax.servlet.ServletResponse;
21 import javax.servlet.http.HttpServlet;
22 import javax.servlet.http.HttpServletRequest;
23 import javax.servlet.http.HttpServletResponse;
24
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27 import org.apache.log4j.BasicConfigurator;
28
29 /**
30 * 实现代理功能
31 * <p>
32 * 请求方只需将URL放在请求数据发送到proxy,然后由proxy去访问URL,并将数据返回给请求方。
33 * </p>
34 * <p>
35 * ---- added at 2006.12.21 ----<br/>有几点是需要注意的:<br/>
36 * <li>1.现在的J2ME客户端访问的时候都是带ACTION的,程序中以此作为区别普通客户端和J2ME客户端,在J2ME客户端访问的时候,proxy会将文本数据用writeUTF来输出.</li>
37 * <li>2.服务器端的数据输出有可能是writeUTF,也有可能是直接用out.print,所以当是J2ME客户端时,会忽略到<zhml>前面的所有数据,这主要是考虑到兼容性问题</li>
38 * <li>3.本proxy现只支持HTTP,如果后期有需要的话,还可以加上FTP等,可以根据url来判断然后再加上相应的实现
39 * </p>
40 *
41 * @version 1.0 Dec 20, 2006
42 * @author Xiang.Xian.Jing (xiangxj@gmail.com)
43 */
44 public class J2meProxy extends HttpServlet {
45
46 static {
47 BasicConfigurator.configure();
48 }
49
50 private static final long serialVersionUID = 1L;
51
52 //private static Log log = LogFactory.getLog(J2meProxy.class);
53
54 /**
55 * 实现代理功能
56 *
57 * @param req
58 * @param resp
59 * @throws ServletException
60 * @throws IOException
61 */
62 public void service(ServletRequest req, ServletResponse resp)
63 throws ServletException, IOException {
64 Date serviceBegintime=new Date();
65 Date serviceEndime=new Date();
66 HttpServletRequest request = (HttpServletRequest) req;
67 HttpServletResponse response = (HttpServletResponse) resp;
68 String action = request.getHeader("ACTION");
69 // 1.获取请求内容,并将其解析为代理需访问的http协议的url
70 byte[] reqData = getRequestData(request);
71 // 解析为url
72 boolean isRequestOK = true; // 请求体内容是否合适
73 String url = null; // 请求体内容被解析为代理需访问的url
74 try {
75 if (reqData == null || reqData.length == 0) {
76 throw new IllegalArgumentException();
77 }
78 url = new String(reqData, "UTF-8");
79 // 对于有些用writeUTF函数过来的数据
80 int beginIndex = url.indexOf("http://");
81 if (beginIndex < 0) {
82 throw new IllegalArgumentException("No \"http://\" Found!");
83 }
84 if (beginIndex > 0) {
85 url = url.substring(beginIndex);
86 }
87 } catch (Exception e) {
88 isRequestOK = false;
89 }
90 // 2.打印请求信息
91 //dumpRequestInfo(request, isRequestOK, reqData, url);
92 // 3.如果访问是有问题的,马上返回
93 if (!isRequestOK) {
94 prepareResponse(request, response,
95 HttpURLConnection.HTTP_BAD_REQUEST);
96 return;
97 }
98 // 4.访问url所指向的内容,返回一个字节数组
99 byte[] respData = null;
100 String h_ACTION = request.getHeader("ACTION");
101 HttpURLConnection connection = null;
102 Date getReponseDataBegintime=new Date();
103 Date getReponseDataEndtime=new Date();
104 try {
105 connection = (HttpURLConnection) (new URL(url).openConnection());
106 HttpURLConnection.setFollowRedirects(true);// 支持重定向
107 transferHeaderInfo1(request, response, connection);// 请求头信息转移
108 connection.connect();
109 int respCode = connection.getResponseCode();
110 if (respCode != HttpURLConnection.HTTP_OK) { // 返回不正常
111 //log.warn("Not HTTP_OK:\r\n" + url + "\r\n" + connection.getResponseMessage());
112 System.out.println("Not HTTP_OK:\r\n" + url + "\r\n" + connection.getResponseMessage());
113 return;
114 } else {
115 respData = getReponseData(request, response, connection);
116 getReponseDataEndtime=new Date();
117 if (respData == null || respData.length == 0) {
118 // 没有获取到响应数据
119 return;
120 }
121
122 }
123 } catch (MalformedURLException e) {
124 //log.error("Unable to parse URL!", e);
125 System.out.println("Unable to parse URL!"+ e);
126 return;
127 } catch (IOException e) {
128 //log.error("I/O Error!", e);
129 System.out.println("I/O Error!"+ e);
130 return;
131 } finally {
132 prepareResponse(request, response, connection);
133 }
134 // 响应内容根据connection的返回头信息来决定
135 Date setReponseDataBegintime=new Date();
136 Date setReponseDataEndtime=new Date();
137 String respContentType1 = connection.getContentType();
138 respContentType1 = (respContentType1 == null) ? "" : respContentType1;
139 String respContentType2 = respContentType1; // 返回给客户端的ContentType
140 String respCharacterEncoding = "UTF-8";
141 transferHeaderInfo2(request, response, connection);// 响应头信息转移
142 boolean isResponseText = true;
143 if(action != null)
144 {
145 if(action.equals("IMG"))
146 {
147 respContentType1="image/png";
148 respContentType2="image/png";
149 }
150 }
151 String respString = null;
152 if (respContentType1.indexOf("text/") >= 0) { // 文本类输入
153 isResponseText = true;
154 String str = tokenOfCharacterEncoding(respContentType1);
155 if (str != null) {
156 respCharacterEncoding = str;
157 respContentType2.replaceFirst(respCharacterEncoding, "UTF-8");
158 }
159 respString = new String(respData, respCharacterEncoding);
160 if (h_ACTION == null || h_ACTION.trim().equals("")) { // 标准的HTTP访问
161 response.setContentType(respContentType2); // 设置响应内容类型
162 response.setCharacterEncoding("UTF-8");
163 transferHeaderInfo2(request, response, connection);
164 PrintWriter out = response.getWriter();
165 out.print(respString); // 输出
166 out.close();
167 } else { // j2me客户端的做法,兼容性问题 <xiangxj>
168 response.setContentType(respContentType2); // 设置响应内容类型
169 // int beginIndex = respString.indexOf("<");
170 int beginIndex = respString.indexOf("<zhml>");
171 if (beginIndex < 0) {
172 // nothing to do
173 } else if (beginIndex == 0) {
174 // good
175 } else {
176 respString = respString.substring(beginIndex);
177 }
178 DataOutputStream out = new DataOutputStream(response
179 .getOutputStream());
180 out.writeUTF(respString);// 输出UTF数据格式
181 out.close();
182 }
183 } else {
184 isResponseText = false;
185 response.setContentType(respContentType2); // 设置响应内容类型
186 transferHeaderInfo2(request, response, connection);
187 BufferedOutputStream out = new BufferedOutputStream(response
188 .getOutputStream());
189 out.write(respData); // 输出
190 out.close();
191 }
192 setReponseDataEndtime=new Date();
193 // 打印响应信息
194 dumpResponseInfo(response, respData, respString, isResponseText);
195 serviceEndime=new Date();
196 dumpTimeinfo(serviceBegintime,serviceEndime,getReponseDataBegintime,getReponseDataEndtime,setReponseDataBegintime,setReponseDataEndtime);
197 }
198
199 private void dumpTimeinfo(Date serviceBegintime, Date serviceEndime,
200 Date getReponseDataBegintime, Date getReponseDataEndtime,
201 Date setReponseDataBegintime, Date setReponseDataEndtime) {
202 StringBuilder sb=new StringBuilder();
203 long serviceLong=serviceEndime.getTime()-serviceBegintime.getTime();
204 long getReponseDataLong=getReponseDataEndtime.getTime()-getReponseDataBegintime.getTime();
205 long setReponseDataLong=setReponseDataEndtime.getTime()-setReponseDataBegintime.getTime();
206 sb.append("serviceLong="+serviceLong);
207 sb.append(";getReponseDataLong="+getReponseDataLong);
208 sb.append(";setReponseDataLong="+setReponseDataLong);
209 System.out.println(sb.toString());
210
211 }
212
213 /**
214 * 打印请求信息
215 *
216 * @param request
217 * @param isRequestOK
218 * @param reqData
219 * @param url
220 */
221 private void dumpRequestInfo(HttpServletRequest request,
222 boolean isRequestOK, byte[] reqData, String url) {
223 //if (isRequestOK && log.isInfoEnabled()) { // 正常
224 if (isRequestOK) { // 正常
225 StringBuffer sb = new StringBuffer();
226 sb.append("request data\r\n");
227 sb.append("---- header information ----\r\n");
228 sb.append(request.getMethod());
229 sb.append(' ');
230 sb.append(request.getRequestURL());
231 sb.append(' ');
232 request.getProtocol();
233 sb.append("\r\n");
234 Enumeration headerNames = request.getHeaderNames();
235 while (headerNames.hasMoreElements()) {
236 String headerName = (String) headerNames.nextElement();
237 String headerValue = request.getHeader(headerName);
238 sb.append(headerName);
239 sb.append(": ");
240 sb.append(headerValue);
241 sb.append("\r\n");
242 }
243 sb.append("---- target url ----\r\n");
244 sb.append(url);
245 sb.append("\r\n");
246 //log.info(sb);
247 System.out.println(sb);
248 }
249 //if (!isRequestOK && log.isWarnEnabled()) { // 有问题的访问
250 if (!isRequestOK) { // 有问题的访问
251 StringBuffer sb = new StringBuffer();
252 sb.append("---- header information ----\r\n");
253 sb.append(request.getMethod());
254 sb.append(' ');
255 sb.append(request.getRequestURL());
256 sb.append(' ');
257 request.getProtocol();
258 sb.append("\r\n");
259 Enumeration headerNames = request.getHeaderNames();
260 while (headerNames.hasMoreElements()) {
261 String headerName = (String) headerNames.nextElement();
262 String headerValue = request.getHeader(headerName);
263 sb.append(headerName);
264 sb.append(": ");
265 sb.append(headerValue);
266 sb.append("\r\n");
267 }
268 sb.append("---- request data ----\r\n");
269 appendHexData(sb, reqData);
270 sb.append("\r\n");
271 //log.warn(sb);
272 System.out.println(sb);
273 }
274 }
275
276 /**
277 * 打印响应内容
278 *
279 * @param response
280 * @param respData
281 */
282 private void dumpResponseInfo(HttpServletResponse response,
283 byte[] respData, String respString, boolean isText) {
284 //if (isText && log.isInfoEnabled()) { // text
285 if (isText) { // text
286 StringBuffer sb = new StringBuffer();
287 sb.append("response data\r\n");
288 sb.append("Content-Type: ");
289 sb.append(response.getContentType());
290 sb.append("\r\n");
291 sb.append("Content-Length: ");
292 sb.append(respData.length);
293 sb.append("\r\n");
294 sb.append(respString);
295 sb.append("\r\n");
296 //log.info(sb.toString());
297 System.out.println(sb);
298 }
299 //if (!isText && log.isInfoEnabled()) { // binary
300 if (!isText) { // binary
301 StringBuffer sb = new StringBuffer();
302 sb.append("Content-Type: ");
303 sb.append(response.getContentType());
304 sb.append("\r\n");
305 sb.append("Content-Length: ");
306 sb.append(respData.length);
307 sb.append("\r\n");
308 appendHexData(sb, respData);
309 sb.append("\r\n");
310 //log.info(sb.toString());
311 System.out.println(sb);
312 }
313 }
314
315 /**
316 * 获取请求数据
317 *
318 * @param request
319 * @return
320 */
321 private byte[] getRequestData(HttpServletRequest request) {
322 try {
323 BufferedInputStream in = new BufferedInputStream(request
324 .getInputStream());
325 byte[] buffer = new byte[200 * 1024]; // 请求内容最大200K
326 int pos = 0;
327 int total = 0;
328 // 获取请求头长度
329 while (true) {
330 int n = in.read();
331 if (n == -1) {
332 break;
333 }
334 buffer[pos] = (byte) n;
335 ++pos;
336 ++total;
337 }
338 if (total > 0) {
339 byte[] data = new byte[total];
340 for (int i = 0; i < data.length; ++i) {
341 data[i] = buffer[i];
342 }
343 return data;
344 } else {
345 return null;
346 }
347 } catch (Exception e) {
348 //log.error("error occurs when fetching the request data!", e);
349 System.out.println("error occurs when fetching the request data!" + e);
350 return null;
351 }
352 }
353
354 /**
355 * 在访问Target URL之前先设置请求头信息
356 *
357 * @param request
358 * @param response
359 * @param connection
360 */
361 private void transferHeaderInfo1(HttpServletRequest request,
362 HttpServletResponse response, HttpURLConnection connection) {
363 Enumeration headerNames = request.getHeaderNames();
364 // List oldCookies = new ArrayList();
365 // String oldUserAgent = "";
366 while (headerNames.hasMoreElements()) {
367 String headerName = (String) (headerNames.nextElement());
368 if ("cookie".equalsIgnoreCase(headerName)) {
369 // oldCookies.add(request.getHeader(headerName));
370 continue;
371 }
372 if ("user-agent".equalsIgnoreCase(headerName)) {
373 // oldUserAgent = request.getHeader(headerName);
374 continue;
375 }
376 // 将原值赋予心的请求头中
377 String headerValue = request.getHeader(headerName);
378 connection.setRequestProperty(headerName, headerValue);
379 }
380 // cookie策略
381 // String h_ID = request.getHeader("ID");
382 // if (h_ID == null || h_ID.lastIndexOf('~') < 0) {
383 // // 没有cookie
384 //
385 // }
386 // connection.addRequestProperty("cookie", "");
387 // user-agent
388 connection.setRequestProperty("user-agent",
389 "XiangXianJing/1.0 J2meProxy.Client/2006.12");
390 connection.setRequestProperty("connection","keep-alive");
391 }
392
393 /**
394 * 在返回数据给Client之前先设置响应头信息
395 *
396 * @param request
397 * @param response
398 * @param connection
399 */
400 private void transferHeaderInfo2(HttpServletRequest request,
401 HttpServletResponse response, HttpURLConnection connection) {
402 try {
403 Map headerFields = connection.getHeaderFields();
404 Object[] headerNames = headerFields.keySet().toArray();
405 for (int i = 0; i < headerNames.length; ++i) {
406 String headerName = (String) headerNames[i];
407 if (headerName == null) {
408 // HTTP/1.1 200 OK这一行是没有HeaderName的
409 continue;
410 }
411 if ("Server".equals(headerName)
412 || "Content-Type".equalsIgnoreCase(headerName)
413 || "Content-Length".equalsIgnoreCase(headerName)
414 || "Cookie".equalsIgnoreCase(headerName)
415 || "Transfer-Encoding".equalsIgnoreCase(headerName)) {
416 continue; // 过滤掉
417 }
418 String headerValue = connection.getHeaderField(headerName);
419 response.setHeader(headerName, headerValue);
420 }
421 } catch (Exception e) {
422 }
423 }
424
425 /**
426 * 返回给j2me客户端之前做一些操作
427 *
428 * @param request
429 * @param response
430 * @param respCode
431 */
432 private void prepareResponse(HttpServletRequest request,
433 HttpServletResponse response, int respCode) {
434 response.setStatus(respCode);
435 response.setHeader("Server",
436 "XiangXianJing/1.0 J2meProxy.Server/2006.12");
437 return;
438 }
439
440 /**
441 * 返回给j2me客户端之前做一些操作
442 *
443 * @param request
444 * @param response
445 * @param connection
446 */
447 private void prepareResponse(HttpServletRequest request,
448 HttpServletResponse response, HttpURLConnection connection) {
449 int respCode = HttpURLConnection.HTTP_OK;
450 try {
451 respCode = connection.getResponseCode();
452 } catch (Exception e) {
453 respCode = HttpURLConnection.HTTP_NO_CONTENT;
454 }
455 prepareResponse(request, response, respCode);
456 }
457
458 /**
459 * 获取URL的服务器端输出
460 *
461 * @param request
462 * @param response
463 * @param connection
464 * @return
465 * @throws IOException
466 */
467 private byte[] getReponseData(HttpServletRequest request,
468 HttpServletResponse response, HttpURLConnection connection)
469 throws IOException {
470 BufferedInputStream bis = new BufferedInputStream(connection
471 .getInputStream());
472
473 int total = 0;
474 int max = 1024 * 1024; // 支持最大1M的数据
475 byte[] buffer = new byte[max];
476
477 long end5 = System.currentTimeMillis();
478 long end6 ;
479 int n = 0;
480
481 while (true) {
482 // int len = bis.read(buffer, total, max-total );
483 int len = bis.read(buffer, total, max-total );
484
485 n++;
486 end6 = System.currentTimeMillis();
487 System.out.println("=====================>"+n+";"+len+";<=========;"+(end6 - end5)+";cl="+connection.getContentLength());
488 end5 = end6;
489
490 if(len != -1)
491 {
492 total += len;
493 }
494 else
495 {
496 break;
497 }
498 }
499
500 byte[] ba = new byte[total];
501 System.arraycopy(buffer, 0, ba, 0, total);
502 buffer = null;
503 // for (int i = 0; i < total; ++i) {
504 // ba[i] = buffer[i];
505 // }
506
507 if(bis != null)
508 {
509 try
510 {
511 bis.close();
512 }
513 catch(Exception e)
514 {
515 e.printStackTrace();
516 }
517
518 bis = null;
519 }
520
521 return ba;
522 }
523
524 /**
525 * 从Content-Type里面读取编码方式
526 *
527 * @param ContentType
528 * @return
529 */
530 private String tokenOfCharacterEncoding(String contentType) {
531 if (contentType == null || contentType.length() < 10) {
532 return null;
533 }
534 StringTokenizer tokenizer = new StringTokenizer(contentType, ";=");
535 while (tokenizer.hasMoreTokens()) {
536 String token = tokenizer.nextToken();
537 if (token.equalsIgnoreCase("charset")) {
538 if (tokenizer.hasMoreTokens()) {
539 return tokenizer.nextToken();
540 }
541 }
542 }
543 return null;
544 }
545
546 /**
547 * 将二进制数组追加到字符串中
548 *
549 * @param sb
550 * @param ba
551 */
552 private void appendHexData(StringBuffer sb, byte[] ba) {
553 if (ba == null || ba.length == 0) {
554 sb.append("null");
555 } else {
556 for (int i = 0; i < ba.length; ++i) {
557 int n = ba[i];
558 if (n < 0x00) {
559 n += 0x100;
560 }
561 if (n < 0x10) {
562 sb.append('0');
563 }
564 sb.append(Integer.toHexString(n));
565 if ((i % 32) == (32 - 1)) {
566 sb.append("\r\n");
567 } else {
568 sb.append(' ');
569 }
570 }
571 }
572 }
573 }
574