Main Page | Packages | Class Hierarchy | Class List | Directories | File List | Class Members | Related Pages

CgiServlet.java

00001 // CgiServlet - runs CGI programs
00002 //
00003 // Copyright (C)1996,1998 by Jef Poskanzer <jef@acme.com>. All rights reserved.
00004 //
00005 // Redistribution and use in source and binary forms, with or without
00006 // modification, are permitted provided that the following conditions
00007 // are met:
00008 // 1. Redistributions of source code must retain the above copyright
00009 //    notice, this list of conditions and the following disclaimer.
00010 // 2. Redistributions in binary form must reproduce the above copyright
00011 //    notice, this list of conditions and the following disclaimer in the
00012 //    documentation and/or other materials provided with the distribution.
00013 //
00014 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
00015 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00016 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00017 // ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
00018 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00019 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00020 // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00021 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00022 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00023 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00024 // SUCH DAMAGE.
00025 //
00026 // Visit the ACME Labs Java page for up-to-date versions of this and other
00027 // fine Java utilities: http://www.acme.com/java/
00028 
00029 package Acme.Serve;
00030 
00031 import java.io.*;
00032 import java.util.*;
00033 import javax.servlet.*;
00034 import javax.servlet.http.*;
00035 
00037 // <P>
00038 // Note: although many implementations of CGI set the working directory of
00039 // the subprocess to be the directory containing the executable file, the
00040 // CGI spec actually says nothing about the working directory.  Since
00041 // Java has no method for setting the working directory, this implementation
00042 // does not set it.
00043 // <P>
00044 // <A HREF="/resources/classes/Acme/Serve/CgiServlet.java">Fetch the software.</A><BR>
00045 // <A HREF="/resources/classes/Acme.tar.Z">Fetch the entire Acme package.</A>
00046 // <P>
00047 // @see Acme.Serve.Serve
00048 
00049 public class CgiServlet extends HttpServlet
00050     {
00051 
00053     // copyright of the servlet.
00054         public String getServletInfo() { 
00055                 return "runs CGI programs";
00056         }
00057 
00059     // @param req the servlet request
00060     // @param req the servlet response
00061     // @exception ServletException when an exception has occurred
00062         public void service( HttpServletRequest req, HttpServletResponse res ) throws ServletException, IOException     {
00063                 if ( ! ( req.getMethod().equalsIgnoreCase( "get" ) ||
00064                         req.getMethod().equalsIgnoreCase( "post" ) ) ) { 
00065                         res.sendError( HttpServletResponse.SC_NOT_IMPLEMENTED );
00066                         return;
00067                 }
00068                 dispatchPathname( req, res, 
00069                                                   getServletContext().getRealPath(req.getServletPath() + req.getPathInfo()));
00070         }
00071 
00072 
00073         private void dispatchPathname( HttpServletRequest req, HttpServletResponse res, String path ) throws IOException {
00074                 if ( new File( path ).exists() )
00075                         serveFile( req, res, path );
00076                 else
00077                         res.sendError( HttpServletResponse.SC_NOT_FOUND );
00078         }
00079 
00080 
00081     private void serveFile( HttpServletRequest req, HttpServletResponse res, String path ) throws IOException
00082         {
00083         String queryString = req.getQueryString();
00084         int contentLength = req.getContentLength();
00085         int c;
00086 
00087         log( "running " + path );
00088 
00089         // Make argument list.
00090         Vector argVec = new Vector();
00091         argVec.addElement( path );
00092         if ( queryString != null && queryString.indexOf( "=" ) == -1 )
00093             {
00094             Enumeration enum = new StringTokenizer( queryString, "+" );
00095             while ( enum.hasMoreElements() )
00096                 argVec.addElement( (String) enum.nextElement() );
00097             }
00098         String argList[] = makeList( argVec );
00099 
00100         // Make environment list.
00101         Vector envVec = new Vector();
00102         envVec.addElement( makeEnv(
00103             "PATH", "/usr/local/bin:/usr/ucb:/bin:/usr/bin" ) );
00104         envVec.addElement( makeEnv( "GATEWAY_INTERFACE", "CGI/1.1" ) );
00105         envVec.addElement( makeEnv(
00106             "SERVER_SOFTWARE", getServletContext().getServerInfo() ) );
00107         envVec.addElement( makeEnv( "SERVER_NAME", req.getServerName() ) );
00108         envVec.addElement( makeEnv(
00109             "SERVER_PORT", Integer.toString( req.getServerPort() ) ) );
00110         envVec.addElement( makeEnv( "REMOTE_ADDR", req.getRemoteAddr() ) );
00111         envVec.addElement( makeEnv( "REMOTE_HOST", req.getRemoteHost() ) );
00112         envVec.addElement( makeEnv( "REQUEST_METHOD", req.getMethod() ) );
00113         if ( contentLength != -1 )
00114             envVec.addElement( makeEnv(
00115                 "CONTENT_LENGTH", Integer.toString( contentLength ) ) );
00116         if ( req.getContentType() != null )
00117             envVec.addElement( makeEnv(
00118                 "CONTENT_TYPE", req.getContentType() ) );
00119         envVec.addElement( makeEnv( "SCRIPT_NAME", req.getServletPath() ) );
00120         if ( req.getPathInfo() != null )
00121             envVec.addElement( makeEnv( "PATH_INFO", req.getPathInfo() ) );
00122         if ( req.getPathTranslated() != null )
00123             envVec.addElement( makeEnv(
00124                 "PATH_TRANSLATED", req.getPathTranslated() ) );
00125         if ( queryString != null )
00126             envVec.addElement( makeEnv( "QUERY_STRING", queryString ) );
00127         envVec.addElement( makeEnv( "SERVER_PROTOCOL", req.getProtocol() ) );
00128         if ( req.getRemoteUser() != null )
00129             envVec.addElement( makeEnv( "REMOTE_USER", req.getRemoteUser() ) );
00130         if ( req.getAuthType() != null )
00131             envVec.addElement( makeEnv( "AUTH_TYPE", req.getAuthType() ) );
00132         Enumeration enum = req.getHeaderNames();
00133         while ( enum.hasMoreElements() )
00134             {
00135             String name = (String) enum.nextElement();
00136             String value = req.getHeader( name );
00137             if ( value == null )
00138                 value = "";
00139             envVec.addElement( makeEnv(
00140                 "HTTP_" + name.toUpperCase().replace( '-', '_' ), value ) );
00141             }
00142         String envList[] = makeList( envVec );
00143 
00144         // Start the command.
00145         Process proc = Runtime.getRuntime().exec( argList, envList );
00146 
00147         try
00148             {
00149             // If it's a POST, copy the request data to the process.
00150             if ( req.getMethod().equalsIgnoreCase( "post" ) )
00151                 {
00152                 InputStream reqIn = req.getInputStream();
00153                 OutputStream procOut = proc.getOutputStream();
00154                 for ( int i = 0; i < contentLength; ++i )
00155                     {
00156                     c = reqIn.read();
00157                     if ( c == -1 )
00158                         break;
00159                     procOut.write( c );
00160                     }
00161                 procOut.close();
00162                 }
00163 
00164             // Now read the response from the process.
00165             BufferedReader procIn = new BufferedReader( new InputStreamReader(
00166                 proc.getInputStream() ) );
00167             OutputStream resOut = res.getOutputStream();
00168             // Some of the headers have to be intercepted and handled.
00169             boolean firstLine = true;
00170             while ( true )
00171                 {
00172                 String line = procIn.readLine();
00173                 if ( line == null )
00174                     break;
00175                 line = line.trim();
00176                 if ( line.equals( "" ) )
00177                     break;
00178                 int colon = line.indexOf( ":" );
00179                 if ( colon == -1 )
00180                     {
00181                     // No colon.  If it's the first line, parse it for status.
00182                     if ( firstLine )
00183                         {
00184                         StringTokenizer tok = new StringTokenizer( line, " " );
00185                         try
00186                             {
00187                             switch( tok.countTokens() )
00188                                 {
00189                                 case 2:
00190                                 tok.nextToken();
00191                                 res.setStatus(
00192                                     Integer.parseInt( tok.nextToken() ) );
00193                                 break;
00194                                 case 3:
00195                                 tok.nextToken();
00196                                 res.setStatus(
00197                                     Integer.parseInt( tok.nextToken() ),
00198                                     tok.nextToken() );
00199                                 break;
00200                                 }
00201                             }
00202                         catch ( NumberFormatException ignore ) {}
00203                         }
00204                     else
00205                         {
00206                         // No colon and it's not the first line?  Ignore.
00207                         }
00208                     }
00209                 else
00210                     {
00211                     // There's a colon.  Check for certain special headers.
00212                     String name = line.substring( 0, colon );
00213                     String value = line.substring( colon + 1 ).trim();
00214                     if ( name.equalsIgnoreCase( "Status" ) )
00215                         {
00216                         StringTokenizer tok = new StringTokenizer( value, " " );
00217                         try
00218                             {
00219                             switch( tok.countTokens() )
00220                                 {
00221                                 case 1:
00222                                 res.setStatus(
00223                                     Integer.parseInt( tok.nextToken() ) );
00224                                 break;
00225                                 case 2:
00226                                 res.setStatus(
00227                                     Integer.parseInt( tok.nextToken() ),
00228                                     tok.nextToken() );
00229                                 break;
00230                                 }
00231                             }
00232                         catch ( NumberFormatException ignore ) {}
00233                         }
00234                     else if ( name.equalsIgnoreCase( "Content-type" ) )
00235                         {
00236                         res.setContentType( value );
00237                         }
00238                     else if ( name.equalsIgnoreCase( "Content-length" ) )
00239                         {
00240                         try
00241                             {
00242                             res.setContentLength( Integer.parseInt( value ) );
00243                             }
00244                         catch ( NumberFormatException ignore ) {}
00245                         }
00246                     else if ( name.equalsIgnoreCase( "Location" ) )
00247                         {
00248                         res.setStatus(
00249                             HttpServletResponse.SC_MOVED_TEMPORARILY );
00250                         res.setHeader( name, value );
00251                         }
00252                     else
00253                         {
00254                         // Not a special header.  Just set it.
00255                         res.setHeader( name, value );
00256                         }
00257                     }
00258                 }
00259             // Copy the rest of the data uninterpreted.
00260             Acme.Utils.copyStream( procIn, resOut );
00261             procIn.close();
00262             resOut.close();
00263             }
00264         catch ( IOException e )
00265             {
00266             //res.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR );
00267             // There's some weird bug in Java, when reading from a Process
00268             // you get a spurious IOException.  We have to ignore it.
00269             }
00270         }
00271 
00272 
00273     private static String makeEnv( String name, String value )
00274         {
00275         return name + "=" + value;
00276         }
00277 
00278 
00279     private static String[] makeList( Vector vec )
00280         {
00281         String list[] = new String[vec.size()];
00282         for ( int i = 0; i < vec.size(); ++i )
00283             list[i] = (String) vec.elementAt( i );
00284         return list;
00285         }
00286 
00287     }

Generated on Wed Dec 14 21:05:32 2005 for OpenMobileIS by  doxygen 1.4.4