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