|
|
Tomcat example source code file (CGIServlet.java)
This example Tomcat source code file (CGIServlet.java) is included in the DevDaily.com
"Java Source Code
Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.
The Tomcat CGIServlet.java source code
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.servlets;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Locale;
import java.util.StringTokenizer;
import java.util.Vector;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.UnavailableException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.catalina.Globals;
import org.apache.catalina.util.IOTools;
/**
* 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.
*
* <p>
* <i>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).</i>
* </p>
* <p>
*
* <b>Example:
* If an instance of this servlet was mapped (using
* <code><web-app>/WEB-INF/web.xml) to:
* </p>
* <p>
* <code>
* <web-app>/cgi-bin/*
* </code>
* </p>
* <p>
* then the following request:
* </p>
* <p>
* <code>
* http://localhost:8080/<web-app>/cgi-bin/dir1/script/pathinfo1
* </code>
* </p>
* <p>
* would result in the execution of the script
* </p>
* <p>
* <code>
* <web-app-root>/WEB-INF/cgi/dir1/script
* </code>
* </p>
* <p>
* with the script's <code>PATH_INFO set to /pathinfo1.
* </p>
* <p>
* Recommendation: House all your CGI scripts under
* <code><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.
* </p>
* <p>
* The default CGI location is mentioned above. You have the flexibility to
* put CGIs wherever you want, however:
* </p>
* <p>
* The CGI search path will start at
* webAppRootDir + File.separator + cgiPathPrefix
* (or webAppRootDir alone if cgiPathPrefix is
* null).
* </p>
* <p>
* cgiPathPrefix is defined by setting
* this servlet's cgiPathPrefix init parameter
* </p>
*
* <p>
*
* <B>CGI Specification: derived from
* <a href="http://cgi-spec.golux.com">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
* <a href="mailto:tomcat-dev@jakarta.apache.org">Jakarta Tomcat group [tomcat-dev@jakarta.apache.org]
* with amendments.
*
* </p>
* <p>
*
* <b>Canonical metavariables:
* The CGI specification defines the following canonical metavariables:
* <br>
* [excerpt from CGI specification]
* <PRE>
* 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
* </PRE>
* <p>
* Metavariables with names beginning with the protocol name (<EM>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].)
* </p>
* [end excerpt]
*
* </p>
* <h2> Implementation notes
* <p>
*
* <b>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).
* <br>
* If you find your cgi scripts are timing out receiving input, you can set
* the init parameter <code> of your webapps' cgi-handling servlet
* to be
* </p>
* <p>
*
* <b>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.
*
* </p>
* <p>
*
* <b>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.
* </p>
* <p>
* 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.
* </p>
* <p>
* 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.
* </p>
* <p>
*
* The CGI specification is located at
* <a href="http://cgi-spec.golux.com">http://cgi-spec.golux.com.
*
* </p>
* <p>
* <h3>TODO:
* <ul>
* <li> Support for setting headers (for example, Location headers don't work)
* <li> Support for collapsing multiple header lines (per RFC 2616)
* <li> Ensure handling of POST method does not interfere with 2.3 Filters
* <li> Refactor some debug code out of core
* <li> Ensure header handling preserves encoding
* <li> Possibly rewrite CGIRunner.run()?
* <li> Possibly refactor CGIRunner and CGIEnvironment as non-inner classes?
* <li> Document handling of cgi stdin when there is no stdin
* <li> Revisit IOException handling in CGIRunner.run()
* <li> Better documentation
* <li> Confirm use of ServletInputStream.available() in CGIRunner.run() is
* not needed
* <li> Make checking for "." and ".." in servlet & cgi PATH_INFO less
* draconian
* <li> [add more to this TODO list]
* </ul>
* </p>
*
* @author Martin T Dengler [root@martindengler.com]
* @author Amy Roh
* @version $Revision: 595801 $, $Date: 2007-11-16 21:07:07 +0100 (ven., 16 nov. 2007) $
* @since Tomcat 4.0
*
*/
public final class CGIServlet extends HttpServlet {
/* some vars below copied from Craig R. McClanahan's InvokerServlet */
/** the debugging detail level for this servlet. */
private int debug = 0;
/**
* The CGI search path will start at
* webAppRootDir + File.separator + cgiPathPrefix
* (or webAppRootDir alone if cgiPathPrefix is
* null)
*/
private String cgiPathPrefix = null;
/** the executable to use with the script */
private String cgiExecutable = "perl";
/** the encoding to use for parameters */
private String parameterEncoding = System.getProperty("file.encoding",
"UTF-8");
/** object used to ensure multiple threads don't try to expand same file */
static Object expandFileLock = new Object();
/** the shell environment variables to be passed to the CGI script */
static Hashtable<String,String> shellEnv = new Hashtable();
/**
* Sets instance variables.
* <P>
* Modified from Craig R. McClanahan's InvokerServlet
* </P>
*
* @param config a <code>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 = getServletConfig().getServletName();
if (servletName == null)
servletName = "";
if (servletName.startsWith("org.apache.catalina.INVOKER."))
throw new UnavailableException
("Cannot invoke CGIServlet through the invoker");
// Set our properties from the initialization parameters
if (getServletConfig().getInitParameter("debug") != null)
debug = Integer.parseInt(getServletConfig().getInitParameter("debug"));
cgiPathPrefix = getServletConfig().getInitParameter("cgiPathPrefix");
boolean passShellEnvironment =
Boolean.valueOf(getServletConfig().getInitParameter("passShellEnvironment")).booleanValue();
if (passShellEnvironment) {
try {
shellEnv.putAll(getShellEnvironment());
} catch (IOException ioe) {
ServletException e = new ServletException(
"Unable to read shell environment variables", ioe);
throw e;
}
}
if (getServletConfig().getInitParameter("executable") != null) {
cgiExecutable = getServletConfig().getInitParameter("executable");
}
if (getServletConfig().getInitParameter("parameterEncoding") != null) {
parameterEncoding = getServletConfig().getInitParameter("parameterEncoding");
}
}
/**
* Prints out important Servlet API and container information
*
* <p>
* Copied from SnoopAllServlet by Craig R. McClanahan
* </p>
*
* @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("<h1>ServletRequest Properties");
out.println("<ul>");
Enumeration attrs = req.getAttributeNames();
while (attrs.hasMoreElements()) {
String attr = (String) attrs.nextElement();
out.println("<li>attribute " + attr + " = " +
req.getAttribute(attr));
}
out.println("<li>characterEncoding = " +
req.getCharacterEncoding());
out.println("<li>contentLength = " +
req.getContentLength());
out.println("<li>contentType = " +
req.getContentType());
Enumeration locales = req.getLocales();
while (locales.hasMoreElements()) {
Locale locale = (Locale) locales.nextElement();
out.println("<li>locale = " + locale);
}
Enumeration params = req.getParameterNames();
while (params.hasMoreElements()) {
String param = (String) params.nextElement();
String values[] = req.getParameterValues(param);
for (int i = 0; i < values.length; i++)
out.println("<li>parameter " + param + " = " +
values[i]);
}
out.println("<li>protocol = " + req.getProtocol());
out.println("<li>remoteAddr = " + req.getRemoteAddr());
out.println("<li>remoteHost = " + req.getRemoteHost());
out.println("<li>scheme = " + req.getScheme());
out.println("<li>secure = " + req.isSecure());
out.println("<li>serverName = " + req.getServerName());
out.println("<li>serverPort = " + req.getServerPort());
out.println("</ul>");
out.println("<hr>");
// Document the properties from HttpServletRequest
out.println("<h1>HttpServletRequest Properties");
out.println("<ul>");
out.println("<li>authType = " + req.getAuthType());
out.println("<li>contextPath = " +
req.getContextPath());
Cookie cookies[] = req.getCookies();
if (cookies!=null) {
for (int i = 0; i < cookies.length; i++)
out.println("<li>cookie " + cookies[i].getName() +" = " +cookies[i].getValue());
}
Enumeration headers = req.getHeaderNames();
while (headers.hasMoreElements()) {
String header = (String) headers.nextElement();
out.println("<li>header " + header + " = " +
req.getHeader(header));
}
out.println("<li>method = " + req.getMethod());
out.println("<li>pathInfo = "
+ req.getPathInfo());
out.println("<li>pathTranslated = " +
req.getPathTranslated());
out.println("<li>queryString = " +
req.getQueryString());
out.println("<li>remoteUser = " +
req.getRemoteUser());
out.println("<li>requestedSessionId = " +
req.getRequestedSessionId());
out.println("<li>requestedSessionIdFromCookie = " +
req.isRequestedSessionIdFromCookie());
out.println("<li>requestedSessionIdFromURL = " +
req.isRequestedSessionIdFromURL());
out.println("<li>requestedSessionIdValid = " +
req.isRequestedSessionIdValid());
out.println("<li>requestURI = " +
req.getRequestURI());
out.println("<li>servletPath = " +
req.getServletPath());
out.println("<li>userPrincipal = " +
req.getUserPrincipal());
out.println("</ul>");
out.println("<hr>");
// Document the servlet request attributes
out.println("<h1>ServletRequest Attributes");
out.println("<ul>");
attrs = req.getAttributeNames();
while (attrs.hasMoreElements()) {
String attr = (String) attrs.nextElement();
out.println("<li>" + attr + " = " +
req.getAttribute(attr));
}
out.println("</ul>");
out.println("<hr>");
// Process the current session (if there is one)
HttpSession session = req.getSession(false);
if (session != null) {
// Document the session properties
out.println("<h1>HttpSession Properties");
out.println("<ul>");
out.println("<li>id = " +
session.getId());
out.println("<li>creationTime = " +
new Date(session.getCreationTime()));
out.println("<li>lastAccessedTime = " +
new Date(session.getLastAccessedTime()));
out.println("<li>maxInactiveInterval = " +
session.getMaxInactiveInterval());
out.println("</ul>");
out.println("<hr>");
// Document the session attributes
out.println("<h1>HttpSession Attributes");
out.println("<ul>");
attrs = session.getAttributeNames();
while (attrs.hasMoreElements()) {
String attr = (String) attrs.nextElement();
out.println("<li>" + attr + " = " +
session.getAttribute(attr));
}
out.println("</ul>");
out.println("<hr>");
}
// Document the servlet configuration properties
out.println("<h1>ServletConfig Properties");
out.println("<ul>");
out.println("<li>servletName = " +
getServletConfig().getServletName());
out.println("</ul>");
out.println("<hr>");
// Document the servlet configuration initialization parameters
out.println("<h1>ServletConfig Initialization Parameters");
out.println("<ul>");
params = getServletConfig().getInitParameterNames();
while (params.hasMoreElements()) {
String param = (String) params.nextElement();
String value = getServletConfig().getInitParameter(param);
out.println("<li>" + param + " = " + value);
}
out.println("</ul>");
out.println("<hr>");
// Document the servlet context properties
out.println("<h1>ServletContext Properties");
out.println("<ul>");
out.println("<li>majorVersion = " +
getServletContext().getMajorVersion());
out.println("<li>minorVersion = " +
getServletContext().getMinorVersion());
out.println("<li>realPath('/') = " +
getServletContext().getRealPath("/"));
out.println("<li>serverInfo = " +
getServletContext().getServerInfo());
out.println("</ul>");
out.println("<hr>");
// Document the servlet context initialization parameters
out.println("<h1>ServletContext Initialization Parameters");
out.println("<ul>");
params = getServletContext().getInitParameterNames();
while (params.hasMoreElements()) {
String param = (String) params.nextElement();
String value = getServletContext().getInitParameter(param);
out.println("<li>" + param + " = " + value);
}
out.println("</ul>");
out.println("<hr>");
// Document the servlet context attributes
out.println("<h1>ServletContext Attributes");
out.println("<ul>");
attrs = getServletContext().getAttributeNames();
while (attrs.hasMoreElements()) {
String attr = (String) attrs.nextElement();
out.println("<li>" + attr + " = " +
getServletContext().getAttribute(attr));
}
out.println("</ul>");
out.println("<hr>");
}
/**
* Provides CGI Gateway service -- delegates to <code>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) != null)
throw new UnavailableException
("Cannot invoke CGIServlet through the invoker");
CGIEnvironment cgiEnv = new CGIEnvironment(req, getServletContext());
if (cgiEnv.isValid()) {
CGIRunner cgi = 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();
}
if (!cgiEnv.isValid()) {
res.setStatus(404);
}
if (debug >= 10) {
ServletOutputStream out = res.getOutputStream();
out.println("<HTML>$Name$");
out.println("<BODY>$Header$");
if (cgiEnv.isValid()) {
out.println(cgiEnv.toString());
} else {
out.println("<H3>");
out.println("CGI script not found or not specified.");
out.println("</H3>");
out.println("<H4>");
out.println("Check the <b>HttpServletRequest ");
out.println("<a href=\"#pathInfo\">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("</H4>");
out.println("<H4>");
out.println("For a good discussion of how CGI scripts ");
out.println("work and what their environment variables ");
out.println("mean, please visit the <a ");
out.println("href=\"http://cgi-spec.golux.com\">CGI ");
out.println("Specification page</a>.");
out.println("</H4>");
}
printServletEnvironment(out, req, res);
out.println("</BODY> |