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.hc.core5.http.impl.io;
29
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.util.ArrayList;
33 import java.util.List;
34
35 import org.apache.hc.core5.http.ConnectionClosedException;
36 import org.apache.hc.core5.http.Header;
37 import org.apache.hc.core5.http.HttpException;
38 import org.apache.hc.core5.http.HttpMessage;
39 import org.apache.hc.core5.http.MessageConstraintException;
40 import org.apache.hc.core5.http.config.Http1Config;
41 import org.apache.hc.core5.http.io.HttpMessageParser;
42 import org.apache.hc.core5.http.io.SessionInputBuffer;
43 import org.apache.hc.core5.http.message.LazyLineParser;
44 import org.apache.hc.core5.http.message.LineParser;
45 import org.apache.hc.core5.util.Args;
46 import org.apache.hc.core5.util.CharArrayBuffer;
47
48
49
50
51
52
53
54 public abstract class AbstractMessageParser<T extends HttpMessage> implements HttpMessageParser<T> {
55
56 private static final int HEAD_LINE = 0;
57 private static final int HEADERS = 1;
58
59 private final Http1Config http1Config;
60 private final List<CharArrayBuffer> headerLines;
61 private final CharArrayBuffer headLine;
62 private final LineParser lineParser;
63
64 private int state;
65 private T message;
66
67
68
69
70 public AbstractMessageParser(final Http1Config http1Config, final LineParser lineParser) {
71 super();
72 this.http1Config = http1Config != null ? http1Config : Http1Config.DEFAULT;
73 this.lineParser = lineParser != null ? lineParser : LazyLineParser.INSTANCE;
74 this.headerLines = new ArrayList<>();
75 this.headLine = new CharArrayBuffer(128);
76 this.state = HEAD_LINE;
77 }
78
79
80
81
82 @Deprecated
83 public AbstractMessageParser(final LineParser lineParser, final Http1Config http1Config) {
84 this(http1Config, lineParser);
85 }
86
87 LineParser getLineParser() {
88 return this.lineParser;
89 }
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111 public static Header[] parseHeaders(
112 final SessionInputBuffer inBuffer,
113 final InputStream inputStream,
114 final int maxHeaderCount,
115 final int maxLineLen,
116 final LineParser lineParser) throws HttpException, IOException {
117 final List<CharArrayBuffer> headerLines = new ArrayList<>();
118 return parseHeaders(inBuffer, inputStream, maxHeaderCount, maxLineLen,
119 lineParser != null ? lineParser : LazyLineParser.INSTANCE, headerLines);
120 }
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147 public static Header[] parseHeaders(
148 final SessionInputBuffer inBuffer,
149 final InputStream inputStream,
150 final int maxHeaderCount,
151 final int maxLineLen,
152 final LineParser parser,
153 final List<CharArrayBuffer> headerLines) throws HttpException, IOException {
154 Args.notNull(inBuffer, "Session input buffer");
155 Args.notNull(inputStream, "Input stream");
156 Args.notNull(parser, "Line parser");
157 Args.notNull(headerLines, "Header line list");
158
159 CharArrayBuffer current = null;
160 CharArrayBuffer previous = null;
161 for (;;) {
162 if (current == null) {
163 current = new CharArrayBuffer(64);
164 } else {
165 current.clear();
166 }
167 final int readLen = inBuffer.readLine(current, inputStream);
168 if (readLen == -1 || current.length() < 1) {
169 break;
170 }
171
172
173
174
175 if ((current.charAt(0) == ' ' || current.charAt(0) == '\t') && previous != null) {
176
177
178 int i = 0;
179 while (i < current.length()) {
180 final char ch = current.charAt(i);
181 if (ch != ' ' && ch != '\t') {
182 break;
183 }
184 i++;
185 }
186 if (maxLineLen > 0
187 && previous.length() + 1 + current.length() - i > maxLineLen) {
188 throw new MessageConstraintException("Maximum line length limit exceeded");
189 }
190 previous.append(' ');
191 previous.append(current, i, current.length() - i);
192 } else {
193 headerLines.add(current);
194 previous = current;
195 current = null;
196 }
197 if (maxHeaderCount > 0 && headerLines.size() >= maxHeaderCount) {
198 throw new MessageConstraintException("Maximum header count exceeded");
199 }
200 }
201 final Header[] headers = new Header[headerLines.size()];
202 for (int i = 0; i < headerLines.size(); i++) {
203 final CharArrayBuffer buffer = headerLines.get(i);
204 headers[i] = parser.parseHeader(buffer);
205 }
206 return headers;
207 }
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224 protected abstract T createMessage(CharArrayBuffer buffer) throws IOException, HttpException;
225
226
227
228
229
230
231
232
233
234 @Deprecated
235 protected IOException createConnectionClosedException() {
236 return new ConnectionClosedException();
237 }
238
239 @Override
240 public T parse(final SessionInputBuffer buffer, final InputStream inputStream) throws IOException, HttpException {
241 Args.notNull(buffer, "Session input buffer");
242 Args.notNull(inputStream, "Input stream");
243 final int st = this.state;
244 switch (st) {
245 case HEAD_LINE:
246 for (int n = 0; n < this.http1Config.getMaxEmptyLineCount(); n++) {
247 this.headLine.clear();
248 final int i = buffer.readLine(this.headLine, inputStream);
249 if (i == -1) {
250 return null;
251 }
252 if (this.headLine.length() > 0) {
253 this.message = createMessage(this.headLine);
254 if (this.message != null) {
255 break;
256 }
257 }
258 }
259 if (this.message == null) {
260 throw new MessageConstraintException("Maximum empty line limit exceeded");
261 }
262 this.state = HEADERS;
263
264 case HEADERS:
265 final Header[] headers = AbstractMessageParser.parseHeaders(
266 buffer,
267 inputStream,
268 this.http1Config.getMaxHeaderCount(),
269 this.http1Config.getMaxLineLength(),
270 this.lineParser,
271 this.headerLines);
272 this.message.setHeaders(headers);
273 final T result = this.message;
274 this.message = null;
275 this.headerLines.clear();
276 this.state = HEAD_LINE;
277 return result;
278 default:
279 throw new IllegalStateException("Inconsistent parser state");
280 }
281 }
282
283 }