comparison src/org/eclipse/jetty/http/AbstractGenerator.java @ 802:3428c60d7cfc

replace jetty jars with source
author Franklin Schmidt <fschmidt@gmail.com>
date Wed, 07 Sep 2016 21:15:48 -0600
parents
children 8e9db0bbf4f9
comparison
equal deleted inserted replaced
801:6a21393191c1 802:3428c60d7cfc
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 */
89 public AbstractGenerator(Buffers buffers, EndPoint io)
90 {
91 this._buffers = buffers;
92 this._endp = io;
93 }
94
95 /* ------------------------------------------------------------------------------- */
96 public abstract boolean isRequest();
97
98 /* ------------------------------------------------------------------------------- */
99 public abstract boolean isResponse();
100
101 /* ------------------------------------------------------------------------------- */
102 public boolean isOpen()
103 {
104 return _endp.isOpen();
105 }
106
107 /* ------------------------------------------------------------------------------- */
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 /* ------------------------------------------------------------------------------- */
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 /* ------------------------------------------------------------------------------- */
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 */
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 */
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 /* ------------------------------------------------------------ */
186 public Buffer getUncheckedBuffer()
187 {
188 return _buffer;
189 }
190
191 /* ------------------------------------------------------------ */
192 public boolean getSendServerVersion ()
193 {
194 return _sendServerVersion;
195 }
196
197 /* ------------------------------------------------------------ */
198 public void setSendServerVersion (boolean sendServerVersion)
199 {
200 _sendServerVersion = sendServerVersion;
201 }
202
203 /* ------------------------------------------------------------ */
204 public int getState()
205 {
206 return _state;
207 }
208
209 /* ------------------------------------------------------------ */
210 public boolean isState(int state)
211 {
212 return _state == state;
213 }
214
215 /* ------------------------------------------------------------ */
216 public boolean isComplete()
217 {
218 return _state == STATE_END;
219 }
220
221 /* ------------------------------------------------------------ */
222 public boolean isIdle()
223 {
224 return _state == STATE_HEADER && _method==null && _status==0;
225 }
226
227 /* ------------------------------------------------------------ */
228 public boolean isCommitted()
229 {
230 return _state != STATE_HEADER;
231 }
232
233 /* ------------------------------------------------------------ */
234 /**
235 * @return Returns the head.
236 */
237 public boolean isHead()
238 {
239 return _head;
240 }
241
242 /* ------------------------------------------------------------ */
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 */
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 */
265 public boolean isPersistent()
266 {
267 return _persistent!=null
268 ?_persistent.booleanValue()
269 :(isRequest()?true:_version>HttpVersions.HTTP_1_0_ORDINAL);
270 }
271
272 /* ------------------------------------------------------------ */
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 */
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 /* ------------------------------------------------------------ */
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 */
302 public void setDate(Buffer timeStampBuffer)
303 {
304 _date=timeStampBuffer;
305 }
306
307 /* ------------------------------------------------------------ */
308 /**
309 */
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 */
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 */
356 public abstract int prepareUncheckedAddContent() throws IOException;
357
358 /* ------------------------------------------------------------ */
359 void uncheckedAddContent(int b)
360 {
361 _buffer.put((byte)b);
362 }
363
364 /* ------------------------------------------------------------ */
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 /* ------------------------------------------------------------ */
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 /* ------------------------------------------------------------ */
394 public boolean isWritten()
395 {
396 return _contentWritten>0;
397 }
398
399 /* ------------------------------------------------------------ */
400 public boolean isAllContentWritten()
401 {
402 return _contentLength>=0 && _contentWritten>=_contentLength;
403 }
404
405 /* ------------------------------------------------------------ */
406 public abstract void completeHeader(HttpFields fields, boolean allContentAdded) throws IOException;
407
408 /* ------------------------------------------------------------ */
409 /**
410 * Complete the message.
411 *
412 * @throws IOException
413 */
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 /* ------------------------------------------------------------ */
430 public abstract int flushBuffer() throws IOException;
431
432
433 /* ------------------------------------------------------------ */
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 */
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 */
498 public long getContentWritten()
499 {
500 return _contentWritten;
501 }
502
503
504
505 /* ------------------------------------------------------------ */
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 }