All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.catalina.util.ProcessHelper Maven / Gradle / Ivy

There is a newer version: 5.5.23
Show newest version
/*
 * Copyright 1999,2004 The Apache Software Foundation.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.catalina.util;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import javax.servlet.http.HttpServletResponse;

/**
     * Encapsulates the knowledge of how to run a CGI script, given the
     * script's desired environment and (optionally) input/output streams
     *
     * 

* * Exposes a run method used to actually invoke the * CGI. * *

*

* * The CGI environment and settings are derived from the information * passed to the constuctor. * *

*

* * The input and output streams can be set by the setInput * and setResponse methods, respectively. *

* * @author Martin Dengler [[email protected]] * @version $Revision: 1.6 $, $Date: 2004/09/16 23:19:54 $ */ public class ProcessHelper { private static org.apache.commons.logging.Log log= org.apache.commons.logging.LogFactory.getLog( ProcessHelper.class ); /** script/command to be executed */ private String command = null; /** environment used when invoking the cgi script */ private Hashtable env = null; /** working directory used when invoking the cgi script */ private File wd = null; /** query parameters to be passed to the invoked script */ private Hashtable params = null; /** stdin to be passed to cgi script */ private InputStream stdin = null; /** response object used to set headers & get output stream */ private HttpServletResponse response = null; /** boolean tracking whether this object has enough info to run() */ private boolean readyToRun = false; /** the debugging detail level for this instance. */ private int debug = 0; /** the time in ms to wait for the client to send us CGI input data */ private int iClientInputTimeout; /** * Creates a ProcessHelper and initializes its environment, working * directory, and query parameters. *
* Input/output streams (optional) are set using the * setInput and setResponse methods, * respectively. * * @param command string full path to command to be executed * @param env Hashtable with the desired script environment * @param wd File with the script's desired working directory * @param params Hashtable with the script's query parameters */ public ProcessHelper( String command, Hashtable env, File wd, Hashtable params) { this.command = command; this.env = env; this.wd = wd; this.params = params; updateReadyStatus(); } /** * Checks & sets ready status */ protected void updateReadyStatus() { if (command != null && env != null && wd != null && params != null && response != null) { readyToRun = true; } else { readyToRun = false; } } /** * Gets ready status * * @return false if not ready (run will throw * an exception), true if ready */ public boolean isReady() { return readyToRun; } /** * Sets HttpServletResponse object used to set headers and send * output to * * @param response HttpServletResponse to be used * */ public void setResponse(HttpServletResponse response) { this.response = response; updateReadyStatus(); } /** * Sets standard input to be passed on to the invoked cgi script * * @param stdin InputStream to be used * */ public void setInput(InputStream stdin) { this.stdin = stdin; updateReadyStatus(); } /** * Converts a Hashtable to a String array by converting each * key/value pair in the Hashtable to a String in the form * "key=value" (hashkey + "=" + hash.get(hashkey).toString()) * * @param h Hashtable to convert * * @return converted string array * * @exception NullPointerException if a hash key has a null value * */ private String[] hashToStringArray(Hashtable h) throws NullPointerException { Vector v = new Vector(); Enumeration e = h.keys(); while (e.hasMoreElements()) { String k = e.nextElement().toString(); v.add(k + "=" + h.get(k)); } String[] strArr = new String[v.size()]; v.copyInto(strArr); return strArr; } /** * Executes a process script with the desired environment, current working * directory, and input/output streams * *

* This implements the following CGI specification recommedations: *

    *
  • Servers SHOULD provide the "query" component of * the script-URI as command-line arguments to scripts if it * does not contain any unencoded "=" characters and the * command-line arguments can be generated in an unambiguous * manner. *
  • Servers SHOULD set the AUTH_TYPE metavariable to the value * of the "auth-scheme" token of the * "Authorization" if it was supplied as part of the * request header. See getCGIEnvironment method. *
  • Where applicable, servers SHOULD set the current working * directory to the directory in which the script is located * before invoking it. *
  • Server implementations SHOULD define their behavior for the * following cases: *
      *
    • Allowed characters in pathInfo: This implementation * does not allow ASCII NUL nor any character which cannot * be URL-encoded according to internet standards; *
    • Allowed characters in path segments: This * implementation does not allow non-terminal NULL * segments in the the path -- IOExceptions may be thrown; *
    • "." and ".." path * segments: * This implementation does not allow "." and * ".." in the the path, and such characters * will result in an IOException being thrown; *
    • Implementation limitations: This implementation * does not impose any limitations except as documented * above. This implementation may be limited by the * servlet container used to house this implementation. * In particular, all the primary CGI variable values * are derived either directly or indirectly from the * container's implementation of the Servlet API methods. *
    *
*

* * For more information, see java.lang.Runtime#exec(String command, * String[] envp, File dir) * * @exception IOException if problems during reading/writing occur * */ public void run() throws IOException { /* * REMIND: this method feels too big; should it be re-written? */ if (!isReady()) { throw new IOException( this.getClass().getName() + ": not ready to run."); } if (log.isDebugEnabled()) { log.debug("runCGI(envp=[" + env + "], command=" + command + ")"); } if ((command.indexOf(File.separator + "." + File.separator) >= 0) || (command.indexOf(File.separator + "..") >= 0) || (command.indexOf(".." + File.separator) >= 0)) { throw new IOException( this.getClass().getName() + "Illegal Character in CGI command " + "path ('.' or '..') detected. Not " + "running CGI [" + command + "]."); } /* original content/structure of this section taken from * http://developer.java.sun.com/developer/ * bugParade/bugs/4216884.html * with major modifications by Martin Dengler */ Runtime rt = null; BufferedReader commandsStdOut = null; BufferedReader commandsStdErr = null; BufferedOutputStream commandsStdIn = null; Process proc = null; byte[] bBuf = new byte[1024]; char[] cBuf = new char[1024]; int bufRead = -1; //create query arguments Enumeration paramNames = params.keys(); StringBuffer cmdAndArgs = new StringBuffer(command); if (paramNames != null && paramNames.hasMoreElements()) { cmdAndArgs.append(" "); while (paramNames.hasMoreElements()) { String k = (String) paramNames.nextElement(); String v = params.get(k).toString(); if ((k.indexOf("=") < 0) && (v.indexOf("=") < 0)) { cmdAndArgs.append(k); cmdAndArgs.append("="); v = java.net.URLEncoder.encode(v); cmdAndArgs.append(v); cmdAndArgs.append(" "); } } } String postIn = getPostInput(params); int contentLength = (postIn.length() + System.getProperty("line.separator").length()); if ("POST".equals(env.get("REQUEST_METHOD"))) { env.put("CONTENT_LENGTH", new Integer(contentLength)); } rt = Runtime.getRuntime(); proc = rt.exec(cmdAndArgs.toString(), hashToStringArray(env), wd); /* * provide input to cgi * First -- parameters * Second -- any remaining input */ commandsStdIn = new BufferedOutputStream(proc.getOutputStream()); if (log.isDebugEnabled()) { log.debug("runCGI stdin=[" + stdin + "], qs=" + env.get("QUERY_STRING")); } if ("POST".equals(env.get("REQUEST_METHOD"))) { if (log.isDebugEnabled()) { log.debug("runCGI: writing ---------------\n"); log.debug(postIn); log.debug( "runCGI: new content_length=" + contentLength + "---------------\n"); } commandsStdIn.write(postIn.getBytes()); } if (stdin != null) { //REMIND: document this /* assume if nothing is available after a time, that nothing is * coming... */ if (stdin.available() <= 0) { if (log.isDebugEnabled()) { log.debug( "runCGI stdin is NOT available [" + stdin.available() + "]"); } try { Thread.sleep(iClientInputTimeout); } catch (InterruptedException ignored) { } } if (stdin.available() > 0) { if (log.isDebugEnabled()) { log.debug( "runCGI stdin IS available [" + stdin.available() + "]"); } bBuf = new byte[1024]; bufRead = -1; try { while ((bufRead = stdin.read(bBuf)) != -1) { if (log.isDebugEnabled()) { log.debug( "runCGI: read [" + bufRead + "] bytes from stdin"); } commandsStdIn.write(bBuf, 0, bufRead); } if (log.isDebugEnabled()) { log.debug("runCGI: DONE READING from stdin"); } } catch (IOException ioe) { log.error("runCGI: couldn't write all bytes.", ioe); } } } commandsStdIn.flush(); commandsStdIn.close(); /* we want to wait for the process to exit, Process.waitFor() * is useless in our situation; see * http://developer.java.sun.com/developer/ * bugParade/bugs/4223650.html */ boolean isRunning = true; commandsStdOut = new BufferedReader(new InputStreamReader(proc.getInputStream())); commandsStdErr = new BufferedReader(new InputStreamReader(proc.getErrorStream())); BufferedWriter servletContainerStdout = null; try { if (response.getOutputStream() != null) { servletContainerStdout = new BufferedWriter( new OutputStreamWriter(response.getOutputStream())); } } catch (IOException ignored) { //NOOP: no output will be written } while (isRunning) { try { //read stderr first cBuf = new char[1024]; while ((bufRead = commandsStdErr.read(cBuf)) != -1) { if (servletContainerStdout != null) { servletContainerStdout.write(cBuf, 0, bufRead); } } //set headers String line = null; while (((line = commandsStdOut.readLine()) != null) && !("".equals(line))) { if (log.isDebugEnabled()) { log.debug("runCGI: addHeader(\"" + line + "\")"); } if (line.startsWith("HTTP")) { //TODO: should set status codes (NPH support) /* * response.setStatus(getStatusCode(line)); */ } else { response.addHeader( line.substring(0, line.indexOf(":")).trim(), line.substring(line.indexOf(":") + 1).trim()); } } //write output cBuf = new char[1024]; while ((bufRead = commandsStdOut.read(cBuf)) != -1) { if (servletContainerStdout != null) { if (log.isDebugEnabled()) { log.debug("runCGI: write(\"" + new String(cBuf) + "\")"); } servletContainerStdout.write(cBuf, 0, bufRead); } } if (servletContainerStdout != null) { servletContainerStdout.flush(); } proc.exitValue(); // Throws exception if alive isRunning = false; } catch (IllegalThreadStateException e) { try { Thread.sleep(500); } catch (InterruptedException ignored) { } } } //replacement for Process.waitFor() } /** * Gets a string for input to a POST cgi script * * @param params Hashtable of query parameters to be passed to * the CGI script * @return for use as input to the CGI script */ protected String getPostInput(Hashtable params) { String lineSeparator = System.getProperty("line.separator"); Enumeration paramNames = params.keys(); StringBuffer postInput = new StringBuffer(""); StringBuffer qs = new StringBuffer(""); if (paramNames != null && paramNames.hasMoreElements()) { while (paramNames.hasMoreElements()) { String k = (String) paramNames.nextElement(); String v = params.get(k).toString(); if ((k.indexOf("=") < 0) && (v.indexOf("=") < 0)) { postInput.append(k); qs.append(k); postInput.append("="); qs.append("="); postInput.append(v); qs.append(v); postInput.append(lineSeparator); qs.append("&"); } } } qs.append(lineSeparator); return qs.append(postInput).toString(); } public int getIClientInputTimeout() { return iClientInputTimeout; } public void setIClientInputTimeout(int iClientInputTimeout) { this.iClientInputTimeout = iClientInputTimeout; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy