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  
26  /* ------------------------------------------------------------ */
27  /**
28   * Abstract Generator. Builds HTTP Messages.
29   * 
30   * Currently this class uses a system parameter "jetty.direct.writers" to control
31   * two optional writer to byte conversions. buffer.writers=true will probably be 
32   * faster, but will consume more memory.   This option is just for testing and tuning.
33   * 
34   * 
35   * 
36   */
37  public abstract class AbstractGenerator implements Generator
38  {
39      // states
40      public final static int STATE_HEADER = 0;
41      public final static int STATE_CONTENT = 2;
42      public final static int STATE_FLUSHING = 3;
43      public final static int STATE_END = 4;
44      
45      public static final byte[] NO_BYTES = {};
46  
47      // data
48  
49      protected final Buffers _buffers; // source of buffers
50      protected final EndPoint _endp;
51      
52      protected int _state = STATE_HEADER;
53      
54      protected int _status = 0;
55      protected int _version = HttpVersions.HTTP_1_1_ORDINAL;
56      protected  Buffer _reason;
57      protected  Buffer _method;
58      protected  String _uri;
59  
60      protected long _contentWritten = 0;
61      protected long _contentLength = HttpTokens.UNKNOWN_CONTENT;
62      protected boolean _last = false;
63      protected boolean _head = false;
64      protected boolean _noContent = false;
65      protected boolean _close = false;
66  
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 headerBufferSize Size of the buffer to allocate for HTTP header
83       * @param contentBufferSize Size of the buffer to allocate for HTTP content
84       */
85      public AbstractGenerator(Buffers buffers, EndPoint io)
86      {
87          this._buffers = buffers;
88          this._endp = io;
89      }
90  
91      /* ------------------------------------------------------------------------------- */
92      public boolean isOpen()
93      {
94          return _endp.isOpen();
95      }
96      
97      /* ------------------------------------------------------------------------------- */
98      public void reset(boolean returnBuffers)
99      {
100         _state = STATE_HEADER;
101         _status = 0;
102         _version = HttpVersions.HTTP_1_1_ORDINAL;
103         _reason = null;
104         _last = false;
105         _head = false;
106         _noContent=false;
107         _close = false;
108         _contentWritten = 0;
109         _contentLength = HttpTokens.UNKNOWN_CONTENT;
110         _date = null;
111 
112         // always return the buffer
113         if (_buffer!=null)
114             _buffers.returnBuffer(_buffer);
115         _buffer=null;
116 
117         if (returnBuffers)
118         {
119             if (_header!=null)
120                 _buffers.returnBuffer(_header);
121             _header=null;
122         }
123         else if (_header != null) 
124             _header.clear();
125 
126         _content = null;
127         _method=null;
128     }
129 
130     /* ------------------------------------------------------------------------------- */
131     public void resetBuffer()
132     {                   
133         if(_state>=STATE_FLUSHING)
134             throw new IllegalStateException("Flushed");
135         
136         _last = false;
137         _close = false;
138         _contentWritten = 0;
139         _contentLength = HttpTokens.UNKNOWN_CONTENT;
140         _content=null;
141         if (_buffer!=null)
142             _buffer.clear();  
143     }
144 
145     /* ------------------------------------------------------------ */
146     /**
147      * @return Returns the contentBufferSize.
148      */
149     public int getContentBufferSize()
150     {
151         if (_buffer==null)
152             _buffer=_buffers.getBuffer();
153         return _buffer.capacity();
154     }
155 
156     /* ------------------------------------------------------------ */
157     /**
158      * @param contentBufferSize The contentBufferSize to set.
159      */
160     public void increaseContentBufferSize(int contentBufferSize)
161     {
162         if (_buffer==null)
163             _buffer=_buffers.getBuffer();
164         if (contentBufferSize > _buffer.capacity())
165         {
166             Buffer nb = _buffers.getBuffer(contentBufferSize);
167             nb.put(_buffer);
168             _buffers.returnBuffer(_buffer);
169             _buffer = nb;
170         }
171     }
172     
173     /* ------------------------------------------------------------ */    
174     public Buffer getUncheckedBuffer()
175     {
176         return _buffer;
177     }
178     
179     /* ------------------------------------------------------------ */    
180     public boolean getSendServerVersion ()
181     {
182         return _sendServerVersion;
183     }
184     
185     /* ------------------------------------------------------------ */    
186     public void setSendServerVersion (boolean sendServerVersion)
187     {
188         _sendServerVersion = sendServerVersion;
189     }
190     
191     /* ------------------------------------------------------------ */
192     public int getState()
193     {
194         return _state;
195     }
196 
197     /* ------------------------------------------------------------ */
198     public boolean isState(int state)
199     {
200         return _state == state;
201     }
202 
203     /* ------------------------------------------------------------ */
204     public boolean isComplete()
205     {
206         return _state == STATE_END;
207     }
208 
209     /* ------------------------------------------------------------ */
210     public boolean isIdle()
211     {
212         return _state == STATE_HEADER && _method==null && _status==0;
213     }
214 
215     /* ------------------------------------------------------------ */
216     public boolean isCommitted()
217     {
218         return _state != STATE_HEADER;
219     }
220 
221     /* ------------------------------------------------------------ */
222     /**
223      * @return Returns the head.
224      */
225     public boolean isHead()
226     {
227         return _head;
228     }
229 
230     /* ------------------------------------------------------------ */
231     public void setContentLength(long value)
232     {
233         if (value<0)
234             _contentLength=HttpTokens.UNKNOWN_CONTENT;
235         else
236             _contentLength=value;
237     }
238     
239     /* ------------------------------------------------------------ */
240     /**
241      * @param head The head to set.
242      */
243     public void setHead(boolean head)
244     {
245         _head = head;
246     }
247 
248     /* ------------------------------------------------------------ */
249     /**
250      * @return <code>false</code> if the connection should be closed after a request has been read,
251      * <code>true</code> if it should be used for additional requests.
252      */
253     public boolean isPersistent()
254     {
255         return !_close;
256     }
257 
258     /* ------------------------------------------------------------ */
259     public void setPersistent(boolean persistent)
260     {
261         _close=!persistent;
262     }
263 
264     /* ------------------------------------------------------------ */
265     /**
266      * @param version The version of the client the response is being sent to (NB. Not the version
267      *            in the response, which is the version of the server).
268      */
269     public void setVersion(int version)
270     {
271         if (_state != STATE_HEADER) 
272             throw new IllegalStateException("STATE!=START "+_state);
273         _version = version;
274         if (_version==HttpVersions.HTTP_0_9_ORDINAL && _method!=null)
275             _noContent=true;
276     }
277 
278     /* ------------------------------------------------------------ */
279     public int getVersion()
280     {
281         return _version;
282     }
283     
284     /* ------------------------------------------------------------ */
285     /**
286      * @see org.eclipse.jetty.http.Generator#setDate(org.eclipse.jetty.io.Buffer)
287      */
288     public void setDate(Buffer timeStampBuffer)
289     {
290         _date=timeStampBuffer;
291     }
292 
293     /* ------------------------------------------------------------ */
294     /**
295      */
296     public void setRequest(String method, String uri)
297     {
298         if (method==null || HttpMethods.GET.equals(method) )
299             _method=HttpMethods.GET_BUFFER;
300         else
301             _method=HttpMethods.CACHE.lookup(method);
302         _uri=uri;
303         if (_version==HttpVersions.HTTP_0_9_ORDINAL)
304             _noContent=true;
305     }
306 
307     /* ------------------------------------------------------------ */
308     /**
309      * @param status The status code to send.
310      * @param reason the status message to send.
311      */
312     public void setResponse(int status, String reason)
313     {
314         if (_state != STATE_HEADER) throw new IllegalStateException("STATE!=START");
315         _method=null;
316         _status = status;
317         if (reason!=null)
318         {
319             int len=reason.length();
320             
321             // TODO don't hard code
322             if (len>1024)
323                 len=1024;
324             _reason=new ByteArrayBuffer(len);
325             for (int i=0;i<len;i++)
326             {
327                 char ch = reason.charAt(i);
328                 if (ch!='\r'&&ch!='\n')
329                     _reason.put((byte)ch);
330                 else
331                     _reason.put((byte)' ');
332             }
333         }
334     }
335 
336     /* ------------------------------------------------------------ */
337     /** Prepare buffer for unchecked writes.
338      * Prepare the generator buffer to receive unchecked writes
339      * @return the available space in the buffer.
340      * @throws IOException
341      */
342     public abstract int prepareUncheckedAddContent() throws IOException;
343 
344     /* ------------------------------------------------------------ */
345     void uncheckedAddContent(int b)
346     {
347         _buffer.put((byte)b);
348     }
349 
350     /* ------------------------------------------------------------ */
351     public void completeUncheckedAddContent()
352     {
353         if (_noContent)
354         {
355             if(_buffer!=null)
356                 _buffer.clear();
357         }
358         else 
359         {
360             _contentWritten+=_buffer.length();
361             if (_head)
362                 _buffer.clear();
363         }
364     }
365     
366     /* ------------------------------------------------------------ */
367     public boolean isBufferFull()
368     {
369         if (_buffer != null && _buffer.space()==0)
370         {
371             if (_buffer.length()==0 && !_buffer.isImmutable())
372                 _buffer.compact();
373             return _buffer.space()==0;
374         }
375 
376         return _content!=null && _content.length()>0;
377     }
378     
379     /* ------------------------------------------------------------ */
380     public boolean isContentWritten()
381     {
382         return _contentLength>=0 && _contentWritten>=_contentLength;
383     }
384     
385     /* ------------------------------------------------------------ */
386     public abstract void completeHeader(HttpFields fields, boolean allContentAdded) throws IOException;
387     
388     /* ------------------------------------------------------------ */
389     /**
390      * Complete the message.
391      * 
392      * @throws IOException
393      */
394     public void complete() throws IOException
395     {
396         if (_state == STATE_HEADER)
397         {
398             throw new IllegalStateException("State==HEADER");
399         }
400 
401         if (_contentLength >= 0 && _contentLength != _contentWritten && !_head)
402         {
403             if (Log.isDebugEnabled())
404                 Log.debug("ContentLength written=="+_contentWritten+" != contentLength=="+_contentLength);
405             _close = true;
406         }
407     }
408 
409     /* ------------------------------------------------------------ */
410     public abstract long flushBuffer() throws IOException;
411 
412     
413     /* ------------------------------------------------------------ */
414     public void flush(long maxIdleTime) throws IOException
415     {
416         // block until everything is flushed
417         Buffer content = _content;
418         Buffer buffer = _buffer;
419         if (content!=null && content.length()>0 || buffer!=null && buffer.length()>0 || isBufferFull())
420         {
421             flushBuffer();
422             
423             while ((content!=null && content.length()>0 ||buffer!=null && buffer.length()>0) && _endp.isOpen())
424                 blockForOutput(maxIdleTime);
425         }
426     }
427 
428     /* ------------------------------------------------------------ */
429     /**
430      * Utility method to send an error response. If the builder is not committed, this call is
431      * equivalent to a setResponse, addcontent and complete call.
432      * 
433      * @param code
434      * @param reason
435      * @param content
436      * @param close
437      * @throws IOException
438      */
439     public void sendError(int code, String reason, String content, boolean close) throws IOException
440     {
441         if (!isCommitted())
442         {
443             setResponse(code, reason);
444             _close = close;
445             completeHeader(null, false);
446             if (content != null) 
447                 addContent(new View(new ByteArrayBuffer(content)), Generator.LAST);
448             complete();
449         }
450     }
451 
452     /* ------------------------------------------------------------ */
453     /**
454      * @return Returns the contentWritten.
455      */
456     public long getContentWritten()
457     {
458         return _contentWritten;
459     }
460     
461 
462 
463     /* ------------------------------------------------------------ */
464     public void  blockForOutput(long maxIdleTime) throws IOException
465     {
466         if (_endp.isBlocking())
467         {
468             try
469             {
470                 flushBuffer();
471             }
472             catch(IOException e)
473             {
474                 _endp.close();
475                 throw e;
476             }
477         }
478         else
479         {
480             if (!_endp.blockWritable(maxIdleTime))
481             {
482                 _endp.close();
483                 throw new EofException("timeout");
484             }
485             
486             flushBuffer();
487         }
488     }
489     
490 }