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