Logo Search packages:      
Sourcecode: paros version File versions  Download package

ProxyThread.java

/*
 * Created on May 25, 2004
 *
 * Paros and its related class files.
 * 
 * Paros is an HTTP/HTTPS proxy for assessing web application security.
 * Copyright (C) 2003-2004 Chinotec Technologies Company
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the Clarified Artistic License
 * as published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * Clarified Artistic License for more details.
 * 
 * You should have received a copy of the Clarified Artistic License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package org.parosproxy.paros.core.proxy;

//import java.io.BufferedInputStream;
//import java.io.BufferedOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
//import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.regex.Pattern;

import org.apache.commons.httpclient.URI;
//import org.apache.commons.httpclient.URIException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.parosproxy.paros.db.RecordHistory;
import org.parosproxy.paros.model.Model;
import org.parosproxy.paros.network.ConnectionParam;
import org.parosproxy.paros.network.HttpBody;
import org.parosproxy.paros.network.HttpHeader;
import org.parosproxy.paros.network.HttpInputStream;
import org.parosproxy.paros.network.HttpMessage;
import org.parosproxy.paros.network.HttpOutputStream;
import org.parosproxy.paros.network.HttpRequestHeader;
import org.parosproxy.paros.network.HttpSender;
import org.parosproxy.paros.network.HttpUtil;



/**
 *
 * To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Generation - Code and Comments
 */

