PageRenderTime 59ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/tomcat-8.0.9-sourcecode/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/HttpProxyService.java

https://gitlab.com/wenzhucjy/tomcat_source
Java | 368 lines | 233 code | 64 blank | 71 comment | 32 complexity | 7393abdf6618e9a3ad0d09dd6f649e10 MD5 | raw file
Possible License(s): Apache-2.0
  1. /*
  2. */
  3. package org.apache.tomcat.lite.proxy;
  4. import java.io.IOException;
  5. import java.util.logging.Level;
  6. import java.util.logging.Logger;
  7. import org.apache.tomcat.lite.http.HttpChannel;
  8. import org.apache.tomcat.lite.http.HttpConnector;
  9. import org.apache.tomcat.lite.http.HttpRequest;
  10. import org.apache.tomcat.lite.http.HttpResponse;
  11. import org.apache.tomcat.lite.http.MultiMap;
  12. import org.apache.tomcat.lite.http.HttpChannel.HttpService;
  13. import org.apache.tomcat.lite.http.HttpChannel.RequestCompleted;
  14. import org.apache.tomcat.lite.io.CBuffer;
  15. import org.apache.tomcat.lite.io.IOChannel;
  16. import org.apache.tomcat.lite.io.IOConnector;
  17. import org.apache.tomcat.lite.io.CBuffer;
  18. import org.apache.tomcat.lite.io.SocketConnector;
  19. /**
  20. * Http callback for the server-side. Will forward all requests to
  21. * a remote http server - either using proxy mode ( GET http://... ) or
  22. * forward requests ( GET /foo -> will be served by the remote server ).
  23. *
  24. * This is not blocking (except the connect, which currenly blocks on dns).
  25. */
  26. public class HttpProxyService implements HttpService {
  27. // target - when used in forwarding mode.
  28. String target = "localhost";
  29. int port = 8802;
  30. static Logger log = Logger.getLogger("HttpProxy");
  31. public static boolean debug = false;
  32. boolean keepOpen = true;
  33. // client side - this connect to the real server that generates the resp.
  34. ProxyClientCallback clientHeadersReceived = new ProxyClientCallback();
  35. HttpConnector httpConnector;
  36. IOConnector ioConnector;
  37. public HttpProxyService withSelector(IOConnector pool) {
  38. this.ioConnector = pool;
  39. return this;
  40. }
  41. public HttpProxyService withHttpClient(HttpConnector pool) {
  42. this.httpConnector = pool;
  43. return this;
  44. }
  45. public HttpProxyService withTarget(String host, int port) {
  46. this.target = host;
  47. this.port = port;
  48. return this;
  49. }
  50. private IOConnector getSelector() {
  51. if (ioConnector == null) {
  52. ioConnector = new SocketConnector();
  53. }
  54. return ioConnector;
  55. }
  56. private HttpConnector getHttpConnector() {
  57. if (httpConnector == null) {
  58. httpConnector = new HttpConnector(getSelector());
  59. }
  60. return httpConnector;
  61. }
  62. // Connects to the target CONNECT server, as client, forwards
  63. static class ProxyConnectClientConnection implements IOConnector.ConnectedCallback {
  64. IOChannel serverNet;
  65. private HttpChannel serverHttp;
  66. public ProxyConnectClientConnection(HttpChannel sproc) throws IOException {
  67. this.serverNet = sproc.getSink();
  68. this.serverHttp = sproc;
  69. }
  70. @Override
  71. public void handleConnected(IOChannel ioch) throws IOException {
  72. if (!ioch.isOpen()) {
  73. serverNet.close();
  74. log.severe("Connection failed");
  75. return;
  76. }
  77. afterClientConnect(ioch);
  78. ioch.setDataReceivedCallback(new CopyCallback(serverNet));
  79. //ioch.setDataFlushedCallback(new ProxyFlushedCallback(serverNet, ioch));
  80. serverNet.setDataReceivedCallback(new CopyCallback(ioch));
  81. //serverNet.setDataFlushedCallback(new ProxyFlushedCallback(ioch, serverNet));
  82. ioch.sendHandleReceivedCallback();
  83. }
  84. static byte[] OK = "HTTP/1.1 200 OK\r\n\r\n".getBytes();
  85. protected void afterClientConnect(IOChannel clientCh) throws IOException {
  86. serverNet.getOut().queue(OK);
  87. serverNet.startSending();
  88. serverHttp.release(); // no longer used
  89. }
  90. }
  91. /**
  92. * Parse the req, dispatch the connection.
  93. */
  94. @Override
  95. public void service(HttpRequest serverHttpReq, HttpResponse serverHttpRes)
  96. throws IOException {
  97. String dstHost = target; // default target ( for normal req ).
  98. int dstPort = port;
  99. // TODO: more flexibility/callbacks on selecting the target, acl, etc
  100. if (serverHttpReq.method().equals("CONNECT")) {
  101. // SSL proxy - just connect and forward all packets
  102. // TODO: optimize, add error checking
  103. String[] hostPort = serverHttpReq.requestURI().toString().split(":");
  104. String host = hostPort[0];
  105. int port = 443;
  106. if (hostPort.length > 1) {
  107. port = Integer.parseInt(hostPort[1]);
  108. }
  109. if (log.isLoggable(Level.FINE)) {
  110. HttpChannel server = serverHttpReq.getHttpChannel();
  111. log.info("NEW: " + server.getId() + " " + dstHost + " " +
  112. server.getRequest().getMethod() +
  113. " " + server.getRequest().getRequestURI() + " " +
  114. server.getIn());
  115. }
  116. try {
  117. getSelector().connect(host, port,
  118. new ProxyConnectClientConnection(serverHttpReq.getHttpChannel()));
  119. } catch (IOException e) {
  120. // TODO Auto-generated catch block
  121. e.printStackTrace();
  122. }
  123. return;
  124. }
  125. CBuffer origURIx = serverHttpReq.requestURI();
  126. // String origURI = origURIx.toString();
  127. // if (origURI.startsWith("http://")) {
  128. // // Real proxy - extract client address, modify the uri.
  129. // // TODO: optimize the strings.
  130. // int start = origURI.indexOf('/', 7);
  131. // String hostPortS = (start == -1) ?
  132. // origURI.subSequence(7, origURI.length()).toString() :
  133. // origURI.subSequence(7, start).toString();
  134. // String[] hostPort = hostPortS.split(":");
  135. //
  136. // dstHost = hostPort[0];
  137. // dstPort = (hostPort.length > 1) ? Integer.parseInt(hostPort[1]) :
  138. // 80;
  139. //
  140. // if (start >= 0) {
  141. // serverHttpReq.requestURI().set(origURI.substring(start));
  142. // } else {
  143. // serverHttpReq.requestURI().set("/");
  144. // }
  145. // } else {
  146. // Adjust the host header.
  147. CBuffer hostHdr =
  148. serverHttpReq.getMimeHeaders().getHeader("host");
  149. if (hostHdr != null) {
  150. hostHdr.recycle();
  151. CBuffer cb = hostHdr;
  152. cb.append(dstHost);
  153. if (dstPort != 80) {
  154. cb.append(':');
  155. cb.appendInt(dstPort);
  156. }
  157. }
  158. // }
  159. if (debug) {
  160. HttpChannel server = serverHttpReq.getHttpChannel();
  161. log.info("START: " + server.getId() + " " + dstHost + " " +
  162. server.getRequest().getMethod() +
  163. " " + server.getRequest().getRequestURI() + " " +
  164. server.getIn());
  165. }
  166. // Send the request with a non-blocking write
  167. HttpChannel serverHttp = serverHttpReq.getHttpChannel();
  168. // Client connection
  169. HttpChannel httpClient = getHttpConnector().get(dstHost, dstPort);
  170. serverHttp.getRequest().setAttribute("CLIENT", httpClient);
  171. httpClient.getRequest().setAttribute("SERVER", serverHttp);
  172. serverHttp.getRequest().setAttribute("P", httpClient);
  173. httpClient.getRequest().setAttribute("P", serverHttp);
  174. httpClient.setHttpService(clientHeadersReceived);
  175. // Will send the original request (TODO: small changes)
  176. // Response is not affected ( we use the callback )
  177. httpClient.getRequest().method().set(serverHttp.getRequest().method());
  178. httpClient.getRequest().requestURI().set(serverHttp.getRequest().requestURI());
  179. if (serverHttp.getRequest().queryString().length() != 0) {
  180. httpClient.getRequest().queryString().set(serverHttp.getRequest().queryString());
  181. }
  182. httpClient.getRequest().protocol().set(serverHttp.getRequest().protocol());
  183. //cstate.reqHeaders.addValue(name)
  184. copyHeaders(serverHttp.getRequest().getMimeHeaders(),
  185. httpClient.getRequest().getMimeHeaders() /*dest*/);
  186. // For debug
  187. httpClient.getRequest().getMimeHeaders().remove("Accept-Encoding");
  188. if (!keepOpen) {
  189. httpClient.getRequest().getMimeHeaders().setValue("Connection").set("Close");
  190. }
  191. // Any data
  192. serverHttp.setDataReceivedCallback(copy);
  193. copy.handleReceived(serverHttp);
  194. httpClient.send();
  195. //serverHttp.handleReceived(serverHttp.getSink());
  196. //httpClient.flush(); // send any data still there
  197. httpClient.setCompletedCallback(done);
  198. // Will call release()
  199. serverHttp.setCompletedCallback(done);
  200. serverHttpReq.async();
  201. }
  202. static HttpDoneCallback done = new HttpDoneCallback();
  203. static CopyCallback copy = new CopyCallback(null);
  204. // POST: after sendRequest(ch) we need to forward the body !!
  205. static void copyHeaders(MultiMap mimeHeaders, MultiMap dest)
  206. throws IOException {
  207. for (int i = 0; i < mimeHeaders.size(); i++) {
  208. CBuffer name = mimeHeaders.getName(i);
  209. CBuffer val = dest.addValue(name.toString());
  210. val.set(mimeHeaders.getValue(i));
  211. }
  212. }
  213. /**
  214. * HTTP _CLIENT_ callback - from tomcat to final target.
  215. */
  216. public class ProxyClientCallback implements HttpService {
  217. /**
  218. * Headers received from the client (content http server).
  219. * TODO: deal with version missmatches.
  220. */
  221. @Override
  222. public void service(HttpRequest clientHttpReq, HttpResponse clientHttpRes) throws IOException {
  223. HttpChannel serverHttp = (HttpChannel) clientHttpReq.getAttribute("SERVER");
  224. try {
  225. serverHttp.getResponse().setStatus(clientHttpRes.getStatus());
  226. serverHttp.getResponse().getMessageBuffer().set(clientHttpRes.getMessageBuffer());
  227. copyHeaders(clientHttpRes.getMimeHeaders(),
  228. serverHttp.getResponse().getMimeHeaders());
  229. serverHttp.getResponse().getMimeHeaders().addValue("TomcatProxy").set("True");
  230. clientHttpReq.getHttpChannel().setDataReceivedCallback(copy);
  231. copy.handleReceived(clientHttpReq.getHttpChannel());
  232. serverHttp.startSending();
  233. //clientHttpReq.flush(); // send any data still there
  234. // if (clientHttpReq.getHttpChannel().getIn().isClosedAndEmpty()) {
  235. // serverHttp.getOut().close(); // all data from client already in buffers
  236. // }
  237. } catch (IOException e) {
  238. // TODO Auto-generated catch block
  239. e.printStackTrace();
  240. }
  241. }
  242. }
  243. static final class HttpDoneCallback implements RequestCompleted {
  244. public HttpDoneCallback() {
  245. }
  246. @Override
  247. public void handle(HttpChannel doneCh, Object extraData) throws IOException {
  248. HttpChannel serverCh =
  249. (HttpChannel) doneCh.getRequest().getAttribute("SERVER");
  250. HttpChannel clientCh = doneCh;
  251. String tgt = "C";
  252. if (serverCh == null) {
  253. serverCh = doneCh;
  254. clientCh =
  255. (HttpChannel) doneCh.getRequest().getAttribute("CLIENT");
  256. tgt = "S";
  257. }
  258. if (serverCh == null || clientCh == null) {
  259. return;
  260. }
  261. if (doneCh.getError()) {
  262. serverCh.abort("Proxy error");
  263. clientCh.abort("Proxy error");
  264. return;
  265. }
  266. if (log.isLoggable(Level.FINE)) {
  267. HttpChannel peerCh =
  268. (HttpChannel) doneCh.getRequest().getAttribute("SERVER");
  269. if (peerCh == null) {
  270. peerCh =
  271. (HttpChannel) doneCh.getRequest().getAttribute("CLIENT");
  272. } else {
  273. }
  274. log.info(tgt + " " + peerCh.getId() + " " +
  275. doneCh.getTarget() + " " +
  276. doneCh.getRequest().getMethod() +
  277. " " + doneCh.getRequest().getRequestURI() + " " +
  278. doneCh.getResponse().getStatus() + " IN:" + doneCh.getIn()
  279. + " OUT:" + doneCh.getOut() +
  280. " SIN:" + peerCh.getIn() +
  281. " SOUT:" + peerCh.getOut() );
  282. }
  283. // stop forwarding. After this call the client object will be
  284. // recycled
  285. //clientCB.outBuffer = null;
  286. // We must releaes both at same time
  287. synchronized (this) {
  288. serverCh.complete();
  289. if (clientCh.getRequest().getAttribute("SERVER") == null) {
  290. return;
  291. }
  292. if (clientCh.isDone() && serverCh.isDone()) {
  293. clientCh.getRequest().setAttribute("SERVER", null);
  294. serverCh.getRequest().setAttribute("CLIENT", null);
  295. clientCh.getRequest().setAttribute("P", null);
  296. serverCh.getRequest().setAttribute("P", null);
  297. // Reuse the objects.
  298. serverCh.release();
  299. clientCh.release();
  300. }
  301. }
  302. }
  303. }
  304. }