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.io.InterruptedIOException;
019:
020: import javax.servlet.ServletInputStream;
021:
022: import org.mortbay.io.Buffer;
023: import org.mortbay.io.BufferUtil;
024: import org.mortbay.io.Buffers;
025: import org.mortbay.io.EndPoint;
026: import org.mortbay.io.View;
027: import org.mortbay.jetty.EofException;
028: import org.mortbay.jetty.HttpTokens;
029: import org.mortbay.jetty.Parser;
030: import org.mortbay.log.Log;
031:
032: /**
033: * @author Markus Kobler
034: */
035: public class Ajp13Parser implements Parser {
036: private final static int STATE_START = -7;
037:
038: private final static int STATE_AJP13HEADER_PACKET_LENGTH = -6;
039:
040: private final static int STATE_AJP13HEADER_PACKET_TYPE = -5;
041:
042: private final static int STATE_AJP13HEADER_REQUEST_ATTR = -4;
043:
044: private final static int STATE_AJP13HEADER_REQUEST_ATTR_VALUE = -3;
045:
046: private final static int STATE_AJP13HEADER_REQUEST_HEADER_NAME = -2;
047:
048: private final static int STATE_AJP13HEADER_REQUEST_METHOD = -1;
049:
050: private final static int STATE_END = 0;
051:
052: private final static int STATE_AJP13CHUNK_START = 1;
053:
054: private final static int STATE_AJP13CHUNK_LENGTH = 2;
055:
056: private final static int STATE_AJP13CHUNK_LENGTH2 = 3;
057:
058: private final static int STATE_AJP13CHUNK = 4;
059:
060: private int _state = STATE_START;
061:
062: private long _contentLength;
063:
064: private long _contentPosition;
065:
066: private int _chunkLength;
067:
068: private int _chunkPosition;
069:
070: private int _headers;
071:
072: private Buffers _buffers;
073:
074: private EndPoint _endp;
075:
076: private Buffer _buffer;
077:
078: private Buffer _header; // Buffer for header data (and small _content)
079:
080: private Buffer _body; // Buffer for large content
081:
082: private View _contentView = new View();
083:
084: private EventHandler _handler;
085:
086: private Ajp13Generator _generator;
087:
088: private View _tok0; // Saved token: header name, request method or
089:
090: // response version
091:
092: private View _tok1; // Saved token: header value, request URI or
093:
094: // response code
095:
096: protected int _length;
097:
098: /* ------------------------------------------------------------------------------- */
099: public Ajp13Parser(Buffers buffers, EndPoint endPoint,
100: EventHandler handler, Ajp13Generator generator) {
101: _buffers = buffers;
102: _endp = endPoint;
103: _handler = handler;
104: _generator = generator;
105:
106: }
107:
108: /* ------------------------------------------------------------------------------- */
109: public long getContentLength() {
110: return _contentLength;
111: }
112:
113: /* ------------------------------------------------------------------------------- */
114: public int getState() {
115: return _state;
116: }
117:
118: /* ------------------------------------------------------------------------------- */
119: public boolean inContentState() {
120: return _state > 0;
121: }
122:
123: /* ------------------------------------------------------------------------------- */
124: public boolean inHeaderState() {
125: return _state < 0;
126: }
127:
128: /* ------------------------------------------------------------------------------- */
129: public boolean isIdle() {
130: return _state == STATE_START;
131: }
132:
133: /* ------------------------------------------------------------------------------- */
134: public boolean isComplete() {
135: return _state == STATE_END;
136: }
137:
138: /* ------------------------------------------------------------------------------- */
139: public boolean isMoreInBuffer() {
140:
141: if (_header != null && _header.hasContent() || _body != null
142: && _body.hasContent())
143: return true;
144:
145: return false;
146: }
147:
148: /* ------------------------------------------------------------------------------- */
149: public boolean isState(int state) {
150: return _state == state;
151: }
152:
153: /* ------------------------------------------------------------------------------- */
154: public void parse() throws IOException {
155: if (_state == STATE_END)
156: reset(false);
157: if (_state != STATE_START)
158: throw new IllegalStateException("!START");
159:
160: // continue parsing
161: while (!isComplete()) {
162: parseNext();
163: }
164: }
165:
166: /* ------------------------------------------------------------------------------- */
167: public long parseAvailable() throws IOException {
168: long len = parseNext();
169: long total = len > 0 ? len : 0;
170:
171: // continue parsing
172: while (!isComplete() && _buffer != null && _buffer.length() > 0) {
173: len = parseNext();
174: if (len > 0)
175: total += len;
176: }
177: return total;
178: }
179:
180: /* ------------------------------------------------------------------------------- */
181: public long parseNext() throws IOException {
182: long total_filled = -1;
183:
184: if (_buffer == null) {
185: if (_header == null) {
186: _header = _buffers
187: .getBuffer(Ajp13Packet.MAX_PACKET_SIZE);
188: _header.clear();
189: }
190: _buffer = _header;
191: _tok0 = new View(_header);
192: _tok1 = new View(_header);
193: _tok0.setPutIndex(_tok0.getIndex());
194: _tok1.setPutIndex(_tok1.getIndex());
195: }
196:
197: if (_state == STATE_END)
198: throw new IllegalStateException("STATE_END");
199: if (_state > STATE_END && _contentPosition == _contentLength) {
200: _state = STATE_END;
201: _handler.messageComplete(_contentPosition);
202: return total_filled;
203: }
204:
205: int length = _buffer.length();
206:
207: // Fill buffer if we can
208: if (length == 0) {
209: int filled = -1;
210: if (_body != null && _buffer != _body) {
211: _buffer = _body;
212: filled = _buffer.length();
213: }
214:
215: if (_buffer.markIndex() == 0
216: && _buffer.putIndex() == _buffer.capacity())
217: throw new IOException("FULL");
218: if (_endp != null && filled <= 0) {
219: // Compress buffer if handling _content buffer
220: // TODO check this is not moving data too much
221: if (_buffer == _body)
222: _buffer.compact();
223:
224: if (_buffer.space() == 0) {
225: throw new IOException("FULL");
226: }
227:
228: try {
229: if (total_filled < 0)
230: total_filled = 0;
231:
232: filled = _endp.fill(_buffer);
233:
234: if (filled > 0)
235: total_filled += filled;
236: } catch (IOException e) {
237: e.printStackTrace();
238: Log.debug(e);
239: reset(true);
240: throw (e instanceof EofException) ? e
241: : new EofException(e);
242: }
243: }
244:
245: if (filled < 0) {
246:
247: if (_state > STATE_END) {
248: _state = STATE_END;
249: _handler.messageComplete(_contentPosition);
250: return total_filled;
251: }
252: reset(true);
253: throw new EofException();
254: }
255: length = _buffer.length();
256: }
257:
258: // Parse Header
259: Buffer bufHeaderName = null;
260: Buffer bufHeaderValue = null;
261: int attr_type = 0;
262:
263: while (_state < STATE_END) {
264:
265: switch (_state) {
266: case STATE_START:
267: _contentLength = HttpTokens.UNKNOWN_CONTENT;
268: int _magic = Ajp13RequestPacket.getInt(_buffer);
269: if (_magic != Ajp13RequestHeaders.MAGIC) {
270: throw new IOException(
271: "Bad AJP13 rcv packet: "
272: + "0x"
273: + Integer.toHexString(_magic)
274: + " expected "
275: + "0x"
276: + Integer
277: .toHexString(Ajp13RequestHeaders.MAGIC)
278: + " " + this );
279: }
280: _state = STATE_AJP13HEADER_PACKET_LENGTH;
281:
282: case STATE_AJP13HEADER_PACKET_LENGTH:
283: int packetLength = Ajp13RequestPacket.getInt(_buffer);
284: if (packetLength > Ajp13Packet.MAX_PACKET_SIZE)
285: throw new IOException("AJP13 packet ("
286: + packetLength
287: + "bytes) too large for buffer");
288: _state = STATE_AJP13HEADER_PACKET_TYPE;
289:
290: case STATE_AJP13HEADER_PACKET_TYPE:
291: byte packetType = Ajp13RequestPacket.getByte(_buffer);
292: switch (packetType) {
293: case Ajp13Packet.FORWARD_REQUEST_ORDINAL:
294: _handler.startForwardRequest();
295: _state = STATE_AJP13HEADER_REQUEST_METHOD;
296: break;
297: case Ajp13Packet.SHUTDOWN_ORDINAL:
298: // Log.warn("AJP13 SHUTDOWN not
299: // supported!");
300: // break;
301: case Ajp13Packet.CPING_REQUEST_ORDINAL:
302: // handler.cpingRequest();
303: // break;
304: default:
305: // XXX Throw an Exception here?? Close
306: // connection!
307: Log.warn(
308: "AJP13 message type ({SHUTDOWN, CPING, PING}) not supported/recognized as a "
309: + "container request", Integer
310: .toString(packetType));
311: throw new IllegalStateException(
312: "SHUTDOWN, CPING, PING is not implemented");
313:
314: }
315: break;
316:
317: case STATE_AJP13HEADER_REQUEST_METHOD:
318: _handler.parsedMethod(Ajp13RequestPacket
319: .getMethod(_buffer));
320: _handler.parsedProtocol(Ajp13RequestPacket.getString(
321: _buffer, _tok0));
322: _handler.parsedUri(Ajp13RequestPacket.getString(
323: _buffer, _tok1));
324: _handler.parsedRemoteAddr(Ajp13RequestPacket.getString(
325: _buffer, _tok1));
326: _handler.parsedRemoteHost(Ajp13RequestPacket.getString(
327: _buffer, _tok1));
328: _handler.parsedServerName(Ajp13RequestPacket.getString(
329: _buffer, _tok1));
330: _handler.parsedServerPort(Ajp13RequestPacket
331: .getInt(_buffer));
332: _handler.parsedSslSecure(Ajp13RequestPacket
333: .getBool(_buffer));
334:
335: _headers = Ajp13RequestPacket.getInt(_buffer);
336: _state = _headers == 0 ? STATE_AJP13HEADER_REQUEST_ATTR
337: : STATE_AJP13HEADER_REQUEST_HEADER_NAME;
338: break;
339:
340: case STATE_AJP13HEADER_REQUEST_HEADER_NAME:
341: bufHeaderName = Ajp13RequestPacket.getHeaderName(
342: _buffer, _tok0);
343: bufHeaderValue = Ajp13RequestPacket.getString(_buffer,
344: _tok1);
345:
346: if (bufHeaderName != null
347: && bufHeaderName.toString().equals(
348: Ajp13RequestHeaders.CONTENT_LENGTH)) {
349: _contentLength = BufferUtil.toLong(bufHeaderValue);
350: if (_contentLength <= 0)
351: _contentLength = HttpTokens.NO_CONTENT;
352: }
353:
354: _handler.parsedHeader(bufHeaderName, bufHeaderValue);
355:
356: _state = --_headers == 0 ? STATE_AJP13HEADER_REQUEST_ATTR
357: : STATE_AJP13HEADER_REQUEST_HEADER_NAME;
358:
359: break;
360:
361: case STATE_AJP13HEADER_REQUEST_ATTR:
362: attr_type = Ajp13RequestPacket.getByte(_buffer) & 0xff;
363: if (attr_type == 0xFF) {
364: _contentPosition = 0;
365: switch ((int) _contentLength) {
366: case HttpTokens.UNKNOWN_CONTENT:
367: case HttpTokens.NO_CONTENT:
368: _state = STATE_END;
369: _generator.setNeedMore(false);
370: _handler.headerComplete();
371: _handler.messageComplete(_contentPosition);
372:
373: break;
374:
375: default:
376:
377: if (_buffers != null
378: && _body == null
379: && _buffer == _header
380: && _contentLength > (_header.capacity() - _header
381: .getIndex())) {
382: _body = _buffers
383: .getBuffer(Ajp13Packet.MAX_PACKET_SIZE);
384: _body.clear();
385:
386: }
387: _state = STATE_AJP13CHUNK_START;
388: _handler.headerComplete(); // May
389: // recurse
390: // here!
391: break;
392: }
393: return total_filled;
394: }
395:
396: _state = STATE_AJP13HEADER_REQUEST_ATTR_VALUE;
397:
398: case STATE_AJP13HEADER_REQUEST_ATTR_VALUE:
399:
400: _state = STATE_AJP13HEADER_REQUEST_ATTR;
401: switch (attr_type) {
402: // XXX How does this plug into the web
403: // containers
404: // authentication?
405: case Ajp13RequestHeaders.REMOTE_USER_ATTR:
406: case Ajp13RequestHeaders.AUTH_TYPE_ATTR:
407: break;
408:
409: case Ajp13RequestHeaders.QUERY_STRING_ATTR:
410: _handler.parsedQueryString(Ajp13RequestPacket
411: .getString(_buffer, _tok1));
412: break;
413:
414: case Ajp13RequestHeaders.JVM_ROUTE_ATTR:
415: // XXX Using old Jetty 5 key,
416: // should change!
417: // Note used in
418: // org.mortbay.jetty.servlet.HashSessionIdManager
419: _handler.parsedRequestAttribute(
420: "org.mortbay.http.ajp.JVMRoute",
421: Ajp13RequestPacket
422: .getString(_buffer, _tok1));
423: break;
424:
425: case Ajp13RequestHeaders.SSL_CERT_ATTR:
426: _handler.parsedRequestAttribute(
427: "javax.servlet.request.X509Certificate",
428: Ajp13RequestPacket
429: .getString(_buffer, _tok1));
430: break;
431:
432: case Ajp13RequestHeaders.SSL_CIPHER_ATTR:
433: _handler.parsedRequestAttribute(
434: "javax.servlet.request.cipher_suite",
435: Ajp13RequestPacket
436: .getString(_buffer, _tok1));
437: // SslSocketConnector.customize()
438: break;
439:
440: case Ajp13RequestHeaders.SSL_SESSION_ATTR:
441: _handler.parsedRequestAttribute(
442: "javax.servlet.request.ssl_session",
443: Ajp13RequestPacket
444: .getString(_buffer, _tok1));
445: break;
446:
447: case Ajp13RequestHeaders.REQUEST_ATTR:
448: _handler.parsedRequestAttribute(Ajp13RequestPacket
449: .getString(_buffer, _tok0).toString(),
450: Ajp13RequestPacket
451: .getString(_buffer, _tok1));
452: break;
453:
454: // New Jk API?
455: // Check if experimental or can they
456: // assumed to be
457: // supported
458: case Ajp13RequestHeaders.SSL_KEYSIZE_ATTR:
459: _handler.parsedRequestAttribute(
460: "javax.servlet.request.key_size",
461: Ajp13RequestPacket
462: .getString(_buffer, _tok1));
463: break;
464:
465: // Used to lock down jk requests with a
466: // secreate
467: // key.
468: case Ajp13RequestHeaders.SECRET_ATTR:
469: // XXX Investigate safest way to
470: // deal with
471: // this...
472: // should this tie into shutdown
473: // packet?
474: break;
475:
476: case Ajp13RequestHeaders.STORED_METHOD_ATTR:
477: // XXX Confirm this should
478: // really overide
479: // previously parsed method?
480: // _handler.parsedMethod(Ajp13PacketMethods.CACHE.get(Ajp13RequestPacket.getString()));
481: break;
482:
483: // Legacy codes, simply ignore
484: case Ajp13RequestHeaders.CONTEXT_ATTR:
485: case Ajp13RequestHeaders.SERVLET_PATH_ATTR:
486: default:
487: Log.warn("Unsupported Ajp13 Request Attribute {}",
488: new Integer(attr_type));
489: break;
490: }
491:
492: break;
493:
494: default:
495: throw new IllegalStateException("State not regonised {"
496: + _state + "}");
497: }
498:
499: } // end of HEADER states loop
500:
501: Buffer chunk;
502:
503: while (_state > STATE_END) {
504:
505: switch (_state) {
506: case STATE_AJP13CHUNK_START:
507:
508: int _magic = Ajp13RequestPacket.getInt(_buffer);
509: if (_magic != Ajp13RequestHeaders.MAGIC) {
510: throw new IOException(
511: "Bad AJP13 rcv packet: "
512: + "0x"
513: + Integer.toHexString(_magic)
514: + " expected "
515: + "0x"
516: + Integer
517: .toHexString(Ajp13RequestHeaders.MAGIC)
518: + " " + this );
519: }
520: _chunkLength = 0;
521: _chunkPosition = 0;
522: _state = STATE_AJP13CHUNK_LENGTH;
523:
524: case STATE_AJP13CHUNK_LENGTH:
525: _chunkLength = Ajp13RequestPacket.getInt(_buffer) - 2;
526: _state = STATE_AJP13CHUNK_LENGTH2;
527:
528: case STATE_AJP13CHUNK_LENGTH2:
529: Ajp13RequestPacket.getInt(_buffer);
530: if (_chunkLength == 0) {
531:
532: // _buffer.clear();
533: _state = STATE_END;
534: _generator.setNeedMore(false);
535: _generator.setExpectMore(false);
536: _handler.messageComplete(_contentPosition);
537: return total_filled;
538: }
539: _state = STATE_AJP13CHUNK;
540:
541: case STATE_AJP13CHUNK:
542: int remaining = _chunkLength - _chunkPosition;
543:
544: if (remaining == 0) {
545: _state = STATE_AJP13CHUNK_START;
546: if (_contentPosition < _contentLength) {
547:
548: _generator.setNeedMore(true);
549: } else {
550: // _state=STATE_END;
551: _generator.setNeedMore(false);
552: _generator.setExpectMore(false);
553: }
554:
555: return total_filled;
556: }
557:
558: if (_buffer.length() < remaining) {
559: remaining = _buffer.length();
560:
561: }
562:
563: chunk = Ajp13RequestPacket
564: .get(_buffer, (int) remaining);
565: _contentPosition += chunk.length();
566: _chunkPosition += chunk.length();
567: _contentView.update(chunk);
568: // _contentView.put(chunk);
569:
570: remaining = _chunkLength - _chunkPosition;
571:
572: if (remaining == 0) {
573: _state = STATE_AJP13CHUNK_START;
574: if (_contentPosition < _contentLength) {
575:
576: _generator.setNeedMore(true);
577: } else {
578:
579: // _state=STATE_END;
580: _generator.setNeedMore(false);
581: _generator.setExpectMore(false);
582: }
583: } else {
584:
585: }
586:
587: _handler.content(chunk);
588:
589: return total_filled;
590:
591: default:
592: throw new IllegalStateException("Invalid Content State");
593:
594: }
595:
596: }
597:
598: return total_filled;
599: }
600:
601: /* ------------------------------------------------------------------------------- */
602: public void reset(boolean returnBuffers) {
603: _state = STATE_START;
604: _contentLength = HttpTokens.UNKNOWN_CONTENT;
605: _contentPosition = 0;
606: _length = 0;
607:
608: if (_body != null) {
609: if (_body.hasContent()) {
610: _header.setMarkIndex(-1);
611: _header.compact();
612: // TODO if pipelined requests received after big
613: // input - maybe this is not good?.
614: _body.skip(_header.put(_body));
615:
616: }
617:
618: if (_body.length() == 0) {
619: if (_buffers != null && returnBuffers)
620: _buffers.returnBuffer(_body);
621: _body = null;
622: } else {
623: _body.setMarkIndex(-1);
624: _body.compact();
625: }
626: }
627:
628: if (_header != null) {
629: _header.setMarkIndex(-1);
630: if (!_header.hasContent() && _buffers != null
631: && returnBuffers) {
632: _buffers.returnBuffer(_header);
633: _header = null;
634: _buffer = null;
635: } else {
636: _header.compact();
637: _tok0.update(_header);
638: _tok0.update(0, 0);
639: _tok1.update(_header);
640: _tok1.update(0, 0);
641: }
642: }
643:
644: _buffer = _header;
645: }
646:
647: /* ------------------------------------------------------------------------------- */
648: Buffer getHeaderBuffer() {
649: return _buffer;
650: }
651:
652: /* ------------------------------------------------------------------------------- */
653: public interface EventHandler {
654:
655: // public void shutdownRequest() throws IOException;
656: // public void cpingRequest() throws IOException;
657:
658: public void content(Buffer ref) throws IOException;
659:
660: public void headerComplete() throws IOException;
661:
662: public void messageComplete(long contextLength)
663: throws IOException;
664:
665: public void parsedHeader(Buffer name, Buffer value)
666: throws IOException;
667:
668: public void parsedMethod(Buffer method) throws IOException;
669:
670: public void parsedProtocol(Buffer protocol) throws IOException;
671:
672: public void parsedQueryString(Buffer value) throws IOException;
673:
674: public void parsedRemoteAddr(Buffer addr) throws IOException;
675:
676: public void parsedRemoteHost(Buffer host) throws IOException;
677:
678: public void parsedRequestAttribute(String key, Buffer value)
679: throws IOException;
680:
681: public void parsedServerName(Buffer name) throws IOException;
682:
683: public void parsedServerPort(int port) throws IOException;
684:
685: public void parsedSslSecure(boolean secure) throws IOException;
686:
687: public void parsedUri(Buffer uri) throws IOException;
688:
689: public void startForwardRequest() throws IOException;
690:
691: }
692:
693: /* ------------------------------------------------------------ */
694: /**
695: * TODO Make this common with HttpParser
696: *
697: */
698: public static class Input extends ServletInputStream {
699: private Ajp13Parser _parser;
700:
701: private EndPoint _endp;
702:
703: private long _maxIdleTime;
704:
705: private View _content;
706:
707: /* ------------------------------------------------------------ */
708: public Input(Ajp13Parser parser, long maxIdleTime) {
709: _parser = parser;
710: _endp = parser._endp;
711: _maxIdleTime = maxIdleTime;
712: _content = _parser._contentView;
713: }
714:
715: /* ------------------------------------------------------------ */
716: public int read() throws IOException {
717: int c = -1;
718: if (blockForContent())
719: c = 0xff & _content.get();
720: return c;
721: }
722:
723: /* ------------------------------------------------------------ */
724: /*
725: * @see java.io.InputStream#read(byte[], int, int)
726: */
727: public int read(byte[] b, int off, int len) throws IOException {
728: int l = -1;
729: if (blockForContent())
730: l = _content.get(b, off, len);
731: return l;
732: }
733:
734: /* ------------------------------------------------------------ */
735: private boolean blockForContent() throws IOException {
736: if (_content.length() > 0)
737: return true;
738: if (_parser.isState(Ajp13Parser.STATE_END))
739: return false;
740:
741: // Handle simple end points.
742: if (_endp == null)
743: _parser.parseNext();
744:
745: // Handle blocking end points
746: else if (_endp.isBlocking()) {
747: long filled = _parser.parseNext();
748:
749: // parse until some progress is made (or
750: // IOException thrown for timeout)
751: while (_content.length() == 0 && filled != 0
752: && !_parser.isState(Ajp13Parser.STATE_END)) {
753: // Try to get more _parser._content
754: filled = _parser.parseNext();
755: }
756:
757: }
758: // Handle non-blocking end point
759: else {
760: long filled = _parser.parseNext();
761: boolean blocked = false;
762:
763: // parse until some progress is made (or
764: // IOException thrown for timeout)
765: while (_content.length() == 0
766: && !_parser.isState(Ajp13Parser.STATE_END)) {
767: // if fill called, but no bytes read,
768: // then block
769: if (filled > 0)
770: blocked = false;
771: else if (filled == 0) {
772: if (blocked)
773: throw new InterruptedIOException("timeout");
774:
775: blocked = true;
776: _endp.blockReadable(_maxIdleTime);
777: }
778:
779: // Try to get more _parser._content
780: filled = _parser.parseNext();
781: }
782: }
783:
784: return _content.length() > 0;
785: }
786:
787: }
788: }
|