KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > servlets > CGIServlet


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Scott Ferguson
28  */

29
30 package com.caucho.servlets;
31
32 import com.caucho.log.Log;
33 import com.caucho.util.Alarm;
34 import com.caucho.util.AlarmListener;
35 import com.caucho.util.CharBuffer;
36 import com.caucho.util.L10N;
37 import com.caucho.vfs.Path;
38 import com.caucho.vfs.ReadStream;
39 import com.caucho.vfs.TempBuffer;
40 import com.caucho.vfs.Vfs;
41
42 import javax.servlet.GenericServlet JavaDoc;
43 import javax.servlet.ServletException JavaDoc;
44 import javax.servlet.ServletInputStream JavaDoc;
45 import javax.servlet.ServletRequest JavaDoc;
46 import javax.servlet.ServletResponse JavaDoc;
47 import javax.servlet.http.HttpServletRequest JavaDoc;
48 import javax.servlet.http.HttpServletResponse JavaDoc;
49 import java.io.File JavaDoc;
50 import java.io.IOException JavaDoc;
51 import java.io.InputStream JavaDoc;
52 import java.io.OutputStream JavaDoc;
53 import java.util.ArrayList JavaDoc;
54 import java.util.Enumeration JavaDoc;
55 import java.util.logging.Level JavaDoc;
56 import java.util.logging.Logger JavaDoc;
57
58 /**
59  * CGI
60  */

