1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.http.impl.nio.codecs;
29
30 import java.io.IOException;
31 import java.nio.channels.ReadableByteChannel;
32 import java.util.ArrayList;
33 import java.util.List;
34
35 import org.apache.http.HttpException;
36 import org.apache.http.HttpMessage;
37 import org.apache.http.MessageConstraintException;
38 import org.apache.http.ParseException;
39 import org.apache.http.ProtocolException;
40 import org.apache.http.config.MessageConstraints;
41 import org.apache.http.message.BasicLineParser;
42 import org.apache.http.message.LineParser;
43 import org.apache.http.nio.NHttpMessageParser;
44 import org.apache.http.nio.reactor.SessionInputBuffer;
45 import org.apache.http.params.HttpParamConfig;
46 import org.apache.http.params.HttpParams;
47 import org.apache.http.util.Args;
48 import org.apache.http.util.CharArrayBuffer;
49
50
51
52
53
54
55
56 @SuppressWarnings("deprecation")
57 public abstract class AbstractMessageParser<T extends HttpMessage> implements NHttpMessageParser<T> {
58
59 private final SessionInputBuffer sessionBuffer;
60
61 private static final int READ_HEAD_LINE = 0;
62 private static final int READ_HEADERS = 1;
63 private static final int COMPLETED = 2;
64
65 private int state;
66 private boolean endOfStream;
67
68 private T message;
69 private CharArrayBuffer lineBuf;
70 private final List<CharArrayBuffer> headerBufs;
71
72 protected final LineParser lineParser;
73 private final MessageConstraints constraints;
74
75
76
77
78
79
80
81
82
83
84
85
86 @Deprecated
87 public AbstractMessageParser(
88 final SessionInputBuffer buffer,
89 final LineParser lineParser,
90 final HttpParams params) {
91 super();
92 Args.notNull(buffer, "Session input buffer");
93 Args.notNull(params, "HTTP parameters");
94 this.sessionBuffer = buffer;
95 this.state = READ_HEAD_LINE;
96 this.endOfStream = false;
97 this.headerBufs = new ArrayList<CharArrayBuffer>();
98 this.constraints = HttpParamConfig.getMessageConstraints(params);
99 this.lineParser = (lineParser != null) ? lineParser : BasicLineParser.INSTANCE;
100 }
101
102
103
104
105
106
107
108
109
110
111
112
113 public AbstractMessageParser(
114 final SessionInputBuffer buffer,
115 final LineParser lineParser,
116 final MessageConstraints constraints) {
117 super();
118 this.sessionBuffer = Args.notNull(buffer, "Session input buffer");
119 this.lineParser = lineParser != null ? lineParser : BasicLineParser.INSTANCE;
120 this.constraints = constraints != null ? constraints : MessageConstraints.DEFAULT;
121 this.headerBufs = new ArrayList<CharArrayBuffer>();
122 this.state = READ_HEAD_LINE;
123 this.endOfStream = false;
124 }
125
126 @Override
127 public void reset() {
128 this.state = READ_HEAD_LINE;
129 this.endOfStream = false;
130 this.headerBufs.clear();
131 this.message = null;
132 }
133
134 @Override
135 public int fillBuffer(final ReadableByteChannel channel) throws IOException {
136 final int bytesRead = this.sessionBuffer.fill(channel);
137 if (bytesRead == -1) {
138 this.endOfStream = true;
139 }
140 return bytesRead;
141 }
142
143
144
145
146
147
148
149
150
151
152 protected abstract T createMessage(CharArrayBuffer buffer)
153 throws HttpException, ParseException;
154
155 private void parseHeadLine() throws HttpException, ParseException {
156 this.message = createMessage(this.lineBuf);
157 }
158
159 private void parseHeader() throws IOException {
160 final CharArrayBuffer current = this.lineBuf;
161 final int count = this.headerBufs.size();
162 if ((this.lineBuf.charAt(0) == ' ' || this.lineBuf.charAt(0) == '\t') && count > 0) {
163
164 final CharArrayBuffer previous = this.headerBufs.get(count - 1);
165 int i = 0;
166 while (i < current.length()) {
167 final char ch = current.charAt(i);
168 if (ch != ' ' && ch != '\t') {
169 break;
170 }
171 i++;
172 }
173 final int maxLineLen = this.constraints.getMaxLineLength();
174 if (maxLineLen > 0 && previous.length() + 1 + current.length() - i > maxLineLen) {
175 throw new MessageConstraintException("Maximum line length limit exceeded");
176 }
177 previous.append(' ');
178 previous.append(current, i, current.length() - i);
179 } else {
180 this.headerBufs.add(current);
181 this.lineBuf = null;
182 }
183 }
184
185 @Override
186 public T parse() throws IOException, HttpException {
187 while (this.state != COMPLETED) {
188 if (this.lineBuf == null) {
189 this.lineBuf = new CharArrayBuffer(64);
190 } else {
191 this.lineBuf.clear();
192 }
193 final boolean lineComplete = this.sessionBuffer.readLine(this.lineBuf, this.endOfStream);
194 final int maxLineLen = this.constraints.getMaxLineLength();
195 if (maxLineLen > 0 &&
196 (this.lineBuf.length() > maxLineLen ||
197 (!lineComplete && this.sessionBuffer.length() > maxLineLen))) {
198 throw new MessageConstraintException("Maximum line length limit exceeded");
199 }
200 if (!lineComplete) {
201 break;
202 }
203
204 switch (this.state) {
205 case READ_HEAD_LINE:
206 try {
207 parseHeadLine();
208 } catch (final ParseException px) {
209 throw new ProtocolException(px.getMessage(), px);
210 }
211 this.state = READ_HEADERS;
212 break;
213 case READ_HEADERS:
214 if (this.lineBuf.length() > 0) {
215 final int maxHeaderCount = this.constraints.getMaxHeaderCount();
216 if (maxHeaderCount > 0 && headerBufs.size() >= maxHeaderCount) {
217 throw new MessageConstraintException("Maximum header count exceeded");
218 }
219
220 parseHeader();
221 } else {
222 this.state = COMPLETED;
223 }
224 break;
225 }
226 if (this.endOfStream && !this.sessionBuffer.hasData()) {
227 this.state = COMPLETED;
228 }
229 }
230 if (this.state == COMPLETED) {
231 for (final CharArrayBuffer buffer : this.headerBufs) {
232 try {
233 this.message.addHeader(lineParser.parseHeader(buffer));
234 } catch (final ParseException ex) {
235 throw new ProtocolException(ex.getMessage(), ex);
236 }
237 }
238 return this.message;
239 }
240 return null;
241 }
242
243 }