1 16 package org.mortbay.jetty.servlet; 17 18 import java.io.IOException ; 19 import java.io.InputStream ; 20 import java.io.OutputStream ; 21 import java.util.Enumeration ; 22 import java.util.List ; 23 24 import javax.servlet.RequestDispatcher ; 25 import javax.servlet.ServletContext ; 26 import javax.servlet.ServletException ; 27 import javax.servlet.UnavailableException ; 28 import javax.servlet.http.HttpServlet ; 29 import javax.servlet.http.HttpServletRequest ; 30 import javax.servlet.http.HttpServletResponse ; 31 32 import org.apache.commons.logging.Log; 33 import org.mortbay.log.LogFactory; 34 import org.mortbay.http.HttpContext; 35 import org.mortbay.http.HttpFields; 36 import org.mortbay.http.HttpRequest; 37 import org.mortbay.http.HttpResponse; 38 import org.mortbay.http.InclusiveByteRange; 39 import org.mortbay.http.MultiPartResponse; 40 import org.mortbay.http.ResourceCache; 41 import org.mortbay.util.CachedResource; 42 import org.mortbay.util.IO; 43 import org.mortbay.util.LogSupport; 44 import org.mortbay.util.Resource; 45 import org.mortbay.util.URI; 46 import org.mortbay.util.WriterOutputStream; 47 48 49 81 public class Default extends HttpServlet 82 { 83 private static Log log = LogFactory.getLog(Default.class); 84 85 private HttpContext _httpContext; 86 private ServletHandler _servletHandler; 87 private String _AllowString = "GET, POST, HEAD, OPTIONS, TRACE"; 88 89 private boolean _acceptRanges = true; 90 private boolean _dirAllowed; 91 private boolean _putAllowed; 92 private boolean _delAllowed; 93 private boolean _redirectWelcomeFiles; 94 private int _minGzipLength = -1; 95 private Resource _resourceBase; 96 97 98 public void init() throws UnavailableException 99 { 100 ServletContext config = getServletContext(); 101 _servletHandler = ((ServletHandler.Context) config).getServletHandler(); 102 _httpContext = _servletHandler.getHttpContext(); 103 104 _acceptRanges = getInitBoolean("acceptRanges"); 105 _dirAllowed = getInitBoolean("dirAllowed"); 106 _putAllowed = getInitBoolean("putAllowed"); 107 _delAllowed = getInitBoolean("delAllowed"); 108 _redirectWelcomeFiles = getInitBoolean("redirectWelcome"); 109 _minGzipLength = getInitInt("minGzipLength"); 110 111 String rrb = getInitParameter("relativeResourceBase"); 112 if (rrb != null) 113 { 114 try 115 { 116 _resourceBase = _httpContext.getBaseResource().addPath(rrb); 117 } 118 catch (Exception e) 119 { 120 log.warn(LogSupport.EXCEPTION, e); 121 throw new UnavailableException (e.toString()); 122 } 123 } 124 125 String rb = getInitParameter("resourceBase"); 126 127 if (rrb != null && rb != null) 128 throw new UnavailableException ("resourceBase & relativeResourceBase"); 129 130 if (rb != null) 131 { 132 try 133 { 134 _resourceBase = Resource.newResource(rb); 135 } 136 catch (Exception e) 137 { 138 log.warn(LogSupport.EXCEPTION, e); 139 throw new UnavailableException (e.toString()); 140 } 141 } 142 if (log.isDebugEnabled()) 143 log.debug("resource base = " + _resourceBase); 144 145 if (_putAllowed) 146 _AllowString += ", PUT"; 147 if (_delAllowed) 148 _AllowString += ", DELETE"; 149 if (_putAllowed && _delAllowed) 150 _AllowString += ", MOVE"; 151 } 152 153 154 private boolean getInitBoolean(String name) 155 { 156 String value = getInitParameter(name); 157 return value != null && value.length() > 0 && (value.startsWith("t") || value.startsWith("T") || value.startsWith("y") || value.startsWith("Y") || value.startsWith("1")); 158 } 159 160 161 private int getInitInt(String name) 162 { 163 String value = getInitParameter(name); 164 if (value != null && value.length() > 0) 165 return Integer.parseInt(value); 166 return -1; 167 } 168 169 170 177 protected Resource getResource(String pathInContext) throws IOException 178 { 179 Resource r = (_resourceBase == null) ? _httpContext.getResource(pathInContext) : _resourceBase.addPath(pathInContext); 180 181 if (log.isDebugEnabled()) 182 log.debug("RESOURCE=" + r); 183 return r; 184 } 185 186 187 protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException , IOException 188 { 189 String servletPath = (String ) request.getAttribute(Dispatcher.__INCLUDE_SERVLET_PATH); 190 String pathInfo = null; 191 if (servletPath == null) 192 { 193 servletPath = request.getServletPath(); 194 pathInfo = request.getPathInfo(); 195 } 196 else 197 pathInfo = (String ) request.getAttribute(Dispatcher.__INCLUDE_PATH_INFO); 198 199 String pathInContext = URI.addPaths(servletPath, pathInfo); 200 201 boolean endsWithSlash = pathInContext.endsWith("/"); 202 Resource resource = getResource(pathInContext); 203 204 String method = request.getMethod(); 206 if (_AllowString.indexOf(method) < 0) 207 { 208 if (resource != null && resource.exists()) 209 { 210 response.setHeader(HttpFields.__Allow, _AllowString); 211 response.sendError(HttpResponse.__405_Method_Not_Allowed); 212 } 213 else 214 response.sendError(HttpResponse.__404_Not_Found); 215 return; 216 } 217 218 try 220 { 221 if (method.equals(HttpRequest.__GET) || method.equals(HttpRequest.__POST) || method.equals(HttpRequest.__HEAD)) 223 handleGet(request, response, pathInContext, resource, endsWithSlash); 224 else if (_putAllowed && method.equals(HttpRequest.__PUT)) 225 handlePut(request, response, pathInContext, resource); 226 else if (_delAllowed && method.equals(HttpRequest.__DELETE)) 227 handleDelete(request, response, pathInContext, resource); 228 else if (_putAllowed && _delAllowed && method.equals(HttpRequest.__MOVE)) 229 handleMove(request, response, pathInContext, resource); 230 else if (method.equals(HttpRequest.__OPTIONS)) 231 handleOptions(request, response); 232 else if (method.equals(HttpRequest.__TRACE)) 233 _servletHandler.handleTrace(request, response); 234 else 235 { 236 try 238 { 239 if (resource.exists()) 240 response.sendError(HttpResponse.__501_Not_Implemented); 241 else 242 _servletHandler.notFound(request, response); 243 } 244 catch (Exception e) 245 { 246 LogSupport.ignore(log, e); 247 } 248 } 249 } 250 catch (IllegalArgumentException e) 251 { 252 LogSupport.ignore(log, e); 253 } 254 finally 255 { 256 if (resource != null && !(resource instanceof CachedResource)) 257 resource.release(); 258 } 259 260 } 261 262 263 public void handleGet(HttpServletRequest request, HttpServletResponse response, String pathInContext, Resource resource, boolean endsWithSlash) throws ServletException , IOException 264 { 265 if (resource == null || !resource.exists()) 266 response.sendError(HttpResponse.__404_Not_Found); 267 else 268 { 269 270 if (resource.isDirectory()) 272 { 273 if (!endsWithSlash && !pathInContext.equals("/")) 274 { 275 String q = request.getQueryString(); 276 StringBuffer buf = request.getRequestURL(); 277 if (q != null && q.length() != 0) 278 { 279 buf.append('?'); 280 buf.append(q); 281 } 282 response.setContentLength(0); 283 response.sendRedirect(response.encodeRedirectURL(URI.addPaths(buf.toString(), "/"))); 284 return; 285 } 286 287 String welcome = _httpContext.getWelcomeFile(resource); 289 if (welcome != null) 290 { 291 String ipath = URI.addPaths(pathInContext, welcome); 292 if (_redirectWelcomeFiles) 293 { 294 response.setContentLength(0); 296 response.sendRedirect(URI.addPaths(_httpContext.getContextPath(), ipath)); 297 } 298 else 299 { 300 RequestDispatcher dispatcher = _servletHandler.getRequestDispatcher(ipath); 302 dispatcher.forward(request, response); 303 } 304 return; 305 } 306 307 if (!passConditionalHeaders(request, response, resource)) 309 return; 310 311 sendDirectory(request, response, resource, pathInContext.length() > 1); 313 } 314 else 315 { 316 if (!passConditionalHeaders(request, response, resource)) 318 return; 319 320 sendData(request, response, pathInContext, resource); 322 } 323 } 324 } 325 326 327 public void handlePut(HttpServletRequest request, HttpServletResponse response, String pathInContext, Resource resource) throws ServletException , IOException 328 { 329 boolean exists = resource != null && resource.exists(); 330 if (exists && !passConditionalHeaders(request, response, resource)) 331 return; 332 333 if (pathInContext.endsWith("/")) 334 { 335 if (!exists) 336 { 337 if (!resource.getFile().mkdirs()) 338 response.sendError(HttpResponse.__403_Forbidden, "Directories could not be created"); 339 else 340 { 341 response.setStatus(HttpResponse.__201_Created); 342 response.flushBuffer(); 343 } 344 } 345 else 346 { 347 response.setStatus(HttpResponse.__200_OK); 348 response.flushBuffer(); 349 } 350 } 351 else 352 { 353 try 354 { 355 int toRead = request.getContentLength(); 356 InputStream in = request.getInputStream(); 357 OutputStream out = resource.getOutputStream(); 358 if (toRead >= 0) 359 IO.copy(in, out, toRead); 360 else 361 IO.copy(in, out); 362 out.close(); 363 364 response.setStatus(exists ? HttpResponse.__200_OK : HttpResponse.__201_Created); 365 response.flushBuffer(); 366 } 367 catch (Exception ex) 368 { 369 log.warn(LogSupport.EXCEPTION, ex); 370 response.sendError(HttpResponse.__403_Forbidden, ex.getMessage()); 371 } 372 } 373 } 374 375 376 public void handleDelete(HttpServletRequest request, HttpServletResponse response, String pathInContext, Resource resource) throws ServletException , IOException 377 { 378 if (!resource.exists() || !passConditionalHeaders(request, response, resource)) 379 return; 380 try 381 { 382 if (resource.delete()) 384 { 385 response.setStatus(HttpResponse.__204_No_Content); 386 response.flushBuffer(); 387 } 388 else 389 response.sendError(HttpResponse.__403_Forbidden); 390 } 391 catch (SecurityException sex) 392 { 393 log.warn(LogSupport.EXCEPTION, sex); 394 response.sendError(HttpResponse.__403_Forbidden, sex.getMessage()); 395 } 396 } 397 398 399 public void handleMove(HttpServletRequest request, HttpServletResponse response, String pathInContext, Resource resource) throws ServletException , IOException 400 { 401 if (!resource.exists() || !passConditionalHeaders(request, response, resource)) 402 return; 403 404 String newPath = URI.canonicalPath(request.getHeader("new-uri")); 405 if (newPath == null) 406 { 407 response.sendError(HttpResponse.__400_Bad_Request, "No new-uri"); 408 return; 409 } 410 411 String contextPath = _httpContext.getContextPath(); 412 if (contextPath != null && !newPath.startsWith(contextPath)) 413 { 414 response.sendError(HttpResponse.__405_Method_Not_Allowed, "Not in context"); 415 return; 416 } 417 418 try 419 { 420 String newInfo = newPath; 421 if (contextPath != null) 422 newInfo = newInfo.substring(contextPath.length()); 423 Resource newFile = _httpContext.getBaseResource().addPath(newInfo); 424 425 resource.renameTo(newFile); 426 response.setStatus(HttpResponse.__204_No_Content); 427 response.flushBuffer(); 428 } 429 catch (Exception ex) 430 { 431 log.warn(LogSupport.EXCEPTION, ex); 432 response.sendError(HttpResponse.__500_Internal_Server_Error, "Error:" + ex); 433 return; 434 } 435 436 } 437 438 439 public void handleOptions(HttpServletRequest request, HttpServletResponse response) throws IOException 440 { 441 response.setIntHeader(HttpFields.__ContentLength, 0); 444 response.setHeader(HttpFields.__Allow, _AllowString); 445 response.flushBuffer(); 446 } 447 448 449 452 protected boolean passConditionalHeaders(HttpServletRequest request, HttpServletResponse response, Resource resource) throws IOException 453 { 454 if (!request.getMethod().equals(HttpRequest.__HEAD) && request.getAttribute(Dispatcher.__INCLUDE_REQUEST_URI) == null) 455 { 456 ResourceCache.ResourceMetaData metaData = _httpContext.getResourceMetaData(resource); 460 if (metaData != null) 461 { 462 String ifms = request.getHeader(HttpFields.__IfModifiedSince); 463 String mdlm = metaData.getLastModified(); 464 if (ifms != null && mdlm != null && ifms.equals(mdlm)) 465 { 466 response.reset(); 467 response.setStatus(HttpResponse.__304_Not_Modified); 468 response.flushBuffer(); 469 return false; 470 } 471 } 472 473 long date = 0; 474 476 if ((date = request.getDateHeader(HttpFields.__IfUnmodifiedSince)) > 0) 477 { 478 if (resource.lastModified() / 1000 > date / 1000) 479 { 480 response.sendError(HttpResponse.__412_Precondition_Failed); 481 return false; 482 } 483 } 484 485 if ((date = request.getDateHeader(HttpFields.__IfModifiedSince)) > 0) 486 { 487 if (resource.lastModified() / 1000 <= date / 1000) 488 { 489 response.reset(); 490 response.setStatus(HttpResponse.__304_Not_Modified); 491 response.flushBuffer(); 492 return false; 493 } 494 } 495 } 496 return true; 497 } 498 499 500 protected void sendDirectory(HttpServletRequest request, HttpServletResponse response, Resource resource, boolean parent) throws IOException 501 { 502 if (!_dirAllowed) 503 { 504 response.sendError(HttpResponse.__403_Forbidden); 505 return; 506 } 507 508 byte[] data = null; 509 if (resource instanceof CachedResource) 510 data = ((CachedResource) resource).getCachedData(); 511 512 if (data == null) 513 { 514 String base = URI.addPaths(request.getRequestURI(), "/"); 515 String dir = resource.getListHTML(base, parent); 516 if (dir == null) 517 { 518 response.sendError(HttpResponse.__403_Forbidden, "No directory"); 519 return; 520 } 521 data = dir.getBytes("UTF-8"); 522 if (resource instanceof CachedResource) 523 ((CachedResource) resource).setCachedData(data); 524 } 525 526 response.setContentType("text/html; charset=UTF-8"); 527 response.setContentLength(data.length); 528 529 if (!request.getMethod().equals(HttpRequest.__HEAD)) 530 response.getOutputStream().write(data); 531 } 532 533 534 protected void sendData(HttpServletRequest request, HttpServletResponse response, String pathInContext, Resource resource) throws IOException 535 { 536 long resLength = resource.length(); 537 538 boolean include = request.getAttribute(Dispatcher.__INCLUDE_REQUEST_URI) != null; 539 540 OutputStream out = null; 542 try 543 { 544 out = response.getOutputStream(); 545 } 546 catch (IllegalStateException e) 547 { 548 out = new WriterOutputStream(response.getWriter()); 549 } 550 551 Enumeration reqRanges = include ? null : request.getHeaders(HttpFields.__Range); 553 554 if (reqRanges == null || !reqRanges.hasMoreElements()) 555 { 556 Resource data = resource; 558 if (!include) 559 { 560 if (_minGzipLength > 0) 562 { 563 String accept = request.getHeader(HttpFields.__AcceptEncoding); 564 if (accept != null && resLength > _minGzipLength && !pathInContext.endsWith(".gz")) 565 { 566 Resource gz = getResource(pathInContext + ".gz"); 567 if (gz.exists() && accept.indexOf("gzip") >= 0 && request.getAttribute(Dispatcher.__INCLUDE_REQUEST_URI) == null) 568 { 569 response.setHeader(HttpFields.__ContentEncoding, "gzip"); 570 data = gz; 571 resLength = data.length(); 572 } 573 } 574 } 575 writeHeaders(response, resource, resLength); 576 } 577 578 data.writeTo(out, 0, resLength); 579 return; 580 } 581 582 List ranges = InclusiveByteRange.satisfiableRanges(reqRanges, resLength); 584 585 if (ranges == null || ranges.size() == 0) 587 { 588 writeHeaders(response, resource, resLength); 589 response.setStatus(HttpResponse.__416_Requested_Range_Not_Satisfiable); 590 response.setHeader(HttpFields.__ContentRange, InclusiveByteRange.to416HeaderRangeString(resLength)); 591 resource.writeTo(out, 0, resLength); 592 return; 593 } 594 595 if (ranges.size() == 1) 598 { 599 InclusiveByteRange singleSatisfiableRange = (InclusiveByteRange) ranges.get(0); 600 long singleLength = singleSatisfiableRange.getSize(resLength); 601 writeHeaders(response, resource, singleLength); 602 response.setStatus(HttpResponse.__206_Partial_Content); 603 response.setHeader(HttpFields.__ContentRange, singleSatisfiableRange.toHeaderRangeString(resLength)); 604 resource.writeTo(out, singleSatisfiableRange.getFirst(resLength), singleLength); 605 return; 606 } 607 608 writeHeaders(response, resource, -1); 613 ResourceCache.ResourceMetaData metaData = _httpContext.getResourceMetaData(resource); 614 String encoding = metaData.getMimeType(); 615 MultiPartResponse multi = new MultiPartResponse(response.getOutputStream()); 616 response.setStatus(HttpResponse.__206_Partial_Content); 617 618 String ctp; 622 if (request.getHeader(HttpFields.__RequestRange) != null) 623 ctp = "multipart/x-byteranges; boundary="; 624 else 625 ctp = "multipart/byteranges; boundary="; 626 response.setContentType(ctp + multi.getBoundary()); 627 628 InputStream in = (resource instanceof CachedResource) ? null : resource.getInputStream(); 629 long pos = 0; 630 631 for (int i = 0; i < ranges.size(); i++) 632 { 633 InclusiveByteRange ibr = (InclusiveByteRange) ranges.get(i); 634 String header = HttpFields.__ContentRange + ": " + ibr.toHeaderRangeString(resLength); 635 multi.startPart(encoding, new String [] 636 { header}); 637 638 long start = ibr.getFirst(resLength); 639 long size = ibr.getSize(resLength); 640 if (in != null) 641 { 642 if (start < pos) 644 { 645 in.close(); 646 in = resource.getInputStream(); 647 pos = 0; 648 } 649 if (pos < start) 650 { 651 in.skip(start - pos); 652 pos = start; 653 } 654 IO.copy(in, out, size); 655 pos += size; 656 } 657 else 658 (resource).writeTo(out, start, size); 660 661 } 662 if (in != null) 663 in.close(); 664 multi.close(); 665 666 return; 667 } 668 669 670 protected void writeHeaders(HttpServletResponse response, Resource resource, long count) throws IOException 671 { 672 ResourceCache.ResourceMetaData metaData = _httpContext.getResourceMetaData(resource); 673 674 response.setContentType(metaData.getMimeType()); 675 if (count != -1) 676 { 677 if (count == resource.length() && response instanceof ServletHttpResponse ) 678 response.setHeader(HttpFields.__ContentLength, metaData.getLength()); 679 else 680 response.setContentLength((int) count); 681 } 682 683 response.setHeader(HttpFields.__LastModified, metaData.getLastModified()); 684 685 if (_acceptRanges) 686 response.setHeader(HttpFields.__AcceptRanges, "bytes"); 687 } 688 689 } 690 | Popular Tags |