001: //========================================================================
002: //Copyright 2006 Mort Bay Consulting Pty. Ltd.
003: //------------------------------------------------------------------------
004: //Licensed under the Apache License, Version 2.0 (the "License");
005: //you may not use this file except in compliance with the License.
006: //You may obtain a copy of the License at
007: //http://www.apache.org/licenses/LICENSE-2.0
008: //Unless required by applicable law or agreed to in writing, software
009: //distributed under the License is distributed on an "AS IS" BASIS,
010: //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011: //See the License for the specific language governing permissions and
012: //limitations under the License.
013: //========================================================================
014:
015: package org.mortbay.jetty.ajp;
016:
017: import java.io.IOException;
018: import java.util.HashMap;
019: import java.util.Iterator;
020:
021: import org.mortbay.io.Buffer;
022: import org.mortbay.io.Buffers;
023: import org.mortbay.io.ByteArrayBuffer;
024: import org.mortbay.io.EndPoint;
025: import org.mortbay.jetty.AbstractGenerator;
026: import org.mortbay.jetty.EofException;
027: import org.mortbay.jetty.HttpFields;
028: import org.mortbay.jetty.HttpVersions;
029: import org.mortbay.jetty.HttpFields.Field;
030: import org.mortbay.log.Log;
031: import org.mortbay.util.TypeUtil;
032:
033: /**
034: * @author lagdeppa (at) exist.com
035: * @author Greg Wilkins
036: */
037: public class Ajp13Generator extends AbstractGenerator {
038: private static HashMap __headerHash = new HashMap();
039:
040: static {
041: byte[] xA001 = { (byte) 0xA0, (byte) 0x01 };
042: byte[] xA002 = { (byte) 0xA0, (byte) 0x02 };
043: byte[] xA003 = { (byte) 0xA0, (byte) 0x03 };
044: byte[] xA004 = { (byte) 0xA0, (byte) 0x04 };
045: byte[] xA005 = { (byte) 0xA0, (byte) 0x05 };
046: byte[] xA006 = { (byte) 0xA0, (byte) 0x06 };
047: byte[] xA007 = { (byte) 0xA0, (byte) 0x07 };
048: byte[] xA008 = { (byte) 0xA0, (byte) 0x08 };
049: byte[] xA009 = { (byte) 0xA0, (byte) 0x09 };
050: byte[] xA00A = { (byte) 0xA0, (byte) 0x0A };
051: byte[] xA00B = { (byte) 0xA0, (byte) 0x0B };
052: __headerHash.put("Content-Type", xA001);
053: __headerHash.put("Content-Language", xA002);
054: __headerHash.put("Content-Length", xA003);
055: __headerHash.put("Date", xA004);
056: __headerHash.put("Last-Modified", xA005);
057: __headerHash.put("Location", xA006);
058: __headerHash.put("Set-Cookie", xA007);
059: __headerHash.put("Set-Cookie2", xA008);
060: __headerHash.put("Servlet-Engine", xA009);
061: __headerHash.put("Status", xA00A);
062: __headerHash.put("WWW-Authenticate", xA00B);
063:
064: }
065:
066: private static final byte[] AJP13_END_RESPONSE = { 'A', 'B', 0, 2,
067: 5, 1 };
068:
069: // AB ajp respose
070: // 0, 3 int = 3 packets in length
071: // 6, send signal to get more data
072: // 31, -7 byte values for int 8185 = (8 * 1024) - 7 MAX_DATA
073: private static final byte[] AJP13_MORE_CONTENT = { 'A', 'B', 0, 3,
074: 6, 31, -7 };
075:
076: private static String SERVER = "Server: Jetty(6.0.x)";
077:
078: public static void setServerVersion(String version) {
079: SERVER = "Jetty(" + version + ")";
080: }
081:
082: /* ------------------------------------------------------------ */
083: private boolean _expectMore = false;
084:
085: private boolean _needMore = false;
086:
087: private boolean _needEOC = false;
088:
089: private boolean _bufferPrepared = false;
090:
091: /* ------------------------------------------------------------ */
092: public Ajp13Generator(Buffers buffers, EndPoint io,
093: int headerBufferSize, int contentBufferSize) {
094: super (buffers, io, headerBufferSize, contentBufferSize);
095: }
096:
097: /* ------------------------------------------------------------ */
098: public void reset(boolean returnBuffers) {
099: super .reset(returnBuffers);
100: _needEOC = false;
101: _needMore = false;
102: _expectMore = false;
103: _bufferPrepared = false;
104: }
105:
106: /* ------------------------------------------------------------ */
107: /**
108: * Add content.
109: *
110: * @param content
111: * @param last
112: * @throws IllegalArgumentException
113: * if <code>content</code> is
114: * {@link Buffer#isImmutable immutable}.
115: * @throws IllegalStateException
116: * If the request is not expecting any more content, or if the
117: * buffers are full and cannot be flushed.
118: * @throws IOException
119: * if there is a problem flushing the buffers.
120: */
121: public void addContent(Buffer content, boolean last)
122: throws IOException {
123:
124: if (_noContent) {
125: content.clear();
126: return;
127: }
128:
129: if (content.isImmutable())
130: throw new IllegalArgumentException("immutable");
131:
132: if (_last || _state == STATE_END) {
133: Log.debug("Ignoring extra content {}", content);
134: content.clear();
135: return;
136: }
137: _last = last;
138:
139: // Handle any unfinished business?
140: if (_content != null && _content.length() > 0) {
141:
142: flush();
143: if (_content != null && _content.length() > 0)
144: throw new IllegalStateException("FULL");
145: }
146:
147: _content = content;
148:
149: _contentWritten += content.length();
150:
151: // Handle the _content
152: if (_head) {
153:
154: content.clear();
155: _content = null;
156: } else {
157: // Yes - so we better check we have a buffer
158: initContent();
159: // Copy _content to buffer;
160: int len = 0;
161: len = _buffer.put(_content);
162:
163: // make sure there is space for a trailing null
164: if (len > 0 && _buffer.space() == 0) {
165: len--;
166: _buffer.setPutIndex(_buffer.putIndex() - 1);
167: }
168:
169: _content.skip(len);
170:
171: if (_content.length() == 0)
172: _content = null;
173: }
174: }
175:
176: /* ------------------------------------------------------------ */
177: /**
178: * Add content.
179: *
180: * @param b
181: * byte
182: * @return true if the buffers are full
183: * @throws IOException
184: */
185: public boolean addContent(byte b) throws IOException {
186: if (_noContent)
187: return false;
188:
189: if (_last || _state == STATE_END)
190: throw new IllegalStateException("Closed");
191:
192: // Handle any unfinished business?
193: if (_content != null && _content.length() > 0) {
194: flush();
195: if (_content != null && _content.length() > 0)
196: throw new IllegalStateException("FULL");
197: }
198:
199: _contentWritten++;
200:
201: // Handle the _content
202: if (_head)
203: return false;
204:
205: // we better check we have a buffer
206: initContent();
207:
208: // Copy _content to buffer;
209:
210: _buffer.put(b);
211:
212: return _buffer.space() <= 1;
213: }
214:
215: /* ------------------------------------------------------------ */
216: /**
217: * Prepare buffer for unchecked writes. Prepare the generator buffer to
218: * receive unchecked writes
219: *
220: * @return the available space in the buffer.
221: * @throws IOException
222: */
223: protected int prepareUncheckedAddContent() throws IOException {
224: if (_noContent)
225: return -1;
226:
227: if (_last || _state == STATE_END)
228: throw new IllegalStateException("Closed");
229:
230: // Handle any unfinished business?
231: Buffer content = _content;
232: if (content != null && content.length() > 0) {
233: flush();
234: if (content != null && content.length() > 0)
235: throw new IllegalStateException("FULL");
236: }
237:
238: // we better check we have a buffer
239: initContent();
240:
241: _contentWritten -= _buffer.length();
242:
243: // Handle the _content
244: if (_head)
245: return Integer.MAX_VALUE;
246:
247: return _buffer.space() - 1;
248: }
249:
250: /* ------------------------------------------------------------ */
251: public void completeHeader(HttpFields fields,
252: boolean allContentAdded) throws IOException {
253: if (_state != STATE_HEADER)
254: return;
255:
256: if (_last && !allContentAdded)
257: throw new IllegalStateException("last?");
258: _last = _last | allContentAdded;
259:
260: boolean has_server = false;
261: if (_version == HttpVersions.HTTP_1_0_ORDINAL)
262: _close = true;
263:
264: // get a header buffer
265: if (_header == null)
266: _header = _buffers.getBuffer(_headerBufferSize);
267:
268: Buffer tmpbuf = _buffer;
269: _buffer = _header;
270:
271: try {
272: // start the header
273: _buffer.put((byte) 'A');
274: _buffer.put((byte) 'B');
275: addInt(0);
276: _buffer.put((byte) 0x4);
277: addInt(_status);
278: if (_reason == null)
279: _reason = getReasonBuffer(_status);
280: if (_reason == null)
281: _reason = new ByteArrayBuffer(TypeUtil
282: .toString(_status));
283: addBuffer(_reason);
284:
285: if (_status == 100 || _status == 204 || _status == 304) {
286: _noContent = true;
287: _content = null;
288: }
289:
290: // allocate 2 bytes for number of headers
291: int field_index = _buffer.putIndex();
292: addInt(0);
293:
294: // Add headers
295: Iterator i = fields.getFields();
296: int num_fields = 0;
297:
298: while (i.hasNext()) {
299: num_fields++;
300: Field f = (Field) i.next();
301:
302: byte[] codes = (byte[]) __headerHash.get(f.getName());
303: if (codes != null) {
304: _buffer.put(codes);
305: } else {
306: addString(f.getName());
307: }
308: addString(f.getValue());
309: }
310:
311: if (!has_server && _status > 100 && getSendServerVersion()) {
312: num_fields++;
313: addString("Server");
314: addString(SERVER);
315: }
316:
317: // TODO Add content length if last content known.
318:
319: // insert the number of headers
320: int tmp = _buffer.putIndex();
321: _buffer.setPutIndex(field_index);
322: addInt(num_fields);
323: _buffer.setPutIndex(tmp);
324:
325: // get the payload size ( - 4 bytes for the ajp header)
326: // excluding the
327: // ajp header
328: int payloadSize = _buffer.length() - 4;
329: // insert the total packet size on 2nd and 3rd byte that
330: // was previously
331: // allocated
332: addInt(2, payloadSize);
333: } finally {
334: _buffer = tmpbuf;
335: }
336:
337: _state = STATE_CONTENT;
338:
339: }
340:
341: /* ------------------------------------------------------------ */
342: /**
343: * Complete the message.
344: *
345: * @throws IOException
346: */
347: public void complete() throws IOException {
348: if (_state == STATE_END)
349: return;
350:
351: super .complete();
352:
353: if (_state < STATE_FLUSHING) {
354: _state = STATE_FLUSHING;
355: _needEOC = true;
356: }
357:
358: flush();
359: }
360:
361: /* ------------------------------------------------------------ */
362: public long flush() throws IOException {
363: try {
364: if (!_expectMore && _state == STATE_HEADER)
365: throw new IllegalStateException("State==HEADER");
366:
367: prepareBuffers();
368:
369: if (_endp == null) {
370: // TODO - probably still needed!
371: // if (_needMore && _buffer != null)
372: // {
373: // if(!_hasSentEOC)
374: // _buffer.put(AJP13_MORE_CONTENT);
375: // }
376: if (!_expectMore && _needEOC && _buffer != null) {
377: _buffer.put(AJP13_END_RESPONSE);
378: }
379: _needEOC = false;
380: return 0;
381: }
382:
383: // Keep flushing while there is something to flush
384: // (except break below)
385: int total = 0;
386: long last_len = -1;
387: Flushing: while (true) {
388: int len = -1;
389: int to_flush = ((_header != null && _header.length() > 0) ? 4
390: : 0)
391: | ((_buffer != null && _buffer.length() > 0) ? 2
392: : 0);
393:
394: switch (to_flush) {
395: case 7:
396: throw new IllegalStateException(); // should
397: // never
398: // happen!
399: case 6:
400: len = _endp.flush(_header, _buffer, null);
401:
402: break;
403: case 5:
404: throw new IllegalStateException(); // should
405: // never
406: // happen!
407: case 4:
408: len = _endp.flush(_header);
409: break;
410: case 3:
411: throw new IllegalStateException(); // should
412: // never
413: // happen!
414: case 2:
415: len = _endp.flush(_buffer);
416:
417: break;
418: case 1:
419: throw new IllegalStateException(); // should
420: // never
421: // happen!
422: case 0: {
423: // Nothing more we can write now.
424: if (_header != null)
425: _header.clear();
426:
427: _bufferPrepared = false;
428:
429: if (_buffer != null) {
430: _buffer.clear();
431:
432: // reserve some space for the
433: // header
434: _buffer.setPutIndex(7);
435: _buffer.setGetIndex(7);
436:
437: // Special case handling for
438: // small left over buffer from
439: // an addContent that caused a
440: // buffer flush.
441: if (_content != null
442: && _content.length() < _buffer.space()
443: && _state != STATE_FLUSHING) {
444:
445: _buffer.put(_content);
446: _content.clear();
447: _content = null;
448: break Flushing;
449: }
450:
451: }
452:
453: // Are we completely finished for now?
454: if (!_expectMore
455: && !_needEOC
456: && (_content == null || _content.length() == 0)) {
457: if (_state == STATE_FLUSHING)
458: _state = STATE_END;
459: if (_state == STATE_END/* &&_close */)
460: _endp.close();
461:
462: break Flushing;
463: }
464:
465: // Try to prepare more to write.
466: prepareBuffers();
467: }
468: }
469:
470: // If we failed to flush anything twice in a row
471: // break
472: if (len <= 0) {
473: if (last_len <= 0)
474: break Flushing;
475: break;
476: }
477: last_len = len;
478: total += len;
479: }
480:
481: return total;
482: } catch (IOException e) {
483: Log.ignore(e);
484: throw (e instanceof EofException) ? e : new EofException(e);
485: }
486:
487: }
488:
489: /* ------------------------------------------------------------ */
490: private void prepareBuffers() {
491: if (!_bufferPrepared) {
492: // Refill buffer if possible
493: if (_content != null && _content.length() > 0
494: && _buffer != null && _buffer.space() > 0) {
495:
496: int len = _buffer.put(_content);
497:
498: // Make sure there is space for a trailing null
499: if (len > 0 && _buffer.space() == 0) {
500: len--;
501: _buffer.setPutIndex(_buffer.putIndex() - 1);
502: }
503: _content.skip(len);
504:
505: if (_content.length() == 0)
506: _content = null;
507:
508: if (_buffer.length() == 0) {
509: _content = null;
510: }
511: }
512:
513: // add header if needed
514: if (_buffer != null) {
515:
516: int payloadSize = _buffer.length();
517:
518: // 4 bytes for the ajp header
519: // 1 byte for response type
520: // 2 bytes for the response size
521: // 1 byte because we count from zero??
522:
523: if (payloadSize > 0) {
524: _bufferPrepared = true;
525:
526: _buffer.put((byte) 0);
527: int put = _buffer.putIndex();
528: _buffer.setGetIndex(0);
529: _buffer.setPutIndex(0);
530: _buffer.put((byte) 'A');
531: _buffer.put((byte) 'B');
532: addInt(payloadSize + 4);
533: _buffer.put((byte) 3);
534: addInt(payloadSize);
535: _buffer.setPutIndex(put);
536: }
537: }
538:
539: if (_needMore) {
540:
541: if (_header == null) {
542: _header = _buffers.getBuffer(_headerBufferSize);
543: }
544:
545: if (_buffer == null && _header != null
546: && _header.space() >= AJP13_MORE_CONTENT.length) {
547: _header.put(AJP13_MORE_CONTENT);
548: _needMore = false;
549: } else if (_buffer != null
550: && _buffer.space() >= AJP13_MORE_CONTENT.length) {
551: // send closing packet if all contents
552: // are added
553: _buffer.put(AJP13_MORE_CONTENT);
554: _needMore = false;
555: _bufferPrepared = true;
556: }
557:
558: }
559:
560: if (!_expectMore && _needEOC) {
561: if (_buffer == null
562: && _header.space() >= AJP13_END_RESPONSE.length) {
563:
564: _header.put(AJP13_END_RESPONSE);
565: _needEOC = false;
566: } else if (_buffer != null
567: && _buffer.space() >= AJP13_END_RESPONSE.length) {
568: // send closing packet if all contents
569: // are added
570:
571: _buffer.put(AJP13_END_RESPONSE);
572: _needEOC = false;
573: _bufferPrepared = true;
574: }
575: }
576: }
577: }
578:
579: /* ------------------------------------------------------------ */
580: public boolean isComplete() {
581: return !_expectMore && _state == STATE_END;
582: }
583:
584: /* ------------------------------------------------------------ */
585: private void initContent() throws IOException {
586: if (_buffer == null) {
587: _buffer = _buffers.getBuffer(_contentBufferSize);
588: _buffer.setPutIndex(7);
589: _buffer.setGetIndex(7);
590: }
591: }
592:
593: /* ------------------------------------------------------------ */
594: private void addInt(int i) {
595: _buffer.put((byte) ((i >> 8) & 0xFF));
596: _buffer.put((byte) (i & 0xFF));
597: }
598:
599: /* ------------------------------------------------------------ */
600: private void addInt(int startIndex, int i) {
601: _buffer.poke(startIndex, (byte) ((i >> 8) & 0xFF));
602: _buffer.poke((startIndex + 1), (byte) (i & 0xFF));
603: }
604:
605: /* ------------------------------------------------------------ */
606: private void addString(String str) {
607: if (str == null) {
608: addInt(0xFFFF);
609: return;
610: }
611:
612: // TODO - need to use a writer to convert, to avoid this hacky
613: // conversion and temp buffer
614: byte[] b = str.getBytes();
615:
616: addInt(b.length);
617:
618: _buffer.put(b);
619: _buffer.put((byte) 0);
620: }
621:
622: /* ------------------------------------------------------------ */
623: private void addBuffer(Buffer b) {
624: if (b == null) {
625: addInt(0xFFFF);
626: return;
627: }
628:
629: addInt(b.length());
630: _buffer.put(b);
631: _buffer.put((byte) 0);
632: }
633:
634: /* ------------------------------------------------------------ */
635: public boolean isNeedMore() {
636: // TODO - this is not correct. See setNeedMore
637: return _expectMore;
638: }
639:
640: /* ------------------------------------------------------------ */
641: public void setNeedMore(boolean needMore) throws IOException {
642: _needMore = needMore;
643: if (needMore) {
644: _expectMore = true;
645: flush();
646: }
647: }
648:
649: public void setExpectMore(boolean expectMore) {
650: _expectMore = expectMore;
651: }
652:
653: }
|