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 public class HttpServerDecoder implements ProtocolDecoder {
39 private static final Logger LOG = LoggerFactory.getLogger(HttpServerCodec.class);
40
41
42 private static final String DECODER_STATE_ATT = "http.ds";
43
44
45 private static final String PARTIAL_HEAD_ATT = "http.ph";
46
47
48 private static final String BODY_REMAINING_BYTES = "http.brb";
49
50
51 public static final Pattern REQUEST_LINE_PATTERN = Pattern.compile(" ");
52
53
54 public static final Pattern QUERY_STRING_PATTERN = Pattern.compile("\\?");
55
56
57 public static final Pattern PARAM_STRING_PATTERN = Pattern.compile("\\&|;");
58
59
60 public static final Pattern KEY_VALUE_PATTERN = Pattern.compile("=");
61
62
63 public static final Pattern RAW_VALUE_PATTERN = Pattern.compile("\\r\\n\\r\\n");
64
65
66 public static final Pattern HEADERS_BODY_PATTERN = Pattern.compile("\\r\\n");
67
68
69 public static final Pattern HEADER_VALUE_PATTERN = Pattern.compile(":");
70
71
72 public static final Pattern COOKIE_SEPARATOR_PATTERN = Pattern.compile(";");
73
74 public void decode(IoSession session, IoBuffer msg, ProtocolDecoderOutput out) {
75 DecoderState state = (DecoderState) session.getAttribute(DECODER_STATE_ATT);
76 if (null == state) {
77 session.setAttribute(DECODER_STATE_ATT, DecoderState.NEW);
78 state = (DecoderState) session.getAttribute(DECODER_STATE_ATT);
79 }
80 switch (state) {
81 case HEAD:
82 LOG.debug("decoding HEAD");
83
84 final ByteBuffer oldBuffer = (ByteBuffer) session.getAttribute(PARTIAL_HEAD_ATT);
85
86
87 msg = IoBuffer.allocate(oldBuffer.remaining() + msg.remaining()).put(oldBuffer).put(msg).flip();
88 case NEW:
89 LOG.debug("decoding NEW");
90 HttpRequestImpl rq = parseHttpRequestHead(msg.buf());
91
92 if (rq == null) {
93
94 ByteBuffer partial = ByteBuffer.allocate(msg.remaining());
95 partial.put(msg.buf());
96 partial.flip();
97
98 session.setAttribute(PARTIAL_HEAD_ATT, partial);
99 session.setAttribute(DECODER_STATE_ATT, DecoderState.HEAD);
100 break;
101 } else {
102 out.write(rq);
103
104 String contentLen = rq.getHeader("content-length");
105
106 if (contentLen != null) {
107 LOG.debug("found content len : {}", contentLen);
108 session.setAttribute(BODY_REMAINING_BYTES, Integer.valueOf(contentLen));
109 session.setAttribute(DECODER_STATE_ATT, DecoderState.BODY);
110
111 } else {
112 LOG.debug("request without content");
113 session.setAttribute(DECODER_STATE_ATT, DecoderState.NEW);
114 out.write(new HttpEndOfContent());
115 break;
116 }
117 }
118
119 case BODY:
120 LOG.debug("decoding BODY: {} bytes", msg.remaining());
121 final int chunkSize = msg.remaining();
122
123 if (chunkSize != 0) {
124 final IoBuffer wb = IoBuffer.allocate(msg.remaining());
125 wb.put(msg);
126 wb.flip();
127 out.write(wb);
128 }
129 msg.position(msg.limit());
130
131 int remaining = (Integer) session.getAttribute(BODY_REMAINING_BYTES);
132 remaining -= chunkSize;
133
134 if (remaining <= 0) {
135 LOG.debug("end of HTTP body");
136 session.setAttribute(DECODER_STATE_ATT, DecoderState.NEW);
137 session.removeAttribute(BODY_REMAINING_BYTES);
138 out.write(new HttpEndOfContent());
139 } else {
140 session.setAttribute(BODY_REMAINING_BYTES, Integer.valueOf(remaining));
141 }
142
143 break;
144
145 default:
146 throw new HttpException(HttpStatus.CLIENT_ERROR_BAD_REQUEST, "Unknonwn decoder state : " + state);
147 }
148 }
149
150 public void finishDecode(final IoSession session, final ProtocolDecoderOutput out) throws Exception {
151 }
152
153 public void dispose(final IoSession session) throws Exception {
154 }
155
156 private HttpRequestImpl parseHttpRequestHead(final ByteBuffer buffer) {
157
158 final String raw = new String(buffer.array(), 0, buffer.limit());
159 final String[] headersAndBody = RAW_VALUE_PATTERN.split(raw, -1);
160
161 if (headersAndBody.length <= 1) {
162
163 return null;
164 }
165
166 String[] headerFields = HEADERS_BODY_PATTERN.split(headersAndBody[0]);
167 headerFields = ArrayUtil.dropFromEndWhile(headerFields, "");
168
169 final String requestLine = headerFields[0];
170 final Map<String, String> generalHeaders = new HashMap<String, String>();
171
172 for (int i = 1; i < headerFields.length; i++) {
173 final String[] header = HEADER_VALUE_PATTERN.split(headerFields[i]);
174 generalHeaders.put(header[0].toLowerCase(), header[1].trim());
175 }
176
177 final String[] elements = REQUEST_LINE_PATTERN.split(requestLine);
178 final HttpMethod method = HttpMethod.valueOf(elements[0]);
179 final HttpVersion version = HttpVersion.fromString(elements[2]);
180 final String[] pathFrags = QUERY_STRING_PATTERN.split(elements[1]);
181 final String requestedPath = pathFrags[0];
182 final String queryString = pathFrags.length == 2 ? pathFrags[1] : "";
183
184
185 buffer.position(headersAndBody[0].length() + 4);
186
187 return new HttpRequestImpl(version, method, requestedPath, queryString, generalHeaders);
188 }
189 }