View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2012 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  
19  package org.eclipse.jetty.server;
20  
21  import java.io.BufferedReader;
22  import java.io.File;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.InputStreamReader;
26  import java.io.UnsupportedEncodingException;
27  import java.net.InetAddress;
28  import java.net.InetSocketAddress;
29  import java.nio.charset.Charset;
30  import java.security.Principal;
31  import java.util.ArrayList;
32  import java.util.Collection;
33  import java.util.Collections;
34  import java.util.Enumeration;
35  import java.util.EventListener;
36  import java.util.HashMap;
37  import java.util.List;
38  import java.util.Locale;
39  import java.util.Map;
40  
41  import javax.servlet.AsyncContext;
42  import javax.servlet.AsyncListener;
43  import javax.servlet.DispatcherType;
44  import javax.servlet.MultipartConfigElement;
45  import javax.servlet.RequestDispatcher;
46  import javax.servlet.ServletContext;
47  import javax.servlet.ServletException;
48  import javax.servlet.ServletInputStream;
49  import javax.servlet.ServletRequest;
50  import javax.servlet.ServletRequestAttributeEvent;
51  import javax.servlet.ServletRequestAttributeListener;
52  import javax.servlet.ServletRequestEvent;
53  import javax.servlet.ServletRequestListener;
54  import javax.servlet.ServletResponse;
55  import javax.servlet.http.Cookie;
56  import javax.servlet.http.HttpServletRequest;
57  import javax.servlet.http.HttpServletResponse;
58  import javax.servlet.http.HttpSession;
59  import javax.servlet.http.Part;
60  
61  import org.eclipse.jetty.http.HttpCookie;
62  import org.eclipse.jetty.http.HttpFields;
63  import org.eclipse.jetty.http.HttpHeader;
64  import org.eclipse.jetty.http.HttpMethod;
65  import org.eclipse.jetty.http.HttpStatus;
66  import org.eclipse.jetty.http.HttpURI;
67  import org.eclipse.jetty.http.HttpVersion;
68  import org.eclipse.jetty.http.MimeTypes;
69  import org.eclipse.jetty.server.handler.ContextHandler;
70  import org.eclipse.jetty.server.handler.ContextHandler.Context;
71  import org.eclipse.jetty.server.session.AbstractSession;
72  import org.eclipse.jetty.server.session.AbstractSessionManager;
73  import org.eclipse.jetty.util.Attributes;
74  import org.eclipse.jetty.util.AttributesMap;
75  import org.eclipse.jetty.util.MultiException;
76  import org.eclipse.jetty.util.MultiMap;
77  import org.eclipse.jetty.util.MultiPartInputStream;
78  import org.eclipse.jetty.util.StringUtil;
79  import org.eclipse.jetty.util.URIUtil;
80  import org.eclipse.jetty.util.UrlEncoded;
81  import org.eclipse.jetty.util.log.Log;
82  import org.eclipse.jetty.util.log.Logger;
83  
84  /* ------------------------------------------------------------ */
85  /**
86   * Jetty Request.
87   * <p>
88   * Implements {@link javax.servlet.http.HttpServletRequest} from the <code>javax.servlet.http</code> package.
89   * </p>
90   * <p>
91   * The standard interface of mostly getters, is extended with setters so that the request is mutable by the handlers that it is passed to. This allows the
92   * request object to be as lightweight as possible and not actually implement any significant behavior. For example
93   * <ul>
94   *
95   * <li>The {@link Request#getContextPath()} method will return null, until the request has been passed to a {@link ContextHandler} which matches the
96   * {@link Request#getPathInfo()} with a context path and calls {@link Request#setContextPath(String)} as a result.</li>
97   *
98   * <li>the HTTP session methods will all return null sessions until such time as a request has been passed to a
99   * {@link org.eclipse.jetty.server.session.SessionHandler} which checks for session cookies and enables the ability to create new sessions.</li>
100  *
101  * <li>The {@link Request#getServletPath()} method will return null until the request has been passed to a <code>org.eclipse.jetty.servlet.ServletHandler</code>
102  * and the pathInfo matched against the servlet URL patterns and {@link Request#setServletPath(String)} called as a result.</li>
103  * </ul>
104  *
105  * A request instance is created for each connection accepted by the server and recycled for each HTTP request received via that connection.
106  * An effort is made to avoid reparsing headers and cookies that are likely to be the same for requests from the same connection.
107  *
108  * <p>
109  * The form content that a request can process is limited to protect from Denial of Service attacks. The size in bytes is limited by
110  * {@link ContextHandler#getMaxFormContentSize()} or if there is no context then the "org.eclipse.jetty.server.Request.maxFormContentSize" {@link Server}
111  * attribute. The number of parameters keys is limited by {@link ContextHandler#getMaxFormKeys()} or if there is no context then the
112  * "org.eclipse.jetty.server.Request.maxFormKeys" {@link Server} attribute.
113  *
114  *
115  */
116 public class Request implements HttpServletRequest
117 {
118     public static final String __MULTIPART_CONFIG_ELEMENT = "org.eclipse.multipartConfig";
119     public static final String __MULTIPART_INPUT_STREAM = "org.eclipse.multiPartInputStream";
120     public static final String __MULTIPART_CONTEXT = "org.eclipse.multiPartContext";
121 
122     private static final Logger LOG = Log.getLogger(Request.class);
123     private static final Collection<Locale> __defaultLocale = Collections.singleton(Locale.getDefault());
124     private static final int __NONE = 0, _STREAM = 1, __READER = 2;
125 
126     private final HttpChannel<?> _channel;
127     private final HttpFields _fields=new HttpFields();
128     private final List<ServletRequestAttributeListener>  _requestAttributeListeners=new ArrayList<>();
129     private final HttpInput<?> _input;
130     
131     public static class MultiPartCleanerListener implements ServletRequestListener
132     {
133         @Override
134         public void requestDestroyed(ServletRequestEvent sre)
135         {
136             //Clean up any tmp files created by MultiPartInputStream
137             MultiPartInputStream mpis = (MultiPartInputStream)sre.getServletRequest().getAttribute(__MULTIPART_INPUT_STREAM);
138             if (mpis != null)
139             {
140                 ContextHandler.Context context = (ContextHandler.Context)sre.getServletRequest().getAttribute(__MULTIPART_CONTEXT);
141 
142                 //Only do the cleanup if we are exiting from the context in which a servlet parsed the multipart files
143                 if (context == sre.getServletContext())
144                 {
145                     try
146                     {
147                         mpis.deleteParts();
148                     }
149                     catch (MultiException e)
150                     {
151                         sre.getServletContext().log("Errors deleting multipart tmp files", e);
152                     }
153                 }
154             }
155         }
156 
157         @Override
158         public void requestInitialized(ServletRequestEvent sre)
159         {
160             //nothing to do, multipart config set up by ServletHolder.handle()
161         }
162         
163     }
164     
165     
166 
167     private boolean _secure;
168     private boolean _asyncSupported = true;
169     private boolean _newContext;
170     private boolean _cookiesExtracted = false;
171     private boolean _handled = false;
172     private boolean _paramsExtracted;
173     private boolean _requestedSessionIdFromCookie = false;
174     private volatile Attributes _attributes;
175     private Authentication _authentication;
176     private MultiMap<String> _baseParameters;
177     private String _characterEncoding;
178     private ContextHandler.Context _context;
179     private String _contextPath;
180     private CookieCutter _cookies;
181     private DispatcherType _dispatcherType;
182     private int _inputState = __NONE;
183     private HttpMethod _httpMethod;
184     private String _httpMethodString;
185     private MultiMap<String> _parameters;
186     private String _pathInfo;
187     private int _port;
188     private HttpVersion _httpVersion = HttpVersion.HTTP_1_1;
189     private String _queryEncoding;
190     private String _queryString;
191     private BufferedReader _reader;
192     private String _readerEncoding;
193     private InetSocketAddress _remote;
194     private String _requestedSessionId;
195     private String _requestURI;
196     private Map<Object, HttpSession> _savedNewSessions;
197     private String _scheme = URIUtil.HTTP;
198     private UserIdentity.Scope _scope;
199     private String _serverName;
200     private String _servletPath;
201     private HttpSession _session;
202     private SessionManager _sessionManager;
203     private long _timeStamp;
204     private long _dispatchTime;
205     private HttpURI _uri;
206     private MultiPartInputStream _multiPartInputStream; //if the request is a multi-part mime
207 
208     /* ------------------------------------------------------------ */
209     public Request(HttpChannel<?> channel, HttpInput<?> input)
210     {
211         _channel = channel;
212         _input = input;
213     }
214 
215     /* ------------------------------------------------------------ */
216     public HttpFields getHttpFields()
217     {
218         return _fields;
219     }
220 
221     /* ------------------------------------------------------------ */
222     public HttpInput<?> getHttpInput()
223     {
224         return _input;
225     }
226 
227     /* ------------------------------------------------------------ */
228     public void addEventListener(final EventListener listener)
229     {
230         if (listener instanceof ServletRequestAttributeListener)
231             _requestAttributeListeners.add((ServletRequestAttributeListener)listener);
232         if (listener instanceof AsyncListener)
233             throw new IllegalArgumentException(listener.getClass().toString());
234     }
235 
236     /* ------------------------------------------------------------ */
237     /**
238      * Extract Parameters from query string and/or form _content.
239      */
240     public void extractParameters()
241     {
242         if (_baseParameters == null)
243             _baseParameters = new MultiMap<>();
244 
245         if (_paramsExtracted)
246         {
247             if (_parameters == null)
248                 _parameters = _baseParameters;
249             return;
250         }
251 
252         _paramsExtracted = true;
253 
254         try
255         {
256             // Handle query string
257             if (_uri != null && _uri.hasQuery())
258             {
259                 if (_queryEncoding == null)
260                     _uri.decodeQueryTo(_baseParameters);
261                 else
262                 {
263                     try
264                     {
265                         _uri.decodeQueryTo(_baseParameters,_queryEncoding);
266                     }
267                     catch (UnsupportedEncodingException e)
268                     {
269                         if (LOG.isDebugEnabled())
270                             LOG.warn(e);
271                         else
272                             LOG.warn(e.toString());
273                     }
274                 }
275             }
276 
277             // handle any _content.
278             String encoding = getCharacterEncoding();
279             String content_type = getContentType();
280             if (content_type != null && content_type.length() > 0)
281             {
282                 content_type = HttpFields.valueParameters(content_type,null);
283 
284                 if (MimeTypes.Type.FORM_ENCODED.is(content_type) && _inputState == __NONE &&
285                     (HttpMethod.POST.is(getMethod()) || HttpMethod.PUT.is(getMethod())))
286                 {
287                     int content_length = getContentLength();
288                     if (content_length != 0)
289                     {
290                         try
291                         {
292                             int maxFormContentSize = -1;
293                             int maxFormKeys = -1;
294 
295                             if (_context != null)
296                             {
297                                 maxFormContentSize = _context.getContextHandler().getMaxFormContentSize();
298                                 maxFormKeys = _context.getContextHandler().getMaxFormKeys();
299                             }
300                             else
301                             {
302                                 Number size = (Number)_channel.getServer()
303                                         .getAttribute("org.eclipse.jetty.server.Request.maxFormContentSize");
304                                 maxFormContentSize = size == null?200000:size.intValue();
305                                 Number keys = (Number)_channel.getServer().getAttribute("org.eclipse.jetty.server.Request.maxFormKeys");
306                                 maxFormKeys = keys == null?1000:keys.intValue();
307                             }
308 
309                             if (content_length > maxFormContentSize && maxFormContentSize > 0)
310                             {
311                                 throw new IllegalStateException("Form too large" + content_length + ">" + maxFormContentSize);
312                             }
313                             InputStream in = getInputStream();
314 
315                             // Add form params to query params
316                             UrlEncoded.decodeTo(in,_baseParameters,encoding,content_length < 0?maxFormContentSize:-1,maxFormKeys);
317                         }
318                         catch (IOException e)
319                         {
320                             if (LOG.isDebugEnabled())
321                                 LOG.warn(e);
322                             else
323                                 LOG.warn(e.toString());
324                         }
325                     }
326                 }
327             }
328 
329             if (_parameters == null)
330                 _parameters = _baseParameters;
331             else if (_parameters != _baseParameters)
332             {
333                 // Merge parameters (needed if parameters extracted after a forward).
334                 _parameters.addAllValues(_baseParameters);
335             }
336         }
337         finally
338         {
339             // ensure params always set (even if empty) after extraction
340             if (_parameters == null)
341                 _parameters = _baseParameters;
342         }
343     }
344 
345     /* ------------------------------------------------------------ */
346     @Override
347     public AsyncContext getAsyncContext()
348     {
349         HttpChannelState continuation = getHttpChannelState();
350         if (continuation.isInitial() && !continuation.isAsync())
351             throw new IllegalStateException(continuation.getStatusString());
352         return continuation;
353     }
354 
355     /* ------------------------------------------------------------ */
356     public HttpChannelState getHttpChannelState()
357     {
358         return _channel.getState();
359     }
360 
361     /* ------------------------------------------------------------ */
362     /*
363      * @see javax.servlet.ServletRequest#getAttribute(java.lang.String)
364      */
365     @Override
366     public Object getAttribute(String name)
367     {
368         return (_attributes == null)?null:_attributes.getAttribute(name);
369     }
370 
371     /* ------------------------------------------------------------ */
372     /*
373      * @see javax.servlet.ServletRequest#getAttributeNames()
374      */
375     @Override
376     public Enumeration<String> getAttributeNames()
377     {
378         if (_attributes == null)
379             return Collections.enumeration(Collections.<String>emptyList());
380 
381         return AttributesMap.getAttributeNamesCopy(_attributes);
382     }
383 
384     /* ------------------------------------------------------------ */
385     /*
386      */
387     public Attributes getAttributes()
388     {
389         if (_attributes == null)
390             _attributes = new AttributesMap();
391         return _attributes;
392     }
393 
394     /* ------------------------------------------------------------ */
395     /**
396      * Get the authentication.
397      *
398      * @return the authentication
399      */
400     public Authentication getAuthentication()
401     {
402         return _authentication;
403     }
404 
405     /* ------------------------------------------------------------ */
406     /*
407      * @see javax.servlet.http.HttpServletRequest#getAuthType()
408      */
409     @Override
410     public String getAuthType()
411     {
412         if (_authentication instanceof Authentication.Deferred)
413             setAuthentication(((Authentication.Deferred)_authentication).authenticate(this));
414 
415         if (_authentication instanceof Authentication.User)
416             return ((Authentication.User)_authentication).getAuthMethod();
417         return null;
418     }
419 
420     /* ------------------------------------------------------------ */
421     /*
422      * @see javax.servlet.ServletRequest#getCharacterEncoding()
423      */
424     @Override
425     public String getCharacterEncoding()
426     {
427         return _characterEncoding;
428     }
429 
430     /* ------------------------------------------------------------ */
431     /**
432      * @return Returns the connection.
433      */
434     public HttpChannel<?> getHttpChannel()
435     {
436         return _channel;
437     }
438 
439     /* ------------------------------------------------------------ */
440     /*
441      * @see javax.servlet.ServletRequest#getContentLength()
442      */
443     @Override
444     public int getContentLength()
445     {
446         return (int)_fields.getLongField(HttpHeader.CONTENT_LENGTH.toString());
447     }
448 
449     /* ------------------------------------------------------------ */
450     /*
451      * @see javax.servlet.ServletRequest#getContentType()
452      */
453     @Override
454     public String getContentType()
455     {
456         return _fields.getStringField(HttpHeader.CONTENT_TYPE);
457     }
458 
459     /* ------------------------------------------------------------ */
460     /**
461      * @return The current {@link Context context} used for this request, or <code>null</code> if {@link #setContext} has not yet been called.
462      */
463     public Context getContext()
464     {
465         return _context;
466     }
467 
468     /* ------------------------------------------------------------ */
469     /*
470      * @see javax.servlet.http.HttpServletRequest#getContextPath()
471      */
472     @Override
473     public String getContextPath()
474     {
475         return _contextPath;
476     }
477 
478     /* ------------------------------------------------------------ */
479     /*
480      * @see javax.servlet.http.HttpServletRequest#getCookies()
481      */
482     @Override
483     public Cookie[] getCookies()
484     {
485         if (_cookiesExtracted)
486             return _cookies == null?null:_cookies.getCookies();
487 
488         _cookiesExtracted = true;
489 
490         Enumeration<?> enm = _fields.getValues(HttpHeader.COOKIE.toString());
491 
492         // Handle no cookies
493         if (enm != null)
494         {
495             if (_cookies == null)
496                 _cookies = new CookieCutter();
497 
498             while (enm.hasMoreElements())
499             {
500                 String c = (String)enm.nextElement();
501                 _cookies.addCookieField(c);
502             }
503         }
504 
505         return _cookies == null?null:_cookies.getCookies();
506     }
507 
508     /* ------------------------------------------------------------ */
509     /*
510      * @see javax.servlet.http.HttpServletRequest#getDateHeader(java.lang.String)
511      */
512     @Override
513     public long getDateHeader(String name)
514     {
515         return _fields.getDateField(name);
516     }
517 
518     /* ------------------------------------------------------------ */
519     @Override
520     public DispatcherType getDispatcherType()
521     {
522         return _dispatcherType;
523     }
524 
525     /* ------------------------------------------------------------ */
526     /*
527      * @see javax.servlet.http.HttpServletRequest#getHeader(java.lang.String)
528      */
529     @Override
530     public String getHeader(String name)
531     {
532         return _fields.getStringField(name);
533     }
534 
535     /* ------------------------------------------------------------ */
536     /*
537      * @see javax.servlet.http.HttpServletRequest#getHeaderNames()
538      */
539     @Override
540     public Enumeration<String> getHeaderNames()
541     {
542         return _fields.getFieldNames();
543     }
544 
545     /* ------------------------------------------------------------ */
546     /*
547      * @see javax.servlet.http.HttpServletRequest#getHeaders(java.lang.String)
548      */
549     @Override
550     public Enumeration<String> getHeaders(String name)
551     {
552         Enumeration<String> e = _fields.getValues(name);
553         if (e == null)
554             return Collections.enumeration(Collections.<String>emptyList());
555         return e;
556     }
557 
558     /* ------------------------------------------------------------ */
559     /**
560      * @return Returns the inputState.
561      */
562     public int getInputState()
563     {
564         return _inputState;
565     }
566 
567     /* ------------------------------------------------------------ */
568     /*
569      * @see javax.servlet.ServletRequest#getInputStream()
570      */
571     @Override
572     public ServletInputStream getInputStream() throws IOException
573     {
574         if (_inputState != __NONE && _inputState != _STREAM)
575             throw new IllegalStateException("READER");
576         _inputState = _STREAM;
577 
578         if (_channel.isExpecting100Continue())
579             _channel.continue100(_input.available());
580 
581         return _input;
582     }
583 
584     /* ------------------------------------------------------------ */
585     /*
586      * @see javax.servlet.http.HttpServletRequest#getIntHeader(java.lang.String)
587      */
588     @Override
589     public int getIntHeader(String name)
590     {
591         return (int)_fields.getLongField(name);
592     }
593 
594 
595     /* ------------------------------------------------------------ */
596     /*
597      * @see javax.servlet.ServletRequest#getLocale()
598      */
599     @Override
600     public Locale getLocale()
601     {
602         Enumeration<String> enm = _fields.getValues(HttpHeader.ACCEPT_LANGUAGE.toString(),HttpFields.__separators);
603 
604         // handle no locale
605         if (enm == null || !enm.hasMoreElements())
606             return Locale.getDefault();
607 
608         // sort the list in quality order
609         List<?> acceptLanguage = HttpFields.qualityList(enm);
610         if (acceptLanguage.size() == 0)
611             return Locale.getDefault();
612 
613         int size = acceptLanguage.size();
614 
615         if (size > 0)
616         {
617             String language = (String)acceptLanguage.get(0);
618             language = HttpFields.valueParameters(language,null);
619             String country = "";
620             int dash = language.indexOf('-');
621             if (dash > -1)
622             {
623                 country = language.substring(dash + 1).trim();
624                 language = language.substring(0,dash).trim();
625             }
626             return new Locale(language,country);
627         }
628 
629         return Locale.getDefault();
630     }
631 
632     /* ------------------------------------------------------------ */
633     /*
634      * @see javax.servlet.ServletRequest#getLocales()
635      */
636     @Override
637     public Enumeration<Locale> getLocales()
638     {
639 
640         Enumeration<String> enm = _fields.getValues(HttpHeader.ACCEPT_LANGUAGE.toString(),HttpFields.__separators);
641 
642         // handle no locale
643         if (enm == null || !enm.hasMoreElements())
644             return Collections.enumeration(__defaultLocale);
645 
646         // sort the list in quality order
647         List<String> acceptLanguage = HttpFields.qualityList(enm);
648 
649         if (acceptLanguage.size() == 0)
650             return Collections.enumeration(__defaultLocale);
651 
652         List<Locale> langs = new ArrayList<>();
653 
654         // convert to locals
655         for (String language : acceptLanguage)
656         {
657             language = HttpFields.valueParameters(language, null);
658             String country = "";
659             int dash = language.indexOf('-');
660             if (dash > -1)
661             {
662                 country = language.substring(dash + 1).trim();
663                 language = language.substring(0, dash).trim();
664             }
665             langs.add(new Locale(language, country));
666         }
667 
668         if (langs.size() == 0)
669             return Collections.enumeration(__defaultLocale);
670 
671         return Collections.enumeration(langs);
672     }
673 
674     /* ------------------------------------------------------------ */
675     /*
676      * @see javax.servlet.ServletRequest#getLocalAddr()
677      */
678     @Override
679     public String getLocalAddr()
680     {
681         InetSocketAddress local=_channel.getLocalAddress();
682         return local.getAddress().getHostAddress();
683     }
684 
685     /* ------------------------------------------------------------ */
686     /*
687      * @see javax.servlet.ServletRequest#getLocalName()
688      */
689     @Override
690     public String getLocalName()
691     {
692         InetSocketAddress local=_channel.getLocalAddress();
693         return local.getHostString();
694     }
695 
696     /* ------------------------------------------------------------ */
697     /*
698      * @see javax.servlet.ServletRequest#getLocalPort()
699      */
700     @Override
701     public int getLocalPort()
702     {
703         InetSocketAddress local=_channel.getLocalAddress();
704         return local.getPort();
705     }
706 
707     /* ------------------------------------------------------------ */
708     /*
709      * @see javax.servlet.http.HttpServletRequest#getMethod()
710      */
711     @Override
712     public String getMethod()
713     {
714         return _httpMethodString;
715     }
716 
717     /* ------------------------------------------------------------ */
718     /*
719      * @see javax.servlet.ServletRequest#getParameter(java.lang.String)
720      */
721     @Override
722     public String getParameter(String name)
723     {
724         if (!_paramsExtracted)
725             extractParameters();
726         return _parameters.getValue(name,0);
727     }
728 
729     /* ------------------------------------------------------------ */
730     /*
731      * @see javax.servlet.ServletRequest#getParameterMap()
732      */
733     @Override
734     public Map<String, String[]> getParameterMap()
735     {
736         if (!_paramsExtracted)
737             extractParameters();
738 
739         return Collections.unmodifiableMap(_parameters.toStringArrayMap());
740     }
741 
742     /* ------------------------------------------------------------ */
743     /*
744      * @see javax.servlet.ServletRequest#getParameterNames()
745      */
746     @Override
747     public Enumeration<String> getParameterNames()
748     {
749         if (!_paramsExtracted)
750             extractParameters();
751         return Collections.enumeration(_parameters.keySet());
752     }
753 
754     /* ------------------------------------------------------------ */
755     /**
756      * @return Returns the parameters.
757      */
758     public MultiMap<String> getParameters()
759     {
760         return _parameters;
761     }
762 
763     /* ------------------------------------------------------------ */
764     /*
765      * @see javax.servlet.ServletRequest#getParameterValues(java.lang.String)
766      */
767     @Override
768     public String[] getParameterValues(String name)
769     {
770         if (!_paramsExtracted)
771             extractParameters();
772         List<String> vals = _parameters.getValues(name);
773         if (vals == null)
774             return null;
775         return vals.toArray(new String[vals.size()]);
776     }
777 
778     /* ------------------------------------------------------------ */
779     /*
780      * @see javax.servlet.http.HttpServletRequest#getPathInfo()
781      */
782     @Override
783     public String getPathInfo()
784     {
785         return _pathInfo;
786     }
787 
788     /* ------------------------------------------------------------ */
789     /*
790      * @see javax.servlet.http.HttpServletRequest#getPathTranslated()
791      */
792     @Override
793     public String getPathTranslated()
794     {
795         if (_pathInfo == null || _context == null)
796             return null;
797         return _context.getRealPath(_pathInfo);
798     }
799 
800     /* ------------------------------------------------------------ */
801     /*
802      * @see javax.servlet.ServletRequest#getProtocol()
803      */
804     @Override
805     public String getProtocol()
806     {
807         return _httpVersion.toString();
808     }
809 
810     /* ------------------------------------------------------------ */
811     /*
812      * @see javax.servlet.ServletRequest#getProtocol()
813      */
814     public HttpVersion getHttpVersion()
815     {
816         return _httpVersion;
817     }
818 
819     /* ------------------------------------------------------------ */
820     public String getQueryEncoding()
821     {
822         return _queryEncoding;
823     }
824 
825     /* ------------------------------------------------------------ */
826     /*
827      * @see javax.servlet.http.HttpServletRequest#getQueryString()
828      */
829     @Override
830     public String getQueryString()
831     {
832         if (_queryString == null && _uri != null)
833         {
834             if (_queryEncoding == null)
835                 _queryString = _uri.getQuery();
836             else
837                 _queryString = _uri.getQuery(_queryEncoding);
838         }
839         return _queryString;
840     }
841 
842     /* ------------------------------------------------------------ */
843     /*
844      * @see javax.servlet.ServletRequest#getReader()
845      */
846     @Override
847     public BufferedReader getReader() throws IOException
848     {
849         if (_inputState != __NONE && _inputState != __READER)
850             throw new IllegalStateException("STREAMED");
851 
852         if (_inputState == __READER)
853             return _reader;
854 
855         String encoding = getCharacterEncoding();
856         if (encoding == null)
857             encoding = StringUtil.__ISO_8859_1;
858 
859         if (_reader == null || !encoding.equalsIgnoreCase(_readerEncoding))
860         {
861             final ServletInputStream in = getInputStream();
862             _readerEncoding = encoding;
863             _reader = new BufferedReader(new InputStreamReader(in,encoding))
864             {
865                 @Override
866                 public void close() throws IOException
867                 {
868                     in.close();
869                 }
870             };
871         }
872         _inputState = __READER;
873         return _reader;
874     }
875 
876     /* ------------------------------------------------------------ */
877     /*
878      * @see javax.servlet.ServletRequest#getRealPath(java.lang.String)
879      */
880     @Override
881     public String getRealPath(String path)
882     {
883         if (_context == null)
884             return null;
885         return _context.getRealPath(path);
886     }
887 
888     /* ------------------------------------------------------------ */
889     /*
890      * @see javax.servlet.ServletRequest#getRemoteAddr()
891      */
892     @Override
893     public String getRemoteAddr()
894     {
895         InetSocketAddress remote=_remote;
896         if (remote==null)
897             remote=_channel.getRemoteAddress();
898 
899         return remote==null?"":remote.getHostString();
900     }
901 
902     /* ------------------------------------------------------------ */
903     /*
904      * @see javax.servlet.ServletRequest#getRemoteHost()
905      */
906     @Override
907     public String getRemoteHost()
908     {
909         InetSocketAddress remote=_remote;
910         if (remote==null)
911             remote=_channel.getRemoteAddress();
912         return remote==null?"":remote.getHostString();
913     }
914 
915     /* ------------------------------------------------------------ */
916     /*
917      * @see javax.servlet.ServletRequest#getRemotePort()
918      */
919     @Override
920     public int getRemotePort()
921     {
922         InetSocketAddress remote=_remote;
923         if (remote==null)
924             remote=_channel.getRemoteAddress();
925         return remote==null?0:remote.getPort();
926     }
927 
928     /* ------------------------------------------------------------ */
929     /*
930      * @see javax.servlet.http.HttpServletRequest#getRemoteUser()
931      */
932     @Override
933     public String getRemoteUser()
934     {
935         Principal p = getUserPrincipal();
936         if (p == null)
937             return null;
938         return p.getName();
939     }
940 
941     /* ------------------------------------------------------------ */
942     /*
943      * @see javax.servlet.ServletRequest#getRequestDispatcher(java.lang.String)
944      */
945     @Override
946     public RequestDispatcher getRequestDispatcher(String path)
947     {
948         if (path == null || _context == null)
949             return null;
950 
951         // handle relative path
952         if (!path.startsWith("/"))
953         {
954             String relTo = URIUtil.addPaths(_servletPath,_pathInfo);
955             int slash = relTo.lastIndexOf("/");
956             if (slash > 1)
957                 relTo = relTo.substring(0,slash + 1);
958             else
959                 relTo = "/";
960             path = URIUtil.addPaths(relTo,path);
961         }
962 
963         return _context.getRequestDispatcher(path);
964     }
965 
966     /* ------------------------------------------------------------ */
967     /*
968      * @see javax.servlet.http.HttpServletRequest#getRequestedSessionId()
969      */
970     @Override
971     public String getRequestedSessionId()
972     {
973         return _requestedSessionId;
974     }
975 
976     /* ------------------------------------------------------------ */
977     /*
978      * @see javax.servlet.http.HttpServletRequest#getRequestURI()
979      */
980     @Override
981     public String getRequestURI()
982     {
983         if (_requestURI == null && _uri != null)
984             _requestURI = _uri.getPathAndParam();
985         return _requestURI;
986     }
987 
988     /* ------------------------------------------------------------ */
989     /*
990      * @see javax.servlet.http.HttpServletRequest#getRequestURL()
991      */
992     @Override
993     public StringBuffer getRequestURL()
994     {
995         final StringBuffer url = new StringBuffer(48);
996         String scheme = getScheme();
997         int port = getServerPort();
998 
999         url.append(scheme);
1000         url.append("://");
1001         url.append(getServerName());
1002         if (_port > 0 && ((scheme.equalsIgnoreCase(URIUtil.HTTP) && port != 80) || (scheme.equalsIgnoreCase(URIUtil.HTTPS) && port != 443)))
1003         {
1004             url.append(':');
1005             url.append(_port);
1006         }
1007 
1008         url.append(getRequestURI());
1009         return url;
1010     }
1011 
1012     /* ------------------------------------------------------------ */
1013     public Response getResponse()
1014     {
1015         return _channel.getResponse();
1016     }
1017 
1018     /* ------------------------------------------------------------ */
1019     /**
1020      * Reconstructs the URL the client used to make the request. The returned URL contains a protocol, server name, port number, and, but it does not include a
1021      * path.
1022      * <p>
1023      * Because this method returns a <code>StringBuffer</code>, not a string, you can modify the URL easily, for example, to append path and query parameters.
1024      *
1025      * This method is useful for creating redirect messages and for reporting errors.
1026      *
1027      * @return "scheme://host:port"
1028      */
1029     public StringBuilder getRootURL()
1030     {
1031         StringBuilder url = new StringBuilder(48);
1032         String scheme = getScheme();
1033         int port = getServerPort();
1034 
1035         url.append(scheme);
1036         url.append("://");
1037         url.append(getServerName());
1038 
1039         if (port > 0 && ((scheme.equalsIgnoreCase("http") && port != 80) || (scheme.equalsIgnoreCase("https") && port != 443)))
1040         {
1041             url.append(':');
1042             url.append(port);
1043         }
1044         return url;
1045     }
1046 
1047     /* ------------------------------------------------------------ */
1048     /*
1049      * @see javax.servlet.ServletRequest#getScheme()
1050      */
1051     @Override
1052     public String getScheme()
1053     {
1054         return _scheme;
1055     }
1056 
1057     /* ------------------------------------------------------------ */
1058     /*
1059      * @see javax.servlet.ServletRequest#getServerName()
1060      */
1061     @Override
1062     public String getServerName()
1063     {
1064         // Return already determined host
1065         if (_serverName != null)
1066             return _serverName;
1067 
1068         if (_uri == null)
1069             throw new IllegalStateException("No uri");
1070 
1071         // Return host from absolute URI
1072         _serverName = _uri.getHost();
1073         _port = _uri.getPort();
1074         if (_serverName != null)
1075             return _serverName;
1076 
1077         // Return host from header field
1078         String hostPort = _fields.getStringField(HttpHeader.HOST);
1079         if (hostPort != null)
1080         {
1081             loop: for (int i = hostPort.length(); i-- > 0;)
1082             {
1083                 char ch = (char)(0xff & hostPort.charAt(i));
1084                 switch (ch)
1085                 {
1086                     case ']':
1087                         break loop;
1088 
1089                     case ':':
1090                         _serverName = hostPort.substring(0,i);
1091                         try
1092                         {
1093                             _port = StringUtil.toInt(hostPort.substring(i+1));
1094                         }
1095                         catch (NumberFormatException e)
1096                         {
1097                             LOG.warn(e);
1098                         }
1099                         return _serverName;
1100                 }
1101             }
1102 
1103             if (_serverName == null || _port < 0)
1104             {
1105                 _serverName = hostPort;
1106                 _port = 0;
1107             }
1108 
1109             return _serverName;
1110         }
1111 
1112         // Return host from connection
1113         if (_channel != null)
1114         {
1115             _serverName = getLocalName();
1116             _port = getLocalPort();
1117             if (_serverName != null && !StringUtil.ALL_INTERFACES.equals(_serverName))
1118                 return _serverName;
1119         }
1120 
1121         // Return the local host
1122         try
1123         {
1124             _serverName = InetAddress.getLocalHost().getHostAddress();
1125         }
1126         catch (java.net.UnknownHostException e)
1127         {
1128             LOG.ignore(e);
1129         }
1130         return _serverName;
1131     }
1132 
1133     /* ------------------------------------------------------------ */
1134     /*
1135      * @see javax.servlet.ServletRequest#getServerPort()
1136      */
1137     @Override
1138     public int getServerPort()
1139     {
1140         if (_port <= 0)
1141         {
1142             if (_serverName == null)
1143                 getServerName();
1144 
1145             if (_port <= 0)
1146             {
1147                 if (_serverName != null && _uri != null)
1148                     _port = _uri.getPort();
1149                 else
1150                 {
1151                     InetSocketAddress local = _channel.getLocalAddress();
1152                     _port = local == null?0:local.getPort();
1153                 }
1154             }
1155         }
1156 
1157         if (_port <= 0)
1158         {
1159             if (getScheme().equalsIgnoreCase(URIUtil.HTTPS))
1160                 return 443;
1161             return 80;
1162         }
1163         return _port;
1164     }
1165 
1166     /* ------------------------------------------------------------ */
1167     @Override
1168     public ServletContext getServletContext()
1169     {
1170         return _context;
1171     }
1172 
1173     /* ------------------------------------------------------------ */
1174     /*
1175      */
1176     public String getServletName()
1177     {
1178         if (_scope != null)
1179             return _scope.getName();
1180         return null;
1181     }
1182 
1183     /* ------------------------------------------------------------ */
1184     /*
1185      * @see javax.servlet.http.HttpServletRequest#getServletPath()
1186      */
1187     @Override
1188     public String getServletPath()
1189     {
1190         if (_servletPath == null)
1191             _servletPath = "";
1192         return _servletPath;
1193     }
1194 
1195     /* ------------------------------------------------------------ */
1196     public ServletResponse getServletResponse()
1197     {
1198         return _channel.getResponse();
1199     }
1200 
1201     /* ------------------------------------------------------------ */
1202     /*
1203      * Add @override when 3.1 api is available
1204      */
1205     public String changeSessionId()
1206     {
1207         HttpSession session = getSession(false);
1208         if (session == null)
1209             throw new IllegalStateException("No session");
1210 
1211         if (session instanceof AbstractSession)
1212         {
1213             AbstractSession abstractSession =  ((AbstractSession)session);
1214             abstractSession.renewId(this);
1215             if (getRemoteUser() != null)
1216                 abstractSession.setAttribute(AbstractSession.SESSION_KNOWN_ONLY_TO_AUTHENTICATED, Boolean.TRUE);
1217             if (abstractSession.isIdChanged())
1218                 _channel.getResponse().addCookie(_sessionManager.getSessionCookie(abstractSession, getContextPath(), isSecure()));
1219         }
1220 
1221         return session.getId();
1222     }
1223 
1224     /* ------------------------------------------------------------ */
1225     /*
1226      * @see javax.servlet.http.HttpServletRequest#getSession()
1227      */
1228     @Override
1229     public HttpSession getSession()
1230     {
1231         return getSession(true);
1232     }
1233 
1234     /* ------------------------------------------------------------ */
1235     /*
1236      * @see javax.servlet.http.HttpServletRequest#getSession(boolean)
1237      */
1238     @Override
1239     public HttpSession getSession(boolean create)
1240     {
1241         if (_session != null)
1242         {
1243             if (_sessionManager != null && !_sessionManager.isValid(_session))
1244                 _session = null;
1245             else
1246                 return _session;
1247         }
1248 
1249         if (!create)
1250             return null;
1251 
1252         if (_sessionManager == null)
1253             throw new IllegalStateException("No SessionManager");
1254 
1255         _session = _sessionManager.newHttpSession(this);
1256         HttpCookie cookie = _sessionManager.getSessionCookie(_session,getContextPath(),isSecure());
1257         if (cookie != null)
1258             _channel.getResponse().addCookie(cookie);
1259 
1260         return _session;
1261     }
1262 
1263     /* ------------------------------------------------------------ */
1264     /**
1265      * @return Returns the sessionManager.
1266      */
1267     public SessionManager getSessionManager()
1268     {
1269         return _sessionManager;
1270     }
1271 
1272     /* ------------------------------------------------------------ */
1273     /**
1274      * Get Request TimeStamp
1275      *
1276      * @return The time that the request was received.
1277      */
1278     public long getTimeStamp()
1279     {
1280         return _timeStamp;
1281     }
1282 
1283     /* ------------------------------------------------------------ */
1284     /**
1285      * @return Returns the uri.
1286      */
1287     public HttpURI getUri()
1288     {
1289         return _uri;
1290     }
1291 
1292     /* ------------------------------------------------------------ */
1293     public UserIdentity getUserIdentity()
1294     {
1295         if (_authentication instanceof Authentication.Deferred)
1296             setAuthentication(((Authentication.Deferred)_authentication).authenticate(this));
1297 
1298         if (_authentication instanceof Authentication.User)
1299             return ((Authentication.User)_authentication).getUserIdentity();
1300         return null;
1301     }
1302 
1303     /* ------------------------------------------------------------ */
1304     /**
1305      * @return The resolved user Identity, which may be null if the {@link Authentication} is not {@link Authentication.User} (eg.
1306      *         {@link Authentication.Deferred}).
1307      */
1308     public UserIdentity getResolvedUserIdentity()
1309     {
1310         if (_authentication instanceof Authentication.User)
1311             return ((Authentication.User)_authentication).getUserIdentity();
1312         return null;
1313     }
1314 
1315     /* ------------------------------------------------------------ */
1316     public UserIdentity.Scope getUserIdentityScope()
1317     {
1318         return _scope;
1319     }
1320 
1321     /* ------------------------------------------------------------ */
1322     /*
1323      * @see javax.servlet.http.HttpServletRequest#getUserPrincipal()
1324      */
1325     @Override
1326     public Principal getUserPrincipal()
1327     {
1328         if (_authentication instanceof Authentication.Deferred)
1329             setAuthentication(((Authentication.Deferred)_authentication).authenticate(this));
1330 
1331         if (_authentication instanceof Authentication.User)
1332         {
1333             UserIdentity user = ((Authentication.User)_authentication).getUserIdentity();
1334             return user.getUserPrincipal();
1335         }
1336         
1337         return null;
1338     }
1339 
1340     /* ------------------------------------------------------------ */
1341     /**
1342      * Get timestamp of the request dispatch
1343      *
1344      * @return timestamp
1345      */
1346     public long getDispatchTime()
1347     {
1348         return _dispatchTime;
1349     }
1350 
1351     /* ------------------------------------------------------------ */
1352     public boolean isHandled()
1353     {
1354         return _handled;
1355     }
1356 
1357     @Override
1358     public boolean isAsyncStarted()
1359     {
1360        return getHttpChannelState().isAsync();
1361     }
1362 
1363 
1364     /* ------------------------------------------------------------ */
1365     @Override
1366     public boolean isAsyncSupported()
1367     {
1368         return _asyncSupported;
1369     }
1370 
1371     /* ------------------------------------------------------------ */
1372     /*
1373      * @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdFromCookie()
1374      */
1375     @Override
1376     public boolean isRequestedSessionIdFromCookie()
1377     {
1378         return _requestedSessionId != null && _requestedSessionIdFromCookie;
1379     }
1380 
1381     /* ------------------------------------------------------------ */
1382     /*
1383      * @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdFromUrl()
1384      */
1385     @Override
1386     public boolean isRequestedSessionIdFromUrl()
1387     {
1388         return _requestedSessionId != null && !_requestedSessionIdFromCookie;
1389     }
1390 
1391     /* ------------------------------------------------------------ */
1392     /*
1393      * @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdFromURL()
1394      */
1395     @Override
1396     public boolean isRequestedSessionIdFromURL()
1397     {
1398         return _requestedSessionId != null && !_requestedSessionIdFromCookie;
1399     }
1400 
1401     /* ------------------------------------------------------------ */
1402     /*
1403      * @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdValid()
1404      */
1405     @Override
1406     public boolean isRequestedSessionIdValid()
1407     {
1408         if (_requestedSessionId == null)
1409             return false;
1410 
1411         HttpSession session = getSession(false);
1412         return (session != null && _sessionManager.getSessionIdManager().getClusterId(_requestedSessionId).equals(_sessionManager.getClusterId(session)));
1413     }
1414 
1415     /* ------------------------------------------------------------ */
1416     /*
1417      * @see javax.servlet.ServletRequest#isSecure()
1418      */
1419     @Override
1420     public boolean isSecure()
1421     {
1422         return _secure;
1423     }
1424 
1425     /* ------------------------------------------------------------ */
1426     public void setSecure(boolean secure)
1427     {
1428         _secure=secure;
1429     }
1430 
1431     /* ------------------------------------------------------------ */
1432     /*
1433      * @see javax.servlet.http.HttpServletRequest#isUserInRole(java.lang.String)
1434      */
1435     @Override
1436     public boolean isUserInRole(String role)
1437     {
1438         if (_authentication instanceof Authentication.Deferred)
1439             setAuthentication(((Authentication.Deferred)_authentication).authenticate(this));
1440 
1441         if (_authentication instanceof Authentication.User)
1442             return ((Authentication.User)_authentication).isUserInRole(_scope,role);
1443         return false;
1444     }
1445 
1446     /* ------------------------------------------------------------ */
1447     public HttpSession recoverNewSession(Object key)
1448     {
1449         if (_savedNewSessions == null)
1450             return null;
1451         return _savedNewSessions.get(key);
1452     }
1453 
1454     /* ------------------------------------------------------------ */
1455     protected void recycle()
1456     {
1457         if (_inputState == __READER)
1458         {
1459             try
1460             {
1461                 int r = _reader.read();
1462                 while (r != -1)
1463                     r = _reader.read();
1464             }
1465             catch (Exception e)
1466             {
1467                 LOG.ignore(e);
1468                 _reader = null;
1469             }
1470         }
1471 
1472         setAuthentication(Authentication.NOT_CHECKED);
1473         getHttpChannelState().recycle();
1474         _asyncSupported = true;
1475         _handled = false;
1476         if (_context != null)
1477             throw new IllegalStateException("Request in context!");
1478         if (_attributes != null)
1479             _attributes.clearAttributes();
1480         _characterEncoding = null;
1481         _contextPath = null;
1482         if (_cookies != null)
1483             _cookies.reset();
1484         _cookiesExtracted = false;
1485         _context = null;
1486         _serverName = null;
1487         _httpMethodString = null;
1488         _pathInfo = null;
1489         _port = 0;
1490         _httpVersion = HttpVersion.HTTP_1_1;
1491         _queryEncoding = null;
1492         _queryString = null;
1493         _requestedSessionId = null;
1494         _requestedSessionIdFromCookie = false;
1495         _session = null;
1496         _sessionManager = null;
1497         _requestURI = null;
1498         _scope = null;
1499         _scheme = URIUtil.HTTP;
1500         _servletPath = null;
1501         _timeStamp = 0;
1502         _uri = null;
1503         if (_baseParameters != null)
1504             _baseParameters.clear();
1505         _parameters = null;
1506         _paramsExtracted = false;
1507         _inputState = __NONE;
1508 
1509         if (_savedNewSessions != null)
1510             _savedNewSessions.clear();
1511         _savedNewSessions=null;
1512         _multiPartInputStream = null;
1513         _remote=null;
1514         _fields.clear();
1515         _input.recycle();
1516     }
1517 
1518     /* ------------------------------------------------------------ */
1519     /*
1520      * @see javax.servlet.ServletRequest#removeAttribute(java.lang.String)
1521      */
1522     @Override
1523     public void removeAttribute(String name)
1524     {
1525         Object old_value = _attributes == null?null:_attributes.getAttribute(name);
1526 
1527         if (_attributes != null)
1528             _attributes.removeAttribute(name);
1529 
1530         if (old_value != null && !_requestAttributeListeners.isEmpty())
1531         {
1532             final ServletRequestAttributeEvent event = new ServletRequestAttributeEvent(_context,this,name,old_value);
1533             for (ServletRequestAttributeListener listener : _requestAttributeListeners)
1534                 listener.attributeRemoved(event);
1535         }
1536     }
1537 
1538     /* ------------------------------------------------------------ */
1539     public void removeEventListener(final EventListener listener)
1540     {
1541         _requestAttributeListeners.remove(listener);
1542     }
1543 
1544     /* ------------------------------------------------------------ */
1545     public void saveNewSession(Object key, HttpSession session)
1546     {
1547         if (_savedNewSessions == null)
1548             _savedNewSessions = new HashMap<>();
1549         _savedNewSessions.put(key,session);
1550     }
1551 
1552     /* ------------------------------------------------------------ */
1553     public void setAsyncSupported(boolean supported)
1554     {
1555         _asyncSupported = supported;
1556     }
1557 
1558     /* ------------------------------------------------------------ */
1559     /*
1560      * Set a request attribute. if the attribute name is "org.eclipse.jetty.server.server.Request.queryEncoding" then the value is also passed in a call to
1561      * {@link #setQueryEncoding}. <p> if the attribute name is "org.eclipse.jetty.server.server.ResponseBuffer", then the response buffer is flushed with @{link
1562      * #flushResponseBuffer} <p> if the attribute name is "org.eclipse.jetty.io.EndPoint.maxIdleTime", then the value is passed to the associated {@link
1563      * EndPoint#setIdleTimeout}.
1564      *
1565      * @see javax.servlet.ServletRequest#setAttribute(java.lang.String, java.lang.Object)
1566      */
1567     @Override
1568     public void setAttribute(String name, Object value)
1569     {
1570         Object old_value = _attributes == null?null:_attributes.getAttribute(name);
1571 
1572         if (name.startsWith("org.eclipse.jetty."))
1573         {
1574             if ("org.eclipse.jetty.server.Request.queryEncoding".equals(name))
1575                 setQueryEncoding(value == null?null:value.toString());
1576             else if ("org.eclipse.jetty.server.sendContent".equals(name))
1577             {
1578                 try
1579                 {
1580                     ((HttpOutput)getServletResponse().getOutputStream()).sendContent(value);
1581                 }
1582                 catch (IOException e)
1583                 {
1584                     throw new RuntimeException(e);
1585                 }
1586             }
1587             else if ("org.eclipse.jetty.server.ResponseBuffer".equals(name))
1588             {
1589                 try
1590                 {
1591                     throw new IOException("not implemented");
1592                     //((HttpChannel.Output)getServletResponse().getOutputStream()).sendResponse(byteBuffer);
1593                 }
1594                 catch (IOException e)
1595                 {
1596                     throw new RuntimeException(e);
1597                 }
1598             }
1599         }
1600 
1601         if (_attributes == null)
1602             _attributes = new AttributesMap();
1603         _attributes.setAttribute(name,value);
1604 
1605         if (!_requestAttributeListeners.isEmpty())
1606         {
1607             final ServletRequestAttributeEvent event = new ServletRequestAttributeEvent(_context,this,name,old_value == null?value:old_value);
1608             for (ServletRequestAttributeListener l : _requestAttributeListeners)
1609             {
1610                 if (old_value == null)
1611                     l.attributeAdded(event);
1612                 else if (value == null)
1613                     l.attributeRemoved(event);
1614                 else
1615                     l.attributeReplaced(event);
1616             }
1617         }
1618     }
1619 
1620     /* ------------------------------------------------------------ */
1621     /*
1622      */
1623     public void setAttributes(Attributes attributes)
1624     {
1625         _attributes = attributes;
1626     }
1627 
1628     /* ------------------------------------------------------------ */
1629 
1630     /* ------------------------------------------------------------ */
1631     /**
1632      * Set the authentication.
1633      *
1634      * @param authentication
1635      *            the authentication to set
1636      */
1637     public void setAuthentication(Authentication authentication)
1638     {
1639         _authentication = authentication;
1640     }
1641 
1642     /* ------------------------------------------------------------ */
1643     /*
1644      * @see javax.servlet.ServletRequest#setCharacterEncoding(java.lang.String)
1645      */
1646     @Override
1647     public void setCharacterEncoding(String encoding) throws UnsupportedEncodingException
1648     {
1649         if (_inputState != __NONE)
1650             return;
1651 
1652         _characterEncoding = encoding;
1653 
1654         // check encoding is supported
1655         if (!StringUtil.isUTF8(encoding))
1656             Charset.forName(encoding);
1657     }
1658 
1659     /* ------------------------------------------------------------ */
1660     /*
1661      * @see javax.servlet.ServletRequest#setCharacterEncoding(java.lang.String)
1662      */
1663     public void setCharacterEncodingUnchecked(String encoding)
1664     {
1665         _characterEncoding = encoding;
1666     }
1667 
1668     /* ------------------------------------------------------------ */
1669     /*
1670      * @see javax.servlet.ServletRequest#getContentType()
1671      */
1672     public void setContentType(String contentType)
1673     {
1674         _fields.put(HttpHeader.CONTENT_TYPE,contentType);
1675 
1676     }
1677 
1678     /* ------------------------------------------------------------ */
1679     /**
1680      * Set request context
1681      *
1682      * @param context
1683      *            context object
1684      */
1685     public void setContext(Context context)
1686     {
1687         _newContext = _context != context;
1688         _context = context;
1689     }
1690 
1691     /* ------------------------------------------------------------ */
1692     /**
1693      * @return True if this is the first call of {@link #takeNewContext()} since the last
1694      *         {@link #setContext(org.eclipse.jetty.server.handler.ContextHandler.Context)} call.
1695      */
1696     public boolean takeNewContext()
1697     {
1698         boolean nc = _newContext;
1699         _newContext = false;
1700         return nc;
1701     }
1702 
1703     /* ------------------------------------------------------------ */
1704     /**
1705      * Sets the "context path" for this request
1706      *
1707      * @see HttpServletRequest#getContextPath()
1708      */
1709     public void setContextPath(String contextPath)
1710     {
1711         _contextPath = contextPath;
1712     }
1713 
1714     /* ------------------------------------------------------------ */
1715     /**
1716      * @param cookies
1717      *            The cookies to set.
1718      */
1719     public void setCookies(Cookie[] cookies)
1720     {
1721         if (_cookies == null)
1722             _cookies = new CookieCutter();
1723         _cookies.setCookies(cookies);
1724     }
1725 
1726     /* ------------------------------------------------------------ */
1727     public void setDispatcherType(DispatcherType type)
1728     {
1729         _dispatcherType = type;
1730     }
1731 
1732     /* ------------------------------------------------------------ */
1733     public void setHandled(boolean h)
1734     {
1735         _handled = h;
1736     }
1737 
1738     /* ------------------------------------------------------------ */
1739     /**
1740      * @param method
1741      *            The method to set.
1742      */
1743     public void setMethod(HttpMethod httpMethod, String method)
1744     {
1745         _httpMethod=httpMethod;
1746         _httpMethodString = method;
1747     }
1748 
1749     /* ------------------------------------------------------------ */
1750     public boolean isHead()
1751     {
1752         return HttpMethod.HEAD==_httpMethod;
1753     }
1754 
1755     /* ------------------------------------------------------------ */
1756     /**
1757      * @param parameters
1758      *            The parameters to set.
1759      */
1760     public void setParameters(MultiMap<String> parameters)
1761     {
1762         _parameters = (parameters == null)?_baseParameters:parameters;
1763         if (_paramsExtracted && _parameters == null)
1764             throw new IllegalStateException();
1765     }
1766 
1767     /* ------------------------------------------------------------ */
1768     /**
1769      * @param pathInfo
1770      *            The pathInfo to set.
1771      */
1772     public void setPathInfo(String pathInfo)
1773     {
1774         _pathInfo = pathInfo;
1775     }
1776 
1777     /* ------------------------------------------------------------ */
1778     /**
1779      * @param version
1780      *            The protocol to set.
1781      */
1782     public void setHttpVersion(HttpVersion version)
1783     {
1784         _httpVersion = version;
1785     }
1786 
1787     /* ------------------------------------------------------------ */
1788     /**
1789      * Set the character encoding used for the query string. This call will effect the return of getQueryString and getParamaters. It must be called before any
1790      * geParameter methods.
1791      *
1792      * The request attribute "org.eclipse.jetty.server.server.Request.queryEncoding" may be set as an alternate method of calling setQueryEncoding.
1793      *
1794      * @param queryEncoding
1795      */
1796     public void setQueryEncoding(String queryEncoding)
1797     {
1798         _queryEncoding = queryEncoding;
1799         _queryString = null;
1800     }
1801 
1802     /* ------------------------------------------------------------ */
1803     /**
1804      * @param queryString
1805      *            The queryString to set.
1806      */
1807     public void setQueryString(String queryString)
1808     {
1809         _queryString = queryString;
1810         _queryEncoding = null; //assume utf-8
1811     }
1812 
1813     /* ------------------------------------------------------------ */
1814     /**
1815      * @param addr
1816      *            The address to set.
1817      */
1818     public void setRemoteAddr(InetSocketAddress addr)
1819     {
1820         _remote = addr;
1821     }
1822 
1823     /* ------------------------------------------------------------ */
1824     /**
1825      * @param requestedSessionId
1826      *            The requestedSessionId to set.
1827      */
1828     public void setRequestedSessionId(String requestedSessionId)
1829     {
1830         _requestedSessionId = requestedSessionId;
1831     }
1832 
1833     /* ------------------------------------------------------------ */
1834     /**
1835      * @param requestedSessionIdCookie
1836      *            The requestedSessionIdCookie to set.
1837      */
1838     public void setRequestedSessionIdFromCookie(boolean requestedSessionIdCookie)
1839     {
1840         _requestedSessionIdFromCookie = requestedSessionIdCookie;
1841     }
1842 
1843     /* ------------------------------------------------------------ */
1844     /**
1845      * @param requestURI
1846      *            The requestURI to set.
1847      */
1848     public void setRequestURI(String requestURI)
1849     {
1850         _requestURI = requestURI;
1851     }
1852 
1853     /* ------------------------------------------------------------ */
1854     /**
1855      * @param scheme
1856      *            The scheme to set.
1857      */
1858     public void setScheme(String scheme)
1859     {
1860         _scheme = scheme;
1861     }
1862 
1863     /* ------------------------------------------------------------ */
1864     /**
1865      * @param host
1866      *            The host to set.
1867      */
1868     public void setServerName(String host)
1869     {
1870         _serverName = host;
1871     }
1872 
1873     /* ------------------------------------------------------------ */
1874     /**
1875      * @param port
1876      *            The port to set.
1877      */
1878     public void setServerPort(int port)
1879     {
1880         _port = port;
1881     }
1882 
1883     /* ------------------------------------------------------------ */
1884     /**
1885      * @param servletPath
1886      *            The servletPath to set.
1887      */
1888     public void setServletPath(String servletPath)
1889     {
1890         _servletPath = servletPath;
1891     }
1892 
1893     /* ------------------------------------------------------------ */
1894     /**
1895      * @param session
1896      *            The session to set.
1897      */
1898     public void setSession(HttpSession session)
1899     {
1900         _session = session;
1901     }
1902 
1903     /* ------------------------------------------------------------ */
1904     /**
1905      * @param sessionManager
1906      *            The sessionManager to set.
1907      */
1908     public void setSessionManager(SessionManager sessionManager)
1909     {
1910         _sessionManager = sessionManager;
1911     }
1912 
1913     /* ------------------------------------------------------------ */
1914     public void setTimeStamp(long ts)
1915     {
1916         _timeStamp = ts;
1917     }
1918 
1919     /* ------------------------------------------------------------ */
1920     /**
1921      * @param uri
1922      *            The uri to set.
1923      */
1924     public void setUri(HttpURI uri)
1925     {
1926         _uri = uri;
1927     }
1928 
1929     /* ------------------------------------------------------------ */
1930     public void setUserIdentityScope(UserIdentity.Scope scope)
1931     {
1932         _scope = scope;
1933     }
1934 
1935     /* ------------------------------------------------------------ */
1936     /**
1937      * Set timetstamp of request dispatch
1938      *
1939      * @param value
1940      *            timestamp
1941      */
1942     public void setDispatchTime(long value)
1943     {
1944         _dispatchTime = value;
1945     }
1946 
1947     /* ------------------------------------------------------------ */
1948     @Override
1949     public AsyncContext startAsync() throws IllegalStateException
1950     {
1951         if (!_asyncSupported)
1952             throw new IllegalStateException("!asyncSupported");
1953         HttpChannelState state = getHttpChannelState();
1954         state.startAsync();
1955         return state;
1956     }
1957 
1958     /* ------------------------------------------------------------ */
1959     @Override
1960     public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException
1961     {
1962         if (!_asyncSupported)
1963             throw new IllegalStateException("!asyncSupported");
1964         HttpChannelState state = getHttpChannelState();
1965         state.startAsync(_context, servletRequest, servletResponse);
1966         return state;
1967     }
1968 
1969     /* ------------------------------------------------------------ */
1970     @Override
1971     public String toString()
1972     {
1973         return (_handled?"[":"(") + getMethod() + " " + _uri + (_handled?"]@":")@") + hashCode() + " " + super.toString();
1974     }
1975 
1976     /* ------------------------------------------------------------ */
1977     @Override
1978     public boolean authenticate(HttpServletResponse response) throws IOException, ServletException
1979     {
1980         if (_authentication instanceof Authentication.Deferred)
1981         {
1982             setAuthentication(((Authentication.Deferred)_authentication).authenticate(this,response));
1983             return !(_authentication instanceof Authentication.ResponseSent);
1984         }
1985         response.sendError(HttpStatus.UNAUTHORIZED_401);
1986         return false;
1987     }
1988 
1989     /* ------------------------------------------------------------ */
1990     @Override
1991     public Part getPart(String name) throws IOException, ServletException
1992     {
1993         if (getContentType() == null || !getContentType().startsWith("multipart/form-data"))
1994             throw new ServletException("Content-Type != multipart/form-data");
1995 
1996         if (_multiPartInputStream == null)
1997         {
1998             MultipartConfigElement config = (MultipartConfigElement)getAttribute(__MULTIPART_CONFIG_ELEMENT);
1999 
2000             if (config == null)
2001                 throw new IllegalStateException("No multipart config for servlet");
2002 
2003             _multiPartInputStream = new MultiPartInputStream(getInputStream(),
2004                                                              getContentType(),config, 
2005                                                              (_context != null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null));
2006             setAttribute(__MULTIPART_INPUT_STREAM, _multiPartInputStream);
2007             setAttribute(__MULTIPART_CONTEXT, _context);
2008             Collection<Part> parts = _multiPartInputStream.getParts(); //causes parsing
2009             for (Part p:parts)
2010             {
2011                 MultiPartInputStream.MultiPart mp = (MultiPartInputStream.MultiPart)p;
2012                 if (mp.getContentDispositionFilename() == null && mp.getFile() == null)
2013                 {
2014                     //Servlet Spec 3.0 pg 23, parts without filenames must be put into init params
2015                     String charset = null;
2016                     if (mp.getContentType() != null)
2017                         charset = MimeTypes.getCharsetFromContentType(mp.getContentType());
2018 
2019                     String content=new String(mp.getBytes(),charset==null?StringUtil.__UTF8:charset);
2020                     getParameter(""); //cause params to be evaluated
2021                     getParameters().add(mp.getName(), content);
2022                 }
2023             }
2024         }
2025         return _multiPartInputStream.getPart(name);
2026     }
2027 
2028     /* ------------------------------------------------------------ */
2029     @Override
2030     public Collection<Part> getParts() throws IOException, ServletException
2031     {
2032         if (getContentType() == null || !getContentType().startsWith("multipart/form-data"))
2033             throw new ServletException("Content-Type != multipart/form-data");
2034 
2035         if (_multiPartInputStream == null)
2036         {
2037             MultipartConfigElement config = (MultipartConfigElement)getAttribute(__MULTIPART_CONFIG_ELEMENT);
2038             
2039             if (config == null)
2040                 throw new IllegalStateException("No multipart config for servlet");
2041             
2042             _multiPartInputStream = new MultiPartInputStream(getInputStream(),
2043                                                              getContentType(), config, 
2044                                                              (_context != null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null));
2045             
2046             setAttribute(__MULTIPART_INPUT_STREAM, _multiPartInputStream);
2047             setAttribute(__MULTIPART_CONTEXT, _context);
2048             Collection<Part> parts = _multiPartInputStream.getParts(); //causes parsing
2049             for (Part p:parts)
2050             {
2051                 MultiPartInputStream.MultiPart mp = (MultiPartInputStream.MultiPart)p;
2052                 if (mp.getContentDispositionFilename() == null && mp.getFile() == null)
2053                 {
2054                     //Servlet Spec 3.0 pg 23, parts without filenames must be put into init params
2055                     String charset = null;
2056                     if (mp.getContentType() != null)
2057                         charset = MimeTypes.getCharsetFromContentType(mp.getContentType());
2058 
2059                     String content=new String(mp.getBytes(),charset==null?StringUtil.__UTF8:charset);
2060                     getParameter(""); //cause params to be evaluated
2061                     getParameters().add(mp.getName(), content);
2062                 }
2063             }
2064         }
2065         return _multiPartInputStream.getParts();
2066     }
2067 
2068     /* ------------------------------------------------------------ */
2069     @Override
2070     public void login(String username, String password) throws ServletException
2071     {
2072         if (_authentication instanceof Authentication.Deferred)
2073         {
2074             _authentication=((Authentication.Deferred)_authentication).login(username,password,this);
2075             if (_authentication == null)
2076                 throw new ServletException();
2077         }
2078         else
2079         {
2080             throw new ServletException("Authenticated as "+_authentication);
2081         }
2082     }
2083 
2084     /* ------------------------------------------------------------ */
2085     @Override
2086     public void logout() throws ServletException
2087     {
2088         if (_authentication instanceof Authentication.User)
2089             ((Authentication.User)_authentication).logout();
2090         _authentication=Authentication.UNAUTHENTICATED;
2091     }
2092 
2093     /* ------------------------------------------------------------ */
2094     /**
2095      * Merge in a new query string. The query string is merged with the existing parameters and {@link #setParameters(MultiMap)} and
2096      * {@link #setQueryString(String)} are called with the result. The merge is according to the rules of the servlet dispatch forward method.
2097      *
2098      * @param query
2099      *            The query string to merge into the request.
2100      */
2101     public void mergeQueryString(String query)
2102     {
2103         // extract parameters from dispatch query
2104         MultiMap<String> parameters = new MultiMap<>();
2105         UrlEncoded.decodeTo(query,parameters, StringUtil.__UTF8); //have to assume UTF-8 because we can't know otherwise
2106 
2107         boolean merge_old_query = false;
2108 
2109         // Have we evaluated parameters
2110         if (!_paramsExtracted)
2111             extractParameters();
2112 
2113         // Are there any existing parameters?
2114         if (_parameters != null && _parameters.size() > 0)
2115         {
2116             // Merge parameters; new parameters of the same name take precedence.
2117             merge_old_query = parameters.addAllValues(_parameters);
2118         }
2119 
2120         if (_queryString != null && _queryString.length() > 0)
2121         {
2122             if (merge_old_query)
2123             {
2124                 StringBuilder overridden_query_string = new StringBuilder();
2125                 MultiMap<String> overridden_old_query = new MultiMap<>();
2126                 UrlEncoded.decodeTo(_queryString,overridden_old_query,getQueryEncoding());//decode using any queryencoding set for the request
2127                 
2128                 
2129                 MultiMap<String> overridden_new_query = new MultiMap<>();
2130                 UrlEncoded.decodeTo(query,overridden_new_query,StringUtil.__UTF8); //have to assume utf8 as we cannot know otherwise
2131 
2132                 for(String name: overridden_old_query.keySet())
2133                 {
2134                     if (!overridden_new_query.containsKey(name))
2135                     {
2136                         List<String> values = overridden_old_query.get(name);
2137                         for(String v: values)
2138                         {
2139                             overridden_query_string.append("&").append(name).append("=").append(v);
2140                         }
2141                     }
2142                 }
2143 
2144                 query = query + overridden_query_string;
2145             }
2146             else
2147             {
2148                 query = query + "&" + _queryString;
2149             }
2150         }
2151 
2152         setParameters(parameters);
2153         setQueryString(query);
2154     }
2155 }