Return-Path: Delivered-To: apmail-jakarta-tomcat-user-archive@apache.org Received: (qmail 41714 invoked from network); 23 Apr 2003 07:38:58 -0000 Received: from exchange.sun.com (192.18.33.10) by daedalus.apache.org with SMTP; 23 Apr 2003 07:38:58 -0000 Received: (qmail 13692 invoked by uid 97); 23 Apr 2003 07:41:06 -0000 Delivered-To: qmlist-jakarta-archive-tomcat-user@nagoya.betaversion.org Received: (qmail 13684 invoked from network); 23 Apr 2003 07:41:05 -0000 Received: from daedalus.apache.org (HELO apache.org) (208.185.179.12) by nagoya.betaversion.org with SMTP; 23 Apr 2003 07:41:05 -0000 Received: (qmail 40277 invoked by uid 500); 23 Apr 2003 07:38:25 -0000 Mailing-List: contact tomcat-user-help@jakarta.apache.org; run by ezmlm Precedence: bulk List-Unsubscribe: List-Subscribe: List-Help: List-Post: List-Id: "Tomcat Users List" Reply-To: "Tomcat Users List" Delivered-To: mailing list tomcat-user@jakarta.apache.org Received: (qmail 40260 invoked from network); 23 Apr 2003 07:38:25 -0000 Received: from smtp.easystreet.com (HELO easystreet01.easystreet.com) (206.26.36.40) by daedalus.apache.org with SMTP; 23 Apr 2003 07:38:25 -0000 Received: from MarcsLaptop (dsl-209-162-200-181.dsl.easystreet.com [209.162.200.181]) by easystreet01.easystreet.com (Postfix) with ESMTP id 9124F845344 for ; Wed, 23 Apr 2003 00:38:32 -0700 (PDT) Message-ID: <1ba501c3096b$47916f10$0a02a8c0@MarcsLaptop> From: "Marc Chamberlin" To: "Tomcat Users List" References: <3EA5DBBE.6020104@adfa.edu.au> Subject: Re: Help! CGI unreliable under Tomcat Date: Wed, 23 Apr 2003 00:38:05 -0700 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_NextPart_000_1BA2_01C30930.9B01C310" X-Priority: 3 X-MSMail-Priority: Normal X-Mailer: Microsoft Outlook Express 6.00.2800.1106 X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2800.1106 X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N ------=_NextPart_000_1BA2_01C30930.9B01C310 Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: 7bit Graham - I recently posted a request to this group requesting help installing Bugzilla on a Linux box using Tomcat as the server. I got one reply which essentially said this was not a good idea, but that was an inappropriate answer for our situation. Anywise, like you, we faced a lot of CGI/Tomcat issues because Bugzilla is based on a lot of Perl CGI scripts. So we have forged ahead and now got it working and I will describe what we found and did below. Some of this may apply to you, and some of this might be of interest to the Tomcat developers. In addition to what I describe below, we had to change the CGIServlet.java file and I have attached it to this email. Bugzilla and Tomcat's CGI-bin servlet don't get along with each other without mods. The issues that we ran into: 1) bugzilla depends on apache's ability to have different behaviors for the files in the same directory based on file extension. Especially, it depends on some files having content to be provided according to mime type, and other files to be executed to create HTML at standard out. 2) tomcat's cgibin behavior provided by CGIServlet.java does not provide for different behaviors as in (1) above. 3) tomcat's cgibin seems to have used perl as a replacement for the natural behaviors of exec() in unix. My suspicion is that this was done to make cgibin easier for people using perl under Windows. The problem is that when Perl is invoked by Perl, it cannot add certain behaviors -- specifically, cannot add "Templates" with a "-T" option. Thus the bugzilla scripts that depend on "-T" must be invoked directly, not by a parent perl "shell". Solution: 1) to eliminate issue (3) above, CGIServlet.java was modified so that when it builds a command to execute, it does not change the cgi commands from ... to perl ... 2) to provide for JPG files and HTML files to be presented directly for their content, the command prefixing capability that used to be used to use perl as a shell is used to invoke a new action that simply puts an HTML/mime header at the front and cat the file contents. % htmlcat This is done so that: a) *.html files are presented as HTML mime type and b) *.jpg files are presented as application/image-jpeg mime type This is done by providing a) new conditional code in CGIServlet.java -- see attached for details. b) the following short bash script made available in tomcat's execution path: htmlcat: #! /bin/bash #date >> /var/log/tomcat4/mine #echo PWD: $PWD >> /var/log/tomcat4/mine #echo $0: $* >> /var/log/tomcat4/mine echo HTTP/1.1 echo Content-Type: $1 echo cat $2 Hope this helps you, other CGI developers and Tomcat users. Marc..... ----- Original Message ----- From: "Graham Freeman" To: Sent: Tuesday, April 22, 2003 5:18 PM Subject: Help! CGI unreliable under Tomcat > (This message was sent last week, without reply.) > > I have successfully installed Tomcat 4.1.24 on port 80 on a Linux box, > with it processing JSP and servlets without error. I have set up a > simple CGI demonstration and enabled CGI in a container. I am observing > unpredictable behaviour. Sometimes (30% of the time) the CGI program > generates a successful response. On all other occasions, the web page > generated is > > HTTP Status 500 > Exception report > The server encountered an internal error () that prevented it from > fulfilling this request. > java.io.IOException: Broken pipe > at java.io.FileOutputStream.writeBytes(Native Method) > at java.io.FileOutputStream.write(FileOutputStream.java:257) > at > java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:69) > at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:127) > at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:128) > at > org.apache.catalina.servlets.CGIServlet$CGIRunner.run(CGIServlet.java:1683) > at org.apache.catalina.servlets.CGIServlet.doGet(CGIServlet.java:635) > at org.apache.catalina.servlets.CGIServlet.doPost(CGIServlet.java:597) > ... > > I understand that messages like this reported in a log file indicate > that connection to the client was lost. This does not seem to be the > case here; the client also receives this "broken pipe" message. > > Has anyone else observed this problem and found a way around it? > > The only connector I am using is the tomcat4.CoyoteConnector. > The CGI servlet is org.apache.catalina.servlets.CGIServlet. > The CGI program is a 4-line shell script. > #!/bin/sh > echo Content-type: text/html > echo > echo CGI is working fine > -- > ------------------------------------------------------------------------ > Dr Graham Freeman g-freeman@adfa.edu.au > School of Computer Science Phone +61-2-6268 8186 > Australian Defence Force Academy Fax +61-2-6268 8581 > > > > --------------------------------------------------------------------- > To unsubscribe, e-mail: tomcat-user-unsubscribe@jakarta.apache.org > For additional commands, e-mail: tomcat-user-help@jakarta.apache.org > > > ------=_NextPart_000_1BA2_01C30930.9B01C310 Content-Type: text/java; name="CGIServlet.java" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="CGIServlet.java" /* * CGIServlet.java $Header: = /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/servl= ets/CGIServlet.java,v 1.11 2002/12/04 21:09:07 amyroh Exp $ * $Revision: 1.11 $, $Date: 2002/12/04 21:09:07 $ * * = =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D * * The Apache Software License, Version 1.1 * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software = itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products = derived * from this software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * = =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * * */ package org.apache.catalina.servlets; import java.lang.Process; import java.io.File; import java.io.Writer; import java.io.Reader; import java.io.PrintWriter; import java.io.BufferedWriter; import java.io.BufferedReader; import java.io.InputStream; import java.io.OutputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.IOException; import java.net.URLEncoder; import java.util.Hashtable; import java.util.Vector; import java.util.Enumeration; import java.util.StringTokenizer; import java.util.Locale; import java.util.Date; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.ServletContext; import javax.servlet.ServletConfig; import javax.servlet.UnavailableException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import javax.servlet.http.Cookie; import org.apache.catalina.Context; import org.apache.catalina.Globals; import org.apache.catalina.Wrapper; // import org.apache.catalina.util.StringManager; /** * CGI-invoking servlet for web applications, used to execute scripts = which * comply to the Common Gateway Interface (CGI) specification and are = named * in the path-info used to invoke this servlet. * *

