View Javadoc

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