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.message;
29
30 import java.util.BitSet;
31
32 import org.apache.hc.core5.annotation.Contract;
33 import org.apache.hc.core5.annotation.ThreadingBehavior;
34 import org.apache.hc.core5.http.Header;
35 import org.apache.hc.core5.http.HttpVersion;
36 import org.apache.hc.core5.http.ParseException;
37 import org.apache.hc.core5.http.ProtocolVersion;
38 import org.apache.hc.core5.util.Args;
39 import org.apache.hc.core5.util.CharArrayBuffer;
40 import org.apache.hc.core5.util.TextUtils;
41
42
43
44
45
46
47 @Contract(threading = ThreadingBehavior.IMMUTABLE)
48 public class BasicLineParser implements LineParser {
49
50 public final static BasicLineParserBasicLineParser.html#BasicLineParser">BasicLineParser INSTANCE = new BasicLineParser();
51
52
53
54 private static final BitSet FULL_STOP = TokenParser.INIT_BITSET('.');
55 private static final BitSet BLANKS = TokenParser.INIT_BITSET(' ', '\t');
56 private static final BitSet COLON = TokenParser.INIT_BITSET(':');
57
58
59
60
61
62 private final ProtocolVersion protocol;
63 private final TokenParser tokenParser;
64
65
66
67
68
69
70
71
72 public BasicLineParser(final ProtocolVersion proto) {
73 this.protocol = proto != null? proto : HttpVersion.HTTP_1_1;
74 this.tokenParser = TokenParser.INSTANCE;
75 }
76
77
78
79
80 public BasicLineParser() {
81 this(null);
82 }
83
84 ProtocolVersion parseProtocolVersion(
85 final CharArrayBuffer buffer,
86 final ParserCursor cursor) throws ParseException {
87 final String protoname = this.protocol.getProtocol();
88 final int protolength = protoname.length();
89
90 this.tokenParser.skipWhiteSpace(buffer, cursor);
91
92 final int pos = cursor.getPos();
93
94
95 if (pos + protolength + 4 > cursor.getUpperBound()) {
96 throw new ParseException("Invalid protocol version",
97 buffer, cursor.getLowerBound(), cursor.getUpperBound(), cursor.getPos());
98 }
99
100
101 boolean ok = true;
102 for (int i = 0; ok && (i < protolength); i++) {
103 ok = buffer.charAt(pos + i) == protoname.charAt(i);
104 }
105 if (ok) {
106 ok = buffer.charAt(pos + protolength) == '/';
107 }
108 if (!ok) {
109 throw new ParseException("Invalid protocol version",
110 buffer, cursor.getLowerBound(), cursor.getUpperBound(), cursor.getPos());
111 }
112
113 cursor.updatePos(pos + protolength + 1);
114
115 final String token1 = this.tokenParser.parseToken(buffer, cursor, FULL_STOP);
116 final int major;
117 try {
118 major = Integer.parseInt(token1);
119 } catch (final NumberFormatException e) {
120 throw new ParseException("Invalid protocol major version number",
121 buffer, cursor.getLowerBound(), cursor.getUpperBound(), cursor.getPos());
122 }
123 if (cursor.atEnd()) {
124 throw new ParseException("Invalid protocol version",
125 buffer, cursor.getLowerBound(), cursor.getUpperBound(), cursor.getPos());
126 }
127 cursor.updatePos(cursor.getPos() + 1);
128 final String token2 = this.tokenParser.parseToken(buffer, cursor, BLANKS);
129 final int minor;
130 try {
131 minor = Integer.parseInt(token2);
132 } catch (final NumberFormatException e) {
133 throw new ParseException("Invalid protocol minor version number",
134 buffer, cursor.getLowerBound(), cursor.getUpperBound(), cursor.getPos());
135 }
136 return HttpVersion.get(major, minor);
137 }
138
139
140
141
142
143
144
145
146
147
148 @Override
149 public RequestLine parseRequestLine(final CharArrayBuffer buffer) throws ParseException {
150 Args.notNull(buffer, "Char array buffer");
151
152 final ParserCursorsage/ParserCursor.html#ParserCursor">ParserCursor cursor = new ParserCursor(0, buffer.length());
153 this.tokenParser.skipWhiteSpace(buffer, cursor);
154 final String method = this.tokenParser.parseToken(buffer, cursor, BLANKS);
155 if (TextUtils.isEmpty(method)) {
156 throw new ParseException("Invalid request line",
157 buffer, cursor.getLowerBound(), cursor.getUpperBound(), cursor.getPos());
158 }
159 this.tokenParser.skipWhiteSpace(buffer, cursor);
160 final String uri = this.tokenParser.parseToken(buffer, cursor, BLANKS);
161 if (TextUtils.isEmpty(uri)) {
162 throw new ParseException("Invalid request line",
163 buffer, cursor.getLowerBound(), cursor.getUpperBound(), cursor.getPos());
164 }
165 final ProtocolVersion ver = parseProtocolVersion(buffer, cursor);
166 this.tokenParser.skipWhiteSpace(buffer, cursor);
167 if (!cursor.atEnd()) {
168 throw new ParseException("Invalid request line",
169 buffer, cursor.getLowerBound(), cursor.getUpperBound(), cursor.getPos());
170 }
171 return new RequestLine(method, uri, ver);
172 }
173
174 @Override
175 public StatusLine parseStatusLine(final CharArrayBuffer buffer) throws ParseException {
176 Args.notNull(buffer, "Char array buffer");
177
178 final ParserCursorsage/ParserCursor.html#ParserCursor">ParserCursor cursor = new ParserCursor(0, buffer.length());
179 this.tokenParser.skipWhiteSpace(buffer, cursor);
180 final ProtocolVersion ver = parseProtocolVersion(buffer, cursor);
181 this.tokenParser.skipWhiteSpace(buffer, cursor);
182 final String s = this.tokenParser.parseToken(buffer, cursor, BLANKS);
183 for (int i = 0; i < s.length(); i++) {
184 if (!Character.isDigit(s.charAt(i))) {
185 throw new ParseException("Status line contains invalid status code",
186 buffer, cursor.getLowerBound(), cursor.getUpperBound(), cursor.getPos());
187 }
188 }
189 final int statusCode;
190 try {
191 statusCode = Integer.parseInt(s);
192 } catch (final NumberFormatException e) {
193 throw new ParseException("Status line contains invalid status code",
194 buffer, cursor.getLowerBound(), cursor.getUpperBound(), cursor.getPos());
195 }
196 final String text = buffer.substringTrimmed(cursor.getPos(), cursor.getUpperBound());
197 return new StatusLine(ver, statusCode, text);
198 }
199
200 @Override
201 public Header parseHeader(final CharArrayBuffer buffer) throws ParseException {
202 Args.notNull(buffer, "Char array buffer");
203
204 final ParserCursorsage/ParserCursor.html#ParserCursor">ParserCursor cursor = new ParserCursor(0, buffer.length());
205 this.tokenParser.skipWhiteSpace(buffer, cursor);
206 final String name = this.tokenParser.parseToken(buffer, cursor, COLON);
207 if (cursor.getPos() == cursor.getLowerBound() || cursor.getPos() == cursor.getUpperBound() ||
208 buffer.charAt(cursor.getPos()) != ':' ||
209 TextUtils.isEmpty(name) ||
210 TokenParser.isWhitespace(buffer.charAt(cursor.getPos() - 1))) {
211 throw new ParseException("Invalid header",
212 buffer, cursor.getLowerBound(), cursor.getUpperBound(), cursor.getPos());
213 }
214 final String value = buffer.substringTrimmed(cursor.getPos() + 1, cursor.getUpperBound());
215 return new BasicHeader(name, value);
216 }
217
218 }