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.nio;
29
30 import java.io.IOException;
31 import java.util.ArrayList;
32 import java.util.List;
33
34 import org.apache.hc.core5.http.HttpException;
35 import org.apache.hc.core5.http.HttpMessage;
36 import org.apache.hc.core5.http.MessageConstraintException;
37 import org.apache.hc.core5.http.config.Http1Config;
38 import org.apache.hc.core5.http.message.LazyLineParser;
39 import org.apache.hc.core5.http.message.LineParser;
40 import org.apache.hc.core5.http.nio.NHttpMessageParser;
41 import org.apache.hc.core5.http.nio.SessionInputBuffer;
42 import org.apache.hc.core5.util.Args;
43 import org.apache.hc.core5.util.CharArrayBuffer;
44
45
46
47
48
49
50
51 public abstract class AbstractMessageParser<T extends HttpMessage> implements NHttpMessageParser<T> {
52
53 private enum State {
54 READ_HEAD_LINE, READ_HEADERS, COMPLETED
55 }
56
57 private final Http1Config http1Config;
58 private final LineParser lineParser;
59
60 private State state;
61
62 private T message;
63 private CharArrayBuffer lineBuf;
64 private final List<CharArrayBuffer> headerBufs;
65 private int emptyLineCount;
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.headerBufs = new ArrayList<>();
75 this.state = State.READ_HEAD_LINE;
76 }
77
78
79
80
81 @Deprecated
82 public AbstractMessageParser(final LineParser lineParser, final Http1Config messageConstraints) {
83 this(messageConstraints, lineParser);
84 }
85
86 LineParser getLineParser() {
87 return this.lineParser;
88 }
89
90 @Override
91 public void reset() {
92 this.state = State.READ_HEAD_LINE;
93 this.headerBufs.clear();
94 this.emptyLineCount = 0;
95 this.message = null;
96 }
97
98
99
100
101
102
103
104
105
106 protected abstract T createMessage(CharArrayBuffer buffer) throws HttpException;
107
108 private T parseHeadLine() throws IOException, HttpException {
109 if (this.lineBuf.isEmpty()) {
110 this.emptyLineCount++;
111 if (this.emptyLineCount >= this.http1Config.getMaxEmptyLineCount()) {
112 throw new MessageConstraintException("Maximum empty line limit exceeded");
113 }
114 return null;
115 }
116 return createMessage(this.lineBuf);
117 }
118
119 private void parseHeader() throws IOException {
120 final CharArrayBuffer current = this.lineBuf;
121 final int count = this.headerBufs.size();
122 if ((this.lineBuf.charAt(0) == ' ' || this.lineBuf.charAt(0) == '\t') && count > 0) {
123
124 final CharArrayBuffer previous = this.headerBufs.get(count - 1);
125 int i = 0;
126 while (i < current.length()) {
127 final char ch = current.charAt(i);
128 if (ch != ' ' && ch != '\t') {
129 break;
130 }
131 i++;
132 }
133 final int maxLineLen = this.http1Config.getMaxLineLength();
134 if (maxLineLen > 0 && previous.length() + 1 + current.length() - i > maxLineLen) {
135 throw new MessageConstraintException("Maximum line length limit exceeded");
136 }
137 previous.append(' ');
138 previous.append(current, i, current.length() - i);
139 } else {
140 this.headerBufs.add(current);
141 this.lineBuf = null;
142 }
143 }
144
145 @Override
146 public T parse(
147 final SessionInputBuffer sessionBuffer, final boolean endOfStream) throws IOException, HttpException {
148 Args.notNull(sessionBuffer, "Session input buffer");
149 while (this.state !=State.COMPLETED) {
150 if (this.lineBuf == null) {
151 this.lineBuf = new CharArrayBuffer(64);
152 } else {
153 this.lineBuf.clear();
154 }
155 final boolean lineComplete = sessionBuffer.readLine(this.lineBuf, endOfStream);
156 final int maxLineLen = this.http1Config.getMaxLineLength();
157 if (maxLineLen > 0 &&
158 (this.lineBuf.length() > maxLineLen ||
159 (!lineComplete && sessionBuffer.length() > maxLineLen))) {
160 throw new MessageConstraintException("Maximum line length limit exceeded");
161 }
162 if (!lineComplete) {
163 break;
164 }
165
166 switch (this.state) {
167 case READ_HEAD_LINE:
168 this.message = parseHeadLine();
169 if (this.message != null) {
170 this.state = State.READ_HEADERS;
171 }
172 break;
173 case READ_HEADERS:
174 if (this.lineBuf.length() > 0) {
175 final int maxHeaderCount = this.http1Config.getMaxHeaderCount();
176 if (maxHeaderCount > 0 && headerBufs.size() >= maxHeaderCount) {
177 throw new MessageConstraintException("Maximum header count exceeded");
178 }
179
180 parseHeader();
181 } else {
182 this.state = State.COMPLETED;
183 }
184 break;
185 }
186 if (endOfStream && !sessionBuffer.hasData()) {
187 this.state = State.COMPLETED;
188 }
189 }
190 if (this.state ==State. COMPLETED) {
191 for (final CharArrayBuffer buffer : this.headerBufs) {
192 this.message.addHeader(this.lineParser.parseHeader(buffer));
193 }
194 return this.message;
195 }
196 return null;
197 }
198
199 }