1 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 ; 43 import javax.servlet.ServletException ; 44 import javax.servlet.ServletInputStream ; 45 import javax.servlet.ServletRequest ; 46 import javax.servlet.ServletResponse ; 47 import javax.servlet.http.HttpServletRequest ; 48 import javax.servlet.http.HttpServletResponse ; 49 import java.io.File ; 50 import java.io.IOException ; 51 import java.io.InputStream ; 52 import java.io.OutputStream ; 53 import java.util.ArrayList ; 54 import java.util.Enumeration ; 55 import java.util.logging.Level ; 56 import java.util.logging.Logger ; 57 58 61 public class CGIServlet extends GenericServlet { 62 static protected final Logger log = Log.open(CGIServlet.class); 63 static final L10N L = new L10N(CGIServlet.class); 64 65 private static String REQUEST_URI = "javax.servlet.include.request_uri"; 66 private static String CONTEXT_PATH = "javax.servlet.include.context_path"; 67 private static String SERVLET_PATH = "javax.servlet.include.servlet_path"; 68 private static String PATH_INFO = "javax.servlet.include.path_info"; 69 private static String QUERY_STRING = "javax.servlet.include.query_string"; 70 71 private String _executable; 72 private boolean _stderrIsException = true; 73 private boolean _ignoreExitCode = false; 74 75 78 public void setExecutable(String executable) 79 { 80 _executable = executable; 81 } 82 83 public void setStderrIsException(boolean isException) 84 { 85 _stderrIsException = isException; 86 } 87 88 91 public void setIgnoreExitCode(boolean ignoreExitCode) 92 { 93 _ignoreExitCode = ignoreExitCode; 94 } 95 96 99 public void service(ServletRequest request, ServletResponse response) 100 throws ServletException , IOException  101 { 102 HttpServletRequest req = (HttpServletRequest ) request; 103 HttpServletResponse res = (HttpServletResponse ) response; 104 105 String requestURI; 106 String contextPath; 107 String servletPath; 108 String servletPathInfo; 109 String queryString; 110 111 requestURI = (String ) req.getAttribute(REQUEST_URI); 112 113 if (requestURI != null) { 114 contextPath = (String ) req.getAttribute(CONTEXT_PATH); 115 servletPath = (String ) req.getAttribute(SERVLET_PATH); 116 servletPathInfo = (String ) req.getAttribute(PATH_INFO); 117 queryString = (String ) 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 scriptPath; 128 String pathInfo; 129 130 if (servletPathInfo == null) { 131 scriptPath = servletPath; 132 pathInfo = null; 133 } 134 else { 135 String 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 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 []env = createEnvironment(req, requestURI, contextPath, 168 scriptPath, pathInfo, queryString); 169 170 String []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 runtime = Runtime.getRuntime(); 180 Process process = null; 181 Alarm alarm = null; 182 183 try { 184 File dir = new File (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 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 envElement : env) 206 log.finest(envElement); 207 } 208 } 209 210 process = runtime.exec(args, env, dir); 211 212 InputStream inputStream = process.getInputStream(); 213 InputStream errorStream = process.getErrorStream(); 214 215 TimeoutAlarm timeout; 216 timeout = new TimeoutAlarm(requestURI, process, inputStream); 217 alarm = new Alarm(timeout, 360 * 1000); 218 219 OutputStream outputStream = process.getOutputStream(); 220 221 TempBuffer tempBuf = TempBuffer.allocate(); 222 byte []buf = tempBuf.getBuffer(); 223 224 try { 225 ServletInputStream 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 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 out = res.getOutputStream(); 246 247 rs.writeToStream(out); 248 } finally { 249 try { 250 rs.close(); 251 } catch (Throwable e) { 252 log.log(Level.FINER, e.toString(), e); 253 254 } 255 256 inputStream.close(); 257 } 258 259 StringBuilder error = new StringBuilder (); 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 errorString = error.toString(); 273 274 log.warning(errorString); 275 276 if (! hasStatus && _stderrIsException) 277 throw new ServletException (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 (L.l("CGI execution failed. Exit code {0}", 293 exitCode)); 294 } 295 } catch (IOException e) { 296 throw e; 297 } catch (ServletException e) { 298 throw e; 299 } catch (Exception e) { 300 throw new ServletException (e); 301 } finally { 302 if (alarm != null) 303 alarm.dequeue(); 304 305 try { 306 process.destroy(); 307 } catch (Throwable e) { 308 } 309 } 310 } 311 312 315 private int findScriptPathIndex(HttpServletRequest req, String fullPath) 316 { 317 String 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 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 []getArgs(String path) 348 { 349 if (_executable != null) 350 return new String [] { _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 [] { path }; 359 else if (is.read() != '!') 360 return new String [] { path }; 361 362 CharBuffer cb = CharBuffer.allocate(); 363 ArrayList <String > list = new ArrayList <String >(); 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 [list.size()]); 374 } 375 else 376 return new String [] { 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 [list.size()]); 395 } 396 else 397 return new String [] { path }; 398 } catch (Exception e) { 399 return new String [] { path }; 400 } finally { 401 if (is != null) { 402 is.close(); 403 } 404 } 405 } 406 407 private String [] createEnvironment(HttpServletRequest req, 408 String requestURI, String contextPath, 409 String scriptPath, String pathInfo, 410 String queryString) 411 { 412 boolean isFine = log.isLoggable(Level.FINE); 413 414 ArrayList <String > env = new ArrayList <String >(); 415 416 env.add("SERVER_SOFTWARE=Resin/" + com.caucho.Version.VERSION); 417 418 env.add("SERVER_NAME=" + req.getServerName()); 419 env.add("SERVER_PORT=" + req.getServerPort()); 421 422 env.add("REMOTE_ADDR=" + req.getRemoteAddr()); 423 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 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 e = req.getHeaderNames(); 464 while (e.hasMoreElements()) { 465 String key = (String ) e.nextElement(); 466 String 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 []) env.toArray(new String [env.size()]); 484 } 485 486 private String convertHeader(String key, String 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 req, 509 HttpServletResponse res, 510 ReadStream rs) 511 throws IOException  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 keyStr = key.toString(); 551 String 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 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 _uri; 591 Process _process; 592 InputStream _is; 593 594 TimeoutAlarm(String uri, Process process, InputStream 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 e) { 608 log.log(Level.WARNING, e.toString(), e); 609 } 610 611 try { 612 _process.destroy(); 613 } catch (Throwable e) { 614 log.log(Level.WARNING, e.toString(), e); 615 } 616 } 617 } 618 } 619
| Popular Tags
|