* Note: This code compiles and even works for simple CGI cases. * Exhaustive testing has not been done. Please consider it = beta * quality. Feedback is appreciated to the author (see = below). *

*

* * Example:
* If an instance of this servlet was mapped (using * <web-app>/WEB-INF/web.xml) to: *

*

* * <web-app>/cgi-bin/* * *

*

* then the following request: *

*

* * http://localhost:8080/<web-app>/cgi-bin/dir1/script/pathinfo1 * *

*

* would result in the execution of the script *

*

* * <web-app-root>/WEB-INF/cgi/dir1/script * *

*

* with the script's PATH_INFO set to = /pathinfo1. *

*

* Recommendation: House all your CGI scripts under * <webapp>/WEB-INF/cgi. This will ensure that you = do not * accidentally expose your cgi scripts' code to the outside world and = that * your cgis will be cleanly ensconced underneath the WEB-INF (i.e., * non-content) area. *

*

* The default CGI location is mentioned above. You have the = flexibility to * put CGIs wherever you want, however: *

*

* The CGI search path will start at * webAppRootDir + File.separator + cgiPathPrefix * (or webAppRootDir alone if cgiPathPrefix is * null). *

*

* cgiPathPrefix is defined by setting * this servlet's cgiPathPrefix init parameter *

* *

* * CGI Specification:
derived from * http://cgi-spec.golux.com. * A work-in-progress & expired Internet Draft. Note no actual RFC = describing * the CGI specification exists. Where the behavior of this servlet = differs * from the specification cited above, it is either documented here, a = bug, * or an instance where the specification cited differs from Best * Community Practice (BCP). * Such instances should be well-documented here. Please email the * Jakarta Tomcat group = [tomcat-dev@jakarta.apache.org] * with amendments. * *

*

* * Canonical metavariables:
* The CGI specification defines the following canonical metavariables: *
* [excerpt from CGI specification] *

 *  AUTH_TYPE
 *  CONTENT_LENGTH
 *  CONTENT_TYPE
 *  GATEWAY_INTERFACE
 *  PATH_INFO
 *  PATH_TRANSLATED
 *  QUERY_STRING
 *  REMOTE_ADDR
 *  REMOTE_HOST
 *  REMOTE_IDENT
 *  REMOTE_USER
 *  REQUEST_METHOD
 *  SCRIPT_NAME
 *  SERVER_NAME
 *  SERVER_PORT
 *  SERVER_PROTOCOL
 *  SERVER_SOFTWARE
 * 
*

* Metavariables with names beginning with the protocol name = (e.g., * "HTTP_ACCEPT") are also canonical in their description of request = header * fields. The number and meaning of these fields may change = independently * of this specification. (See also section 6.1.5 [of the CGI = specification].) *

* [end excerpt] * *

*

Implementation notes

*

* * standard input handling: If your script accepts standard = input, * then the client must start sending input within a certain timeout = period, * otherwise the servlet will assume no input is coming and carry on = running * the script. The script's the standard input will be closed and = handling of * any further input from the client is undefined. Most likely it will = be * ignored. If this behavior becomes undesirable, then this servlet = needs * to be enhanced to handle threading of the spawned process' stdin, = stdout, * and stderr (which should not be too hard). *
* If you find your cgi scripts are timing out receiving input, you can = set * the init parameter of your webapps' cgi-handling = servlet * to be *

*

* * Metavariable Values: According to the CGI specificion, * implementations may choose to represent both null or missing values = in an * implementation-specific manner, but must define that manner. This * implementation chooses to always define all required metavariables, = but * set the value to "" for all metavariables whose value is either null = or * undefined. PATH_TRANSLATED is the sole exception to this rule, as = per the * CGI Specification. * *

*

* * NPH -- Non-parsed-header implementation: This implementation = does * not support the CGI NPH concept, whereby server ensures that the data * supplied to the script are preceisely as supplied by the client and * unaltered by the server. *

*

* The function of a servlet container (including Tomcat) is = specifically * designed to parse and possible alter CGI-specific variables, and as * such makes NPH functionality difficult to support. *

*

* The CGI specification states that compliant servers MAY support NPH = output. * It does not state servers MUST support NPH output to be = unconditionally * compliant. Thus, this implementation maintains unconditional = compliance * with the specification though NPH support is not present. *

*

* * The CGI specification is located at * http://cgi-spec.golux.com. * *

*

*

TODO:

*
    *
  • Support for setting headers (for example, Location headers don't = work) *
  • Support for collapsing multiple header lines (per RFC 2616) *
  • Ensure handling of POST method does not interfere with 2.3 = Filters *
  • Refactor some debug code out of core *
  • Ensure header handling preserves encoding *
  • Possibly rewrite CGIRunner.run()? *
  • Possibly refactor CGIRunner and CGIEnvironment as non-inner = classes? *
  • Document handling of cgi stdin when there is no stdin *
  • Revisit IOException handling in CGIRunner.run() *
  • Better documentation *
  • Confirm use of ServletInputStream.available() in CGIRunner.run() = is * not needed *
  • Make checking for "." and ".." in servlet & cgi PATH_INFO less * draconian *
  • [add more to this TODO list] *
*

* * @author Martin T Dengler [root@martindengler.com] * @author Amy Roh * @version $Revision: 1.11 $, $Date: 2002/12/04 21:09:07 $ * @since Tomcat 4.0 * */ public class CGIServlet extends HttpServlet { /* some vars below copied from Craig R. McClanahan's InvokerServlet = */ /** the string manager for this package. */ /* YAGNI private static StringManager sm =3D StringManager.getManager(Constants.Package); */ /** the Context container associated with our web application. */ private ServletContext context =3D null; /** the debugging detail level for this servlet. */ private int debug =3D 0; /** the time in ms to wait for the client to send us CGI input data = */ private int iClientInputTimeout =3D 100; /** * The CGI search path will start at * webAppRootDir + File.separator + cgiPathPrefix * (or webAppRootDir alone if cgiPathPrefix is * null) */ private String cgiPathPrefix =3D null; /** * Sets instance variables. *

* Modified from Craig R. McClanahan's InvokerServlet *

* * @param config a ServletConfig = object * containing the servlet's * configuration and initialization * parameters * * @exception ServletException if an exception has occurred = that * interferes with the servlet's = normal * operation */ public void init(ServletConfig config) throws ServletException { super.init(config); // Verify that we were not accessed using the invoker servlet String servletName =3D getServletConfig().getServletName(); if (servletName =3D=3D null) servletName =3D ""; if (servletName.startsWith("org.apache.catalina.INVOKER.")) throw new UnavailableException ("Cannot invoke CGIServlet through the invoker"); // Set our properties from the initialization parameters String value =3D null; try { value =3D getServletConfig().getInitParameter("debug"); debug =3D Integer.parseInt(value); cgiPathPrefix =3D getServletConfig().getInitParameter("cgiPathPrefix"); value =3D = getServletConfig().getInitParameter("iClientInputTimeout"); iClientInputTimeout =3D Integer.parseInt(value); } catch (Throwable t) { //NOOP } log("init: loglevel set to " + debug); // Identify the internal container resources we need //Wrapper wrapper =3D (Wrapper) getServletConfig(); //context =3D (Context) wrapper.getParent(); context =3D config.getServletContext(); if (debug >=3D 1) { //log("init: Associated with Context '" + context.getPath() = + "'"); } } /** * Prints out important Servlet API and container information * *

* Copied from SnoopAllServlet by Craig R. McClanahan *

* * @param out ServletOutputStream as target of the information * @param req HttpServletRequest object used as source of = information * @param res HttpServletResponse object currently not used but = could * provide future information * * @exception IOException if a write operation exception occurs * */ protected void printServletEnvironment(ServletOutputStream out, HttpServletRequest req, HttpServletResponse res) throws = IOException { // Document the properties from ServletRequest out.println("

ServletRequest Properties

"); out.println("
    "); Enumeration attrs =3D req.getAttributeNames(); while (attrs.hasMoreElements()) { String attr =3D (String) attrs.nextElement(); out.println("
  • attribute " + attr + " =3D " + req.getAttribute(attr)); } out.println("
  • characterEncoding =3D " + req.getCharacterEncoding()); out.println("
  • contentLength =3D " + req.getContentLength()); out.println("
  • contentType =3D " + req.getContentType()); Enumeration locales =3D req.getLocales(); while (locales.hasMoreElements()) { Locale locale =3D (Locale) locales.nextElement(); out.println("
  • locale =3D " + locale); } Enumeration params =3D req.getParameterNames(); while (params.hasMoreElements()) { String param =3D (String) params.nextElement(); String values[] =3D req.getParameterValues(param); for (int i =3D 0; i < values.length; i++) out.println("
  • parameter " + param + " =3D " + values[i]); } out.println("
  • protocol =3D " + req.getProtocol()); out.println("
  • remoteAddr =3D " + req.getRemoteAddr()); out.println("
  • remoteHost =3D " + req.getRemoteHost()); out.println("
  • scheme =3D " + req.getScheme()); out.println("
  • secure =3D " + req.isSecure()); out.println("
  • serverName =3D " + req.getServerName()); out.println("
  • serverPort =3D " + req.getServerPort()); out.println("
"); out.println("
"); // Document the properties from HttpServletRequest out.println("

HttpServletRequest Properties

"); out.println("
    "); out.println("
  • authType =3D " + req.getAuthType()); out.println("
  • contextPath =3D " + req.getContextPath()); Cookie cookies[] =3D req.getCookies(); if (cookies!=3Dnull) { for (int i =3D 0; i < cookies.length; i++) out.println("
  • cookie " + cookies[i].getName() = +" =3D " +cookies[i].getValue()); } Enumeration headers =3D req.getHeaderNames(); while (headers.hasMoreElements()) { String header =3D (String) headers.nextElement(); out.println("
  • header " + header + " =3D " + req.getHeader(header)); } out.println("
  • method =3D " + req.getMethod()); out.println("
  • pathInfo =3D = " + req.getPathInfo()); out.println("
  • pathTranslated =3D " + req.getPathTranslated()); out.println("
  • queryString =3D " + req.getQueryString()); out.println("
  • remoteUser =3D " + req.getRemoteUser()); out.println("
  • requestedSessionId =3D " + req.getRequestedSessionId()); out.println("
  • requestedSessionIdFromCookie =3D " + req.isRequestedSessionIdFromCookie()); out.println("
  • requestedSessionIdFromURL =3D " + req.isRequestedSessionIdFromURL()); out.println("
  • requestedSessionIdValid =3D " + req.isRequestedSessionIdValid()); out.println("
  • requestURI =3D " + req.getRequestURI()); out.println("
  • servletPath =3D " + req.getServletPath()); out.println("
  • userPrincipal =3D " + req.getUserPrincipal()); out.println("
"); out.println("
"); // Document the servlet request attributes out.println("

ServletRequest Attributes

"); out.println("
    "); attrs =3D req.getAttributeNames(); while (attrs.hasMoreElements()) { String attr =3D (String) attrs.nextElement(); out.println("
  • " + attr + " =3D " + req.getAttribute(attr)); } out.println("
"); out.println("
"); // Process the current session (if there is one) HttpSession session =3D req.getSession(false); if (session !=3D null) { // Document the session properties out.println("

HttpSession Properties

"); out.println("
    "); out.println("
  • id =3D " + session.getId()); out.println("
  • creationTime =3D " + new Date(session.getCreationTime())); out.println("
  • lastAccessedTime =3D " + new Date(session.getLastAccessedTime())); out.println("
  • maxInactiveInterval =3D " + session.getMaxInactiveInterval()); out.println("
"); out.println("
"); // Document the session attributes out.println("

HttpSession Attributes

"); out.println("
    "); attrs =3D session.getAttributeNames(); while (attrs.hasMoreElements()) { String attr =3D (String) attrs.nextElement(); out.println("
  • " + attr + " =3D " + session.getAttribute(attr)); } out.println("
"); out.println("
"); } // Document the servlet configuration properties out.println("

ServletConfig Properties

"); out.println("
    "); out.println("
  • servletName =3D " + getServletConfig().getServletName()); out.println("
"); out.println("
"); // Document the servlet configuration initialization parameters out.println("

ServletConfig Initialization Parameters

"); out.println("
    "); params =3D getServletConfig().getInitParameterNames(); while (params.hasMoreElements()) { String param =3D (String) params.nextElement(); String value =3D getServletConfig().getInitParameter(param); out.println("
  • " + param + " =3D " + value); } out.println("
"); out.println("
"); // Document the servlet context properties out.println("

ServletContext Properties

"); out.println("
    "); out.println("
  • majorVersion =3D " + getServletContext().getMajorVersion()); out.println("
  • minorVersion =3D " + getServletContext().getMinorVersion()); out.println("
  • realPath('/') =3D " + getServletContext().getRealPath("/")); out.println("
  • serverInfo =3D " + getServletContext().getServerInfo()); out.println("
"); out.println("
"); // Document the servlet context initialization parameters out.println("

ServletContext Initialization = Parameters

"); out.println("
    "); params =3D getServletContext().getInitParameterNames(); while (params.hasMoreElements()) { String param =3D (String) params.nextElement(); String value =3D = getServletContext().getInitParameter(param); out.println("
  • " + param + " =3D " + value); } out.println("
"); out.println("
"); // Document the servlet context attributes out.println("

ServletContext Attributes

"); out.println("
    "); attrs =3D getServletContext().getAttributeNames(); while (attrs.hasMoreElements()) { String attr =3D (String) attrs.nextElement(); out.println("
  • " + attr + " =3D " + getServletContext().getAttribute(attr)); } out.println("
"); out.println("
"); } /** * Provides CGI Gateway service -- delegates to doGet * * @param req HttpServletRequest passed in by servlet container * @param res HttpServletResponse passed in by servlet container * * @exception ServletException if a servlet-specific exception = occurs * @exception IOException if a read/write exception occurs * * @see javax.servlet.http.HttpServlet * */ protected void doPost(HttpServletRequest req, HttpServletResponse = res) throws IOException, ServletException { doGet(req, res); } /** * Provides CGI Gateway service * * @param req HttpServletRequest passed in by servlet container * @param res HttpServletResponse passed in by servlet container * * @exception ServletException if a servlet-specific exception = occurs * @exception IOException if a read/write exception occurs * * @see javax.servlet.http.HttpServlet * */ protected void doGet(HttpServletRequest req, HttpServletResponse = res) throws ServletException, IOException { // Verify that we were not accessed using the invoker servlet if (req.getAttribute(Globals.INVOKED_ATTR) !=3D null) throw new UnavailableException ("Cannot invoke CGIServlet through the invoker"); CGIEnvironment cgiEnv =3D new CGIEnvironment(req, = getServletContext()); if (cgiEnv.isValid()) { CGIRunner cgi =3D new CGIRunner(cgiEnv.getCommand(), cgiEnv.getEnvironment(), cgiEnv.getWorkingDirectory(), cgiEnv.getParameters()); //if POST, we need to cgi.setInput //REMIND: how does this interact with Servlet API 2.3's = Filters?! if ("POST".equals(req.getMethod())) { cgi.setInput(req.getInputStream()); } cgi.setResponse(res); cgi.run(); } //REMIND: change to debug method or something if ((req.getParameter("X_TOMCAT_CGI_DEBUG") !=3D null) || (!cgiEnv.isValid())) { try { ServletOutputStream out =3D res.getOutputStream(); out.println("$Name: = $"); out.println("$Header: = /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/servl= ets/CGIServlet.java,v 1.11 2002/12/04 21:09:07 amyroh Exp $

"); if (cgiEnv.isValid()) { out.println(cgiEnv.toString()); } else { res.setStatus(404); out.println("

"); out.println("CGI script not found or not = specified."); out.println("

"); out.println("

"); out.println("Check the HttpServletRequest "); out.println("pathInfo "); out.println("property to see if it is what you meant = "); out.println("it to be. You must specify an existant = "); out.println("and executable file as part of the "); out.println("path-info."); out.println("

"); out.println("

"); out.println("For a good discussion of how CGI = scripts "); out.println("work and what their environment = variables "); out.println("mean, please visit the CGI "); out.println("Specification page."); out.println("

"); } printServletEnvironment(out, req, res); out.println(""); } catch (IOException ignored) { } } //debugging } //doGet /** For future testing use only; does nothing right now */ public static void main(String[] args) { System.out.println("$Header: = /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/servl= ets/CGIServlet.java,v 1.11 2002/12/04 21:09:07 amyroh Exp $"); } /** * Encapsulates the CGI environment and rules to derive * that environment from the servlet container and request = information. * *

*

* * @author Martin Dengler [root@martindengler.com] * @version $Revision: 1.11 $, $Date: 2002/12/04 21:09:07 $ * @since Tomcat 4.0 * */ protected class CGIEnvironment { /** context of the enclosing servlet */ private ServletContext context =3D null; /** context path of enclosing servlet */ private String contextPath =3D null; /** servlet URI of the enclosing servlet */ private String servletPath =3D null; /** pathInfo for the current request */ private String pathInfo =3D null; /** real file system directory of the enclosing servlet's web = app */ private String webAppRootDir =3D null; /** derived cgi environment */ private Hashtable env =3D null; /** cgi command to be invoked */ private String command =3D null; /** cgi command's desired working directory */ private File workingDirectory =3D null; /** cgi command's query parameters */ private Hashtable queryParameters =3D null; /** whether or not this object is valid or not */ private boolean valid =3D false; /** * Creates a CGIEnvironment and derives the necessary = environment, * query parameters, working directory, cgi command, etc. * * @param req HttpServletRequest for information provided = by * the Servlet API * @param context ServletContext for information provided by = the * Servlet API * */ protected CGIEnvironment(HttpServletRequest req, ServletContext context) { setupFromContext(context); setupFromRequest(req); queryParameters =3D new Hashtable(); Enumeration paramNames =3D req.getParameterNames(); while (paramNames !=3D null && paramNames.hasMoreElements()) = { String param =3D paramNames.nextElement().toString(); if (param !=3D null) { queryParameters.put( param, = URLEncoder.encode(req.getParameter(param))); } } this.valid =3D setCGIEnvironment(req); if (this.valid) { workingDirectory =3D new File(command.substring(0, command.lastIndexOf(File.separator))); } } /** * Uses the ServletContext to set some CGI variables * * @param context ServletContext for information provided by = the * Servlet API */ protected void setupFromContext(ServletContext context) { this.context =3D context; this.webAppRootDir =3D context.getRealPath("/"); } /** * Uses the HttpServletRequest to set most CGI variables * * @param req HttpServletRequest for information provided by * the Servlet API */ protected void setupFromRequest(HttpServletRequest req) { this.contextPath =3D req.getContextPath(); this.pathInfo =3D req.getPathInfo(); this.servletPath =3D req.getServletPath(); } /** * Resolves core information about the cgi script. * *

* Example URI: *

 /servlet/cgigateway/dir1/realCGIscript/pathinfo1 
*
    *
  • path =3D = $CATALINA_HOME/mywebapp/dir1/realCGIscript *
  • scriptName =3D = /servlet/cgigateway/dir1/realCGIscript *
  • cgiName =3D /dir1/realCGIscript *
  • name =3D realCGIscript *
*

*

* CGI search algorithm: search the real path below * <my-webapp-root> and find the first non-directory in * the getPathTranslated("/"), reading/searching from = left-to-right. *

*

* The CGI search path will start at * webAppRootDir + File.separator + cgiPathPrefix * (or webAppRootDir alone if cgiPathPrefix is * null). *

*

* cgiPathPrefix is defined by setting * this servlet's cgiPathPrefix init parameter * *

* * @param pathInfo String from = HttpServletRequest.getPathInfo() * @param webAppRootDir String from context.getRealPath("/") * @param contextPath String as from * HttpServletRequest.getContextPath() * @param servletPath String as from * HttpServletRequest.getServletPath() * @param cgiPathPrefix subdirectory of webAppRootDir below = which * the web app's CGIs may be stored; can = be null. * The CGI search path will start at * webAppRootDir + File.separator + = cgiPathPrefix * (or webAppRootDir alone if = cgiPathPrefix is * null). cgiPathPrefix is defined by = setting * the servlet's cgiPathPrefix init = parameter. * * * @return *
    *
  • * path - full file-system path to valid cgi = script, * or null if no cgi was found *
  • * scriptName - * CGI variable SCRIPT_NAME; the full URL = path * to valid cgi script or null if no cgi = was * found *
  • * cgiName - servlet pathInfo fragment = corresponding to * the cgi script itself, or null if not = found *
  • * name - simple name (no directories) of the * cgi script, or null if no cgi was = found *
* * @author Martin Dengler [root@martindengler.com] * @since Tomcat 4.0 */ protected String[] findCGI(String pathInfo, String = webAppRootDir, String contextPath, String = servletPath, String cgiPathPrefix) { String path =3D null; String name =3D null; String scriptname =3D null; String cginame =3D null; if ((webAppRootDir !=3D null) && (webAppRootDir.lastIndexOf(File.separator) =3D=3D (webAppRootDir.length() - 1))) { //strip the trailing "/" from the webAppRootDir webAppRootDir =3D webAppRootDir.substring(0, (webAppRootDir.length() - = 1)); } if (cgiPathPrefix !=3D null) { webAppRootDir =3D webAppRootDir + File.separator + cgiPathPrefix; } if (debug >=3D 2) { log("findCGI: path=3D" + pathInfo + ", " + = webAppRootDir); } File currentLocation =3D new File(webAppRootDir); StringTokenizer dirWalker =3D new StringTokenizer(pathInfo, File.separator); if (debug >=3D 3) { log("findCGI: currentLoc=3D" + currentLocation); } while (!currentLocation.isFile() && = dirWalker.hasMoreElements()) { if (debug >=3D 3) { log("findCGI: currentLoc=3D" + currentLocation); } currentLocation =3D new File(currentLocation, (String) = dirWalker.nextElement()); } if (!currentLocation.isFile()) { return new String[] { null, null, null, null }; } else { if (debug >=3D 2) { log("findCGI: FOUND cgi at " + currentLocation); } path =3D currentLocation.getAbsolutePath(); name =3D currentLocation.getName(); cginame =3D = currentLocation.getParent().substring(webAppRootDir.length()) + File.separator + name; if (".".equals(contextPath)) { scriptname =3D servletPath + cginame; } else { scriptname =3D contextPath + servletPath + cginame; } } if (debug >=3D 1) { log("findCGI calc: name=3D" + name + ", path=3D" + path + ", scriptname=3D" + scriptname + ", cginame=3D" + = cginame); } return new String[] { path, scriptname, cginame, name }; } /** * Constructs the CGI environment to be supplied to the invoked = CGI * script; relies heavliy on Servlet API methods and findCGI * * @param HttpServletRequest request associated with the CGI * invokation * * @return true if environment was set OK, false if there * was a problem and no environment was set */ protected boolean setCGIEnvironment(HttpServletRequest req) { /* * This method is slightly ugly; c'est la vie. * "You cannot stop [ugliness], you can only hope to contain = [it]" * (apologies to Marv Albert regarding MJ) */ Hashtable envp =3D new Hashtable(); String sPathInfoOrig =3D null; String sPathTranslatedOrig =3D null; String sPathInfoCGI =3D null; String sPathTranslatedCGI =3D null; String sCGIFullPath =3D null; String sCGIScriptName =3D null; String sCGIFullName =3D null; String sCGIName =3D null; String[] sCGINames; if (null !=3D req.getAttribute(Globals.SSI_FLAG_ATTR)) { // invoked by SSIServlet, which eats our = req.getPathInfo() data sPathInfoOrig =3D (String) = req.getAttribute(Globals.PATH_INFO_ATTR); } else { sPathInfoOrig =3D this.pathInfo; } sPathInfoOrig =3D sPathInfoOrig =3D=3D null ? "" : = sPathInfoOrig; sPathTranslatedOrig =3D req.getPathTranslated(); sPathTranslatedOrig =3D sPathTranslatedOrig =3D=3D null ? "" : = sPathTranslatedOrig; sCGINames =3D findCGI(sPathInfoOrig, webAppRootDir, contextPath, servletPath, cgiPathPrefix); sCGIFullPath =3D sCGINames[0]; sCGIScriptName =3D sCGINames[1]; sCGIFullName =3D sCGINames[2]; sCGIName =3D sCGINames[3]; if (sCGIFullPath =3D=3D null || sCGIScriptName =3D=3D null || sCGIFullName =3D=3D null || sCGIName =3D=3D null) { return false; } envp.put("SERVER_SOFTWARE", "TOMCAT"); envp.put("SERVER_NAME", nullsToBlanks(req.getServerName())); envp.put("GATEWAY_INTERFACE", "CGI/1.1"); envp.put("SERVER_PROTOCOL", = nullsToBlanks(req.getProtocol())); int port =3D req.getServerPort(); Integer iPort =3D (port =3D=3D 0 ? new Integer(-1) : new = Integer(port)); envp.put("SERVER_PORT", iPort.toString()); envp.put("REQUEST_METHOD", nullsToBlanks(req.getMethod())); /*- * PATH_INFO should be determined by using sCGIFullName: * 1) Let sCGIFullName not end in a "/" (see method findCGI) * 2) Let sCGIFullName equal the pathInfo fragment which * corresponds to the actual cgi script. * 3) Thus, PATH_INFO =3D request.getPathInfo().substring( * sCGIFullName.length()) * * (see method findCGI, where the real work is done) * */ if (pathInfo =3D=3D null || (pathInfo.substring(sCGIFullName.length()).length() = <=3D 0)) { sPathInfoCGI =3D ""; } else { sPathInfoCGI =3D = pathInfo.substring(sCGIFullName.length()); } envp.put("PATH_INFO", sPathInfoCGI); /*- * PATH_TRANSLATED must be determined after PATH_INFO (and = the * implied real cgi-script) has been taken into account. * * The following example demonstrates: * * servlet info =3D = /servlet/cgigw/dir1/dir2/cgi1/trans1/trans2 * cgifullpath =3D /servlet/cgigw/dir1/dir2/cgi1 * path_info =3D /trans1/trans2 * webAppRootDir =3D servletContext.getRealPath("/") * * path_translated =3D = servletContext.getRealPath("/trans1/trans2") * * That is, PATH_TRANSLATED =3D webAppRootDir + sPathInfoCGI * (unless sPathInfoCGI is null or blank, then the CGI * specification dictates that the PATH_TRANSLATED = metavariable * SHOULD NOT be defined. * */ if (sPathInfoCGI !=3D null && !("".equals(sPathInfoCGI))) { sPathTranslatedCGI =3D = context.getRealPath(sPathInfoCGI); } else { sPathTranslatedCGI =3D null; } if (sPathTranslatedCGI =3D=3D null || = "".equals(sPathTranslatedCGI)) { //NOOP } else { envp.put("PATH_TRANSLATED", = nullsToBlanks(sPathTranslatedCGI)); } envp.put("SCRIPT_NAME", nullsToBlanks(sCGIScriptName)); envp.put("QUERY_STRING", = nullsToBlanks(req.getQueryString())); envp.put("REMOTE_HOST", nullsToBlanks(req.getRemoteHost())); envp.put("REMOTE_ADDR", nullsToBlanks(req.getRemoteAddr())); envp.put("AUTH_TYPE", nullsToBlanks(req.getAuthType())); envp.put("REMOTE_USER", nullsToBlanks(req.getRemoteUser())); envp.put("REMOTE_IDENT", ""); //not necessary for full = compliance envp.put("CONTENT_TYPE", = nullsToBlanks(req.getContentType())); /* Note CGI spec says CONTENT_LENGTH must be NULL ("") or = undefined * if there is no content, so we cannot put 0 or -1 in as = per the * Servlet API spec. */ int contentLength =3D req.getContentLength(); String sContentLength =3D (contentLength <=3D 0 ? "" : (new = Integer(contentLength)).toString()); envp.put("CONTENT_LENGTH", sContentLength); Enumeration headers =3D req.getHeaderNames(); String header =3D null; while (headers.hasMoreElements()) { header =3D null; header =3D ((String) = headers.nextElement()).toUpperCase(); //REMIND: rewrite multiple headers as if received as = single //REMIND: change character set //REMIND: I forgot what the previous REMIND means if ("AUTHORIZATION".equalsIgnoreCase(header) || "PROXY_AUTHORIZATION".equalsIgnoreCase(header)) { //NOOP per CGI specification section 11.2 } else if("HOST".equalsIgnoreCase(header)) { String host =3D req.getHeader(header); int idx =3D host.indexOf(":"); if(idx < 0) idx =3D host.length(); envp.put("HTTP_" + header.replace('-', '_'), host.substring(0, idx)); } else { envp.put("HTTP_" + header.replace('-', '_'), req.getHeader(header)); } } command =3D sCGIFullPath; envp.put("X_TOMCAT_SCRIPT_PATH", command); //for kicks this.env =3D envp; return true; } /** * Print important CGI environment information in a easy-to-read = HTML * table * * @return HTML string containing CGI environment info * */ public String toString() { StringBuffer sb =3D new StringBuffer(); sb.append(""); sb.append(""); sb.append(""); sb.append(""); if (isValid()) { Enumeration envk =3D env.keys(); while (envk.hasMoreElements()) { String s =3D (String) envk.nextElement(); sb.append(""); } } sb.append(""); sb.append(""); sb.append(""); sb.append(""); Enumeration paramk =3D queryParameters.keys(); while (paramk.hasMoreElements()) { String s =3D paramk.nextElement().toString(); sb.append(""); } sb.append("
"); sb.append("CGIEnvironment Info
Debug Level"); sb.append(debug); sb.append("
Validity:"); sb.append(isValid()); sb.append("
"); sb.append(s); sb.append(""); sb.append(blanksToString((String) env.get(s), "[will be set to blank]")); sb.append("

Derived Command"); sb.append(nullsToBlanks(command)); sb.append("
Working Directory"); if (workingDirectory !=3D null) { sb.append(workingDirectory.toString()); } sb.append("
Query Params
"); sb.append(s); sb.append(""); sb.append(queryParameters.get(s).toString()); sb.append("

end."); return sb.toString(); } /** * Gets derived command string * * @return command string * */ protected String getCommand() { return command; } /** * Gets derived CGI working directory * * @return working directory * */ protected File getWorkingDirectory() { return workingDirectory; } /** * Gets derived CGI environment * * @return CGI environment * */ protected Hashtable getEnvironment() { return env; } /** * Gets derived CGI query parameters * * @return CGI query parameters * */ protected Hashtable getParameters() { return queryParameters; } /** * Gets validity status * * @return true if this environment is valid, false * otherwise * */ protected boolean isValid() { return valid; } /** * Converts null strings to blank strings ("") * * @param string to be converted if necessary * @return a non-null string, either the original or the empty = string * ("") if the original was null */ protected String nullsToBlanks(String s) { return nullsToString(s, ""); } /** * Converts null strings to another string * * @param string to be converted if necessary * @param string to return instead of a null string * @return a non-null string, either the original or the = substitute * string if the original was null */ protected String nullsToString(String couldBeNull, String subForNulls) { return (couldBeNull =3D=3D null ? subForNulls : = couldBeNull); } /** * Converts blank strings to another string * * @param string to be converted if necessary * @param string to return instead of a blank string * @return a non-null string, either the original or the = substitute * string if the original was null or = empty ("") */ protected String blanksToString(String couldBeBlank, String subForBlanks) { return (("".equals(couldBeBlank) || couldBeBlank =3D=3D = null) ? subForBlanks : couldBeBlank); } } //class CGIEnvironment /** * 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 [root@martindengler.com] * @version $Revision: 1.11 $, $Date: 2002/12/04 21:09:07 $ */ protected class CGIRunner { /** script/command to be executed */ private String command =3D null; /** environment used when invoking the cgi script */ private Hashtable env =3D null; /** working directory used when invoking the cgi script */ private File wd =3D null; /** query parameters to be passed to the invoked script */ private Hashtable params =3D null; /** stdin to be passed to cgi script */ private InputStream stdin =3D null; /** response object used to set headers & get output stream */ private HttpServletResponse response =3D null; /** boolean tracking whether this object has enough info to = run() */ private boolean readyToRun =3D false; /** * Creates a CGIRunner 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 * * @param res HttpServletResponse object for setting = headers * based on CGI script output * */ protected CGIRunner(String command, Hashtable env, File wd, Hashtable params) { this.command =3D command; this.env =3D env; this.wd =3D wd; this.params =3D params; updateReadyStatus(); } /** * Checks & sets ready status */ protected void updateReadyStatus() { if (command !=3D null && env !=3D null && wd !=3D null && params !=3D null && response !=3D null) { readyToRun =3D true; } else { readyToRun =3D false; } } /** * Gets ready status * * @return false if not ready (run will throw * an exception), true if ready */ protected boolean isReady() { return readyToRun; } /** * Sets HttpServletResponse object used to set headers and send * output to * * @param response HttpServletResponse to be used * */ protected void setResponse(HttpServletResponse response) { this.response =3D response; updateReadyStatus(); } /** * Sets standard input to be passed on to the invoked cgi script * * @param stdin InputStream to be used * */ protected void setInput(InputStream stdin) { this.stdin =3D 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=3Dvalue" (hashkey + "=3D" + = hash.get(hashkey).toString()) * * @param h Hashtable to convert * * @return converted string array * * @exception NullPointerException if a hash key has a null = value * */ protected String[] hashToStringArray(Hashtable h) throws NullPointerException { Vector v =3D new Vector(); Enumeration e =3D h.keys(); while (e.hasMoreElements()) { String k =3D e.nextElement().toString(); v.add(k + "=3D" + h.get(k)); } String[] strArr =3D new String[v.size()]; v.copyInto(strArr); return strArr; } /** * Executes a CGI 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 "=3D" 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. *
    *
*

* * @exception IOException if problems during reading/writing = occur * * @see java.lang.Runtime#exec(String command, String[] envp, * File dir) */ protected 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 (debug >=3D 1 ) { log("runCGI(envp=3D[" + env + "], command=3D" + command = + ")"); } if ((command.indexOf(File.separator + "." + File.separator) = >=3D 0) || (command.indexOf(File.separator + "..") >=3D 0) || (command.indexOf(".." + File.separator) >=3D 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 =3D null; BufferedReader commandsStdOut =3D null; BufferedReader commandsStdErr =3D null; BufferedOutputStream commandsStdIn =3D null; Process proc =3D null; int bufRead =3D -1; //create query arguments Enumeration paramNames =3D params.keys(); StringBuffer cmdAndArgs =3D new StringBuffer(command); if (paramNames !=3D null && paramNames.hasMoreElements()) { cmdAndArgs.append(" "); while (paramNames.hasMoreElements()) { String k =3D (String) paramNames.nextElement(); String v =3D params.get(k).toString(); if ((k.indexOf("=3D") < 0) && (v.indexOf("=3D") < = 0)) { cmdAndArgs.append(k); cmdAndArgs.append("=3D"); v =3D java.net.URLEncoder.encode(v); cmdAndArgs.append(v); cmdAndArgs.append(" "); } } } =20 /*String postIn =3D getPostInput(params); int contentLength =3D (postIn.length() + System.getProperty("line.separator").length()); if ("POST".equals(env.get("REQUEST_METHOD"))) { env.put("CONTENT_LENGTH", new Integer(contentLength)); }*/ =20 if (command.endsWith(".html") ) { StringBuffer perlCommand =3D new StringBuffer("htmlcat html = "); perlCommand.append(cmdAndArgs.toString()); cmdAndArgs =3D perlCommand; } if (command.endsWith(".jpg") ) { StringBuffer perlCommand =3D new StringBuffer("htmlcat = image/jpeg "); perlCommand.append(cmdAndArgs.toString()); cmdAndArgs =3D perlCommand; } if (command.endsWith(".pl") ) { StringBuffer perlCommand =3D new StringBuffer("perl "); perlCommand.append(cmdAndArgs.toString()); cmdAndArgs =3D perlCommand; } /* if ( command.endsWith(".cgi")) { StringBuffer perlCommand =3D new StringBuffer("bash "); perlCommand.append(cmdAndArgs.toString()); cmdAndArgs =3D perlCommand; } */ if (debug >=3D 1 ) { log(" cmdAndArgs=3D" + cmdAndArgs + ")"); } rt =3D Runtime.getRuntime(); proc =3D rt.exec(cmdAndArgs.toString(), = hashToStringArray(env), wd); /* * provide input to cgi * First -- parameters * Second -- any remaining input */ /*commandsStdIn =3D new = BufferedOutputStream(proc.getOutputStream()); if (debug >=3D 2 ) { log("runCGI stdin=3D[" + stdin + "], qs=3D" + env.get("QUERY_STRING")); } if ("POST".equals(env.get("REQUEST_METHOD"))) { if (debug >=3D 2) { log("runCGI: writing ---------------\n"); log(postIn); log("runCGI: new content_length=3D" + contentLength + "---------------\n"); } commandsStdIn.write(postIn.getBytes()); } if (stdin !=3D null) { //REMIND: document this /* assume if nothing is available after a time, that = nothing is * coming... */ /*if (stdin.available() <=3D 0) { if (debug >=3D 2 ) { log("runCGI stdin is NOT available [" + stdin.available() + "]"); } try { = Thread.currentThread().sleep(iClientInputTimeout); } catch (InterruptedException ignored) { } } if (stdin.available() > 0) { if (debug >=3D 2 ) { log("runCGI stdin IS available [" + stdin.available() + "]"); } byte[] bBuf =3D new byte[1024]; bufRead =3D -1; try { while ((bufRead =3D stdin.read(bBuf)) !=3D -1) { if (debug >=3D 2 ) { log("runCGI: read [" + bufRead + "] bytes from stdin"); } commandsStdIn.write(bBuf, 0, bufRead); } if (debug >=3D 2 ) { log("runCGI: DONE READING from stdin"); } } catch (IOException ioe) { //REMIND: replace with logging //REMIND: should I throw this exception? log("runCGI: couldn't write all bytes."); ioe.printStackTrace(); } } } commandsStdIn.flush(); commandsStdIn.close();*/ String sContentLength =3D (String) env.get("CONTENT_LENGTH"); if(!"".equals(sContentLength)) { commandsStdIn =3D new = BufferedOutputStream(proc.getOutputStream()); byte[] content =3D new byte[Integer.parseInt(sContentLength)]; int lenRead =3D stdin.read(content); if ("POST".equals(env.get("REQUEST_METHOD"))) { String paramStr =3D getPostInput(params); if (paramStr !=3D null) { byte[] paramBytes =3D paramStr.getBytes(); commandsStdIn.write(paramBytes); int contentLength =3D paramBytes.length; if (lenRead > 0) { String lineSep =3D = System.getProperty("line.separator"); commandsStdIn.write(lineSep.getBytes()); contentLength =3D lineSep.length() + lenRead; } env.put("CONTENT_LENGTH", new Integer(contentLength)); } } if (lenRead > 0) { commandsStdIn.write(content, 0, lenRead); } 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 =3D true; commandsStdOut =3D new BufferedReader (new InputStreamReader(proc.getInputStream())); commandsStdErr =3D new BufferedReader (new InputStreamReader(proc.getErrorStream())); BufferedWriter servletContainerStdout =3D null; try { if (response.getOutputStream() !=3D null) { servletContainerStdout =3D new BufferedWriter(new OutputStreamWriter (response.getOutputStream())); } } catch (IOException ignored) { //NOOP: no output will be written } final BufferedReader stdErrRdr =3D commandsStdErr ; new Thread() { public void run () { sendToLog(stdErrRdr) ; } ; }.start() ; while (isRunning) { try { //set headers String line =3D null; while (((line =3D commandsStdOut.readLine()) !=3D = null) && !("".equals(line))) { if (debug >=3D 2) { log("runCGI: addHeader(\"" + line + "\")"); } if (line.startsWith("HTTP")) { //TODO: should set status codes (NPH = support) /* * response.setStatus(getStatusCode(line)); */ } else if (line.indexOf(":") >=3D 0) { response.addHeader (line.substring(0, = line.indexOf(":")).trim(), line.substring(line.indexOf(":") + = 1).trim()); } else { log("runCGI: bad header line \"" + line + = "\""); } } //write output char[] cBuf =3D new char[1024]; while ((bufRead =3D commandsStdOut.read(cBuf)) !=3D = -1) { if (servletContainerStdout !=3D null) { if (debug >=3D 4) { log("runCGI: write(\"" + new = String(cBuf, 0, bufRead) + "\")"); } servletContainerStdout.write(cBuf, 0, = bufRead); } } if (servletContainerStdout !=3D null) { servletContainerStdout.flush(); } proc.exitValue(); // Throws exception if alive isRunning =3D false; } catch (IllegalThreadStateException e) { try { Thread.currentThread().sleep(500); } catch (InterruptedException ignored) { } } } //replacement for Process.waitFor() commandsStdOut.close() ; } private void sendToLog(BufferedReader rdr) { String line =3D null; int lineCount =3D 0 ; try { while ((line =3D rdr.readLine()) !=3D null) { log("runCGI (stderr):" + line) ; } lineCount++ ; } catch (IOException e) { log("sendToLog error", e) ; } finally { try { rdr.close() ; } catch (IOException ce) { log("sendToLog error", ce) ; } ; } ; if ( lineCount > 0 && debug > 2) { log("runCGI: " + lineCount + " lines received on = stderr") ; } ; } /** * 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 =3D = System.getProperty("line.separator"); Enumeration paramNames =3D params.keys(); StringBuffer postInput =3D new StringBuffer(""); StringBuffer qs =3D new StringBuffer(""); if (paramNames !=3D null && paramNames.hasMoreElements()) { while (paramNames.hasMoreElements()) { String k =3D (String) paramNames.nextElement(); String v =3D params.get(k).toString(); if ((k.indexOf("=3D") < 0) && (v.indexOf("=3D") < = 0)) { postInput.append(k); qs.append(k); postInput.append("=3D"); qs.append("=3D"); postInput.append(v); qs.append(v); postInput.append(lineSeparator); qs.append("&"); } } } qs.append(lineSeparator); return qs.append(postInput.toString()).toString(); } } //class CGIRunner } //class CGIServlet ------=_NextPart_000_1BA2_01C30930.9B01C310 Content-Type: text/plain; charset=us-ascii --------------------------------------------------------------------- To unsubscribe, e-mail: tomcat-user-unsubscribe@jakarta.apache.org For additional commands, e-mail: tomcat-user-help@jakarta.apache.org ------=_NextPart_000_1BA2_01C30930.9B01C310--