1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.mina.http;
21
22 import java.nio.ByteBuffer;
23 import java.util.HashMap;
24 import java.util.Map;
25 import java.util.regex.Pattern;
26
27 import org.apache.mina.core.buffer.IoBuffer;
28 import org.apache.mina.core.session.IoSession;
29 import org.apache.mina.filter.codec.ProtocolDecoder;
30 import org.apache.mina.filter.codec.ProtocolDecoderOutput;
31 import org.apache.mina.http.api.HttpEndOfContent;
32 import org.apache.mina.http.api.HttpMethod;
33 import org.apache.mina.http.api.HttpStatus;
34 import org.apache.mina.http.api.HttpVersion;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38
39
40
41
42
43 public class HttpServerDecoder implements ProtocolDecoder {
44 private static final Logger LOGGER = LoggerFactory.getLogger(HttpServerCodec.class);
45
46
47 private static final String DECODER_STATE_ATT = "http.ds";
48
49
50 private static final String PARTIAL_HEAD_ATT = "http.ph";
51
52
53 private static final String BODY_REMAINING_BYTES = "http.brb";
54
55
56 public static final Pattern REQUEST_LINE_PATTERN = Pattern.compile(" ");
57
58
59 public static final Pattern QUERY_STRING_PATTERN = Pattern.compile("\\?");
60
61
62 public static final Pattern PARAM_STRING_PATTERN = Pattern.compile("\\&|;");
63
64
65 public static final Pattern KEY_VALUE_PATTERN = Pattern.compile("=");
66
67
68 public static final Pattern RAW_VALUE_PATTERN = Pattern.compile("\\r\\n\\r\\n");
69
70
71 public static final Pattern HEADERS_BODY_PATTERN = Pattern.compile("\\r\\n");
72
73
74 public static final Pattern HEADER_VALUE_PATTERN = Pattern.compile(":");
75
76
77 public static final Pattern COOKIE_SEPARATOR_PATTERN = Pattern.compile(";");
78
79
80
81
82 @Override
83 public void decode(IoSession session, IoBuffer msg, ProtocolDecoderOutput out) {
84 DecoderState/../../org/apache/mina/http/DecoderState.html#DecoderState">DecoderState state = (DecoderState) session.getAttribute(DECODER_STATE_ATT);
85
86 if (null == state) {
87 session.setAttribute(DECODER_STATE_ATT, DecoderState.NEW);
88 state = (DecoderState) session.getAttribute(DECODER_STATE_ATT);
89 }
90
91 switch (state) {
92 case HEAD:
93 if (LOGGER.isDebugEnabled()) {
94 LOGGER.debug("decoding HEAD");
95 }
96
97
98 ByteBuffer oldBuffer = (ByteBuffer) session.getAttribute(PARTIAL_HEAD_ATT);
99
100
101 msg = IoBuffer.allocate(oldBuffer.remaining() + msg.remaining()).put(oldBuffer).put(msg).flip();
102
103 case NEW:
104 if (LOGGER.isDebugEnabled()) {
105 LOGGER.debug("decoding NEW");
106 }
107
108 HttpRequestImpl rq = parseHttpRequestHead(msg.buf());
109
110 if (rq == null) {
111
112 ByteBuffer partial = ByteBuffer.allocate(msg.remaining());
113 partial.put(msg.buf());
114 partial.flip();
115
116 session.setAttribute(PARTIAL_HEAD_ATT, partial);
117 session.setAttribute(DECODER_STATE_ATT, DecoderState.HEAD);
118 break;
119 } else {
120 out.write(rq);
121
122 String contentLen = rq.getHeader("content-length");
123
124 if (contentLen != null) {
125 if (LOGGER.isDebugEnabled()) {
126 LOGGER.debug("found content len : {}", contentLen);
127 }
128
129 session.setAttribute(BODY_REMAINING_BYTES, Integer.valueOf(contentLen));
130 session.setAttribute(DECODER_STATE_ATT, DecoderState.BODY);
131
132 } else {
133 if (LOGGER.isDebugEnabled()) {
134 LOGGER.debug("request without content");
135 }
136
137 session.setAttribute(DECODER_STATE_ATT, DecoderState.NEW);
138 out.write(new HttpEndOfContent());
139 break;
140 }
141 }
142
143 case BODY:
144 if (LOGGER.isDebugEnabled()) {
145 LOGGER.debug("decoding BODY: {} bytes", msg.remaining());
146 }
147
148 int chunkSize = msg.remaining();
149
150
151 if (chunkSize != 0) {
152 IoBuffer wb = IoBuffer.allocate(msg.remaining());
153 wb.put(msg);
154 wb.flip();
155 out.write(wb);
156 }
157
158 msg.position(msg.limit());
159
160 int remaining = (Integer) session.getAttribute(BODY_REMAINING_BYTES);
161 remaining -= chunkSize;
162
163 if (remaining <= 0) {
164 if (LOGGER.isDebugEnabled()) {
165 LOGGER.debug("end of HTTP body");
166 }
167
168 session.setAttribute(DECODER_STATE_ATT, DecoderState.NEW);
169 session.removeAttribute(BODY_REMAINING_BYTES);
170 out.write(new HttpEndOfContent());
171 } else {
172 session.setAttribute(BODY_REMAINING_BYTES, Integer.valueOf(remaining));
173 }
174
175 break;
176
177 default:
178 throw new HttpException(HttpStatus.CLIENT_ERROR_BAD_REQUEST, "Unknonwn decoder state : " + state);
179 }
180 }
181
182
183
184
185 @Override
186 public void finishDecode(IoSession session, ProtocolDecoderOutput out) throws Exception {
187 }
188
189
190
191
192 @Override
193 public void dispose(IoSession session) throws Exception {
194 }
195
196 private HttpRequestImpl parseHttpRequestHead(ByteBuffer buffer) {
197 String raw = new String(buffer.array(), 0, buffer.limit());
198 String[] headersAndBody = RAW_VALUE_PATTERN.split(raw, -1);
199
200 if (headersAndBody.length <= 1) {
201
202 return null;
203 }
204
205 String[] headerFields = HEADERS_BODY_PATTERN.split(headersAndBody[0]);
206 headerFields = ArrayUtil.dropFromEndWhile(headerFields, "");
207
208 String requestLine = headerFields[0];
209 Map<String, String> generalHeaders = new HashMap<>();
210
211 for (int i = 1; i < headerFields.length; i++) {
212 String[] header = HEADER_VALUE_PATTERN.split(headerFields[i]);
213 generalHeaders.put(header[0].toLowerCase(), header[1].trim());
214 }
215
216 String[] elements = REQUEST_LINE_PATTERN.split(requestLine);
217 HttpMethod method = HttpMethod.valueOf(elements[0]);
218 HttpVersion version = HttpVersion.fromString(elements[2]);
219 String[] pathFrags = QUERY_STRING_PATTERN.split(elements[1]);
220 String requestedPath = pathFrags[0];
221 String queryString = pathFrags.length == 2 ? pathFrags[1] : "";
222
223
224 buffer.position(headersAndBody[0].length() + 4);
225
226 return new HttpRequestImpl(version, method, requestedPath, queryString, generalHeaders);
227 }
228 }