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