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(final IoSession session, final IoBuffer msg, final 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 IoBuffer.allocate(oldBuffer.remaining() + msg.remaining()).put(oldBuffer).put(msg).flip();
87
88
89 case NEW:
90 LOG.debug("decoding NEW");
91 final HttpRequestImpl rq = parseHttpRequestHead(msg.buf());
92
93 if (rq == null) {
94
95 final ByteBuffer partial = ByteBuffer.allocate(msg.remaining());
96 partial.put(msg.buf());
97 partial.flip();
98
99 session.setAttribute(PARTIAL_HEAD_ATT, partial);
100 session.setAttribute(DECODER_STATE_ATT, DecoderState.HEAD);
101 } else {
102 out.write(rq);
103
104 if (rq.getMethod() == HttpMethod.POST || rq.getMethod() == HttpMethod.PUT) {
105 LOG.debug("request with content");
106 session.setAttribute(DECODER_STATE_ATT, DecoderState.BODY);
107
108 final String contentLen = rq.getHeader("content-length");
109
110 if (contentLen != null) {
111 LOG.debug("found content len : {}", contentLen);
112 session.setAttribute(BODY_REMAINING_BYTES, Integer.valueOf(contentLen));
113 } else {
114 throw new HttpException(HttpStatus.CLIENT_ERROR_LENGTH_REQUIRED, "no content length !");
115 }
116 } else {
117 LOG.debug("request without content");
118 session.setAttribute(DECODER_STATE_ATT, DecoderState.NEW);
119 out.write(new HttpEndOfContent());
120 }
121 }
122
123 break;
124
125 case BODY:
126 LOG.debug("decoding BODY: {} bytes", msg.remaining());
127 final int chunkSize = msg.remaining();
128
129 if (chunkSize != 0) {
130 final IoBuffer wb = IoBuffer.allocate(msg.remaining());
131 wb.put(msg);
132 wb.flip();
133 out.write(wb);
134 }
135 msg.position(msg.limit());
136
137 int remaining = (Integer) session.getAttribute(BODY_REMAINING_BYTES);
138 remaining -= chunkSize;
139
140 if (remaining <= 0) {
141 LOG.debug("end of HTTP body");
142 session.setAttribute(DECODER_STATE_ATT, DecoderState.NEW);
143 session.removeAttribute(BODY_REMAINING_BYTES);
144 out.write(new HttpEndOfContent());
145 } else {
146 session.setAttribute(BODY_REMAINING_BYTES, Integer.valueOf(remaining));
147 }
148
149 break;
150
151 default:
152 throw new HttpException(HttpStatus.CLIENT_ERROR_BAD_REQUEST, "Unknonwn decoder state : " + state);
153 }
154 }
155
156 public void finishDecode(final IoSession session, final ProtocolDecoderOutput out) throws Exception {
157 }
158
159 public void dispose(final IoSession session) throws Exception {
160 }
161
162 private HttpRequestImpl parseHttpRequestHead(final ByteBuffer buffer) {
163
164 final String raw = new String(buffer.array(), 0, buffer.limit());
165 final String[] headersAndBody = RAW_VALUE_PATTERN.split(raw, -1);
166
167 if (headersAndBody.length <= 1) {
168
169 return null;
170 }
171
172 String[] headerFields = HEADERS_BODY_PATTERN.split(headersAndBody[0]);
173 headerFields = ArrayUtil.dropFromEndWhile(headerFields, "");
174
175 final String requestLine = headerFields[0];
176 final Map<String, String> generalHeaders = new HashMap<String, String>();
177
178 for (int i = 1; i < headerFields.length; i++) {
179 final String[] header = HEADER_VALUE_PATTERN.split(headerFields[i]);
180 generalHeaders.put(header[0].toLowerCase(), header[1]);
181 }
182
183 final String[] elements = REQUEST_LINE_PATTERN.split(requestLine);
184 final HttpMethod method = HttpMethod.valueOf(elements[0]);
185 final HttpVersion version = HttpVersion.fromString(elements[2]);
186 final String[] pathFrags = QUERY_STRING_PATTERN.split(elements[1]);
187 final String requestedPath = pathFrags[0];
188 final String queryString = pathFrags.length == 2 ? pathFrags[1] : "";
189
190
191 buffer.position(headersAndBody[0].length() + 4);
192
193 return new HttpRequestImpl(version, method, requestedPath, queryString, generalHeaders);
194 }
195 }