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.annotation.NotThreadSafe;
41 import org.apache.http.config.MessageConstraints;
42 import org.apache.http.message.BasicLineParser;
43 import org.apache.http.message.LineParser;
44 import org.apache.http.nio.NHttpMessageParser;
45 import org.apache.http.nio.reactor.SessionInputBuffer;
46 import org.apache.http.params.HttpParamConfig;
47 import org.apache.http.params.HttpParams;
48 import org.apache.http.util.Args;
49 import org.apache.http.util.CharArrayBuffer;
50
51
52
53
54
55
56
57 @SuppressWarnings("deprecation")
58 @NotThreadSafe
59 public abstract class AbstractMessageParser<T extends HttpMessage> implements NHttpMessageParser<T> {
60
61 private final SessionInputBuffer sessionBuffer;
62
63 private static final int READ_HEAD_LINE = 0;
64 private static final int READ_HEADERS = 1;
65 private static final int COMPLETED = 2;
66
67 private int state;
68 private boolean endOfStream;
69
70 private T message;
71 private CharArrayBuffer lineBuf;
72 private final List<CharArrayBuffer> headerBufs;
73
74 protected final LineParser lineParser;
75 private final MessageConstraints constraints;
76
77
78
79
80
81
82
83
84
85
86
87
88 @Deprecated
89 public AbstractMessageParser(
90 final SessionInputBuffer buffer,
91 final LineParser lineParser,
92 final HttpParams params) {
93 super();
94 Args.notNull(buffer, "Session input buffer");
95 Args.notNull(params, "HTTP parameters");
96 this.sessionBuffer = buffer;
97 this.state = READ_HEAD_LINE;
98 this.endOfStream = false;
99 this.headerBufs = new ArrayList<CharArrayBuffer>();
100 this.constraints = HttpParamConfig.getMessageConstraints(params);
101 this.lineParser = (lineParser != null) ? lineParser : BasicLineParser.INSTANCE;
102 }
103
104
105
106
107
108
109
110
111
112
113
114
115 public AbstractMessageParser(
116 final SessionInputBuffer buffer,
117 final LineParser lineParser,
118 final MessageConstraints constraints) {
119 super();
120 this.sessionBuffer = Args.notNull(buffer, "Session input buffer");
121 this.lineParser = lineParser != null ? lineParser : BasicLineParser.INSTANCE;
122 this.constraints = constraints != null ? constraints : MessageConstraints.DEFAULT;
123 this.headerBufs = new ArrayList<CharArrayBuffer>();
124 this.state = READ_HEAD_LINE;
125 this.endOfStream = false;
126 }
127
128 public void reset() {
129 this.state = READ_HEAD_LINE;
130 this.endOfStream = false;
131 this.headerBufs.clear();
132 this.message = null;
133 }
134
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 public T parse() throws IOException, HttpException {
186 while (this.state != COMPLETED) {
187 if (this.lineBuf == null) {
188 this.lineBuf = new CharArrayBuffer(64);
189 } else {
190 this.lineBuf.clear();
191 }
192 final boolean lineComplete = this.sessionBuffer.readLine(this.lineBuf, this.endOfStream);
193 final int maxLineLen = this.constraints.getMaxLineLength();
194 if (maxLineLen > 0 &&
195 (this.lineBuf.length() > maxLineLen ||
196 (!lineComplete && this.sessionBuffer.length() > maxLineLen))) {
197 throw new MessageConstraintException("Maximum line length limit exceeded");
198 }
199 if (!lineComplete) {
200 break;
201 }
202
203 switch (this.state) {
204 case READ_HEAD_LINE:
205 try {
206 parseHeadLine();
207 } catch (final ParseException px) {
208 throw new ProtocolException(px.getMessage(), px);
209 }
210 this.state = READ_HEADERS;
211 break;
212 case READ_HEADERS:
213 if (this.lineBuf.length() > 0) {
214 final int maxHeaderCount = this.constraints.getMaxHeaderCount();
215 if (maxHeaderCount > 0 && headerBufs.size() >= maxHeaderCount) {
216 throw new MessageConstraintException("Maximum header count exceeded");
217 }
218
219 parseHeader();
220 } else {
221 this.state = COMPLETED;
222 }
223 break;
224 }
225 if (this.endOfStream && !this.sessionBuffer.hasData()) {
226 this.state = COMPLETED;
227 }
228 }
229 if (this.state == COMPLETED) {
230 for (int i = 0; i < this.headerBufs.size(); i++) {
231 final CharArrayBuffer buffer = this.headerBufs.get(i);
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 } else {
240 return null;
241 }
242 }
243
244 }