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
71
72
73
74
75
76
77 public AbstractMessageParser(final LineParser lineParser, final Http1Config http1Config) {
78 super();
79 this.lineParser = lineParser != null ? lineParser : LazyLineParser.INSTANCE;
80 this.http1Config = http1Config != null ? http1Config : Http1Config.DEFAULT;
81 this.headerLines = new ArrayList<>();
82 this.headLine = new CharArrayBuffer(128);
83 this.state = HEAD_LINE;
84 }
85
86 LineParser getLineParser() {
87 return this.lineParser;
88 }
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110 public static Header[] parseHeaders(
111 final SessionInputBuffer inBuffer,
112 final InputStream inputStream,
113 final int maxHeaderCount,
114 final int maxLineLen,
115 final LineParser lineParser) throws HttpException, IOException {
116 final List<CharArrayBuffer> headerLines = new ArrayList<>();
117 return parseHeaders(inBuffer, inputStream, maxHeaderCount, maxLineLen,
118 lineParser != null ? lineParser : LazyLineParser.INSTANCE, headerLines);
119 }
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 public static Header[] parseHeaders(
147 final SessionInputBuffer inBuffer,
148 final InputStream inputStream,
149 final int maxHeaderCount,
150 final int maxLineLen,
151 final LineParser parser,
152 final List<CharArrayBuffer> headerLines) throws HttpException, IOException {
153 Args.notNull(inBuffer, "Session input buffer");
154 Args.notNull(inputStream, "Input stream");
155 Args.notNull(parser, "Line parser");
156 Args.notNull(headerLines, "Header line list");
157
158 CharArrayBuffer current = null;
159 CharArrayBuffer previous = null;
160 for (;;) {
161 if (current == null) {
162 current = new CharArrayBuffer(64);
163 } else {
164 current.clear();
165 }
166 final int readLen = inBuffer.readLine(current, inputStream);
167 if (readLen == -1 || current.length() < 1) {
168 break;
169 }
170
171
172
173
174 if ((current.charAt(0) == ' ' || current.charAt(0) == '\t') && previous != null) {
175
176
177 int i = 0;
178 while (i < current.length()) {
179 final char ch = current.charAt(i);
180 if (ch != ' ' && ch != '\t') {
181 break;
182 }
183 i++;
184 }
185 if (maxLineLen > 0
186 && previous.length() + 1 + current.length() - i > maxLineLen) {
187 throw new MessageConstraintException("Maximum line length limit exceeded");
188 }
189 previous.append(' ');
190 previous.append(current, i, current.length() - i);
191 } else {
192 headerLines.add(current);
193 previous = current;
194 current = null;
195 }
196 if (maxHeaderCount > 0 && headerLines.size() >= maxHeaderCount) {
197 throw new MessageConstraintException("Maximum header count exceeded");
198 }
199 }
200 final Header[] headers = new Header[headerLines.size()];
201 for (int i = 0; i < headerLines.size(); i++) {
202 final CharArrayBuffer buffer = headerLines.get(i);
203 headers[i] = parser.parseHeader(buffer);
204 }
205 return headers;
206 }
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223 protected abstract T createMessage(CharArrayBuffer buffer) throws IOException, HttpException;
224
225
226
227
228
229
230
231
232
233 @Deprecated
234 protected IOException createConnectionClosedException() {
235 return new ConnectionClosedException();
236 }
237
238 @Override
239 public T parse(final SessionInputBuffer buffer, final InputStream inputStream) throws IOException, HttpException {
240 Args.notNull(buffer, "Session input buffer");
241 Args.notNull(inputStream, "Input stream");
242 final int st = this.state;
243 switch (st) {
244 case HEAD_LINE:
245 for (int n = 0; n < this.http1Config.getMaxEmptyLineCount(); n++) {
246 this.headLine.clear();
247 final int i = buffer.readLine(this.headLine, inputStream);
248 if (i == -1) {
249 return null;
250 }
251 if (this.headLine.length() > 0) {
252 this.message = createMessage(this.headLine);
253 if (this.message != null) {
254 break;
255 }
256 }
257 }
258 if (this.message == null) {
259 throw new MessageConstraintException("Maximum empty line limit exceeded");
260 }
261 this.state = HEADERS;
262
263 case HEADERS:
264 final Header[] headers = AbstractMessageParser.parseHeaders(
265 buffer,
266 inputStream,
267 this.http1Config.getMaxHeaderCount(),
268 this.http1Config.getMaxLineLength(),
269 this.lineParser,
270 this.headerLines);
271 this.message.setHeaders(headers);
272 final T result = this.message;
273 this.message = null;
274 this.headerLines.clear();
275 this.state = HEAD_LINE;
276 return result;
277 default:
278 throw new IllegalStateException("Inconsistent parser state");
279 }
280 }
281
282 }