Quadcap Embeddable Server

com/quadcap/http/servlets/cgi/CgiScript.java

Go to the documentation of this file.
00001 package com.quadcap.http.servlets.cgi; 00002 00003 /* Copyright 1998 - 2003 Quadcap Software. All rights reserved. 00004 * 00005 * This software is distributed under the Quadcap Free Software License. 00006 * This software may be used or modified for any purpose, personal or 00007 * commercial. Open Source redistributions are permitted. Commercial 00008 * redistribution of larger works derived from, or works which bundle 00009 * this software requires a "Commercial Redistribution License"; see 00010 * http://www.quadcap.com/purchase. 00011 * 00012 * Redistributions qualify as "Open Source" under one of the following terms: 00013 * 00014 * Redistributions are made at no charge beyond the reasonable cost of 00015 * materials and delivery. 00016 * 00017 * Redistributions are accompanied by a copy of the Source Code or by an 00018 * irrevocable offer to provide a copy of the Source Code for up to three 00019 * years at the cost of materials and delivery. Such redistributions 00020 * must allow further use, modification, and redistribution of the Source 00021 * Code under substantially the same terms as this license. 00022 * 00023 * Redistributions of source code must retain the copyright notices as they 00024 * appear in each source code file, these license terms, and the 00025 * disclaimer/limitation of liability set forth as paragraph 6 below. 00026 * 00027 * Redistributions in binary form must reproduce this Copyright Notice, 00028 * these license terms, and the disclaimer/limitation of liability set 00029 * forth as paragraph 6 below, in the documentation and/or other materials 00030 * provided with the distribution. 00031 * 00032 * The Software is provided on an "AS IS" basis. No warranty is 00033 * provided that the Software is free of defects, or fit for a 00034 * particular purpose. 00035 * 00036 * Limitation of Liability. Quadcap Software shall not be liable 00037 * for any damages suffered by the Licensee or any third party resulting 00038 * from use of the Software. 00039 */ 00040 00041 import java.io.ByteArrayOutputStream; 00042 import java.io.File; 00043 import java.io.FileOutputStream; 00044 import java.io.InputStream; 00045 import java.io.IOException; 00046 import java.io.OutputStream; 00047 00048 import java.util.Hashtable; 00049 import java.util.Enumeration; 00050 import java.util.Vector; 00051 00052 import java.net.URLEncoder; 00053 00054 import javax.servlet.ServletException; 00055 00056 import javax.servlet.http.HttpServletRequest; 00057 import javax.servlet.http.HttpServletResponse; 00058 00059 import com.quadcap.http.util.HeaderParser; 00060 00061 import com.quadcap.io.LogInputStream; 00062 00063 import com.quadcap.util.text.Scanner; 00064 00065 import com.quadcap.util.Debug; 00066 00067 /** 00068 * Handle a single CGI script execution. 00069 * 00070 * @author Stan Bailes 00071 */ 00072 public class CgiScript { 00073 CgiServlet servlet; 00074 File script; 00075 String interp; 00076 00077 static final String FORM_ENCODED = "application/x-www-form-urlencoded"; 00078 00079 public CgiScript(CgiServlet servlet, String interp, File script) { 00080 this.servlet = servlet; 00081 this.interp = interp; 00082 this.script = script; 00083 } 00084 00085 final String ns(String s) { 00086 return s == null ? "" : s; 00087 } 00088 00089 public synchronized void service(HttpServletRequest req, 00090 HttpServletResponse res) 00091 throws IOException, ServletException 00092 { 00093 boolean form = false; 00094 byte[] formData = null; 00095 if (req.getMethod().equalsIgnoreCase("POST") && 00096 req.getContentType().equalsIgnoreCase(FORM_ENCODED)) { 00097 form = true; 00098 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 00099 Enumeration ed = req.getParameterNames(); 00100 boolean first = true; 00101 while (ed.hasMoreElements()) { 00102 String name = ed.nextElement().toString(); 00103 String [] vals = req.getParameterValues(name); 00104 for (int i = 0; i < vals.length; i++) { 00105 if (!first) bos.write('&'); 00106 first = false; 00107 bos.write(URLEncoder.encode(name).getBytes()); 00108 bos.write('='); 00109 bos.write(URLEncoder.encode(vals[i]).getBytes()); 00110 } 00111 } 00112 formData = bos.toByteArray(); 00113 } else { 00114 formData = new byte[0]; 00115 } 00116 //#ifdef DEBUG 00117 if (Trace.bit(2)) { 00118 Debug.println("formData = " + new String(formData)); 00119 } 00120 //#endif 00121 00122 00123 Vector env = new Vector(); 00124 if (form) { 00125 env.addElement("CONTENT_LENGTH=" + formData.length); 00126 } else { 00127 env.addElement("CONTENT_LENGTH=" + req.getContentLength()); 00128 } 00129 env.addElement("CONTENT_TYPE=" + req.getContentType()); 00130 env.addElement("DOCUMENT_ROOT=" + 00131 servlet.getServletConfig().getServletContext().getRealPath(".")); 00132 env.addElement("GATEWAY_INTERFACE=1.1"); 00133 env.addElement("HTTP_ACCEPT=" + req.getHeader("Http-Accept")); 00134 env.addElement("HTTP_USER_AGENT=" + req.getHeader("User-Agent")); 00135 env.addElement("PATH_INFO=" + ns(req.getPathInfo())); 00136 env.addElement("QUERY_STRING=" + req.getQueryString()); 00137 env.addElement("REQUEST_METHOD=" + req.getMethod()); 00138 env.addElement("SCRIPT_NAME=" + req.getServletPath()); 00139 env.addElement("SERVER_NAME=" + req.getServerName()); 00140 env.addElement("SERVER_PORT=" + req.getServerPort()); 00141 env.addElement("SERVER_PROTOCOL=" + req.getProtocol()); 00142 00143 String[] envp = new String[env.size()]; 00144 for (int i = 0; i < env.size(); i++) { 00145 envp[i] = env.elementAt(i).toString(); 00146 //#ifdef DEBUG 00147 if (Trace.bit(2)) { 00148 Debug.println(" " + envp[i]); 00149 } 00150 //#endif 00151 } 00152 00153 String[] cmdp = new String[2]; 00154 00155 cmdp[0] = interp; 00156 cmdp[1] = script.getAbsolutePath(); 00157 00158 res.setContentType("text/html"); 00159 00160 //#ifdef DEBUG 00161 if (Trace.bit(0)) { 00162 Debug.println("Invoking: " + cmdp[0] + " " + cmdp[1]); 00163 } 00164 //#endif 00165 Process cgi = Runtime.getRuntime().exec(cmdp, envp); 00166 InputStream pi = cgi.getInputStream(); 00167 00168 // FileOutputStream fos = new FileOutputStream("cgi.log"); 00169 // LogInputStream lis = new LogInputStream(pi, fos, ""); 00170 // pi = lis; 00171 00172 OutputStream po = cgi.getOutputStream(); 00173 InputStream pe = cgi.getErrorStream(); 00174 00175 OutputStream so = res.getOutputStream(); 00176 ByteArrayOutputStream se = new ByteArrayOutputStream(); 00177 00178 Thread t1 = makeInputThread(formData, po); 00179 Thread t2 = makeOutputThread(pi, so, res); 00180 Thread t3 = makeCopyThread(pe, se); 00181 00182 t1.start(); 00183 t2.start(); 00184 t3.start(); 00185 int ret = 0; 00186 00187 try { ret = cgi.waitFor(); } catch (Exception e) {} 00188 try { t1.join(); } catch (Exception e) {} 00189 try { t2.join(); } catch (Exception e) {} 00190 try { t3.join(); } catch (Exception e) {} 00191 00192 //#ifdef DEBUG 00193 if (Trace.bit(1)) { 00194 Debug.println("stderr: " + se.toString()); 00195 } 00196 //#endif 00197 00198 //fos.flush(); 00199 if (ret != 0) { 00200 throw new ServletException("Script failed: " + se.toString()); 00201 } 00202 } 00203 00204 /** 00205 * Create a thread which copies bytes from an input stream to 00206 * an output stream until end of file is reached. 00207 */ 00208 public static Thread makeInputThread(final byte[] formData, 00209 final OutputStream out) { 00210 Thread t = new Thread() { 00211 public void run() { 00212 int c; 00213 try { 00214 out.write(formData); 00215 out.close(); 00216 } catch (IOException e) { 00217 Debug.print(e); 00218 } 00219 } 00220 }; 00221 return t; 00222 } 00223 00224 public static Thread makeOutputThread(final InputStream in, 00225 final OutputStream out, 00226 final HttpServletResponse res) { 00227 Thread t = new Thread() { 00228 Hashtable headers = new Hashtable(); 00229 Scanner scanner = new Scanner(in); 00230 boolean terminate = false; 00231 public void run() { 00232 int c; 00233 try { 00234 try { 00235 HeaderParser.parseHeaders(scanner, headers); 00236 } catch (Throwable e) { 00237 Debug.print(e); 00238 //#ifdef DEBUG 00239 Debug.println("scanner = " + scanner.getLog()); 00240 //#endif 00241 } 00242 Enumeration e = headers.keys(); 00243 while (e.hasMoreElements()) { 00244 String hdr = (String)e.nextElement(); 00245 String val = (String)headers.get(hdr); 00246 if (hdr.equals("status")) { 00247 int code = Integer.parseInt(val.substring(0,3)); 00248 String reason = val.substring(4); 00249 if (code == 303) { 00250 res.sendRedirect(reason); 00251 return; 00252 } else { 00253 res.setStatus(code, reason); 00254 } 00255 } else if (hdr.equals("content-type")) { 00256 res.setContentType(val); 00257 } else if (hdr.equals("location")) { 00258 res.sendRedirect(val); 00259 } else { 00260 res.setHeader(hdr, val); 00261 } 00262 } 00263 00264 while (!terminate && (c = in.read()) >= 0) { 00265 out.write(c); 00266 } 00267 } catch (IOException e) { 00268 Debug.print(e); 00269 } 00270 } 00271 public void terminate() { 00272 this.terminate = true; 00273 } 00274 }; 00275 return t; 00276 } 00277 00278 /** 00279 * Create a thread which copies bytes from an input stream to 00280 * an output stream until end of file is reached. 00281 */ 00282 public static Thread makeCopyThread(final InputStream in, 00283 final OutputStream out) { 00284 Thread t = new Thread() { 00285 boolean terminate = false; 00286 public void run() { 00287 int c; 00288 try { 00289 while (!terminate && (c = in.read()) >= 0) out.write(c); 00290 } catch (IOException e) { 00291 Debug.print(e); 00292 } 00293 } 00294 public void terminate() { 00295 this.terminate = true; 00296 } 00297 }; 00298 return t; 00299 } 00300 00301 } 00302