1// 2// ======================================================================== 3// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. 4// ------------------------------------------------------------------------ 5// All rights reserved. This program and the accompanying materials 6// are made available under the terms of the Eclipse Public License v1.0 7// and Apache License v2.0 which accompanies this distribution. 8// 9// The Eclipse Public License is available at 10// http://www.eclipse.org/legal/epl-v10.html 11// 12// The Apache License v2.0 is available at 13// http://www.opensource.org/licenses/apache2.0.php 14// 15// You may elect to redistribute this code under either of these licenses. 16// ======================================================================== 17// 18 19package org.eclipse.jetty.server; 20 21import java.io.IOException; 22import java.util.Collections; 23import java.util.Enumeration; 24import java.util.HashSet; 25import java.util.Iterator; 26import java.util.Map; 27 28import javax.servlet.DispatcherType; 29import javax.servlet.RequestDispatcher; 30import javax.servlet.ServletException; 31import javax.servlet.ServletRequest; 32import javax.servlet.ServletResponse; 33import javax.servlet.http.HttpServletRequest; 34import javax.servlet.http.HttpServletResponse; 35 36import org.eclipse.jetty.server.handler.ContextHandler; 37import org.eclipse.jetty.util.Attributes; 38import org.eclipse.jetty.util.LazyList; 39import org.eclipse.jetty.util.MultiMap; 40import org.eclipse.jetty.util.UrlEncoded; 41 42/* ------------------------------------------------------------ */ 43/** Servlet RequestDispatcher. 44 * 45 * 46 */ 47public class Dispatcher implements RequestDispatcher 48{ 49 /** Dispatch include attribute names */ 50 public final static String __INCLUDE_PREFIX="javax.servlet.include."; 51 52 /** Dispatch include attribute names */ 53 public final static String __FORWARD_PREFIX="javax.servlet.forward."; 54 55 /** JSP attributes */ 56 public final static String __JSP_FILE="org.apache.catalina.jsp_file"; 57 58 /* ------------------------------------------------------------ */ 59 private final ContextHandler _contextHandler; 60 private final String _uri; 61 private final String _path; 62 private final String _dQuery; 63 private final String _named; 64 65 /* ------------------------------------------------------------ */ 66 /** 67 * @param contextHandler 68 * @param uri 69 * @param pathInContext 70 * @param query 71 */ 72 public Dispatcher(ContextHandler contextHandler, String uri, String pathInContext, String query) 73 { 74 _contextHandler=contextHandler; 75 _uri=uri; 76 _path=pathInContext; 77 _dQuery=query; 78 _named=null; 79 } 80 81 82 /* ------------------------------------------------------------ */ 83 /** Constructor. 84 * @param contextHandler 85 * @param name 86 */ 87 public Dispatcher(ContextHandler contextHandler,String name) 88 throws IllegalStateException 89 { 90 _contextHandler=contextHandler; 91 _named=name; 92 _uri=null; 93 _path=null; 94 _dQuery=null; 95 } 96 97 /* ------------------------------------------------------------ */ 98 /* 99 * @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse) 100 */ 101 public void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException 102 { 103 forward(request, response, DispatcherType.FORWARD); 104 } 105 106 /* ------------------------------------------------------------ */ 107 /* 108 * @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse) 109 */ 110 public void error(ServletRequest request, ServletResponse response) throws ServletException, IOException 111 { 112 forward(request, response, DispatcherType.ERROR); 113 } 114 115 /* ------------------------------------------------------------ */ 116 /* 117 * @see javax.servlet.RequestDispatcher#include(javax.servlet.ServletRequest, javax.servlet.ServletResponse) 118 */ 119 public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException 120 { 121 Request baseRequest=(request instanceof Request)?((Request)request):AbstractHttpConnection.getCurrentConnection().getRequest(); 122 123 124 if (!(request instanceof HttpServletRequest)) 125 request = new ServletRequestHttpWrapper(request); 126 if (!(response instanceof HttpServletResponse)) 127 response = new ServletResponseHttpWrapper(response); 128 129 130 // TODO - allow stream or writer???? 131 132 final DispatcherType old_type = baseRequest.getDispatcherType(); 133 final Attributes old_attr=baseRequest.getAttributes(); 134 MultiMap old_params=baseRequest.getParameters(); 135 try 136 { 137 baseRequest.setDispatcherType(DispatcherType.INCLUDE); 138 baseRequest.getConnection().include(); 139 if (_named!=null) 140 _contextHandler.handle(_named,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response); 141 else 142 { 143 String query=_dQuery; 144 145 if (query!=null) 146 { 147 // force parameter extraction 148 if (old_params==null) 149 { 150 baseRequest.extractParameters(); 151 old_params=baseRequest.getParameters(); 152 } 153 154 MultiMap parameters=new MultiMap(); 155 UrlEncoded.decodeTo(query,parameters,baseRequest.getCharacterEncoding()); 156 157 if (old_params!=null && old_params.size()>0) 158 { 159 // Merge parameters. 160 Iterator iter = old_params.entrySet().iterator(); 161 while (iter.hasNext()) 162 { 163 Map.Entry entry = (Map.Entry)iter.next(); 164 String name=(String)entry.getKey(); 165 Object values=entry.getValue(); 166 for (int i=0;i<LazyList.size(values);i++) 167 parameters.add(name, LazyList.get(values, i)); 168 } 169 } 170 baseRequest.setParameters(parameters); 171 } 172 173 IncludeAttributes attr = new IncludeAttributes(old_attr); 174 175 attr._requestURI=_uri; 176 attr._contextPath=_contextHandler.getContextPath(); 177 attr._servletPath=null; // set by ServletHandler 178 attr._pathInfo=_path; 179 attr._query=query; 180 181 baseRequest.setAttributes(attr); 182 183 _contextHandler.handle(_path,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response); 184 } 185 } 186 finally 187 { 188 baseRequest.setAttributes(old_attr); 189 baseRequest.getConnection().included(); 190 baseRequest.setParameters(old_params); 191 baseRequest.setDispatcherType(old_type); 192 } 193 } 194 195 196 /* ------------------------------------------------------------ */ 197 /* 198 * @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse) 199 */ 200 protected void forward(ServletRequest request, ServletResponse response, DispatcherType dispatch) throws ServletException, IOException 201 { 202 Request baseRequest=(request instanceof Request)?((Request)request):AbstractHttpConnection.getCurrentConnection().getRequest(); 203 Response base_response=baseRequest.getResponse(); 204 response.resetBuffer(); 205 base_response.fwdReset(); 206 207 208 if (!(request instanceof HttpServletRequest)) 209 request = new ServletRequestHttpWrapper(request); 210 if (!(response instanceof HttpServletResponse)) 211 response = new ServletResponseHttpWrapper(response); 212 213 final boolean old_handled=baseRequest.isHandled(); 214 final String old_uri=baseRequest.getRequestURI(); 215 final String old_context_path=baseRequest.getContextPath(); 216 final String old_servlet_path=baseRequest.getServletPath(); 217 final String old_path_info=baseRequest.getPathInfo(); 218 final String old_query=baseRequest.getQueryString(); 219 final Attributes old_attr=baseRequest.getAttributes(); 220 final DispatcherType old_type=baseRequest.getDispatcherType(); 221 MultiMap<String> old_params=baseRequest.getParameters(); 222 223 try 224 { 225 baseRequest.setHandled(false); 226 baseRequest.setDispatcherType(dispatch); 227 228 if (_named!=null) 229 _contextHandler.handle(_named,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response); 230 else 231 { 232 233 // process any query string from the dispatch URL 234 String query=_dQuery; 235 if (query!=null) 236 { 237 // force parameter extraction 238 if (old_params==null) 239 { 240 baseRequest.extractParameters(); 241 old_params=baseRequest.getParameters(); 242 } 243 244 baseRequest.mergeQueryString(query); 245 } 246 247 ForwardAttributes attr = new ForwardAttributes(old_attr); 248 249 //If we have already been forwarded previously, then keep using the established 250 //original value. Otherwise, this is the first forward and we need to establish the values. 251 //Note: the established value on the original request for pathInfo and 252 //for queryString is allowed to be null, but cannot be null for the other values. 253 if (old_attr.getAttribute(FORWARD_REQUEST_URI) != null) 254 { 255 attr._pathInfo=(String)old_attr.getAttribute(FORWARD_PATH_INFO); 256 attr._query=(String)old_attr.getAttribute(FORWARD_QUERY_STRING); 257 attr._requestURI=(String)old_attr.getAttribute(FORWARD_REQUEST_URI); 258 attr._contextPath=(String)old_attr.getAttribute(FORWARD_CONTEXT_PATH); 259 attr._servletPath=(String)old_attr.getAttribute(FORWARD_SERVLET_PATH); 260 } 261 else 262 { 263 attr._pathInfo=old_path_info; 264 attr._query=old_query; 265 attr._requestURI=old_uri; 266 attr._contextPath=old_context_path; 267 attr._servletPath=old_servlet_path; 268 } 269 270 baseRequest.setRequestURI(_uri); 271 baseRequest.setContextPath(_contextHandler.getContextPath()); 272 baseRequest.setServletPath(null); 273 baseRequest.setPathInfo(_uri); 274 baseRequest.setAttributes(attr); 275 276 _contextHandler.handle(_path,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response); 277 278 if (!baseRequest.getAsyncContinuation().isAsyncStarted()) 279 commitResponse(response,baseRequest); 280 } 281 } 282 finally 283 { 284 baseRequest.setHandled(old_handled); 285 baseRequest.setRequestURI(old_uri); 286 baseRequest.setContextPath(old_context_path); 287 baseRequest.setServletPath(old_servlet_path); 288 baseRequest.setPathInfo(old_path_info); 289 baseRequest.setAttributes(old_attr); 290 baseRequest.setParameters(old_params); 291 baseRequest.setQueryString(old_query); 292 baseRequest.setDispatcherType(old_type); 293 } 294 } 295 296 297 /* ------------------------------------------------------------ */ 298 private void commitResponse(ServletResponse response, Request baseRequest) throws IOException 299 { 300 if (baseRequest.getResponse().isWriting()) 301 { 302 try 303 { 304 response.getWriter().close(); 305 } 306 catch (IllegalStateException e) 307 { 308 response.getOutputStream().close(); 309 } 310 } 311 else 312 { 313 try 314 { 315 response.getOutputStream().close(); 316 } 317 catch (IllegalStateException e) 318 { 319 response.getWriter().close(); 320 } 321 } 322 } 323 324 325 /* ------------------------------------------------------------ */ 326 /* ------------------------------------------------------------ */ 327 /* ------------------------------------------------------------ */ 328 private class ForwardAttributes implements Attributes 329 { 330 final Attributes _attr; 331 332 String _requestURI; 333 String _contextPath; 334 String _servletPath; 335 String _pathInfo; 336 String _query; 337 338 ForwardAttributes(Attributes attributes) 339 { 340 _attr=attributes; 341 } 342 343 /* ------------------------------------------------------------ */ 344 public Object getAttribute(String key) 345 { 346 if (Dispatcher.this._named==null) 347 { 348 if (key.equals(FORWARD_PATH_INFO)) 349 return _pathInfo; 350 if (key.equals(FORWARD_REQUEST_URI)) 351 return _requestURI; 352 if (key.equals(FORWARD_SERVLET_PATH)) 353 return _servletPath; 354 if (key.equals(FORWARD_CONTEXT_PATH)) 355 return _contextPath; 356 if (key.equals(FORWARD_QUERY_STRING)) 357 return _query; 358 } 359 360 if (key.startsWith(__INCLUDE_PREFIX)) 361 return null; 362 363 return _attr.getAttribute(key); 364 } 365 366 /* ------------------------------------------------------------ */ 367 public Enumeration getAttributeNames() 368 { 369 HashSet set=new HashSet(); 370 Enumeration e=_attr.getAttributeNames(); 371 while(e.hasMoreElements()) 372 { 373 String name=(String)e.nextElement(); 374 if (!name.startsWith(__INCLUDE_PREFIX) && 375 !name.startsWith(__FORWARD_PREFIX)) 376 set.add(name); 377 } 378 379 if (_named==null) 380 { 381 if (_pathInfo!=null) 382 set.add(FORWARD_PATH_INFO); 383 else 384 set.remove(FORWARD_PATH_INFO); 385 set.add(FORWARD_REQUEST_URI); 386 set.add(FORWARD_SERVLET_PATH); 387 set.add(FORWARD_CONTEXT_PATH); 388 if (_query!=null) 389 set.add(FORWARD_QUERY_STRING); 390 else 391 set.remove(FORWARD_QUERY_STRING); 392 } 393 394 return Collections.enumeration(set); 395 } 396 397 /* ------------------------------------------------------------ */ 398 public void setAttribute(String key, Object value) 399 { 400 if (_named==null && key.startsWith("javax.servlet.")) 401 { 402 if (key.equals(FORWARD_PATH_INFO)) 403 _pathInfo=(String)value; 404 else if (key.equals(FORWARD_REQUEST_URI)) 405 _requestURI=(String)value; 406 else if (key.equals(FORWARD_SERVLET_PATH)) 407 _servletPath=(String)value; 408 else if (key.equals(FORWARD_CONTEXT_PATH)) 409 _contextPath=(String)value; 410 else if (key.equals(FORWARD_QUERY_STRING)) 411 _query=(String)value; 412 413 else if (value==null) 414 _attr.removeAttribute(key); 415 else 416 _attr.setAttribute(key,value); 417 } 418 else if (value==null) 419 _attr.removeAttribute(key); 420 else 421 _attr.setAttribute(key,value); 422 } 423 424 /* ------------------------------------------------------------ */ 425 @Override 426 public String toString() 427 { 428 return "FORWARD+"+_attr.toString(); 429 } 430 431 /* ------------------------------------------------------------ */ 432 public void clearAttributes() 433 { 434 throw new IllegalStateException(); 435 } 436 437 /* ------------------------------------------------------------ */ 438 public void removeAttribute(String name) 439 { 440 setAttribute(name,null); 441 } 442 } 443 444 /* ------------------------------------------------------------ */ 445 private class IncludeAttributes implements Attributes 446 { 447 final Attributes _attr; 448 449 String _requestURI; 450 String _contextPath; 451 String _servletPath; 452 String _pathInfo; 453 String _query; 454 455 IncludeAttributes(Attributes attributes) 456 { 457 _attr=attributes; 458 } 459 460 /* ------------------------------------------------------------ */ 461 /* ------------------------------------------------------------ */ 462 /* ------------------------------------------------------------ */ 463 public Object getAttribute(String key) 464 { 465 if (Dispatcher.this._named==null) 466 { 467 if (key.equals(INCLUDE_PATH_INFO)) return _pathInfo; 468 if (key.equals(INCLUDE_SERVLET_PATH)) return _servletPath; 469 if (key.equals(INCLUDE_CONTEXT_PATH)) return _contextPath; 470 if (key.equals(INCLUDE_QUERY_STRING)) return _query; 471 if (key.equals(INCLUDE_REQUEST_URI)) return _requestURI; 472 } 473 else if (key.startsWith(__INCLUDE_PREFIX)) 474 return null; 475 476 477 return _attr.getAttribute(key); 478 } 479 480 /* ------------------------------------------------------------ */ 481 public Enumeration getAttributeNames() 482 { 483 HashSet set=new HashSet(); 484 Enumeration e=_attr.getAttributeNames(); 485 while(e.hasMoreElements()) 486 { 487 String name=(String)e.nextElement(); 488 if (!name.startsWith(__INCLUDE_PREFIX)) 489 set.add(name); 490 } 491 492 if (_named==null) 493 { 494 if (_pathInfo!=null) 495 set.add(INCLUDE_PATH_INFO); 496 else 497 set.remove(INCLUDE_PATH_INFO); 498 set.add(INCLUDE_REQUEST_URI); 499 set.add(INCLUDE_SERVLET_PATH); 500 set.add(INCLUDE_CONTEXT_PATH); 501 if (_query!=null) 502 set.add(INCLUDE_QUERY_STRING); 503 else 504 set.remove(INCLUDE_QUERY_STRING); 505 } 506 507 return Collections.enumeration(set); 508 } 509 510 /* ------------------------------------------------------------ */ 511 public void setAttribute(String key, Object value) 512 { 513 if (_named==null && key.startsWith("javax.servlet.")) 514 { 515 if (key.equals(INCLUDE_PATH_INFO)) _pathInfo=(String)value; 516 else if (key.equals(INCLUDE_REQUEST_URI)) _requestURI=(String)value; 517 else if (key.equals(INCLUDE_SERVLET_PATH)) _servletPath=(String)value; 518 else if (key.equals(INCLUDE_CONTEXT_PATH)) _contextPath=(String)value; 519 else if (key.equals(INCLUDE_QUERY_STRING)) _query=(String)value; 520 else if (value==null) 521 _attr.removeAttribute(key); 522 else 523 _attr.setAttribute(key,value); 524 } 525 else if (value==null) 526 _attr.removeAttribute(key); 527 else 528 _attr.setAttribute(key,value); 529 } 530 531 /* ------------------------------------------------------------ */ 532 @Override 533 public String toString() 534 { 535 return "INCLUDE+"+_attr.toString(); 536 } 537 538 /* ------------------------------------------------------------ */ 539 public void clearAttributes() 540 { 541 throw new IllegalStateException(); 542 } 543 544 /* ------------------------------------------------------------ */ 545 public void removeAttribute(String name) 546 { 547 setAttribute(name,null); 548 } 549 } 550} 551