View Javadoc

1   // ========================================================================
2   // Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at 
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses. 
12  // ========================================================================
13  
14  package org.eclipse.jetty.http;
15  
16  import java.io.IOException;
17  
18  import org.eclipse.jetty.io.Buffer;
19  import org.eclipse.jetty.io.Buffers;
20  import org.eclipse.jetty.io.ByteArrayBuffer;
21  import org.eclipse.jetty.io.EndPoint;
22  import org.eclipse.jetty.io.EofException;
23  import org.eclipse.jetty.io.View;
24  import org.eclipse.jetty.util.log.Log;
25  import org.eclipse.jetty.util.log.Logger;
26  
27  /* ------------------------------------------------------------ */
28  /**
29   * Abstract Generator. Builds HTTP Messages.
30   * 
31   * Currently this class uses a system parameter "jetty.direct.writers" to control
32   * two optional writer to byte conversions. buffer.writers=true will probably be 
33   * faster, but will consume more memory.   This option is just for testing and tuning.
34   * 
35   */
36  public abstract class AbstractGenerator implements Generator
37  {
38      private static final Logger LOG = Log.getLogger(AbstractGenerator.class);
39  
40      // states
41      public final static int STATE_HEADER = 0;
42      public final static int STATE_CONTENT = 2;
43      public final static int STATE_FLUSHING = 3;
44      public final static int STATE_END = 4;
45      
46      public static final byte[] NO_BYTES = {};
47  
48      // data
49  
50      protected final Buffers _buffers; // source of buffers
51      protected final EndPoint _endp;
52      
53      protected int _state = STATE_HEADER;
54      
55      protected int _status = 0;
56      protected int _version = HttpVersions.HTTP_1_1_ORDINAL;
57      protected  Buffer _reason;
58      protected  Buffer _method;
59      protected  String _uri;
60  
61      protected long _contentWritten = 0;
62      protected long _contentLength = HttpTokens.UNKNOWN_CONTENT;
63      protected boolean _last = false;
64      protected boolean _head = false;
65      protected boolean _noContent = false;
66      protected Boolean _persistent = null;
67      
68      protected Buffer _header; // Buffer for HTTP header (and maybe small _content)
69      protected Buffer _buffer; // Buffer for copy of passed _content
70      protected Buffer _content; // Buffer passed to addContent
71      
72      protected Buffer _date;
73      
74      private boolean _sendServerVersion;
75  
76      
77      /* ------------------------------------------------------------------------------- */
78      /**
79       * Constructor.
80       * 
81       * @param buffers buffer pool
82       * @param io the end point
83       */
84      public AbstractGenerator(Buffers buffers, EndPoint io)
85      {
86          this._buffers = buffers;
87          this._endp = io;
88      }
89  
90      /* ------------------------------------------------------------------------------- */
91      public abstract boolean isRequest();
92      
93      /* ------------------------------------------------------------------------------- */
94      public abstract boolean isResponse();
95      
96      /* ------------------------------------------------------------------------------- */
97      public boolean isOpen()
98      {
99          return _endp.isOpen();
100     }
101     
102     /* ------------------------------------------------------------------------------- */
103     public void reset(boolean returnBuffers)
104     {
105         _state = STATE_HEADER;
106         _status = 0;
107         _version = HttpVersions.HTTP_1_1_ORDINAL;
108         _reason = null;
109         _last = false;
110         _head = false;
111         _noContent=false;
112         _persistent = null;
113         _contentWritten = 0;
114         _contentLength = HttpTokens.UNKNOWN_CONTENT;
115         _date = null;
116 
117         // always return the body buffer
118         if (_buffer!=null)
119             _buffers.returnBuffer(_buffer);
120         _buffer=null;
121 
122         if (returnBuffers)
123         {
124             if (_header!=null)
125                 _buffers.returnBuffer(_header);
126             _header=null;
127         }
128         else if (_header != null) 
129             _header.clear();
130 
131         _content = null;
132         _method=null;
133     }
134 
135     /* ------------------------------------------------------------------------------- */
136     public void returnBuffers()
137     {     
138         if (_buffer!=null && _buffer.length()==0)
139         {
140             _buffers.returnBuffer(_buffer);
141             _buffer=null;
142         }
143 
144         if (_header!=null && _header.length()==0)
145         {
146             _buffers.returnBuffer(_header);
147             _header=null;
148         }         
149     }
150     
151     /* ------------------------------------------------------------------------------- */
152     public void resetBuffer()
153     {                   
154         if(_state>=STATE_FLUSHING)
155             throw new IllegalStateException("Flushed");
156         
157         _last = false;
158         _persistent=null;
159         _contentWritten = 0;
160         _contentLength = HttpTokens.UNKNOWN_CONTENT;
161         _content=null;
162         if (_buffer!=null)
163             _buffer.clear();  
164     }
165 
166     /* ------------------------------------------------------------ */
167     /**
168      * @return Returns the contentBufferSize.
169      */
170     public int getContentBufferSize()
171     {
172         if (_buffer==null)
173             _buffer=_buffers.getBuffer();
174         return _buffer.capacity();
175     }
176 
177     /* ------------------------------------------------------------ */
178     /**
179      * @param contentBufferSize The contentBufferSize to set.
180      */
181     public void increaseContentBufferSize(int contentBufferSize)
182     {
183         if (_buffer==null)
184             _buffer=_buffers.getBuffer();
185         if (contentBufferSize > _buffer.capacity())
186         {
187             Buffer nb = _buffers.getBuffer(contentBufferSize);
188             nb.put(_buffer);
189             _buffers.returnBuffer(_buffer);
190             _buffer = nb;
191         }
192     }
193     
194     /* ------------------------------------------------------------ */    
195     public Buffer getUncheckedBuffer()
196     {
197         return _buffer;
198     }
199     
200     /* ------------------------------------------------------------ */    
201     public boolean getSendServerVersion ()
202     {
203         return _sendServerVersion;
204     }
205     
206     /* ------------------------------------------------------------ */    
207     public void setSendServerVersion (boolean sendServerVersion)
208     {
209         _sendServerVersion = sendServerVersion;
210     }
211     
212     /* ------------------------------------------------------------ */
213     public int getState()
214     {
215         return _state;
216     }
217 
218     /* ------------------------------------------------------------ */
219     public boolean isState(int state)
220     {
221         return _state == state;
222     }
223 
224     /* ------------------------------------------------------------ */
225     public boolean isComplete()
226     {
227         return _state == STATE_END;
228     }
229 
230     /* ------------------------------------------------------------ */
231     public boolean isIdle()
232     {
233         return _state == STATE_HEADER && _method==null && _status==0;
234     }
235 
236     /* ------------------------------------------------------------ */
237     public boolean isCommitted()
238     {
239         return _state != STATE_HEADER;
240     }
241 
242     /* ------------------------------------------------------------ */
243     /**
244      * @return Returns the head.
245      */
246     public boolean isHead()
247     {
248         return _head;
249     }
250 
251     /* ------------------------------------------------------------ */
252     public void setContentLength(long value)
253     {
254         if (value<0)
255             _contentLength=HttpTokens.UNKNOWN_CONTENT;
256         else
257             _contentLength=value;
258     }
259     
260     /* ------------------------------------------------------------ */
261     /**
262      * @param head The head to set.
263      */
264     public void setHead(boolean head)
265     {
266         _head = head;
267     }
268 
269     /* ------------------------------------------------------------ */
270     /**
271      * @return <code>false</code> if the connection should be closed after a request has been read,
272      * <code>true</code> if it should be used for additional requests.
273      */
274     public boolean isPersistent()
275     {
276         return _persistent!=null
277         ?_persistent.booleanValue()
278         :(isRequest()?true:_version>HttpVersions.HTTP_1_0_ORDINAL);
279     }
280     
281     /* ------------------------------------------------------------ */
282     public void setPersistent(boolean persistent)
283     {
284         _persistent=persistent;
285     }
286 
287     /* ------------------------------------------------------------ */
288     /**
289      * @param version The version of the client the response is being sent to (NB. Not the version
290      *            in the response, which is the version of the server).
291      */
292     public void setVersion(int version)
293     {
294         if (_state != STATE_HEADER) 
295             throw new IllegalStateException("STATE!=START "+_state);
296         _version = version;
297         if (_version==HttpVersions.HTTP_0_9_ORDINAL && _method!=null)
298             _noContent=true;
299     }
300 
301     /* ------------------------------------------------------------ */
302     public int getVersion()
303     {
304         return _version;
305     }
306     
307     /* ------------------------------------------------------------ */
308     /**
309      * @see org.eclipse.jetty.http.Generator#setDate(org.eclipse.jetty.io.Buffer)
310      */
311     public void setDate(Buffer timeStampBuffer)
312     {
313         _date=timeStampBuffer;
314     }
315 
316     /* ------------------------------------------------------------ */
317     /**
318      */
319     public void setRequest(String method, String uri)
320     {
321         if (method==null || HttpMethods.GET.equals(method) )
322             _method=HttpMethods.GET_BUFFER;
323         else
324             _method=HttpMethods.CACHE.lookup(method);
325         _uri=uri;
326         if (_version==HttpVersions.HTTP_0_9_ORDINAL)
327             _noContent=true;
328     }
329 
330     /* ------------------------------------------------------------ */
331     /**
332      * @param status The status code to send.
333      * @param reason the status message to send.
334      */
335     public void setResponse(int status, String reason)
336     {
337         if (_state != STATE_HEADER) throw new IllegalStateException("STATE!=START");
338         _method=null;
339         _status = status;
340         if (reason!=null)
341         {
342             int len=reason.length();
343             
344             // TODO don't hard code
345             if (len>1024)
346                 len=1024;
347             _reason=new ByteArrayBuffer(len);
348             for (int i=0;i<len;i++)
349             {
350                 char ch = reason.charAt(i);
351                 if (ch!='\r'&&ch!='\n')
352                     _reason.put((byte)ch);
353                 else
354                     _reason.put((byte)' ');
355             }
356         }
357     }
358 
359     /* ------------------------------------------------------------ */
360     /** Prepare buffer for unchecked writes.
361      * Prepare the generator buffer to receive unchecked writes
362      * @return the available space in the buffer.
363      * @throws IOException
364      */
365     public abstract int prepareUncheckedAddContent() throws IOException;
366 
367     /* ------------------------------------------------------------ */
368     void uncheckedAddContent(int b)
369     {
370         _buffer.put((byte)b);
371     }
372 
373     /* ------------------------------------------------------------ */
374     public void completeUncheckedAddContent()
375     {
376         if (_noContent)
377         {
378             if(_buffer!=null)
379                 _buffer.clear();
380         }
381         else 
382         {
383             _contentWritten+=_buffer.length();
384             if (_head)
385                 _buffer.clear();
386         }
387     }
388     
389     /* ------------------------------------------------------------ */
390     public boolean isBufferFull()
391     {
392         if (_buffer != null && _buffer.space()==0)
393         {
394             if (_buffer.length()==0 && !_buffer.isImmutable())
395                 _buffer.compact();
396             return _buffer.space()==0;
397         }
398 
399         return _content!=null && _content.length()>0;
400     }
401 
402     /* ------------------------------------------------------------ */
403     public boolean isWritten()
404     {
405         return _contentWritten>0;
406     }
407     
408     /* ------------------------------------------------------------ */
409     public boolean isAllContentWritten()
410     {
411         return _contentLength>=0 && _contentWritten>=_contentLength;
412     }
413     
414     /* ------------------------------------------------------------ */
415     public abstract void completeHeader(HttpFields fields, boolean allContentAdded) throws IOException;
416     
417     /* ------------------------------------------------------------ */
418     /**
419      * Complete the message.
420      * 
421      * @throws IOException
422      */
423     public void complete() throws IOException
424     {
425         if (_state == STATE_HEADER)
426         {
427             throw new IllegalStateException("State==HEADER");
428         }
429 
430         if (_contentLength >= 0 && _contentLength != _contentWritten && !_head)
431         {
432             if (LOG.isDebugEnabled())
433                 LOG.debug("ContentLength written=="+_contentWritten+" != contentLength=="+_contentLength);
434             _persistent = false;
435         }
436     }
437 
438     /* ------------------------------------------------------------ */
439     public abstract long flushBuffer() throws IOException;
440 
441     
442     /* ------------------------------------------------------------ */
443     public void flush(long maxIdleTime) throws IOException
444     {
445         // block until everything is flushed
446         long now=System.currentTimeMillis();
447         long end=now+maxIdleTime;
448         Buffer content = _content;
449         Buffer buffer = _buffer;
450         if (content!=null && content.length()>0 || buffer!=null && buffer.length()>0 || isBufferFull())
451         {
452             flushBuffer();
453             
454             while (now<end && (content!=null && content.length()>0 ||buffer!=null && buffer.length()>0) && _endp.isOpen()&& !_endp.isOutputShutdown())
455             {
456                 blockForOutput(end-now);
457                 now=System.currentTimeMillis();
458             }
459         }
460         
461         // make sure buffered data is also flushed
462         while (now<end && _endp.isBufferingOutput() && _endp.isOpen() && !_endp.isOutputShutdown())
463         {
464             if (!_endp.isBlocking())
465                 _endp.blockWritable(end-now);
466             _endp.flush();
467             now=System.currentTimeMillis();
468         }
469     }
470 
471     /* ------------------------------------------------------------ */
472     /**
473      * Utility method to send an error response. If the builder is not committed, this call is
474      * equivalent to a setResponse, addContent and complete call.
475      * 
476      * @param code The error code
477      * @param reason The error reason
478      * @param content Contents of the error page
479      * @param close True if the connection should be closed
480      * @throws IOException if there is a problem flushing the response
481      */
482     public void sendError(int code, String reason, String content, boolean close) throws IOException
483     {
484         if (close)
485             _persistent=false;
486         if (isCommitted())
487         {
488             LOG.debug("sendError on committed: {} {}",code,reason);
489         }
490         else
491         {
492             LOG.debug("sendError: {} {}",code,reason);
493             setResponse(code, reason);
494             if (content != null) 
495             {
496                 completeHeader(null, false);
497                 addContent(new View(new ByteArrayBuffer(content)), Generator.LAST);
498             }
499             else
500             {
501                 completeHeader(null, true);
502             }
503             complete();
504         }
505     }
506 
507     /* ------------------------------------------------------------ */
508     /**
509      * @return Returns the contentWritten.
510      */
511     public long getContentWritten()
512     {
513         return _contentWritten;
514     }
515     
516 
517 
518     /* ------------------------------------------------------------ */
519     public void  blockForOutput(long maxIdleTime) throws IOException
520     {
521         if (_endp.isBlocking())
522         {
523             try
524             {
525                 flushBuffer();
526             }
527             catch(IOException e)
528             {
529                 _endp.close();
530                 throw e;
531             }
532         }
533         else
534         {
535             if (!_endp.blockWritable(maxIdleTime))
536             {
537                 _endp.close();
538                 throw new EofException("timeout");
539             }
540             
541             flushBuffer();
542         }
543     }
544     
545 }