61 public class CGIServlet extends GenericServlet JavaDoc {
62   static protected final Logger JavaDoc log = Log.open(CGIServlet.class);
63   static final L10N L = new L10N(CGIServlet.class);
64
65   private static String JavaDoc REQUEST_URI = "javax.servlet.include.request_uri";
66   private static String JavaDoc CONTEXT_PATH = "javax.servlet.include.context_path";
67   private static String JavaDoc SERVLET_PATH = "javax.servlet.include.servlet_path";
68   private static String JavaDoc PATH_INFO = "javax.servlet.include.path_info";
69   private static String JavaDoc QUERY_STRING = "javax.servlet.include.query_string";
70
71   private String JavaDoc _executable;
72   private boolean _stderrIsException = true;
73   private boolean _ignoreExitCode = false;
74
75   /**
76    * Sets an executable to run the script.
77    */

78   public void setExecutable(String JavaDoc executable)
79   {
80     _executable = executable;
81   }
82
83   public void setStderrIsException(boolean isException)
84   {
85     _stderrIsException = isException;
86   }
87
88   /**
89    * If true, do not treat a non-zero exit code as an error, default false.
90    */

91   public void setIgnoreExitCode(boolean ignoreExitCode)
92   {
93     _ignoreExitCode = ignoreExitCode;
94   }
95
96   /**
97    * Handle the request.
98    */

99   public void service(ServletRequest JavaDoc request, ServletResponse JavaDoc response)
100     throws ServletException JavaDoc, IOException JavaDoc
101   {
102     HttpServletRequest JavaDoc req = (HttpServletRequest JavaDoc) request;
103     HttpServletResponse JavaDoc res = (HttpServletResponse JavaDoc) response;
104
105     String JavaDoc requestURI;
106     String JavaDoc contextPath;
107     String JavaDoc servletPath;
108     String JavaDoc servletPathInfo;
109     String JavaDoc queryString;
110
111     requestURI = (String JavaDoc) req.getAttribute(REQUEST_URI);
112
113     if (requestURI != null) {
114       contextPath = (String JavaDoc) req.getAttribute(CONTEXT_PATH);
115       servletPath = (String JavaDoc) req.getAttribute(SERVLET_PATH);
116       servletPathInfo = (String JavaDoc) req.getAttribute(PATH_INFO);
117       queryString = (String JavaDoc) req.getAttribute(QUERY_STRING);
118     }
119     else {
120       requestURI = req.getRequestURI();
121       contextPath = req.getContextPath();
122       servletPath = req.getServletPath();
123       servletPathInfo = req.getPathInfo();
124       queryString = req.getQueryString();
125     }
126
127     String JavaDoc scriptPath;
128     String JavaDoc pathInfo;
129
130     if (servletPathInfo == null) {
131       scriptPath = servletPath;
132       pathInfo = null;
133     }
134     else {
135       String JavaDoc fullPath = servletPath + servletPathInfo;
136       int i = findScriptPathIndex(req, fullPath);
137
138       if (i < 0) {
139         if (log.isLoggable(Level.FINE))
140           log.fine(L.l("no script path index for `{0}'", fullPath));
141
142         res.sendError(res.SC_NOT_FOUND);
143
144         return;
145       }
146
147       scriptPath = fullPath.substring(0, i);
148       pathInfo = fullPath.substring(i);
149
150       if ("".equals(pathInfo))
151     pathInfo = null;
152     }
153
154     String JavaDoc realPath = req.getRealPath(scriptPath);
155
156     Path vfsPath = Vfs.lookup(realPath);
157
158     if (! vfsPath.canRead() || vfsPath.isDirectory()) {
159       if (log.isLoggable(Level.FINE))
160         log.fine(L.l("script `{0}' is unreadable", vfsPath));
161
162       res.sendError(res.SC_NOT_FOUND);
163
164       return;
165     }
166
167     String JavaDoc []env = createEnvironment(req, requestURI, contextPath,
168                                      scriptPath, pathInfo, queryString);
169
170     String JavaDoc []args = getArgs(realPath);
171
172     if (log.isLoggable(Level.FINER)) {
173       if (args.length > 1)
174     log.finer("[cgi] exec " + args[0] + " " + args[1]);
175       else if (args.length > 0)
176     log.finer("[cgi] exec " + args[0]);
177     }
178
179     Runtime JavaDoc runtime = Runtime.getRuntime();
180     Process JavaDoc process = null;
181     Alarm alarm = null;
182
183     try {
184       File JavaDoc dir = new File JavaDoc(Vfs.lookup(realPath).getParent().getNativePath());
185
186       if (log.isLoggable(Level.FINE)) {
187         CharBuffer argsBuf = new CharBuffer();
188
189         argsBuf.append('[');
190
191         for (String JavaDoc arg : args) {
192           if (argsBuf.length() > 1)
193             argsBuf.append(", ");
194
195           argsBuf.append('"');
196           argsBuf.append(arg);
197           argsBuf.append('"');
198         }
199
200         argsBuf.append(']');
201
202         log.fine(L.l("exec {0} (pwd={1})", argsBuf, dir));
203
204         if (log.isLoggable(Level.FINEST)) {
205           for (String JavaDoc envElement : env)
206             log.finest(envElement);
207         }
208       }
209
210       process = runtime.exec(args, env, dir);
211
212       InputStream JavaDoc inputStream = process.getInputStream();
213       InputStream JavaDoc errorStream = process.getErrorStream();
214
215       TimeoutAlarm timeout;
216       timeout = new TimeoutAlarm(requestURI, process, inputStream);
217       alarm = new Alarm(timeout, 360 * 1000);
218
219       OutputStream JavaDoc outputStream = process.getOutputStream();
220
221       TempBuffer tempBuf = TempBuffer.allocate();
222       byte []buf = tempBuf.getBuffer();
223       
224       try {
225     ServletInputStream JavaDoc sis = req.getInputStream();
226     int len;
227
228     while ((len = sis.read(buf, 0, buf.length)) > 0) {
229       outputStream.write(buf, 0, len);
230     }
231
232     outputStream.flush();
233       } catch (IOException JavaDoc e) {
234     log.log(Level.FINER, e.toString(), e);
235       } finally {
236     outputStream.close();
237       }
238
239       ReadStream rs = Vfs.openRead(inputStream);
240       boolean hasStatus = false;
241
242       try {
243     hasStatus = parseHeaders(req, res, rs);
244
245     OutputStream JavaDoc out = res.getOutputStream();
246
247     rs.writeToStream(out);
248       } finally {
249     try {
250       rs.close();
251     } catch (Throwable JavaDoc e) {
252       log.log(Level.FINER, e.toString(), e);
253
254     }
255
256     inputStream.close();
257       }
258
259       StringBuilder JavaDoc error = new StringBuilder JavaDoc();
260       boolean hasContent = false;
261       int ch;
262
263       while (errorStream.available() > 0 && (ch = errorStream.read()) > 0) {
264     error.append((char) ch);
265
266     if (! Character.isWhitespace((char) ch))
267       hasContent = true;
268       }
269       errorStream.close();
270
271       if (hasContent) {
272     String JavaDoc errorString = error.toString();
273
274     log.warning(errorString);
275
276     if (! hasStatus && _stderrIsException)
277       throw new ServletException JavaDoc(errorString);
278       }
279
280       int exitCode = process.waitFor();
281
282       if (exitCode != 0) {
283         if (hasStatus) {
284           if (log.isLoggable(Level.FINER))
285             log.finer(L.l("exit code {0} (ignored, hasStatus)", exitCode));
286         }
287         else if (_ignoreExitCode) {
288           if (log.isLoggable(Level.FINER))
289             log.finer(L.l("exit code {0} (ignored)", exitCode));
290         }
291         else
292       throw new ServletException JavaDoc(L.l("CGI execution failed. Exit code {0}",
293                          exitCode));
294       }
295     } catch (IOException JavaDoc e) {
296       throw e;
297     } catch (ServletException JavaDoc e) {
298       throw e;
299     } catch (Exception JavaDoc e) {
300       throw new ServletException JavaDoc(e);
301     } finally {
302       if (alarm != null)
303     alarm.dequeue();
304
305       try {
306     process.destroy();
307       } catch (Throwable JavaDoc e) {
308       }
309     }
310   }
311
312   /**
313    * Returns the index to the script path.
314    */

315   private int findScriptPathIndex(HttpServletRequest JavaDoc req, String JavaDoc fullPath)
316   {
317     String JavaDoc realPath = req.getRealPath(fullPath);
318     Path path = Vfs.lookup(realPath);
319
320     if (log.isLoggable(Level.FINER))
321       log.finer(L.l("real-path is `{0}'", path));
322
323     if (path.canRead() && ! path.isDirectory())
324       return fullPath.length();
325
326     int tail = fullPath.length();
327     int head;
328
329     while ((head = fullPath.lastIndexOf('/', tail)) >= 0) {
330       String JavaDoc subPath = fullPath.substring(0, head);
331
332       realPath = req.getRealPath(subPath);
333       path = Vfs.lookup(realPath);
334
335       if (log.isLoggable(Level.FINEST))
336         log.finest(L.l("trying script path {0}", path));
337
338       if (path.canRead() && ! path.isDirectory())
339     return head;
340
341       tail = head - 1;
342     }
343
344     return -1;
345   }
346
347   private String JavaDoc []getArgs(String JavaDoc path)
348   {
349     if (_executable != null)
350       return new String JavaDoc[] { _executable, path };
351
352     ReadStream is = null;
353     try {
354       is = Vfs.lookup(path).openRead();
355
356       int ch;
357       if (is.read() != '#')
358         return new String JavaDoc[] { path };
359       else if (is.read() != '!')
360         return new String JavaDoc[] { path };
361
362       CharBuffer cb = CharBuffer.allocate();
363       ArrayList JavaDoc<String JavaDoc> list = new ArrayList JavaDoc<String JavaDoc>();
364       ch = is.read();
365
366       while ((ch >= 0 && ch != '\r' && ch != '\n')) {
367         for (; ch == ' ' || ch == '\t'; ch = is.read()) {
368         }
369
370         if (ch < 0 || ch == '\r' || ch == '\n') {
371           if (list.size() > 0) {
372             list.add(path);
373             return list.toArray(new String JavaDoc[list.size()]);
374           }
375           else
376             return new String JavaDoc[] { path };
377         }
378
379         cb.clear();
380         while (ch > 0 && ch != ' ' && ch != '\t' && ch != '\r' && ch != '\n') {
381           cb.append((char) ch);
382
383           ch = is.read();
384         }
385
386         list.add(cb.toString());
387
388         for (; ch == ' ' || ch == '\t'; ch = is.read()) {
389         }
390       }
391
392       if (list.size() > 0) {
393         list.add(path);
394         return list.toArray(new String JavaDoc[list.size()]);
395       }
396       else
397         return new String JavaDoc[] { path };
398     } catch (Exception JavaDoc e) {
399       return new String JavaDoc[] { path };
400     } finally {
401       if (is != null) {
402     is.close();
403       }
404     }
405   }
406
407   private String JavaDoc[] createEnvironment(HttpServletRequest JavaDoc req,
408                                      String JavaDoc requestURI, String JavaDoc contextPath,
409                                      String JavaDoc scriptPath, String JavaDoc pathInfo,
410                                      String JavaDoc queryString)
411   {
412     boolean isFine = log.isLoggable(Level.FINE);
413     
414     ArrayList JavaDoc<String JavaDoc> env = new ArrayList JavaDoc<String JavaDoc>();
415
416     env.add("SERVER_SOFTWARE=Resin/" + com.caucho.Version.VERSION);
417
418     env.add("SERVER_NAME=" + req.getServerName());
419     //env.add("SERVER_ADDR=" + req.getServerAddr());
420
env.add("SERVER_PORT=" + req.getServerPort());
421
422     env.add("REMOTE_ADDR=" + req.getRemoteAddr());
423     // env.add("REMOTE_PORT=" + req.getRemotePort());
424

425     if (req.getRemoteUser() != null)
426       env.add("REMOTE_USER=" + req.getRemoteUser());
427     if (req.getAuthType() != null)
428       env.add("AUTH_TYPE=" + req.getAuthType());
429
430     env.add("GATEWAY_INTERFACE=CGI/1.1");
431     env.add("SERVER_PROTOCOL=" + req.getProtocol());
432     env.add("REQUEST_METHOD=" + req.getMethod());
433     if (isFine)
434       log.fine("[cgi] REQUEST_METHOD=" + req.getMethod());
435     
436     if (queryString != null) {
437       env.add("QUERY_STRING="+ queryString);
438       
439       if (isFine)
440       log.fine("[cgi] QUERY_STRING=" + queryString);
441     }
442
443     env.add("REQUEST_URI=" + requestURI);
444
445     if (isFine)
446       log.fine("[cgi] REQUEST_URI=" + requestURI);
447
448     // PHP needs SCRIPT_FILENAME or it reports "No input file specified."
449
env.add("SCRIPT_FILENAME=" + req.getRealPath(scriptPath));
450
451     scriptPath = contextPath + scriptPath;
452
453     env.add("SCRIPT_NAME=" + scriptPath);
454     
455     if (isFine)
456       log.fine("[cgi] SCRIPT_NAME=" + scriptPath);
457     
458     if (pathInfo != null) {
459       env.add("PATH_INFO=" + pathInfo);
460       env.add("PATH_TRANSLATED=" + req.getRealPath(pathInfo));
461     }
462
463     Enumeration JavaDoc e = req.getHeaderNames();
464     while (e.hasMoreElements()) {
465       String JavaDoc key = (String JavaDoc) e.nextElement();
466       String JavaDoc value = req.getHeader(key);
467
468       if (isFine)
469     log.fine("[cgi] " + key + "=" + value);
470
471       if (key.equalsIgnoreCase("content-length"))
472         env.add("CONTENT_LENGTH=" + value);
473       else if (key.equalsIgnoreCase("content-type"))
474         env.add("CONTENT_TYPE=" + value);
475       else if (key.equalsIgnoreCase("authorization")) {
476       }
477       else if (key.equalsIgnoreCase("proxy-authorization")) {
478       }
479       else
480         env.add(convertHeader(key, value));
481     }
482
483     return (String JavaDoc []) env.toArray(new String JavaDoc[env.size()]);
484   }
485
486   private String JavaDoc convertHeader(String JavaDoc key, String JavaDoc value)
487   {
488     CharBuffer cb = new CharBuffer();
489
490     cb.append("HTTP_");
491
492     for (int i = 0; i < key.length(); i++) {
493       char ch = key.charAt(i);
494       if (ch == '-')
495         cb.append('_');
496       else if (ch >= 'a' && ch <= 'z')
497         cb.append((char) (ch + 'A' - 'a'));
498       else
499         cb.append(ch);
500     }
501
502     cb.append('=');
503     cb.append(value);
504
505     return cb.close();
506   }
507
508   private boolean parseHeaders(HttpServletRequest JavaDoc req,
509                    HttpServletResponse JavaDoc res,
510                    ReadStream rs)
511     throws IOException JavaDoc
512   {
513     boolean hasStatus = false;
514
515     CharBuffer key = new CharBuffer();
516     CharBuffer value = new CharBuffer();
517
518     int ch;
519
520     while (true) {
521       key.clear();
522       value.clear();
523
524       for (ch = rs.read();
525            ch >= 0 && ch != ' ' && ch != '\r' && ch != '\n' && ch != ':';
526            ch = rs.read()) {
527         key.append((char) ch);
528       }
529
530       for (;
531            ch >= 0 && ch == ' ' || ch == ':';
532            ch = rs.read()) {
533       }
534
535       for (;
536            ch >= 0 && ch != '\r' && ch != '\n';
537            ch = rs.read()) {
538         value.append((char) ch);
539       }
540
541       if (ch == '\r') {
542         ch = rs.read();
543         if (ch != '\n')
544           rs.unread();
545       }
546
547       if (key.length() == 0)
548         return hasStatus;
549
550       String JavaDoc keyStr = key.toString();
551       String JavaDoc valueStr = value.toString();
552
553       if (log.isLoggable(Level.FINER))
554     log.finer(keyStr + ": " + valueStr);
555
556       if (keyStr.equalsIgnoreCase("Status")) {
557         int status = 0;
558         int len = valueStr.length();
559         int i = 0;
560
561     hasStatus = true;
562     
563         for (; i < len && (ch = valueStr.charAt(i)) >= '0' && ch <= '9'; i++)
564           status = 10 * status + ch - '0';
565         
566         for (; i < len && (ch = valueStr.charAt(i)) == ' '; i++) {
567         }
568
569     if (status < 304)
570       res.setStatus(status);
571     else
572       res.sendError(status, valueStr.substring(i));
573       }
574       else if (keyStr.equalsIgnoreCase("Location")) {
575     String JavaDoc uri;
576     
577     if (valueStr.startsWith("/"))
578       uri = req.getContextPath() + valueStr;
579     else
580       uri = valueStr;
581
582     res.setHeader("Location", res.encodeRedirectURL(uri));
583       }
584       else
585         res.addHeader(keyStr, valueStr);
586     }
587   }
588
589   class TimeoutAlarm implements AlarmListener {
590     String JavaDoc _uri;
591     Process JavaDoc _process;
592     InputStream JavaDoc _is;
593
594     TimeoutAlarm(String JavaDoc uri, Process JavaDoc process, InputStream JavaDoc is)
595     {
596       _uri = uri;
597       _process = process;
598       _is = is;
599     }
600
601     public void handleAlarm(Alarm alarm)
602     {
603       log.warning("timing out CGI process for '" + _uri + "'");
604       
605       try {
606     _is.close();
607       } catch (Throwable JavaDoc e) {
608     log.log(Level.WARNING, e.toString(), e);
609       }
610       
611       try {
612     _process.destroy();
613       } catch (Throwable JavaDoc e) {
614     log.log(Level.WARNING, e.toString(), e);
615       }
616     }
617   }
618 }
619
Popular Tags