00059 class ProxyThread implements Runnable {

      private static final int            TIME_OUT = 60000;
//    private static final int            BUFFEREDSTREAM_SIZE = 4096;
      private static final String         CONNECT_HTTP_200 = "HTTP/1.1 200 Connection established\r\nProxy-connection: Keep-alive\r\n\r\n";
//    private static ArrayList            processForwardList = new ArrayList();
    
      private static Log log = LogFactory.getLog(ProxyThread.class);
    
      // change httpSender to static to be shared among proxies to reuse keep-alive connections

      protected ProxyServer parentServer = null;
      protected ProxyParam proxyParam = null;
      protected ConnectionParam connectionParam = null;
      protected Thread thread = null;
      protected Socket inSocket     = null;
      protected Socket outSocket = null;
      protected HttpInputStream httpIn = null;
      protected HttpOutputStream httpOut = null;
      protected ProxyThread originProcess = this;
      
      private HttpSender            httpSender = null;
//    private BufferedOutputStream forwardOut = null;
//    private BufferedInputStream forwardIn = null;
//    private boolean disconnect = false;
      private Object semaphore = this;
      private static Object semaphoreSingleton = new Object();
//    private Thread forwardThread = null;
    
    private static Vector proxyThreadList = new Vector();
    
      ProxyThread(ProxyServer server, Socket socket) {

            inSocket = socket;
      try {
                  inSocket.setTcpNoDelay(true);
            inSocket.setSoTimeout(60000);
            } catch (SocketException e) {
            }

            thread = new Thread(this);
            thread.setDaemon(true);
            thread.setPriority(Thread.NORM_PRIORITY-1);
            parentServer = server;
            proxyParam = parentServer.getProxyParam();
            connectionParam = parentServer.getConnectionParam();
            
      }

      public void start() {
            thread.start();
        
      }

      private void beginSSL() throws IOException {

        boolean isSecure = true;
        HttpRequestHeader firstHeader = null;

        inSocket = HttpSender.getSSLConnector().createTunnelServerSocket(inSocket);
        
        httpIn = new HttpInputStream(inSocket);
        httpOut = new HttpOutputStream(inSocket.getOutputStream());
        
        firstHeader = httpIn.readRequestHeader(isSecure);
        processHttp(firstHeader, isSecure);
    }
      
      public void run() {
        proxyThreadList.add(thread);
            boolean isSecure = this instanceof ProxyThreadSSL;
            HttpRequestHeader firstHeader = null;
            
            try {
                  httpIn = new HttpInputStream(inSocket);
                  httpOut = new HttpOutputStream(inSocket.getOutputStream());
                  
                  firstHeader = httpIn.readRequestHeader(isSecure);
            
                  if (firstHeader.getMethod().equalsIgnoreCase(HttpRequestHeader.CONNECT)) {
                        httpOut.write(CONNECT_HTTP_200);
                httpOut.flush();
                        
                beginSSL();
                //processForwardPort();
                        
                  } else {
                        processHttp(firstHeader, isSecure);
                  }
            } catch (IOException e) {
                log.warn(e.getMessage());

            } finally {
            proxyThreadList.remove(thread);
                  disconnect();
            }
      }
      
      protected void processHttp(HttpRequestHeader requestHeader, boolean isSecure) throws IOException {

            HttpBody reqBody = null;
            boolean isFirstRequest = true;
            HttpMessage msg = null;
        
        if (isRecursive(requestHeader)) {
            throw new IOException("Recursive request to proxy itself stopped.");
        }
        
        // reduce socket timeout after first read
        inSocket.setSoTimeout(2500);

            do {

                  if (isFirstRequest) {
                        isFirstRequest = false;
                  } else {
                      try {
                          requestHeader = httpIn.readRequestHeader(isSecure);

                      } catch (SocketTimeoutException e) {
                          return;
                      }
                  }

                  msg = new HttpMessage();
                  msg.setRequestHeader(requestHeader);
                  
                  if (msg.getRequestHeader().getContentLength() > 0) {
                        reqBody           = httpIn.readBody(requestHeader);
                        msg.setRequestBody(reqBody);
                  }
            
                  modifyHeader(msg);
            
            if (isProcessCache(msg)) {
                continue;
            }
          
//            System.out.println("send required: " + msg.getRequestHeader().getURI().toString());
            
                  if (parentServer.isSerialize()) {
                      semaphore = semaphoreSingleton;
                  } else {
                      semaphore = this;
                  }
                  
                  synchronized (semaphore) {
                      
                      notifyListenerRequestSend(msg);
                      
                      
                      try {

//                            bug occur where response cannot be processed by various listener
//                        first so streaming feature was disabled               
//                            getHttpSender().sendAndReceive(msg, httpOut, buffer);

                              getHttpSender().sendAndReceive(msg);
                          notifyListenerResponseReceive(msg);
                          
                          // write out response header and body
                          httpOut.write(msg.getResponseHeader());
                        httpOut.flush();
                          
                          if (msg.getResponseBody().length() > 0) {
                              httpOut.write(msg.getResponseBody().getBytes());
                              httpOut.flush();
                          }
                          
//                        notifyWrittenToForwardProxy();
                          
                      } catch (IOException e) {
                          throw e;
                      }
                  }     // release semaphore
            

          } while (!isConnectionClose(msg) && !inSocket.isClosed());
    }
      
      private boolean isConnectionClose(HttpMessage msg) {
            
            if (msg == null || msg.getResponseHeader().isEmpty()) {
                return true;
            }
            
            if (msg.getRequestHeader().isConnectionClose()) {
                return true;
            }
                        
            if (msg.getResponseHeader().isConnectionClose()) {
                return true;
            }
        
        if (msg.getResponseHeader().getContentLength() == -1 && msg.getResponseBody().length() > 0) {
            // no length and body > 0 must terminate otherwise cannot there is no way for client browser to know the length.
            // terminate early can give better response by client.
            return true;
        }
            
            return false;
      }
      
      protected void disconnect() {
            try {
            if (httpIn != null) {
                httpIn.close();
            }
        } catch (Exception e) {}
        
        try {
            if (httpOut != null) {
                httpOut.close();
            }
        } catch (Exception e) {}
            HttpUtil.closeSocket(inSocket);
        
            if (httpSender != null) {
            httpSender.shutdown();
        }

      }
      
//    private void processForwardPort() {
//        StreamForwarder forwarder = null;
//        
//        setDisconnect(false);
//        
//        try {
//            
//            synchronized (processForwardList) {
//                outSocket = new Socket(proxyParam.getProxyIp(), parentServer.getForwardPort());
//                outSocket.setTcpNoDelay(true);
//                processForwardList.add(this);
//                forwardOut = new BufferedOutputStream(outSocket.getOutputStream());
//                forwardIn = new BufferedInputStream(outSocket.getInputStream());
//            }
//            
//            forwarder = new StreamForwarder(inSocket, httpIn, forwardOut);
//            forwarder.start();
//            
//            byte[] buffer = new byte[BUFFEREDSTREAM_SIZE*2];
//            int len = -1;
//            long startTime = System.currentTimeMillis();
//            int continuousCount = 0;
//            
//            outSocket.setSoTimeout(150);
//
//            do {
//                try {
//                    len = forwardIn.read(buffer);
//                    
//                    if (len > 0) {
//                        httpOut.write(buffer, 0, len);
//                        httpOut.flush();
//                        startTime = System.currentTimeMillis();
//                        continuousCount++;
//                        if (continuousCount % 10 == 9) Thread.yield(); // time slice to avoid same thread occupy all CPU time.
//                    }
//                } catch (SocketTimeoutException ex) {
//                      len = 0;
//                      continuousCount = 0;
//                }
//
//                if (len > 0) {
//                    continue;
//                } else {
//                    
//                    if (forwardIn.available() == 0) {
//                        setForwardInputBufferEmpty(true);
//                        if (System.currentTimeMillis() - startTime > TIME_OUT) {
//                            break;
//                        }
//                    }
//                }
//
//            } while (!isDisconnect());
//            
//        } catch (Exception e) {
//            //showErrMessage("Error connecting to internal SSL proxy.");
//        } finally {
//            
//            if (forwardThread != null) {
//                forwardThread.interrupt();
//            }
//            if (forwarder != null) {
//                forwarder.setStop(true);
//            }
//            removeFromList();     // end forward port processing
//            
//            HttpUtil.closeInputStream(forwardIn);
//            HttpUtil.closeOutputStream(forwardOut);
//            HttpUtil.closeSocket(outSocket);
//            
//        }
//        
//    }
      


//    protected void removeFromList() {
//          synchronized (processForwardList) {
//                processForwardList.remove(this);
//          }
//    }
//
//    protected synchronized boolean isDisconnect() {
//          return disconnect;
//    }
//
//    protected synchronized void setDisconnect(boolean flag) {
//          disconnect = flag;
//    }
//    
//    private boolean isForwardInputBufferEmpty = true;     
//
//    /**
//    Set if tunnel buffer empty.
//    @param      bufferEmpty true if tunnel buffer is empty
//    */
//    protected synchronized void setForwardInputBufferEmpty(boolean bufferEmpty) {
//          this.isForwardInputBufferEmpty = bufferEmpty;
//    }
//
//    /**
//    Check if tunnel buffer empty here
//    @return     true = tunnel buffer is empty
//    */
//    protected synchronized boolean isForwardInputBufferEmpty() {
//          try {
//                if (forwardIn.available() > 0) {
//                      setForwardInputBufferEmpty(false);
//                }
//          } catch (Exception e) {
//          }
//
//          return isForwardInputBufferEmpty;
//    }

//    protected static ProxyThread getOriginatingProcess (int remotePortUsing) {
//          ProxyThread process = null;
//          synchronized (processForwardList) {
//                for (int i=0;i<processForwardList.size();i++) {
//                      process = (ProxyThread) processForwardList.get(i);
//                      if (process.outSocket.getLocalPort() == remotePortUsing) {
//                            return process;
//                      }
//                }
//          }
//          return null;
//    }
//    
//    public Thread getThread() {
//          return thread;
//    }
//    
//    private void notifyWrittenToForwardProxy() {
//        if (originProcess == null) {
//            return;
//        }
//        
//          synchronized (originProcess) {
//                originProcess.setForwardInputBufferEmpty(false);
//                originProcess.notify();
//          }
//    }

      /**
       * Go through each observers to process a request in each observers.
       * The method can be modified in each observers.
       * @param httpMessage
       */
00431       private void notifyListenerRequestSend(HttpMessage httpMessage) {
            ProxyListener listener = null;
            List listenerList = parentServer.getListenerList();
            for (int i=0;i<listenerList.size();i++) {
                  listener = (ProxyListener) listenerList.get(i);
                  try {
                      listener.onHttpRequestSend(httpMessage);
                  } catch (Exception e) {
                  }
            }     
      }

      /**
       * Go thru each observers and process the http message in each observers.
       * The msg can be changed by each observers.
       * @param msg
       */
00448       private void notifyListenerResponseReceive(HttpMessage httpMessage) {
            ProxyListener listener = null;
            List listenerList = parentServer.getListenerList();
            for (int i=0;i<listenerList.size();i++) {
                  listener = (ProxyListener) listenerList.get(i);
                  try {
                      listener.onHttpResponseReceive(httpMessage);
                  } catch (Exception e) {
                  }
            }
      }
      
      private boolean isRecursive(HttpRequestHeader header) {
        boolean isRecursive = false;
        try {
            URI uri = header.getURI();
            if (uri.getHost().equals(proxyParam.getProxyIp())) {
                if (uri.getPort() == proxyParam.getProxyPort()) {
                    isRecursive = true;
                }
            }
        } catch (Exception e) {
        }
        return isRecursive;
    }
          
    private static final Pattern remove_gzip1 = Pattern.compile("(gzip|deflate|compress|x-gzip|x-compress)[^,]*,?\\s*", Pattern.CASE_INSENSITIVE);
    private static final Pattern remove_gzip2 = Pattern.compile("[,]\\z", Pattern.CASE_INSENSITIVE);
    
    private void modifyHeader(HttpMessage msg) {
        String encoding = msg.getRequestHeader().getHeader(HttpHeader.ACCEPT_ENCODING);
        if (encoding == null) {
            return;
        }
        
        encoding = remove_gzip1.matcher(encoding).replaceAll("");
        encoding = remove_gzip2.matcher(encoding).replaceAll("");
        // avoid returning gzip encoding
        
        if (encoding.length() == 0) {
            encoding = null;
        }
        msg.getRequestHeader().setHeader(HttpHeader.ACCEPT_ENCODING,encoding);
        
//        msg.getRequestHeader().setHeader("TE", "chunked;q=0");

    }
    
      protected HttpSender getHttpSender() {

          if (httpSender == null) {
                httpSender = new HttpSender(connectionParam, true);
            }

          return httpSender;
      }

    static boolean isAnyProxyThreadRunning() {
        return !proxyThreadList.isEmpty();
    }
    
    protected boolean isProcessCache(HttpMessage msg) throws IOException {
        if (!parentServer.isEnableCacheProcessing()) {
            return false;
        }
        
        if (parentServer.getCacheProcessingList().isEmpty()) {
            return false;
        }
        
        CacheProcessingItem item = (CacheProcessingItem) parentServer.getCacheProcessingList().get(0);
        if (msg.equals(item.message)) {
            HttpMessage newMsg = item.message.cloneAll();
            msg.setResponseHeader(newMsg.getResponseHeader());
            msg.setResponseBody(newMsg.getResponseBody());

            httpOut.write(msg.getResponseHeader());
            httpOut.flush();

            if (msg.getResponseBody().length() > 0) {
                httpOut.write(msg.getResponseBody().getBytes());
                httpOut.flush();

            }
            
            return true;
            
        } else {

            try {
                RecordHistory history = Model.getSingleton().getDb().getTableHistory().getHistoryCache(item.reference, msg);
                if (history == null) {
                    return false;
                }
                
                msg.setResponseHeader(history.getHttpMessage().getResponseHeader());
                msg.setResponseBody(history.getHttpMessage().getResponseBody());

                httpOut.write(msg.getResponseHeader());
                httpOut.flush();

                if (msg.getResponseBody().length() > 0) {
                    httpOut.write(msg.getResponseBody().getBytes());                    
                    httpOut.flush();

                }
//                System.out.println("cached:" + msg.getRequestHeader().getURI().toString());
                
                return true;
                
            } catch (Exception e) {
                return true;
            }
            
        }
        
        
//        return false;
        
    }
    
//    protected void setForwardThread(Thread forwardThread) {
//        this.forwardThread = forwardThread;
//    }
}

Generated by  Doxygen 1.6.0   Back to index