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.nio.charset.Charset;
24 import java.util.HashMap;
25 import java.util.Map;
26 import java.util.regex.Pattern;
27
28 import org.apache.mina.codec.ProtocolDecoder;
29 import org.apache.mina.http.api.HttpContentChunk;
30 import org.apache.mina.http.api.HttpEndOfContent;
31 import org.apache.mina.http.api.HttpMethod;
32 import org.apache.mina.http.api.HttpPdu;
33 import org.apache.mina.http.api.HttpVersion;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37
38
39
40
41
42 public class HttpServerDecoder implements ProtocolDecoder<ByteBuffer, HttpPdu, HttpDecoderState> {
43 private static final Logger LOG = LoggerFactory.getLogger(HttpServerDecoder.class);
44
45
46 public static final Pattern REQUEST_LINE_PATTERN = Pattern.compile(" ");
47
48
49 public static final Pattern QUERY_STRING_PATTERN = Pattern.compile("\\?");
50
51
52 public static final Pattern PARAM_STRING_PATTERN = Pattern.compile("\\&|;");
53
54
55 public static final Pattern KEY_VALUE_PATTERN = Pattern.compile("=");
56
57
58 public static final Pattern RAW_VALUE_PATTERN = Pattern.compile("\\r\\n\\r\\n");
59
60
61 public static final Pattern HEADERS_BODY_PATTERN = Pattern.compile("\\r\\n");
62
63
64 public static final Pattern HEADER_VALUE_PATTERN = Pattern.compile(":");
65
66
67 public static final Pattern COOKIE_SEPARATOR_PATTERN = Pattern.compile(";");
68
69
70
71
72 @Override
73 public HttpDecoderState createDecoderState() {
74 return new HttpDecoderState();
75 }
76
77
78
79
80 @Override
81 public HttpPdu decode(ByteBuffer msg, HttpDecoderState context) {
82 LOG.debug("decode : {}", msg);
83 if (msg.remaining() <= 0) {
84 return null;
85 }
86 switch (context.getState()) {
87 case HEAD:
88 LOG.debug("decoding HEAD");
89
90 msg = ByteBuffer.allocate(context.getPartial().remaining() + msg.remaining()).put(context.getPartial())
91 .put(msg);
92 msg.flip();
93
94
95 case NEW:
96 LOG.debug("decoding NEW");
97 HttpRequestImpl rq = parseHttpRequestHead(msg);
98
99 if (rq == null) {
100
101 context.setPartial(ByteBuffer.allocate(msg.remaining()));
102 context.getPartial().put(msg);
103 context.getPartial().flip();
104 } else {
105 return rq;
106 }
107 return null;
108 case BODY:
109 LOG.debug("decoding BODY");
110 int chunkSize = msg.remaining();
111
112 HttpContentChunk chunk = new HttpContentChunk(msg);
113
114 context.setRemainingBytes(context.getRemainingBytes() - chunkSize);
115
116 if (context.getRemainingBytes() <= 0) {
117 LOG.debug("end of HTTP body");
118 context.setState(DecoderState.NEW);
119 context.setRemainingBytes(0);
120 context.setState(DecoderState.DONE);
121 return chunk;
122
123 }
124 break;
125 case DONE:
126 return new HttpEndOfContent();
127 default:
128 throw new IllegalStateException("Unknonwn decoder state : " + context.getState());
129 }
130
131 return null;
132 }
133
134 private HttpRequestImpl parseHttpRequestHead(ByteBuffer buffer) {
135 String raw = new String(buffer.array(), 0, buffer.limit(), Charset.forName("ISO-8859-1"));
136 String[] headersAndBody = RAW_VALUE_PATTERN.split(raw, -1);
137
138 if (headersAndBody.length <= 1) {
139
140 return null;
141 }
142
143 String[] headerFields = HEADERS_BODY_PATTERN.split(headersAndBody[0]);
144 headerFields = ArrayUtil.dropFromEndWhile(headerFields, "");
145
146 String requestLine = headerFields[0];
147 Map<String, String> generalHeaders = new HashMap<String, String>();
148
149 for (int i = 1; i < headerFields.length; i++) {
150 String[] header = HEADER_VALUE_PATTERN.split(headerFields[i]);
151 generalHeaders.put(header[0].toLowerCase(), header[1].trim());
152 }
153
154 String[] elements = REQUEST_LINE_PATTERN.split(requestLine);
155 HttpMethod method = HttpMethod.valueOf(elements[0]);
156 HttpVersion version = HttpVersion.fromString(elements[2]);
157 String[] pathFrags = QUERY_STRING_PATTERN.split(elements[1]);
158 String requestedPath = pathFrags[0];
159
160
161 buffer.position(headersAndBody[0].length() + 4);
162
163 return new HttpRequestImpl(version, method, requestedPath, generalHeaders);
164 }
165
166
167
168
169 @Override
170 public void finishDecode(HttpDecoderState context) {
171
172 }
173 }