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