CgiServlet Class

package javaxt.http.servlet;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.StringTokenizer;

//******************************************************************************
//**  CGI Servlet
//******************************************************************************
/**
 *   Http Servlet used to run CGI programs. Based on CgiServlet.java, v1.8
 *   developed by Jef Poskanzer (acme.com).
 *
 ******************************************************************************/

public class CgiServlet extends HttpServlet {
    
    private java.io.File executable;
    
  //**************************************************************************
  //** Constructor
  //**************************************************************************
    
    public CgiServlet(java.io.File executable){
        this.executable = executable;
    }


  //**************************************************************************
  //** getServletInfo
  //**************************************************************************
  /** Returns a string containing information about the author, version, and 
   *  copyright of the servlet.
   */
    public String getServletInfo() {
        return "JavaXT CGI Servlet";
    }


  //**************************************************************************
  //** getParameters
  //**************************************************************************
  /** Returns a list of parameters that are used to instantiate the CGI 
   *  application.
   */
    protected java.util.ArrayList<String> getParameters(HttpServletRequest request){
        java.util.ArrayList<String> env = new java.util.ArrayList<String>();
        //env.add("PATH=" + "/usr/local/bin:/usr/ucb:/bin:/usr/bin");
        env.add("GATEWAY_INTERFACE=" + "CGI/1.1");
        env.add("SERVER_SOFTWARE=" + getServletContext().getServerInfo());
        env.add("SERVER_PROTOCOL=" + request.getProtocol());
        env.add("SERVER_NAME=" + request.getServerName());
        env.add("SERVER_PORT=" + request.getServerPort());
        env.add("REMOTE_ADDR=" + request.getRemoteAddr());
        env.add("REMOTE_HOST=" + request.getRemoteHost());
        env.add("REQUEST_METHOD=" + request.getMethod());
        env.add("SCRIPT_NAME=" + request.getServletPath());

        int contentLength = request.getContentLength();
        if (contentLength!=-1) env.add("CONTENT_LENGTH=" + contentLength);

        String contentType = request.getContentType();
        if (contentType!=null) env.add("CONTENT_TYPE=" + contentType);

        String pathInfo = request.getPathInfo();
        if (pathInfo!=null) env.add("PATH_INFO=" + pathInfo);

        String pathTranslated = request.getPathTranslated();
        if (pathTranslated!=null) env.add("PATH_TRANSLATED=" + pathTranslated);

        String queryString = request.getQueryString();
        if (queryString!=null) env.add("QUERY_STRING=" + queryString);

        String remoteUser = request.getRemoteUser();
        if (remoteUser!=null) env.add("REMOTE_USER=" + remoteUser);

        String authType = request.getAuthType();
        if (authType!=null) env.add("AUTH_TYPE=" + authType);

        java.util.Enumeration<String> hnEnum = request.getHeaderNames();
        while (hnEnum.hasMoreElements()) {
            String name = hnEnum.nextElement();
            String value = request.getHeader(name);
            if (value == null) value = "";
            env.add("HTTP_" + name.toUpperCase().replace('-', '_') + "=" + value);
        }
        return env;
    }


  //**************************************************************************
  //** processRequest
  //**************************************************************************
  /** Services a single request from the client.
   *  @param request the servlet request
   *  @param response the servlet response
   *  @exception ServletException when an exception has occurred
   */
    public void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String method = request.getMethod().toUpperCase();
        if (!(method.equals("GET") || method.equals("POST"))) {
            response.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
            return;
        }


      //Generate a list of parameters used to instantiate the CGI application
        java.util.ArrayList<String> env = getParameters(request);
        String[] parameters = new String[env.size()+1];
        parameters[0] = executable.toString();
        for (int i=0; i<parameters.length; i++){
            if (i>0) parameters[i] = env.get(i-1);
        }


