View Javadoc

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