|
Jetty example source code file (Ajp13Parser.java)
The Jetty Ajp13Parser.java source code//======================================================================== //Copyright 2006 Mort Bay Consulting Pty. Ltd. //------------------------------------------------------------------------ //Licensed under the Apache License, Version 2.0 (the "License"); //you may not use this file except in compliance with the License. //You may obtain a copy of the License at //http://www.apache.org/licenses/LICENSE-2.0 //Unless required by applicable law or agreed to in writing, software //distributed under the License is distributed on an "AS IS" BASIS, //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //See the License for the specific language governing permissions and //limitations under the License. //======================================================================== package org.mortbay.jetty.ajp; import java.io.IOException; import java.io.InterruptedIOException; import javax.servlet.ServletInputStream; import org.mortbay.io.Buffer; import org.mortbay.io.BufferUtil; import org.mortbay.io.Buffers; import org.mortbay.io.EndPoint; import org.mortbay.io.View; import org.mortbay.jetty.EofException; import org.mortbay.jetty.HttpTokens; import org.mortbay.jetty.Parser; import org.mortbay.log.Log; /** * @author Markus Kobler */ public class Ajp13Parser implements Parser { private final static int STATE_START = -1; private final static int STATE_END = 0; private final static int STATE_AJP13CHUNK_START = 1; private final static int STATE_AJP13CHUNK = 2; private int _state = STATE_START; private long _contentLength; private long _contentPosition; private int _chunkLength; private int _chunkPosition; private int _headers; private Buffers _buffers; private EndPoint _endp; private Buffer _buffer; private Buffer _header; // Buffer for header data (and small _content) private Buffer _body; // Buffer for large content private View _contentView = new View(); private EventHandler _handler; private Ajp13Generator _generator; private View _tok0; // Saved token: header name, request method or response version private View _tok1; // Saved token: header value, request URI orresponse code protected int _length; protected int _packetLength; /* ------------------------------------------------------------------------------- */ public Ajp13Parser(Buffers buffers, EndPoint endPoint, EventHandler handler, Ajp13Generator generator) { _buffers = buffers; _endp = endPoint; _handler = handler; _generator = generator; } /* ------------------------------------------------------------------------------- */ public long getContentLength() { return _contentLength; } /* ------------------------------------------------------------------------------- */ public int getState() { return _state; } /* ------------------------------------------------------------------------------- */ public boolean inContentState() { return _state > 0; } /* ------------------------------------------------------------------------------- */ public boolean inHeaderState() { return _state < 0; } /* ------------------------------------------------------------------------------- */ public boolean isIdle() { return _state == STATE_START; } /* ------------------------------------------------------------------------------- */ public boolean isComplete() { return _state == STATE_END; } /* ------------------------------------------------------------------------------- */ public boolean isMoreInBuffer() { if (_header != null && _header.hasContent() || _body != null && _body.hasContent()) return true; return false; } /* ------------------------------------------------------------------------------- */ public boolean isState(int state) { return _state == state; } /* ------------------------------------------------------------------------------- */ public void parse() throws IOException { if (_state == STATE_END) reset(false); if (_state != STATE_START) throw new IllegalStateException("!START"); // continue parsing while (!isComplete()) { parseNext(); } } /* ------------------------------------------------------------------------------- */ public long parseAvailable() throws IOException { long len = parseNext(); long total = len > 0 ? len : 0; // continue parsing while (!isComplete() && _buffer != null && _buffer.length() > 0) { len = parseNext(); if (len > 0) total += len; else break; } return total; } /* ------------------------------------------------------------------------------- */ private int fill() throws IOException { int filled = -1; if (_body != null && _buffer != _body) { // mod_jk implementations may have some partial data from header // check if there are partial contents in the header // copy it to the body if there are any if(_header.length() > 0) { // copy the patial data from the header to the body _body.put(_header); } _buffer = _body; if (_buffer.length()>0) { filled = _buffer.length(); return filled; } } if (_buffer.markIndex() == 0 && _buffer.putIndex() == _buffer.capacity()) throw new IOException("FULL"); if (_endp != null && filled <= 0) { // Compress buffer if handling _content buffer // TODO check this is not moving data too much if (_buffer == _body) _buffer.compact(); if (_buffer.space() == 0) throw new IOException("FULL"); try { filled = _endp.fill(_buffer); } catch (IOException e) { // This is normal in AJP since the socket closes on timeout only Log.debug(e); reset(true); throw (e instanceof EofException) ? e : new EofException(e); } } if (filled < 0) { if (_state > STATE_END) { _state = STATE_END; _handler.messageComplete(_contentPosition); return filled; } reset(true); throw new EofException(); } return filled; } /* ------------------------------------------------------------------------------- */ public long parseNext() throws IOException { long total_filled = -1; if (_buffer == null) { if (_header == null) { _header = _buffers.getBuffer(Ajp13Packet.MAX_PACKET_SIZE); _header.clear(); } _buffer = _header; _tok0 = new View(_header); _tok1 = new View(_header); _tok0.setPutIndex(_tok0.getIndex()); _tok1.setPutIndex(_tok1.getIndex()); } if (_state == STATE_END) throw new IllegalStateException("STATE_END"); if (_state > STATE_END && _contentPosition == _contentLength) { _state = STATE_END; _handler.messageComplete(_contentPosition); return total_filled; } if (_state < 0) { // have we seen a packet? if (_packetLength<=0) { if (_buffer.length()<4) { if (total_filled<0) total_filled=0; total_filled+=fill(); if (_buffer.length()<4) return total_filled; } _contentLength = HttpTokens.UNKNOWN_CONTENT; int _magic = Ajp13RequestPacket.getInt(_buffer); if (_magic != Ajp13RequestHeaders.MAGIC) throw new IOException("Bad AJP13 rcv packet: " + "0x" + Integer.toHexString(_magic) + " expected " + "0x" + Integer.toHexString(Ajp13RequestHeaders.MAGIC) + " " + this); _packetLength = Ajp13RequestPacket.getInt(_buffer); if (_packetLength > Ajp13Packet.MAX_PACKET_SIZE) throw new IOException("AJP13 packet (" + _packetLength + "bytes) too large for buffer"); } if (_buffer.length() < _packetLength) { if (total_filled<0) total_filled=0; total_filled+=fill(); if (_buffer.length() < _packetLength) return total_filled; } // Parse Header Buffer bufHeaderName = null; Buffer bufHeaderValue = null; int attr_type = 0; byte packetType = Ajp13RequestPacket.getByte(_buffer); switch (packetType) { case Ajp13Packet.FORWARD_REQUEST_ORDINAL: _handler.startForwardRequest(); break; case Ajp13Packet.CPING_REQUEST_ORDINAL: ((Ajp13Generator) _generator).sendCPong(); if(_header != null) { _buffers.returnBuffer(_header); _header = null; } if(_body != null) { _buffers.returnBuffer(_body); _body = null; } _buffer= null; reset(true); return -1; case Ajp13Packet.SHUTDOWN_ORDINAL: shutdownRequest(); return -1; default: // XXX Throw an Exception here?? Close // connection! Log.warn("AJP13 message type ({PING}: "+packetType+" ) not supported/recognized as an AJP request"); throw new IllegalStateException("PING is not implemented"); } _handler.parsedMethod(Ajp13RequestPacket.getMethod(_buffer)); _handler.parsedProtocol(Ajp13RequestPacket.getString(_buffer, _tok0)); _handler.parsedUri(Ajp13RequestPacket.getString(_buffer, _tok1)); _handler.parsedRemoteAddr(Ajp13RequestPacket.getString(_buffer, _tok1)); _handler.parsedRemoteHost(Ajp13RequestPacket.getString(_buffer, _tok1)); _handler.parsedServerName(Ajp13RequestPacket.getString(_buffer, _tok1)); _handler.parsedServerPort(Ajp13RequestPacket.getInt(_buffer)); _handler.parsedSslSecure(Ajp13RequestPacket.getBool(_buffer)); _headers = Ajp13RequestPacket.getInt(_buffer); for (int h=0;h<_headers;h++) { bufHeaderName = Ajp13RequestPacket.getHeaderName(_buffer, _tok0); bufHeaderValue = Ajp13RequestPacket.getString(_buffer, _tok1); if (bufHeaderName != null && bufHeaderName.toString().equals(Ajp13RequestHeaders.CONTENT_LENGTH)) { _contentLength = BufferUtil.toLong(bufHeaderValue); if (_contentLength == 0) _contentLength = HttpTokens.NO_CONTENT; } _handler.parsedHeader(bufHeaderName, bufHeaderValue); } attr_type = Ajp13RequestPacket.getByte(_buffer) & 0xff; while (attr_type != 0xFF) { switch (attr_type) { // XXX How does this plug into the web // containers // authentication? case Ajp13RequestHeaders.REMOTE_USER_ATTR: _handler.parsedRemoteUser(Ajp13RequestPacket.getString(_buffer, _tok1)); break; case Ajp13RequestHeaders.AUTH_TYPE_ATTR: _handler.parsedAuthorizationType(Ajp13RequestPacket.getString(_buffer, _tok1)); break; case Ajp13RequestHeaders.QUERY_STRING_ATTR: _handler.parsedQueryString(Ajp13RequestPacket.getString(_buffer, _tok1)); break; case Ajp13RequestHeaders.JVM_ROUTE_ATTR: // XXX Using old Jetty 5 key, // should change! // Note used in // org.mortbay.jetty.servlet.HashSessionIdManager _handler.parsedRequestAttribute("org.mortbay.http.ajp.JVMRoute", Ajp13RequestPacket.getString(_buffer, _tok1)); break; case Ajp13RequestHeaders.SSL_CERT_ATTR: _handler.parsedSslCert(Ajp13RequestPacket.getString(_buffer, _tok1)); break; case Ajp13RequestHeaders.SSL_CIPHER_ATTR: _handler.parsedSslCipher(Ajp13RequestPacket.getString(_buffer, _tok1)); // SslSocketConnector.customize() break; case Ajp13RequestHeaders.SSL_SESSION_ATTR: _handler.parsedSslSession(Ajp13RequestPacket.getString(_buffer, _tok1)); break; case Ajp13RequestHeaders.REQUEST_ATTR: _handler.parsedRequestAttribute(Ajp13RequestPacket.getString(_buffer, _tok0).toString(), Ajp13RequestPacket.getString(_buffer, _tok1)); break; // New Jk API? // Check if experimental or can they // assumed to be // supported case Ajp13RequestHeaders.SSL_KEYSIZE_ATTR: // This has been implemented as either a string or a integer. // Does it look like a string containing digits? int length = Ajp13RequestPacket.getInt(_buffer); if (length>0 && length<16) { // this must be a string length rather than a key length _buffer.skip(-2); _handler.parsedRequestAttribute("javax.servlet.request.key_size", Ajp13RequestPacket.getString(_buffer, _tok1)); } else _handler.parsedRequestAttribute("javax.servlet.request.key_size",length); break; // Used to lock down jk requests with a // secreate // key. case Ajp13RequestHeaders.SECRET_ATTR: // XXX Investigate safest way to // deal with // this... // should this tie into shutdown // packet? break; case Ajp13RequestHeaders.STORED_METHOD_ATTR: // XXX Confirm this should // really overide // previously parsed method? // _handler.parsedMethod(Ajp13PacketMethods.CACHE.get(Ajp13RequestPacket.getString())); break; case Ajp13RequestHeaders.CONTEXT_ATTR: _handler.parsedContextPath(Ajp13RequestPacket.getString(_buffer, _tok1)); break; case Ajp13RequestHeaders.SERVLET_PATH_ATTR: _handler.parsedServletPath(Ajp13RequestPacket.getString(_buffer, _tok1)); break; default: Log.warn("Unsupported Ajp13 Request Attribute {}", new Integer(attr_type)); break; } attr_type = Ajp13RequestPacket.getByte(_buffer) & 0xff; } _contentPosition = 0; switch ((int) _contentLength) { case HttpTokens.NO_CONTENT: _state = STATE_END; _handler.headerComplete(); _handler.messageComplete(_contentPosition); break; case HttpTokens.UNKNOWN_CONTENT: _generator.getBodyChunk(); if (_buffers != null && _body == null && _buffer == _header && _header.length() <= 0) { _body = _buffers.getBuffer(Ajp13Packet.MAX_PACKET_SIZE); _body.clear(); } _state = STATE_AJP13CHUNK_START; _handler.headerComplete(); // May recurse here! return total_filled; default: if (_buffers != null && _body == null && _buffer == _header && _contentLength > (_header.capacity() - _header.getIndex())) { _body = _buffers.getBuffer(Ajp13Packet.MAX_PACKET_SIZE); _body.clear(); } _state = STATE_AJP13CHUNK_START; _handler.headerComplete(); // May recurse here! return total_filled; } } Buffer chunk; while (_state>STATE_END) { switch (_state) { case STATE_AJP13CHUNK_START: if (_buffer.length()<6) { if (total_filled<0) total_filled=0; total_filled+=fill(); if (_buffer.length()<6) return total_filled; } int _magic=Ajp13RequestPacket.getInt(_buffer); if (_magic!=Ajp13RequestHeaders.MAGIC) { throw new IOException("Bad AJP13 rcv packet: "+"0x"+Integer.toHexString(_magic)+" expected "+"0x" +Integer.toHexString(Ajp13RequestHeaders.MAGIC)+" "+this); } _chunkPosition=0; _chunkLength=Ajp13RequestPacket.getInt(_buffer)-2; Ajp13RequestPacket.getInt(_buffer); if (_chunkLength==0) { _state=STATE_END; _generator.gotBody(); _handler.messageComplete(_contentPosition); return total_filled; } _state=STATE_AJP13CHUNK; case STATE_AJP13CHUNK: if (_buffer.length()<_chunkLength) { if (total_filled<0) total_filled=0; total_filled+=fill(); if (_buffer.length()<_chunkLength) return total_filled; } int remaining=_chunkLength-_chunkPosition; if (remaining==0) { _state=STATE_AJP13CHUNK_START; if (_contentPosition<_contentLength) { _generator.getBodyChunk(); } else { _generator.gotBody(); } return total_filled; } if (_buffer.length()<remaining) { remaining=_buffer.length(); } chunk=Ajp13RequestPacket.get(_buffer,(int)remaining); _contentPosition+=chunk.length(); _chunkPosition+=chunk.length(); _contentView.update(chunk); remaining=_chunkLength-_chunkPosition; if (remaining==0) { _state=STATE_AJP13CHUNK_START; if (_contentPosition<_contentLength || _contentLength == HttpTokens.UNKNOWN_CONTENT) { _generator.getBodyChunk(); } else { _generator.gotBody(); } } _handler.content(chunk); return total_filled; default: throw new IllegalStateException("Invalid Content State"); } } return total_filled; } /* ------------------------------------------------------------------------------- */ public void reset(boolean returnBuffers) { _state = STATE_START; _contentLength = HttpTokens.UNKNOWN_CONTENT; _contentPosition = 0; _length = 0; _packetLength = 0; if (_body != null) { if (_body.hasContent()) { _header.setMarkIndex(-1); _header.compact(); // TODO if pipelined requests received after big // input - maybe this is not good?. _body.skip(_header.put(_body)); } if (_body.length() == 0) { if (_buffers != null && returnBuffers) _buffers.returnBuffer(_body); _body = null; } else { _body.setMarkIndex(-1); _body.compact(); } } if (_header != null) { _header.setMarkIndex(-1); if (!_header.hasContent() && _buffers != null && returnBuffers) { _buffers.returnBuffer(_header); _header = null; _buffer = null; } else { _header.compact(); _tok0.update(_header); _tok0.update(0, 0); _tok1.update(_header); _tok1.update(0, 0); } } _buffer = _header; } /* ------------------------------------------------------------------------------- */ Buffer getHeaderBuffer() { return _buffer; } private void shutdownRequest() { _state = STATE_END; if(!Ajp13SocketConnector.__allowShutdown) { Log.warn("AJP13: Shutdown Request is Denied, allowShutdown is set to false!!!"); return; } if(Ajp13SocketConnector.__secretWord != null) { Log.warn("AJP13: Validating Secret Word"); try { String secretWord = Ajp13RequestPacket.getString(_buffer, _tok1).toString(); if(!Ajp13SocketConnector.__secretWord.equals(secretWord)) { Log.warn("AJP13: Shutdown Request Denied, Invalid Sercret word!!!"); throw new IllegalStateException("AJP13: Secret Word is Invalid: Peer has requested shutdown but, Secret Word did not match"); } } catch (Exception e) { Log.warn("AJP13: Secret Word is Required!!!"); Log.debug(e); throw new IllegalStateException("AJP13: Secret Word is Required: Peer has requested shutdown but, has not provided a Secret Word"); } Log.warn("AJP13: Shutdown Request is Denied, allowShutdown is set to false!!!"); return; } Log.warn("AJP13: Peer Has Requested for Shutdown!!!"); Log.warn("AJP13: Jetty 6 is shutting down !!!"); System.exit(0); } /* ------------------------------------------------------------------------------- */ public interface EventHandler { // public void shutdownRequest() throws IOException; // public void cpingRequest() throws IOException; public void content(Buffer ref) throws IOException; public void headerComplete() throws IOException; public void messageComplete(long contextLength) throws IOException; public void parsedHeader(Buffer name, Buffer value) throws IOException; public void parsedMethod(Buffer method) throws IOException; public void parsedProtocol(Buffer protocol) throws IOException; public void parsedQueryString(Buffer value) throws IOException; public void parsedRemoteAddr(Buffer addr) throws IOException; public void parsedRemoteHost(Buffer host) throws IOException; public void parsedRequestAttribute(String key, Buffer value) throws IOException; public void parsedRequestAttribute(String key, int value) throws IOException; public void parsedServerName(Buffer name) throws IOException; public void parsedServerPort(int port) throws IOException; public void parsedSslSecure(boolean secure) throws IOException; public void parsedUri(Buffer uri) throws IOException; public void startForwardRequest() throws IOException; public void parsedAuthorizationType(Buffer authType) throws IOException; public void parsedRemoteUser(Buffer remoteUser) throws IOException; public void parsedServletPath(Buffer servletPath) throws IOException; public void parsedContextPath(Buffer context) throws IOException; public void parsedSslCert(Buffer sslCert) throws IOException; public void parsedSslCipher(Buffer sslCipher) throws IOException; public void parsedSslSession(Buffer sslSession) throws IOException; } /* ------------------------------------------------------------ */ /** * TODO Make this common with HttpParser * */ public static class Input extends ServletInputStream { private Ajp13Parser _parser; private EndPoint _endp; private long _maxIdleTime; private View _content; /* ------------------------------------------------------------ */ public Input(Ajp13Parser parser, long maxIdleTime) { _parser = parser; _endp = parser._endp; _maxIdleTime = maxIdleTime; _content = _parser._contentView; } /* ------------------------------------------------------------ */ public int read() throws IOException { int c = -1; if (blockForContent()) c = 0xff & _content.get(); return c; } /* ------------------------------------------------------------ */ /* * @see java.io.InputStream#read(byte[], int, int) */ public int read(byte[] b, int off, int len) throws IOException { int l = -1; if (blockForContent()) l = _content.get(b, off, len); return l; } /* ------------------------------------------------------------ */ private boolean blockForContent() throws IOException { if (_content.length() > 0) return true; if (_parser.isState(Ajp13Parser.STATE_END)) return false; // Handle simple end points. if (_endp == null) _parser.parseNext(); // Handle blocking end points else if (_endp.isBlocking()) { _parser.parseNext(); // parse until some progress is made (or IOException thrown for timeout) while (_content.length() == 0 && !_parser.isState(Ajp13Parser.STATE_END)) { // Try to get more _parser._content _parser.parseNext(); } } else // Handle non-blocking end point { long filled = _parser.parseNext(); boolean blocked = false; // parse until some progress is made (or // IOException thrown for timeout) while (_content.length() == 0 && !_parser.isState(Ajp13Parser.STATE_END)) { // if fill called, but no bytes read, // then block if (filled > 0) blocked = false; else if (filled == 0) { if (blocked) throw new InterruptedIOException("timeout"); blocked = true; _endp.blockReadable(_maxIdleTime); } // Try to get more _parser._content filled = _parser.parseNext(); } } return _content.length() > 0; } } } Other Jetty examples (source code examples)Here is a short list of links related to this Jetty Ajp13Parser.java source code file: |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.