        try{

          //Run executable via Command Line 
            Runtime runtime = Runtime.getRuntime();
            Process process = runtime.exec(parameters, null, executable.getParentFile());


          //If this is a POST, pass the body of the request to the process
            if (method.equals("POST")) {
                OutputStream outputStream = process.getOutputStream();
                InputStream inputStream = request.getInputStream();
                byte[] b = new byte[1024];
                int x=0;
                while ( (x = inputStream.read(b)) != -1) {
                    outputStream.write(b,0,x);
                }
                inputStream.close();
                outputStream.close();
            }


          //Parse output streams
            StreamReader s1 = new StreamReader(process.getInputStream(), response);
            ErrorStreamReader s2 = new ErrorStreamReader(process.getErrorStream());
            s1.start();
            s2.start();
            process.waitFor();
            s1.join();
            s2.join();


          //Explicitly clean up every the process by calling close on each stream
            try{process.getInputStream().close();} catch(Exception ex){}
            try{process.getErrorStream().close();} catch(Exception ex){}
            try{process.getOutputStream().close();} catch(Exception ex){}

          //Explicitly destroy the process even if the process is already terminated
            try{process.destroy();} catch(Exception ex){}
            
            process = null;
            
        }
        catch(IOException e){
            throw e;
        }
        catch(InterruptedException e){
            //throw e;
            return;
        }
    }


  //**************************************************************************
  //** StreamReader Class
  //**************************************************************************
  /** Thread used to process the standard output stream. */

    private class StreamReader implements Runnable {

        private InputStream is;
        private HttpServletResponse response;
        private Thread thread;
        private byte[] b = new byte[1];

        public StreamReader(InputStream is, HttpServletResponse response){
            this.is = is;
            this.response = response;
        }

        public void start() {
            thread = new Thread(this);
            thread.start();
        }

        public void run() {
            
            try {             
              //Parse the list few lines returned from the executable. These 
              //may contain HTTP response headers
                boolean firstLine = true;
                while (true) {
                    String line = readLine();
                    if (line==null) break;
                    line = line.trim();
                    if (line.equals("")) break;
                    
                    int colon = line.indexOf(":");
                    if (colon == -1) {
                         // No colon. If it's the first line, parse it for status.
                         if (firstLine) {
                              StringTokenizer tok = new StringTokenizer(line, " ");
                              try {
                                   switch (tok.countTokens()) {
                                   case 2:
                                        tok.nextToken();
                                        response.setStatus(Integer.parseInt(tok.nextToken()));
                                        break;
                                   case 3:
                                        tok.nextToken();
                                        response.setStatus(Integer.parseInt(tok.nextToken()), tok.nextToken());
                                        break;
                                   }
                              } catch (NumberFormatException ignore) {
                              }
                         } else {
                              // No colon and it's not the first line? Ignore.
                         }
                    } else {
                         // There's a colon. Check for certain special headers.
                         String name = line.substring(0, colon);
                         String value = line.substring(colon + 1).trim();
                         if (name.equalsIgnoreCase("Status")) {
                              StringTokenizer tok = new StringTokenizer(value, " ");
                              try {
                                   switch (tok.countTokens()) {
                                   case 1:
                                        response.setStatus(Integer.parseInt(tok.nextToken()));
                                        break;
                                   case 2:
                                        response.setStatus(Integer.parseInt(tok.nextToken()), tok.nextToken());
                                        break;
                                   }
                              } catch (NumberFormatException ignore) {
                              }
                         } else if (name.equalsIgnoreCase("Content-type")) {
                              response.setContentType(value);
                         } else if (name.equalsIgnoreCase("Content-length")) {
                              try {
                                   response.setContentLength(Integer.parseInt(value));
                              } catch (NumberFormatException ignore) {
                              }
                         } else if (name.equalsIgnoreCase("Location")) {
                              response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
                              response.setHeader(name, value);
                         } else if (name.equalsIgnoreCase("Set-Cookie")) {
                              int x = value.indexOf("=");
                              if (x > 0) {
                                   String n = value.substring(0, x);
                                   String v = value.substring(x + 1).trim();
                                   response.addCookie(new Cookie(n, v));
                              }
                         } else {
                              // Not a special header. Just set it.
                              response.setHeader(name, value);
                         }
                    }
                }

                
              //Set transfer encoding
                response.setHeader("Transfer-Encoding", "Chunked");
                

              //Tranfer remaining bytes from the standard output stream to
              //the servlet output stream
                OutputStream outputStream = response.getOutputStream();
                byte[] b = new byte[1024];
                int x=0;
                while ( (x = is.read(b)) != -1) {
                    outputStream.write(b,0,x);
                }

              //Close the input and output streams
                outputStream.close();
                is.close();

            }
            catch (IOException e) {
               // response.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR );
               // There's some weird bug in Java, when reading from a Process
               // you get a spurious IOException. We have to ignore it.
            }
            catch (Exception e) {
                e.printStackTrace();
                return;
            }
        }
        
        private String readLine() throws IOException {
            StringBuffer str = new StringBuffer();
            while (true){
                if (is.read(b)==-1) break;
                byte c = b[0];
                if (c=='\n') break;
                str.append((char) c);
            }
            return str.toString();
        }
        
        public void join() throws InterruptedException {
            thread.join();
        }

    } //End StreamReader Class


  //**************************************************************************
  //** ErrorStreamReader Class
  //**************************************************************************
  /** Thread used to read the standard output streams. */

    private class ErrorStreamReader implements Runnable {

        private InputStream is;
        private Thread thread;
        private byte[] b = new byte[1];

        public ErrorStreamReader(InputStream is){
            this.is = is;
        }

        public void start() {
            thread = new Thread(this);
            thread.start();
        }

        public void run() {
            try {
                while (true) {
                    if (is.read(b)==-1) break;
                }
                is.close();
            }
            catch (Exception e) {
                //System.out.println ("Problem reading stream... :" + ex);
                e.printStackTrace();
                return;
            }
        }

        public void join() throws InterruptedException {
            thread.join();
        }

    } //End ErrorStreamReader Class
}