📄 httpparser.java
字号:
// ========================================================================// Copyright 2004-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;import java.io.IOException;import javax.servlet.ServletInputStream;import javax.servlet.http.HttpServletResponse;import org.mortbay.io.Buffer;import org.mortbay.io.BufferUtil;import org.mortbay.io.Buffers;import org.mortbay.io.ByteArrayBuffer;import org.mortbay.io.EndPoint;import org.mortbay.io.View;import org.mortbay.io.BufferCache.CachedBuffer;import org.mortbay.log.Log;/* ------------------------------------------------------------------------------- *//** * @author gregw */public class HttpParser implements Parser{ // States public static final int STATE_START=-13; public static final int STATE_FIELD0=-12; public static final int STATE_SPACE1=-11; public static final int STATE_FIELD1=-10; public static final int STATE_SPACE2=-9; public static final int STATE_END0=-8; public static final int STATE_END1=-7; public static final int STATE_FIELD2=-6; public static final int STATE_HEADER=-5; public static final int STATE_HEADER_NAME=-4; public static final int STATE_HEADER_IN_NAME=-3; public static final int STATE_HEADER_VALUE=-2; public static final int STATE_HEADER_IN_VALUE=-1; public static final int STATE_END=0; public static final int STATE_EOF_CONTENT=1; public static final int STATE_CONTENT=2; public static final int STATE_CHUNKED_CONTENT=3; public static final int STATE_CHUNK_SIZE=4; public static final int STATE_CHUNK_PARAMS=5; public static final int STATE_CHUNK=6; private Buffers _buffers; // source of buffers private EndPoint _endp; private Buffer _header; // Buffer for header data (and small _content) private Buffer _body; // Buffer for large content private Buffer _buffer; // The current buffer in use (either _header or _content) private View _contentView=new View(); // View of the content in the buffer for {@link Input} private int _headerBufferSize; private int _contentBufferSize; private EventHandler _handler; private CachedBuffer _cached; private View.CaseInsensitive _tok0; // Saved token: header name, request method or response version private View.CaseInsensitive _tok1; // Saved token: header value, request URI or response code private String _multiLineValue; private int _responseStatus; // If >0 then we are parsing a response private boolean _forceContentBuffer; private Input _input; /* ------------------------------------------------------------------------------- */ protected int _state=STATE_START; protected byte _eol; protected int _length; protected long _contentLength; protected long _contentPosition; protected int _chunkLength; protected int _chunkPosition; /* ------------------------------------------------------------------------------- */ /** * Constructor. */ public HttpParser(Buffer buffer, EventHandler handler) { this._header=buffer; this._buffer=buffer; this._handler=handler; if (buffer != null) { _tok0=new View.CaseInsensitive(buffer); _tok1=new View.CaseInsensitive(buffer); _tok0.setPutIndex(_tok0.getIndex()); _tok1.setPutIndex(_tok1.getIndex()); } } /* ------------------------------------------------------------------------------- */ /** * Constructor. * @param headerBufferSize size in bytes of header buffer * @param contentBufferSize size in bytes of content buffer */ public HttpParser(Buffers buffers, EndPoint endp, EventHandler handler, int headerBufferSize, int contentBufferSize) { _buffers=buffers; _endp=endp; _handler=handler; _headerBufferSize=headerBufferSize; _contentBufferSize=contentBufferSize; } /* ------------------------------------------------------------------------------- */ public long getContentLength() { return _contentLength; } public long getContentRead() { return _contentPosition; } /* ------------------------------------------------------------------------------- */ public int getState() { return _state; } /* ------------------------------------------------------------------------------- */ public boolean inContentState() { return _state > 0; } /* ------------------------------------------------------------------------------- */ public boolean inHeaderState() { return _state < 0; } /* ------------------------------------------------------------------------------- */ public boolean isChunking() { return _contentLength==HttpTokens.CHUNKED_CONTENT; } /* ------------------------------------------------------------ */ public boolean isIdle() { return isState(STATE_START); } /* ------------------------------------------------------------ */ public boolean isComplete() { return isState(STATE_END); } /* ------------------------------------------------------------ */ public boolean isMoreInBuffer() throws IOException { if ( _header!=null && _header.hasContent() || _body!=null && _body.hasContent()) return true; return false; } /* ------------------------------------------------------------------------------- */ public boolean isState(int state) { return _state == state; } /* ------------------------------------------------------------------------------- */ /** * Parse until {@link #STATE_END END} state. * If the parser is already in the END state, then it is {@link #reset reset} and re-parsed. * @throws IllegalStateException If the buffers have already been partially parsed. */ public void parse() throws IOException { if (_state==STATE_END) reset(false); if (_state!=STATE_START) throw new IllegalStateException("!START"); // continue parsing while (_state != STATE_END) parseNext(); } /* ------------------------------------------------------------------------------- */ /** * Parse until END state. * This method will parse any remaining content in the current buffer. It does not care about the * {@link #getState current state} of the parser. * @see #parse * @see #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; } return total; } /* ------------------------------------------------------------------------------- */ /** * Parse until next Event. * @returns number of bytes filled from endpoint or -1 if fill never called. */ public long parseNext() throws IOException { long total_filled=-1; if (_state == STATE_END) return -1; if (_buffer==null) { if (_header == null) { _header=_buffers.getBuffer(_headerBufferSize); } _buffer=_header; _tok0=new View.CaseInsensitive(_header); _tok1=new View.CaseInsensitive(_header); _tok0.setPutIndex(_tok0.getIndex()); _tok1.setPutIndex(_tok1.getIndex()); } if (_state == STATE_CONTENT && _contentPosition == _contentLength) { _state=STATE_END; _handler.messageComplete(_contentPosition); return total_filled; } int length=_buffer.length(); // Fill buffer if we can if (length == 0) { int filled=-1; if (_body!=null && _buffer!=_body) { _buffer=_body; filled=_buffer.length(); } if (_buffer.markIndex() == 0 && _buffer.putIndex() == _buffer.capacity()) throw new HttpException(HttpStatus.ORDINAL_413_Request_Entity_Too_Large, "FULL"); IOException ioex=null; 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 HttpException(HttpStatus.ORDINAL_413_Request_Entity_Too_Large, "FULL "+(_buffer==_body?"body":"head")); try { if (total_filled<0) total_filled=0; filled=_endp.fill(_buffer); if (filled>0) total_filled+=filled; } catch(IOException e) { Log.debug(e); ioex=e; filled=-1; } } if (filled < 0) { if ( _state == STATE_EOF_CONTENT) { if (_buffer.length()>0) { // TODO should we do this here or fall down to main loop? Buffer chunk=_buffer.get(_buffer.length()); _contentPosition += chunk.length(); _contentView.update(chunk); _handler.content(chunk); // May recurse here } _state=STATE_END; _handler.messageComplete(_contentPosition); return total_filled; } reset(true); throw new EofException(ioex); } length=_buffer.length(); } // EventHandler header byte ch; byte[] array=_buffer.array(); while (_state<STATE_END && length-->0) { ch=_buffer.get(); if (_eol == HttpTokens.CARRIAGE_RETURN && ch == HttpTokens.LINE_FEED) { _eol=HttpTokens.LINE_FEED; continue; } _eol=0; switch (_state) { case STATE_START: _contentLength=HttpTokens.UNKNOWN_CONTENT; _cached=null; if (ch > HttpTokens.SPACE || ch<0) { _buffer.mark(); _state=STATE_FIELD0; } break; case STATE_FIELD0: if (ch == HttpTokens.SPACE) { _tok0.update(_buffer.markIndex(), _buffer.getIndex() - 1); _state=STATE_SPACE1; continue; } else if (ch < HttpTokens.SPACE && ch>=0) { throw new HttpException(HttpServletResponse.SC_BAD_REQUEST); } break; case STATE_SPACE1: if (ch > HttpTokens.SPACE || ch<0) { _buffer.mark(); _state=STATE_FIELD1; } else if (ch < HttpTokens.SPACE) { throw new HttpException(HttpServletResponse.SC_BAD_REQUEST); } break; case STATE_FIELD1: if (ch == HttpTokens.SPACE) { _tok1.update(_buffer.markIndex(), _buffer.getIndex() - 1); _state=STATE_SPACE2; continue; } else if (ch < HttpTokens.SPACE && ch>=0) { // HTTP/0.9 _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _buffer .sliceFromMark(), null); _state=STATE_END; _handler.headerComplete(); _handler.messageComplete(_contentPosition); return total_filled; } break; case STATE_SPACE2:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -