View Javadoc

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