getEventHandler(SelectionKey event) {
20 | return this.eventHandler;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/io/utils/IoUtils.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.io.utils;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 | import java.io.Closeable;
7 | import java.io.IOException;
8 |
9 | /**
10 | * @author 李佳明 https://github.com/pkpk1234
11 | * @date 2018-01-2018/1/5
12 | */
13 | public class IoUtils {
14 |
15 | private static final Logger logger = LoggerFactory.getLogger(IoUtils.class);
16 |
17 | /**
18 | * 安静地关闭,不抛出异常
19 | * @param closeable
20 | */
21 | public static void closeQuietly(Closeable closeable) {
22 | if(closeable != null) {
23 | try {
24 | closeable.close();
25 | } catch (IOException e) {
26 | logger.error(e.getMessage(),e);
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/Protocol.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol;
2 |
3 | /**
4 | * @author 李佳明 https://github.com/pkpk1234
5 | * @date 2018-01-2018/1/12
6 | *
7 | * 应用协议接口
8 | */
9 | public interface Protocol {
10 | /**
11 | * 返回协议名
12 | * @return
13 | */
14 | String getName();
15 |
16 | /**
17 | * 返回协议版本
18 | * @return
19 | */
20 | String version();
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/ContentTypeUtil.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http;
2 |
3 |
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.io.IOException;
8 | import java.util.Properties;
9 |
10 | /**
11 | * Content Type工具类
12 | *
13 | * @author 李佳明 https://github.com/pkpk1234
14 | * @date 2018/1/24
15 | */
16 | public class ContentTypeUtil {
17 | private static final Logger LOGGER = LoggerFactory.getLogger(ContentTypeUtil.class);
18 | private static Properties properties;
19 |
20 | private ContentTypeUtil() {
21 | }
22 |
23 | static {
24 | properties
25 | = new Properties();
26 | try {
27 | properties.load(ContentTypeUtil.class
28 | .getClassLoader().getResourceAsStream("mime-mapping.properties"));
29 | } catch (IOException e) {
30 | LOGGER.error("load mime-mapping.properties failed", e);
31 | }
32 | }
33 |
34 | /**
35 | * 通过文件后缀获取到Content-Type
36 | *
37 | * @param filePrefix
38 | * @return
39 | */
40 | public static String getCotentType(String filePrefix) {
41 | return properties.getProperty(filePrefix, "application/octet-stream");
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/HttpConstants.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http;
2 |
3 | /**
4 | * @author 李佳明 https://github.com/pkpk1234
5 | */
6 | public interface HttpConstants {
7 | String KV_SPLITTER = "=";
8 | String CRLF = "\r\n";
9 | String CONTENT_TYPE = "Content-Type";
10 | String CONTENT_LENGTH = "Content-Length";
11 | String CONTENT_ENCODING = "Content-Encoding";
12 | String TRANSFER_ENCODING = "Transfer-Encoding";
13 | String ENCODING_CHUNKED = "chunked";
14 | String ENCODING_IDENTITY = "identity";
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/HttpMessage.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http;
2 |
3 | import com.ljm.server.protocol.http.body.HttpBody;
4 | import com.ljm.server.protocol.http.header.IMessageHeaders;
5 |
6 | import java.util.Optional;
7 |
8 | /**
9 | * @author 李佳明 https://github.com/pkpk1234
10 | * @date 2018-01-2018/1/12
11 | *
12 | * HTTP消息
13 | * 参考 https://tools.ietf.org/html/rfc2616#page-31
14 | *
15 | * generic-message = start-line
16 | * *(message-header CRLF)
17 | * CRLF
18 | * [ message-body ]
19 | *
20 | *
21 | * start-line = Request-Line | Status-Line
22 | */
23 | public interface HttpMessage {
24 | /**
25 | * 获取起始行
26 | *
27 | * @return
28 | */
29 | StartLine getStartLine();
30 |
31 | /**
32 | * 获取HTTP头集合
33 | *
34 | * @return
35 | */
36 | IMessageHeaders getMessageHeaders();
37 |
38 | /**
39 | * 获取HTTP Body
40 | *
41 | * @return
42 | */
43 | Optional getHttpBody();
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/HttpProtocol.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http;
2 |
3 | import com.ljm.server.protocol.Protocol;
4 |
5 | /**
6 | * @author 李佳明 https://github.com/pkpk1234
7 | * @date 2018-01-2018/1/13
8 | */
9 | public class HttpProtocol implements Protocol {
10 | private final String name = "http";
11 | private final String version;
12 |
13 | private HttpProtocol(String version) {
14 | this.version = version;
15 | }
16 |
17 | public static final HttpProtocol VERSION11 = new HttpProtocol("1.1");
18 | public static final HttpProtocol VERSION20 = new HttpProtocol("2.0");
19 |
20 | @Override
21 | public String getName() {
22 | return this.name;
23 | }
24 |
25 | @Override
26 | public String version() {
27 | return this.version;
28 | }
29 |
30 | @Override
31 | public String toString() {
32 | return this.name + "/" + this.version;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/HttpQueryParameter.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http;
2 |
3 | /**
4 | * @author 李佳明 https://github.com/pkpk1234
5 | * @date 2018-01-2018/1/15
6 | */
7 | public class HttpQueryParameter {
8 | private final String name;
9 | private final String value;
10 |
11 | public HttpQueryParameter(String name, String value) {
12 | this.name = name;
13 | this.value = value;
14 | }
15 |
16 | public String getName() {
17 | return name;
18 | }
19 |
20 | public String getValue() {
21 | return value;
22 | }
23 |
24 | @Override
25 | public String toString() {
26 | return "HttpQueryParameter{" +
27 | "name='" + name + '\'' +
28 | ", value='" + value + '\'' +
29 | '}';
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/HttpQueryParameters.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http;
2 |
3 | import com.google.common.collect.LinkedListMultimap;
4 |
5 | import java.util.List;
6 | import java.util.Set;
7 |
8 | /**
9 | * @author 李佳明 https://github.com/pkpk1234
10 | * @date 2018-01-2018/1/15
11 | */
12 | public class HttpQueryParameters {
13 |
14 | private final LinkedListMultimap prametersMultiMap;
15 |
16 | public HttpQueryParameters() {
17 | this.prametersMultiMap = LinkedListMultimap.create();
18 | }
19 |
20 |
21 | public List getQueryParameter(String name) {
22 | return this.prametersMultiMap.get(name);
23 | }
24 |
25 | public HttpQueryParameter getFirstQueryParameter(String name) {
26 | if (this.prametersMultiMap.containsKey(name)) {
27 | return this.prametersMultiMap.get(name).get(0);
28 | }
29 | return null;
30 | }
31 |
32 | public List getAllQueryParameters() {
33 | return this.prametersMultiMap.values();
34 | }
35 |
36 | public void addQueryParameter(HttpQueryParameter httpQueryParameter) {
37 | this.prametersMultiMap.put(httpQueryParameter.getName(), httpQueryParameter);
38 | }
39 |
40 | public void removeQueryParameter(HttpQueryParameter httpQueryParameter) {
41 | this.prametersMultiMap.remove(httpQueryParameter.getName(), httpQueryParameter);
42 | }
43 |
44 | public void removeQueryParameter(String httpQueryParameter) {
45 | this.prametersMultiMap.removeAll(httpQueryParameter);
46 | }
47 |
48 | public boolean hasRequestParameter(String httpQueryParameter) {
49 | return this.prametersMultiMap.containsKey(httpQueryParameter);
50 | }
51 |
52 | public Set getQueryParameterNames() {
53 | return this.prametersMultiMap.keySet();
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/HttpRequestMessage.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http;
2 |
3 | import com.ljm.server.protocol.http.body.HttpBody;
4 | import com.ljm.server.protocol.http.header.IMessageHeaders;
5 |
6 | import java.util.Optional;
7 |
8 | /**
9 | * @author 李佳明 https://github.com/pkpk1234
10 | * @date 2018-01-2018/1/12
11 | *
12 | * HTTP请求
13 | */
14 | public class HttpRequestMessage implements HttpMessage {
15 | private final RequestLine requestLine;
16 | private final IMessageHeaders messageHeaders;
17 | private final Optional httpBody;
18 | private final HttpQueryParameters httpQueryParameters;
19 |
20 | public HttpRequestMessage(RequestLine requestLine, IMessageHeaders messageHeaders, Optional httpBody,
21 | HttpQueryParameters httpQueryParameters) {
22 | this.requestLine = requestLine;
23 | this.messageHeaders = messageHeaders;
24 | this.httpBody = httpBody;
25 | this.httpQueryParameters = httpQueryParameters;
26 | }
27 |
28 | @Override
29 | public StartLine getStartLine() {
30 | return StartLine.class.cast(this.requestLine);
31 | }
32 |
33 | @Override
34 | public IMessageHeaders getMessageHeaders() {
35 | return this.messageHeaders;
36 | }
37 |
38 | @Override
39 | public Optional getHttpBody() {
40 | return this.httpBody;
41 | }
42 |
43 | public RequestLine getRequestLine() {
44 | return requestLine;
45 | }
46 |
47 | public HttpQueryParameters getHttpQueryParameters() {
48 | return httpQueryParameters;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/HttpResponseMessage.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http;
2 |
3 |
4 | import com.ljm.server.protocol.http.body.HttpBody;
5 | import com.ljm.server.protocol.http.header.IMessageHeaders;
6 |
7 | import java.util.Optional;
8 |
9 | /**
10 | * @author 李佳明 https://github.com/pkpk1234
11 | * @date 2018-01-2018/1/12
12 | */
13 | public class HttpResponseMessage implements HttpMessage {
14 | private final ResponseLine responseLine;
15 | private final IMessageHeaders messageHeaders;
16 | private final Optional httpBody;
17 |
18 | public HttpResponseMessage(ResponseLine responseLine,
19 | IMessageHeaders messageHeaders, Optional httpBody) {
20 | this.responseLine = responseLine;
21 | this.messageHeaders = messageHeaders;
22 | this.httpBody = httpBody;
23 | }
24 |
25 | @Override
26 | public StartLine getStartLine() {
27 | return StartLine.class.cast(this.responseLine);
28 | }
29 |
30 | @Override
31 | public IMessageHeaders getMessageHeaders() {
32 | return this.messageHeaders;
33 | }
34 |
35 | @Override
36 | public Optional getHttpBody() {
37 | return this.httpBody;
38 | }
39 |
40 | public ResponseLine getResponseLine() {
41 | return responseLine;
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/RequestLine.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http;
2 |
3 | import java.net.URI;
4 |
5 | /**
6 | * @author 李佳明 https://github.com/pkpk1234
7 | * @date 2018-01-2018/1/13
8 | *
9 | * HTTP Request 请求行
10 | * 参考https://tools.ietf.org/html/rfc2616#page-31
11 | * Request-Line = Method SP Request-URI SP HTTP-Version CRLF
12 | */
13 | public class RequestLine implements StartLine {
14 | private final String method;
15 | private final URI requestURI;
16 | private final String httpVersion;
17 |
18 | public RequestLine(String method, URI requestURI, String httpVersion) {
19 | this.method = method;
20 | this.requestURI = requestURI;
21 | this.httpVersion = httpVersion;
22 | }
23 |
24 | public String getMethod() {
25 | return method;
26 | }
27 |
28 | public URI getRequestURI() {
29 | return requestURI;
30 | }
31 |
32 | @Override
33 | public String getHttpVersion() {
34 | return httpVersion;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/ResponseLine.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http;
2 |
3 | /**
4 | * @author 李佳明 https://github.com/pkpk1234
5 | * @date 2018-01-2018/1/13
6 | *
7 | * 参考 https://tools.ietf.org/html/rfc2616#page-31 Status-Line = HTTP-Version SP
8 | * Status-Code SP Reason-Phrase CRLF
9 | */
10 | public class ResponseLine implements StartLine {
11 | private final String httpVersion;
12 | private final int statusCode;
13 | private final String reason;
14 |
15 | public ResponseLine(String httpVersion, int statusCode, String reason) {
16 | this.httpVersion = httpVersion;
17 | this.statusCode = statusCode;
18 | this.reason = reason;
19 | }
20 |
21 | public int getStatusCode() {
22 | return statusCode;
23 | }
24 |
25 | public String getReason() {
26 | return reason;
27 | }
28 |
29 | public String asString() {
30 | StringBuffer stringBuffer = new StringBuffer();
31 | stringBuffer.append(this.httpVersion).append(" ").append(this.statusCode)
32 | .append(" ").append(this.reason);
33 | return stringBuffer.toString();
34 | }
35 |
36 | @Override
37 | public String getHttpVersion() {
38 | return httpVersion;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/StartLine.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http;
2 |
3 | /**
4 | * @author 李佳明 https://github.com/pkpk1234
5 | * @date 2018-01-2018/1/13
6 | */
7 | public interface StartLine {
8 | /**
9 | * 获取HTTP 版本号
10 | *
11 | * @return
12 | */
13 | String getHttpVersion();
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/body/HttpBody.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.body;
2 |
3 | import com.ljm.server.protocol.http.HttpConstants;
4 |
5 | import java.io.InputStream;
6 | import java.io.UnsupportedEncodingException;
7 |
8 | /**
9 | * @author 李佳明 https://github.com/pkpk1234
10 | * @date 2018-01-2018/1/13
11 | */
12 | public class HttpBody {
13 | private String contentType;
14 | //压缩格式:deflate、gzip...
15 | private String encoding;
16 | private String transferEncoding;
17 | //编码格式:utf-8、gb2312...
18 | private String charSet;
19 | private HttpBodyInputStream inputStream;
20 | //Body段长度
21 | private long contentLength;
22 |
23 | public HttpBody(InputStream inputStream, String transferEncoding) {
24 | this.transferEncoding = transferEncoding;
25 | if (transferEncoding.equals(HttpConstants.ENCODING_CHUNKED)) {
26 | this.inputStream = new HttpBodyInputStream(inputStream, true);
27 | } else {
28 | this.inputStream = new HttpBodyInputStream(inputStream, false);
29 | }
30 |
31 | }
32 |
33 | public String getContentType() {
34 | return contentType;
35 | }
36 |
37 | public void setContentType(String contentType) {
38 | this.contentType = contentType;
39 | }
40 |
41 | public String getEncoding() {
42 | return encoding;
43 | }
44 |
45 | public void setEncoding(String encoding) {
46 | this.encoding = encoding;
47 | }
48 |
49 | public String getCharSet() {
50 | return charSet;
51 | }
52 |
53 | public void setCharSet(String charSet) {
54 | this.charSet = charSet;
55 | }
56 |
57 | public InputStream getInputStream() {
58 | return inputStream;
59 | }
60 |
61 |
62 | public long getContentLength() {
63 | return contentLength;
64 | }
65 |
66 | public void setContentLength(long contentLength) {
67 | this.inputStream.setContentLength(contentLength);
68 | this.contentLength = contentLength;
69 | }
70 |
71 | public String getTransferEncoding() {
72 | return transferEncoding;
73 | }
74 |
75 | public void setTransferEncoding(String transferEncoding) {
76 | this.transferEncoding = transferEncoding;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/body/HttpBodyInputStream.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.body;
2 |
3 | import java.io.IOException;
4 | import java.io.InputStream;
5 |
6 | /**
7 | * 封装Body流
8 | *
9 | * @author 李佳明 https://github.com/pkpk1234
10 | * @date 2018-01-2018/1/30
11 | */
12 | public class HttpBodyInputStream extends InputStream {
13 | //chunked body使用 0\r\n\r\n结尾
14 | private static final char[] TAILER_CHARS = {'0', '\r', '\n', '\r', '\n'};
15 | private final InputStream inputStream;
16 | private long contentLength;
17 | private boolean isChuncked;
18 | private long readed = 0;
19 | private boolean isFinished = false;
20 | private int idx = 0;
21 |
22 | public HttpBodyInputStream(InputStream inputStream, boolean ifChuncked) {
23 | this.inputStream = inputStream;
24 | this.isChuncked = ifChuncked;
25 | }
26 |
27 | @Override
28 | public int read() throws IOException {
29 | if (isChuncked) {
30 | return readChunked();
31 | } else {
32 | return readByte();
33 | }
34 | }
35 |
36 | /**
37 | * 读取chunked body
38 | *
39 | * @return
40 | * @throws IOException
41 | */
42 | private int readChunked() throws IOException {
43 | if (isFinished) {
44 | return -1;
45 | }
46 | int i = this.inputStream.read();
47 | if (i == -1) {
48 | return i;
49 | } else {
50 | if (idx == TAILER_CHARS.length - 1) {
51 | isFinished = true;
52 | } else if (TAILER_CHARS[idx++] != (char) i) {
53 | idx = 0;
54 | }
55 | }
56 | return i;
57 | }
58 |
59 | /**
60 | * 读取非chunked body
61 | *
62 | * @return
63 | * @throws IOException
64 | */
65 | private int readByte() throws IOException {
66 | readed++;
67 | if (readed > contentLength) {
68 | return -1;
69 | } else {
70 | return this.inputStream.read();
71 | }
72 | }
73 |
74 | public long getContentLength() {
75 | return contentLength;
76 | }
77 |
78 | public void setContentLength(long contentLength) {
79 | this.contentLength = contentLength;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/handler/AbstractHttpEventHandler.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.handler;
2 |
3 | import com.ljm.server.event.handler.AbstractEventHandler;
4 | import com.ljm.server.event.handler.HandlerException;
5 | import com.ljm.server.io.connection.Connection;
6 | import com.ljm.server.io.connection.socket.SocketConnection;
7 | import com.ljm.server.protocol.http.HttpRequestMessage;
8 | import com.ljm.server.protocol.http.HttpResponseMessage;
9 | import org.apache.commons.io.IOUtils;
10 |
11 | import java.io.IOException;
12 |
13 | /**
14 | * @author 李佳明 https://github.com/pkpk1234
15 | * @date 2018-01-2018/1/22
16 | */
17 | public abstract class AbstractHttpEventHandler extends AbstractEventHandler {
18 | @Override
19 | protected void doHandle(Connection connection) {
20 | //从输入中构造出HTTP请求对象,Body的内容是延迟读取
21 | HttpRequestMessage requestMessage = doParserRequestMessage(connection);
22 | //构造HTTP响应对象
23 | HttpResponseMessage responseMessage = doGenerateResponseMessage(requestMessage);
24 | try {
25 | //输出响应到客户端
26 | doTransferToClient(responseMessage, connection);
27 | } catch (IOException e) {
28 | throw new HandlerException(e);
29 | } finally {
30 | //完成响应后,关闭Socket
31 | if (connection instanceof SocketConnection) {
32 | IOUtils.closeQuietly(((SocketConnection) connection).getSocket());
33 | }
34 | }
35 |
36 | }
37 |
38 | /**
39 | * 通过输入构造HttpRequestMessage
40 | *
41 | * @param connection
42 | * @return
43 | */
44 | protected abstract HttpRequestMessage doParserRequestMessage(Connection connection);
45 |
46 | /**
47 | * 根据HttpRequestMessage生成HttpResponseMessage
48 | *
49 | * @param httpRequestMessage
50 | * @return
51 | */
52 | protected abstract HttpResponseMessage doGenerateResponseMessage(
53 | HttpRequestMessage httpRequestMessage);
54 |
55 | /**
56 | * 写入HttpResponseMessage到客户端
57 | *
58 | * @param responseMessage
59 | * @param connection
60 | * @throws IOException
61 | */
62 | protected abstract void doTransferToClient(HttpResponseMessage responseMessage,
63 | Connection connection) throws IOException;
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/handler/HttpStaticResourceEventHandler.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.handler;
2 |
3 | import com.ljm.server.event.handler.HandlerException;
4 | import com.ljm.server.io.connection.Connection;
5 | import com.ljm.server.protocol.http.*;
6 | import com.ljm.server.protocol.http.body.HttpBody;
7 | import com.ljm.server.protocol.http.header.HttpHeader;
8 | import com.ljm.server.protocol.http.header.HttpMessageHeaders;
9 | import com.ljm.server.protocol.http.parser.AbstractHttpRequestMessageParser;
10 | import com.ljm.server.protocol.http.response.HttpResponseConstants;
11 | import com.ljm.server.protocol.http.response.HttpResponseMessageWriter;
12 | import com.ljm.server.protocol.http.response.ResponseLineConstants;
13 |
14 | import javax.activation.MimetypesFileTypeMap;
15 | import java.io.File;
16 | import java.io.FileInputStream;
17 | import java.io.FileNotFoundException;
18 | import java.io.IOException;
19 | import java.nio.file.Files;
20 | import java.nio.file.Path;
21 | import java.nio.file.Paths;
22 | import java.util.Optional;
23 |
24 | /**
25 | * @author 李佳明 https://github.com/pkpk1234
26 | * @date 2018-01-2018/1/8
27 | */
28 | public class HttpStaticResourceEventHandler extends AbstractHttpEventHandler {
29 |
30 | private final String docBase;
31 | private final AbstractHttpRequestMessageParser httpRequestMessageParser;
32 |
33 | public HttpStaticResourceEventHandler(String docBase,
34 | AbstractHttpRequestMessageParser httpRequestMessageParser) {
35 | this.docBase = docBase;
36 | this.httpRequestMessageParser = httpRequestMessageParser;
37 | }
38 |
39 | @Override
40 | protected HttpRequestMessage doParserRequestMessage(Connection connection) {
41 | try {
42 | HttpRequestMessage httpRequestMessage = httpRequestMessageParser
43 | .parse(connection.getInputStream());
44 | return httpRequestMessage;
45 | } catch (IOException e) {
46 | throw new HandlerException(e);
47 | }
48 | }
49 |
50 | @Override
51 | protected HttpResponseMessage doGenerateResponseMessage(
52 | HttpRequestMessage httpRequestMessage) {
53 | String path = httpRequestMessage.getRequestLine().getRequestURI().getPath();
54 | Path filePath = Paths.get(docBase, path);
55 | //目录、无法读取的文件都返回404
56 | if (Files.isDirectory(filePath) || !Files.isReadable(filePath)) {
57 | return HttpResponseConstants.HTTP_404;
58 | } else {
59 | ResponseLine ok = ResponseLineConstants.RES_200;
60 | HttpMessageHeaders headers = HttpMessageHeaders.newBuilder()
61 | .addHeader("status", "200").build();
62 | HttpBody httpBody = null;
63 | try {
64 | setContentType(filePath, headers);
65 | File file = filePath.toFile();
66 | httpBody = new HttpBody(new FileInputStream(file), HttpConstants.ENCODING_IDENTITY);
67 | httpBody.setContentLength(file.length());
68 | } catch (FileNotFoundException e) {
69 | return HttpResponseConstants.HTTP_404;
70 | }
71 | HttpResponseMessage httpResponseMessage = new HttpResponseMessage(ok, headers,
72 | Optional.ofNullable(httpBody));
73 | return httpResponseMessage;
74 | }
75 |
76 | }
77 |
78 | /**
79 | * 根据文件后缀设置文件Content-Type
80 | *
81 | * @param filePath
82 | * @param headers
83 | * @throws IOException
84 | */
85 | private void setContentType(Path filePath, HttpMessageHeaders headers) {
86 | String fileName = filePath.toFile().getName();
87 | if (fileName.contains(".")) {
88 | int idx = fileName.lastIndexOf(".");
89 | fileName = fileName.substring(idx);
90 | }
91 | String contentType = ContentTypeUtil.getCotentType(fileName);
92 | headers.addHeader(new HttpHeader("Content-Type", contentType));
93 | }
94 |
95 | @Override
96 | protected void doTransferToClient(HttpResponseMessage responseMessage,
97 | Connection connection) throws IOException {
98 | HttpResponseMessageWriter httpResponseMessageWriter = new HttpResponseMessageWriter();
99 | httpResponseMessageWriter.write(responseMessage, connection);
100 | }
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/header/HttpHeader.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.header;
2 |
3 | /**
4 | * @author 李佳明 https://github.com/pkpk1234
5 | * @date 2018-01-2018/1/13
6 | *
7 | * HTTP 头
8 | */
9 | public class HttpHeader {
10 | private final String name;
11 | private final String value;
12 |
13 | public HttpHeader(String name, String value) {
14 | this.name = name;
15 | this.value = value;
16 | }
17 |
18 | public String getName() {
19 | return name;
20 | }
21 |
22 | public String getValue() {
23 | return value;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/header/HttpMessageHeaders.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.header;
2 |
3 | import com.google.common.collect.LinkedListMultimap;
4 |
5 | import java.util.List;
6 | import java.util.Set;
7 |
8 | /**
9 | * @author 李佳明 https://github.com/pkpk1234
10 | * @date 2018-01-2018/1/13
11 | */
12 | public class HttpMessageHeaders implements IMessageHeaders {
13 |
14 | private final LinkedListMultimap headersMultiMap;
15 |
16 | public HttpMessageHeaders() {
17 | this.headersMultiMap = LinkedListMultimap.create();
18 | }
19 |
20 | private HttpMessageHeaders(Builder builder) {
21 | headersMultiMap = builder.headersMultiMap;
22 | }
23 |
24 | public static Builder newBuilder() {
25 | return new Builder();
26 | }
27 |
28 | @Override
29 | public List getHeaders(String headerName) {
30 | return this.headersMultiMap.get(headerName);
31 | }
32 |
33 | @Override
34 | public HttpHeader getFirstHeader(String headerName) {
35 | List headers = this.headersMultiMap.get(headerName);
36 | if (headers.isEmpty()) {
37 | return null;
38 | }
39 | else {
40 | return headers.get(0);
41 | }
42 | }
43 |
44 | @Override
45 | public List getAllHeaders() {
46 | return this.headersMultiMap.values();
47 | }
48 |
49 | @Override
50 | public void addHeader(HttpHeader httpHeader) {
51 | this.headersMultiMap.put(httpHeader.getName(), httpHeader);
52 | }
53 |
54 | @Override
55 | public void removeHeader(HttpHeader httpHeader) {
56 | this.headersMultiMap.remove(httpHeader.getName(), httpHeader);
57 | }
58 |
59 | @Override
60 | public void removeHeaders(String headerName) {
61 | this.headersMultiMap.removeAll(headerName);
62 | }
63 |
64 | @Override
65 | public boolean hasHeader(String headerName) {
66 | return this.headersMultiMap.containsKey(headerName);
67 | }
68 |
69 | @Override
70 | public Set getHeaderNames() {
71 | return this.headersMultiMap.keySet();
72 | }
73 |
74 | @Override
75 | public String asString() {
76 | StringBuffer stringBuffer = new StringBuffer();
77 | for (HttpHeader header : this.headersMultiMap.values()) {
78 | stringBuffer.append(header.getName()).append(":").append(header.getValue())
79 | .append("\r\n");
80 | }
81 | return stringBuffer.toString();
82 | }
83 |
84 | public static final class Builder {
85 | private final LinkedListMultimap headersMultiMap;
86 |
87 | private Builder() {
88 | this.headersMultiMap = LinkedListMultimap.create();
89 | }
90 |
91 | public Builder addHeader(String name, String value) {
92 | this.headersMultiMap.put(name, new HttpHeader(name, value));
93 | return this;
94 | }
95 |
96 | public HttpMessageHeaders build() {
97 | return new HttpMessageHeaders(this);
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/header/IMessageHeaders.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.header;
2 |
3 | import java.util.List;
4 | import java.util.Set;
5 |
6 | /**
7 | * @author 李佳明 https://github.com/pkpk1234
8 | * @date 2018-01-2018/1/13
9 | */
10 | public interface IMessageHeaders {
11 |
12 | /**
13 | * 获取名字为headerName的HttpHeader列表
14 | *
15 | * @param headerName
16 | * @return
17 | */
18 | List getHeaders(String headerName);
19 |
20 | /**
21 | * 获取第一个名字为headerName的HttpHeader
22 | *
23 | * @param headerName
24 | * @return
25 | */
26 | HttpHeader getFirstHeader(String headerName);
27 |
28 | /**
29 | * 获取所有的HttpHeader
30 | *
31 | * @return
32 | */
33 | List getAllHeaders();
34 |
35 | /**
36 | * 添加HttpHeader到集合中
37 | *
38 | * @param httpHeader
39 | */
40 | void addHeader(HttpHeader httpHeader);
41 |
42 | /**
43 | * 从集合中移除HttpHeader
44 | *
45 | * @param httpHeader
46 | */
47 | void removeHeader(HttpHeader httpHeader);
48 |
49 | /**
50 | * 从集合中移除名字为headerName的所有HttpHeader
51 | *
52 | * @param headerName
53 | */
54 | void removeHeaders(String headerName);
55 |
56 | /**
57 | * 判断集合中是否包含名字为headerName的HttpHeader
58 | *
59 | * @param headerName
60 | * @return
61 | */
62 | boolean hasHeader(String headerName);
63 |
64 | /**
65 | * 返回所有HttpHeader的名字,并去重
66 | *
67 | * @return
68 | */
69 | Set getHeaderNames();
70 |
71 | /**
72 | * 将Headers集合中的键值对转换为HTTP协议中规定的字符串格式:
73 | * key:value CRLF
74 | * ...
75 | *
76 | * @return
77 | */
78 | String asString();
79 | }
80 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/parser/AbstractHttpRequestMessageParser.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.parser;
2 |
3 | import com.ljm.server.protocol.http.HttpQueryParameters;
4 | import com.ljm.server.protocol.http.HttpRequestMessage;
5 | import com.ljm.server.protocol.http.RequestLine;
6 | import com.ljm.server.protocol.http.body.HttpBody;
7 | import com.ljm.server.protocol.http.header.IMessageHeaders;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 |
11 | import java.io.ByteArrayOutputStream;
12 | import java.io.IOException;
13 | import java.io.InputStream;
14 | import java.util.Optional;
15 |
16 | /**
17 | * @author 李佳明 https://github.com/pkpk1234
18 | * @date 2018-01-2018/1/14
19 | */
20 | public abstract class AbstractHttpRequestMessageParser implements HttpRequestMessageParser {
21 | private static final Logger LOGGER = LoggerFactory.getLogger(AbstractHttpRequestMessageParser.class);
22 |
23 | /**
24 | * 定义parse流程
25 | *
26 | * @return
27 | */
28 | @Override
29 | public HttpRequestMessage parse(InputStream inputStream) throws IOException {
30 | //1.设置上下文:设置是否有body、body之前byte数组,以及body之前byte数组长度到上下文中
31 | getAndSetBytesBeforeBodyToContext(inputStream);
32 | //2.解析构造RequestLine
33 | RequestLine requestLine = parseRequestLine();
34 | //3.解析构造QueryParameters
35 | HttpQueryParameters httpQueryParameters = parseHttpQueryParameters();
36 | //4.解析构造HTTP请求头
37 | IMessageHeaders messageHeaders = parseRequestHeaders();
38 | //5.解析构造HTTP Body,如果有个的话
39 | Optional httpBody = parseRequestBody();
40 |
41 | HttpRequestMessage httpRequestMessage = new HttpRequestMessage(requestLine, messageHeaders, httpBody, httpQueryParameters);
42 | return httpRequestMessage;
43 | }
44 |
45 | /**
46 | * 读取请求发送的数据,并保存为byte数组设置到解析上下文中
47 | *
48 | * @param inputStream
49 | * @throws IOException
50 | */
51 | private void getAndSetBytesBeforeBodyToContext(InputStream inputStream) throws IOException {
52 | byte[] bytes = copyRequestBytesBeforeBody(inputStream);
53 | HttpParserContext.setHttpMessageBytes(bytes);
54 | HttpParserContext.setBytesLengthBeforeBody(bytes.length);
55 | HttpParserContext.setInputStream(inputStream);
56 | }
57 |
58 | /**
59 | * 解析并构建RequestLine
60 | *
61 | * @return
62 | */
63 | protected abstract RequestLine parseRequestLine();
64 |
65 | /**
66 | * 解析并构建HTTP请求Headers集合
67 | *
68 | * @return
69 | */
70 | protected abstract IMessageHeaders parseRequestHeaders();
71 |
72 | /**
73 | * 解析并构建HTTP 请求Body
74 | *
75 | * @return
76 | */
77 | protected abstract Optional parseRequestBody();
78 |
79 | /**
80 | * 解析并构建QueryParameter集合
81 | *
82 | * @return
83 | */
84 | protected abstract HttpQueryParameters parseHttpQueryParameters();
85 |
86 | /**
87 | * 构造body(如果有)之前的字节数组
88 | *
89 | * @param inputStream
90 | * @return
91 | * @throws IOException
92 | */
93 | private byte[] copyRequestBytesBeforeBody(InputStream inputStream) throws IOException {
94 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(inputStream.available());
95 | int i = -1;
96 | byte[] temp = new byte[3];
97 | while ((i = inputStream.read()) != -1) {
98 | byteArrayOutputStream.write(i);
99 | if ((char) i == '\r') {
100 | int len = inputStream.read(temp, 0, temp.length);
101 | byteArrayOutputStream.write(temp, 0, len);
102 | if ("\n\r\n".equals(new String(temp))) {
103 | break;
104 | }
105 | }
106 | }
107 | return byteArrayOutputStream.toByteArray();
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/parser/BodyInfo.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.parser;
2 |
3 | /**
4 | * @author 李佳明 https://github.com/pkpk1234
5 | * @date 2018/1/21
6 | */
7 | public class BodyInfo {
8 | private String contentType;
9 | //Content-Type中的Charset:如UTF-8,GB2312等
10 | private String charset;
11 | //Content-Encoding:压缩格式,如gzip、deflate等
12 | private String encoding;
13 | private boolean hasBody;
14 | private long contentLength;
15 | private String transferEncoding;
16 |
17 | public BodyInfo() {
18 | }
19 |
20 | public BodyInfo(String contentType, String encoding, boolean hasBody, int contentLength) {
21 | this.contentType = contentType;
22 | this.charset = encoding;
23 | this.hasBody = hasBody;
24 | this.contentLength = contentLength;
25 | }
26 |
27 | public String getContentType() {
28 | return contentType;
29 | }
30 |
31 | public void setContentType(String contentType) {
32 | this.contentType = contentType;
33 | }
34 |
35 | public String getCharset() {
36 | return charset;
37 | }
38 |
39 | public void setCharset(String charset) {
40 | this.charset = charset;
41 | }
42 |
43 | public boolean isHasBody() {
44 | return hasBody;
45 | }
46 |
47 | public void setHasBody(boolean hasBody) {
48 | this.hasBody = hasBody;
49 | }
50 |
51 | public long getContentLength() {
52 | return contentLength;
53 | }
54 |
55 | public void setContentLength(long contentLength) {
56 | this.contentLength = contentLength;
57 | }
58 |
59 | public String getEncoding() {
60 | return encoding;
61 | }
62 |
63 | public void setEncoding(String encoding) {
64 | this.encoding = encoding;
65 | }
66 |
67 | public String getTransferEncoding() {
68 | return transferEncoding;
69 | }
70 |
71 | public void setTransferEncoding(String transferEncoding) {
72 | this.transferEncoding = transferEncoding;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/parser/DefaultHttpBodyParser.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.parser;
2 |
3 | import com.ljm.server.protocol.http.body.HttpBody;
4 | import org.apache.commons.lang3.StringUtils;
5 |
6 | import java.io.InputStream;
7 |
8 | /**
9 | * @author 李佳明 https://github.com/pkpk1234
10 | * @date 2018-01-2018/1/21
11 | */
12 | public class DefaultHttpBodyParser implements HttpBodyParser {
13 | @Override
14 | public HttpBody parse() {
15 | long contentLength = HttpParserContext.getBodyInfo().getContentLength();
16 | InputStream inputStream = HttpParserContext.getInputStream();
17 | String contentType = HttpParserContext.getContentType();
18 | String encoding = HttpParserContext.getEncoding();
19 | String charset = getCharset(contentType);
20 | String transferEncoding = HttpParserContext.getTransferEncoding();
21 | HttpBody httpBody =
22 | new HttpBody(inputStream, transferEncoding);
23 | httpBody.setCharSet(charset);
24 | httpBody.setEncoding(encoding);
25 | httpBody.setContentLength(contentLength);
26 | return httpBody;
27 | }
28 |
29 | /**
30 | * 获取encoding
31 | * 例如:Content-type: application/json; charset=utf-8
32 | *
33 | * @param contentType
34 | * @return
35 | */
36 | private String getCharset(String contentType) {
37 | String charset = "utf-8";
38 | if (StringUtils.isNotBlank(contentType) && contentType.contains(";")) {
39 | charset = contentType.split(";")[1].trim().replace("charset=", "");
40 | }
41 | return charset;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/parser/DefaultHttpHeaderParser.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.parser;
2 |
3 | import com.ljm.server.protocol.http.HttpConstants;
4 | import com.ljm.server.protocol.http.header.HttpHeader;
5 | import com.ljm.server.protocol.http.header.HttpMessageHeaders;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 |
9 | import java.io.UnsupportedEncodingException;
10 |
11 | import static com.ljm.server.protocol.http.HttpConstants.*;
12 |
13 | /**
14 | * @author 李佳明 https://github.com/pkpk1234
15 | * @date 2018-01-2018/1/16
16 | */
17 | public class DefaultHttpHeaderParser implements HttpHeaderParser {
18 | private static final String SPLITTER = ":";
19 | private static final Logger LOGGER = LoggerFactory.getLogger(DefaultHttpHeaderParser.class);
20 |
21 | @Override
22 | public HttpMessageHeaders parse() {
23 | try {
24 | String httpText = getHttpTextFromContext();
25 | HttpMessageHeaders httpMessageHeaders = doParseHttpMessageHeaders(httpText);
26 | setHasBody(httpMessageHeaders);
27 | return httpMessageHeaders;
28 | } catch (UnsupportedEncodingException e) {
29 | throw new ParserException("Unsupported Encoding", e);
30 | }
31 | }
32 |
33 | /**
34 | * 从上下文获取bytes并转换为String
35 | *
36 | * @return
37 | * @throws UnsupportedEncodingException
38 | */
39 | private String getHttpTextFromContext() throws UnsupportedEncodingException {
40 | byte[] bytes = HttpParserContext.getHttpMessageBytes();
41 | return new String(bytes, "utf-8");
42 | }
43 |
44 | /**
45 | * 解析Body之前的文本构建HttpHeader,并保存到HttpMessageHeaders中
46 | *
47 | * @param httpText
48 | * @return
49 | */
50 | private HttpMessageHeaders doParseHttpMessageHeaders(String httpText) {
51 | HttpMessageHeaders httpMessageHeaders = new HttpMessageHeaders();
52 | String[] lines = httpText.split(CRLF);
53 | //跳过第一行
54 | for (int i = 1; i < lines.length; i++) {
55 | String keyValue = lines[i];
56 | if ("".equals(keyValue)) {
57 | break;
58 | }
59 | String[] temp = keyValue.split(SPLITTER);
60 | if (temp.length == 2) {
61 | httpMessageHeaders.addHeader(new HttpHeader(temp[0], temp[1].trim()));
62 | }
63 | }
64 | return httpMessageHeaders;
65 | }
66 |
67 | /**
68 | * 设置报文是否包含Body到上下文中
69 | */
70 | private void setHasBody(HttpMessageHeaders httpMessageHeaders) {
71 | if (httpMessageHeaders.hasHeader(CONTENT_LENGTH)
72 | ) {
73 | HttpParserContext.setHasBody(true);
74 | if (httpMessageHeaders.hasHeader(CONTENT_LENGTH)) {
75 | HttpParserContext.getBodyInfo()
76 | .setContentLength(Integer.valueOf(httpMessageHeaders.getFirstHeader
77 | (CONTENT_LENGTH).getValue()));
78 | }
79 | if (httpMessageHeaders.hasHeader(CONTENT_ENCODING)) {
80 | HttpParserContext.setEncoding(httpMessageHeaders.getFirstHeader
81 | (CONTENT_ENCODING).getValue());
82 | }
83 |
84 | } else if ((httpMessageHeaders.hasHeader(TRANSFER_ENCODING)
85 | && HttpConstants.ENCODING_CHUNKED.equals(httpMessageHeaders.getFirstHeader(TRANSFER_ENCODING).getValue()))) {
86 | HttpParserContext.setHasBody(true);
87 | if (httpMessageHeaders.hasHeader(TRANSFER_ENCODING)) {
88 | HttpParserContext.setTransferEncoding(httpMessageHeaders.getFirstHeader
89 | (TRANSFER_ENCODING).getValue());
90 | }
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/parser/DefaultHttpQueryParameterParser.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.parser;
2 |
3 | import com.ljm.server.protocol.http.HttpQueryParameter;
4 | import com.ljm.server.protocol.http.HttpQueryParameters;
5 |
6 | import static com.ljm.server.protocol.http.HttpConstants.KV_SPLITTER;
7 |
8 | /**
9 | * @author 李佳明 https://github.com/pkpk1234
10 | * @date 2018-01-2018/1/15
11 | */
12 | public class DefaultHttpQueryParameterParser implements HttpQueryParameterParser {
13 | private final HttpQueryParameters httpQueryParameters;
14 | private static final String SPLITTER = "&";
15 |
16 | public DefaultHttpQueryParameterParser() {
17 | this.httpQueryParameters = new HttpQueryParameters();
18 | }
19 |
20 | @Override
21 | public HttpQueryParameters parse() {
22 | String queryString = HttpParserContext.getRequestQueryString();
23 | if (queryString != null) {
24 | String[] keyValues = queryString.split(SPLITTER);
25 | for (String keyValue : keyValues) {
26 | if (keyValue.contains(KV_SPLITTER)) {
27 | String[] temp = keyValue.split(KV_SPLITTER);
28 | if (temp.length == 2) {
29 | this.httpQueryParameters
30 | .addQueryParameter(new HttpQueryParameter(temp[0], temp[1]));
31 | }
32 |
33 | }
34 | }
35 | }
36 | return this.httpQueryParameters;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/parser/DefaultHttpRequestLineParser.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.parser;
2 |
3 | import com.ljm.server.protocol.http.RequestLine;
4 |
5 | import java.io.UnsupportedEncodingException;
6 | import java.net.URI;
7 |
8 | import static com.ljm.server.protocol.http.HttpConstants.*;
9 | /**
10 | * @author 李佳明 https://github.com/pkpk1234
11 | * @date 2018-01-2018/1/14
12 | *
13 | * Method SP Request-URI SP HTTP-Version CRLF
14 | */
15 | public class DefaultHttpRequestLineParser implements HttpRequestLineParser {
16 | private static final String SPLITTER = "\\s+";
17 |
18 | @Override
19 | public RequestLine parse() {
20 | String exceptionMsg = "startline format illegal:";
21 | try {
22 | byte[] bytes = HttpParserContext.getHttpMessageBytes();
23 | String httpString = new String(bytes, "utf-8");
24 | //按CRLF将字符串拆分为长度为2的数组,取第一个值即为startLine
25 | String startLine = httpString.split(CRLF, 2)[0];
26 | //去掉末尾的CRLF和空格,转化为Method SP Request-URI SP HTTP-Version
27 | String str = startLine.replaceAll(CRLF, "").trim();
28 | String[] parts = str.split(SPLITTER);
29 | //数组格式{Method,Request-URI,HTTP-Version}
30 | if (parts.length == 3) {
31 | String method = parts[0];
32 | URI uri = URI.create(parts[1]);
33 | HttpParserContext.setRequestQueryString(uri.getQuery());
34 | String httpVersion = parts[2];
35 | return new RequestLine(method, uri, httpVersion);
36 | }
37 | exceptionMsg += startLine;
38 | } catch (UnsupportedEncodingException e) {
39 | throw new ParserException("Unsupported Encoding", e);
40 | }
41 | //如果不满足RequestLine的格式,抛出异常
42 | throw new ParserException(exceptionMsg);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/parser/DefaultHttpRequestMessageParser.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.parser;
2 |
3 | import com.ljm.server.protocol.http.HttpQueryParameters;
4 | import com.ljm.server.protocol.http.RequestLine;
5 | import com.ljm.server.protocol.http.body.HttpBody;
6 | import com.ljm.server.protocol.http.header.HttpMessageHeaders;
7 | import com.ljm.server.protocol.http.header.IMessageHeaders;
8 |
9 | import java.util.Optional;
10 |
11 | import static com.ljm.server.protocol.http.HttpConstants.CONTENT_TYPE;
12 |
13 | /**
14 | * @author 李佳明 https://github.com/pkpk1234
15 | * @date 2018-01-2018/1/14
16 | */
17 | public class DefaultHttpRequestMessageParser extends AbstractHttpRequestMessageParser {
18 | private final HttpRequestLineParser httpRequestLineParser;
19 | private final HttpQueryParameterParser httpQueryParameterParser;
20 | private final HttpHeaderParser httpHeaderParser;
21 | private final HttpBodyParser httpBodyParser;
22 |
23 | public DefaultHttpRequestMessageParser(HttpRequestLineParser httpRequestLineParser,
24 | HttpQueryParameterParser httpQueryParameterParser,
25 | HttpHeaderParser httpHeaderParser,
26 | HttpBodyParser httpBodyParser) {
27 | this.httpRequestLineParser = httpRequestLineParser;
28 | this.httpQueryParameterParser = httpQueryParameterParser;
29 | this.httpHeaderParser = httpHeaderParser;
30 | this.httpBodyParser = httpBodyParser;
31 | }
32 |
33 | @Override
34 | protected RequestLine parseRequestLine() {
35 | RequestLine requestLine = this.httpRequestLineParser.parse();
36 | HttpParserContext.setHttpMethod(requestLine.getMethod());
37 | HttpParserContext.setRequestQueryString(requestLine.getRequestURI().getQuery());
38 | return requestLine;
39 | }
40 |
41 | @Override
42 | protected HttpQueryParameters parseHttpQueryParameters() {
43 | return this.httpQueryParameterParser.parse();
44 | }
45 |
46 | @Override
47 | protected IMessageHeaders parseRequestHeaders() {
48 | HttpMessageHeaders httpMessageHeaders = this.httpHeaderParser.parse();
49 | if (httpMessageHeaders.hasHeader(CONTENT_TYPE)) {
50 | HttpParserContext.setContentType(httpMessageHeaders.getFirstHeader(CONTENT_TYPE).getValue());
51 | }
52 | return httpMessageHeaders;
53 | }
54 |
55 | @Override
56 | protected Optional parseRequestBody() {
57 | if (isHasBodyMethod()) {
58 | HttpBody httpBody = this.httpBodyParser.parse();
59 | return Optional.ofNullable(httpBody);
60 | }
61 | return Optional.empty();
62 | }
63 |
64 | /**
65 | * 判断HTTP请求是否有Body,只支持POST和PUT
66 | * @return
67 | */
68 | private boolean isHasBodyMethod() {
69 | return ("POST".equals(HttpParserContext.getHttpMethod())
70 | || "PUT".equals(HttpParserContext.getHttpMethod()))
71 | && HttpParserContext.getHasBody();
72 | }
73 |
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/parser/HttpBodyParser.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.parser;
2 |
3 | import com.ljm.server.protocol.http.body.HttpBody;
4 |
5 | /**
6 | * @author 李佳明 https://github.com/pkpk1234
7 | * @date 2018-01-2018/1/14
8 | */
9 | public interface HttpBodyParser {
10 | /**
11 | * 解析并构建HttpBody对象
12 | *
13 | * @return
14 | */
15 | HttpBody parse();
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/parser/HttpHeaderParser.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.parser;
2 |
3 | import com.ljm.server.protocol.http.header.HttpMessageHeaders;
4 |
5 | /**
6 | * @author 李佳明 https://github.com/pkpk1234
7 | * @date 2018-01-2018/1/14
8 | */
9 | public interface HttpHeaderParser {
10 | /**
11 | * 解析并返回HttpMessageHeader集合
12 | *
13 | * @return
14 | */
15 | HttpMessageHeaders parse();
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/parser/HttpParserContext.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.parser;
2 |
3 | import com.ljm.server.protocol.http.HttpConstants;
4 | import org.apache.commons.lang3.StringUtils;
5 |
6 | import java.io.InputStream;
7 |
8 | /**
9 | * HTTP Message解析上下文
10 | *
11 | * @author 李佳明 https://github.com/pkpk1234
12 | */
13 | public class HttpParserContext {
14 |
15 | private static final ThreadLocal HTTP_MESSAGE_BYTES = new ThreadLocal<>();
16 | private static final ThreadLocal REQUEST_QUERY_STRING = new ThreadLocal<>();
17 | private static final ThreadLocal BODY_INFO = ThreadLocal.withInitial(() -> new BodyInfo());
18 | private static final ThreadLocal BYTES_LENGTH_BEFORE_BODY = new ThreadLocal<>();
19 | private static final ThreadLocal HTTP_METHOD = new ThreadLocal<>();
20 | private static final ThreadLocal INPUT_STREAM = new ThreadLocal<>();
21 |
22 | public static byte[] getHttpMessageBytes() {
23 | return HTTP_MESSAGE_BYTES.get();
24 | }
25 |
26 | public static void setHttpMessageBytes(byte[] iHttpMessageBytes) {
27 | HTTP_MESSAGE_BYTES.set(iHttpMessageBytes);
28 | }
29 |
30 | public static String getRequestQueryString() {
31 | return REQUEST_QUERY_STRING.get();
32 | }
33 |
34 | public static void setRequestQueryString(String iRequestQueryString) {
35 | REQUEST_QUERY_STRING.set(iRequestQueryString);
36 | }
37 |
38 | public static boolean getHasBody() {
39 | return BODY_INFO.get().isHasBody();
40 | }
41 |
42 | public static void setHasBody(boolean iHasBody) {
43 | BODY_INFO.get().setHasBody(iHasBody);
44 | }
45 |
46 | public static int getBytesLengthBeforeBody() {
47 | return BYTES_LENGTH_BEFORE_BODY.get();
48 | }
49 |
50 | public static void setBytesLengthBeforeBody(int iBytesLengthBeforeBody) {
51 | BYTES_LENGTH_BEFORE_BODY.set(iBytesLengthBeforeBody);
52 | }
53 |
54 | public static String getContentType() {
55 | return BODY_INFO.get().getContentType();
56 | }
57 |
58 | public static void setContentType(String contentType) {
59 | BODY_INFO.get().setContentType(contentType);
60 | }
61 |
62 | public static String getHttpMethod() {
63 | return HTTP_METHOD.get();
64 | }
65 |
66 | public static void setHttpMethod(String method) {
67 | HTTP_METHOD.set(method);
68 | }
69 |
70 | public static String getCharset() {
71 | return BODY_INFO.get().getCharset();
72 | }
73 |
74 | public static void setCharset(String charset) {
75 | BODY_INFO.get().setCharset(charset);
76 | }
77 |
78 | public static String getEncoding() {
79 | return BODY_INFO.get().getEncoding();
80 | }
81 |
82 | public static void setEncoding(String encoding) {
83 | BODY_INFO.get().setEncoding(encoding);
84 | }
85 |
86 | public static InputStream getInputStream() {
87 | return INPUT_STREAM.get();
88 | }
89 |
90 | public static void setInputStream(InputStream inputStream) {
91 | INPUT_STREAM.set(inputStream);
92 | }
93 |
94 | public static BodyInfo getBodyInfo() {
95 | return BODY_INFO.get();
96 | }
97 |
98 | public static void setBodyInfo(BodyInfo bodyInfo) {
99 | BODY_INFO.set(bodyInfo);
100 | }
101 |
102 | public static void setTransferEncoding(String transferEncoding) {
103 | BODY_INFO.get().setTransferEncoding(transferEncoding);
104 | }
105 |
106 | public static String getTransferEncoding() {
107 | String transferEncoding = BODY_INFO.get().getTransferEncoding();
108 | return StringUtils.isBlank(transferEncoding)
109 | ? HttpConstants.ENCODING_IDENTITY : transferEncoding;
110 | }
111 |
112 | public static void removeAll() {
113 | HTTP_MESSAGE_BYTES.remove();
114 | REQUEST_QUERY_STRING.remove();
115 | BODY_INFO.remove();
116 | BYTES_LENGTH_BEFORE_BODY.remove();
117 | HTTP_METHOD.remove();
118 | INPUT_STREAM.remove();
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/parser/HttpQueryParameterParser.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.parser;
2 |
3 | import com.ljm.server.protocol.http.HttpQueryParameters;
4 |
5 | /**
6 | * @author 李佳明 https://github.com/pkpk1234
7 | * @date 2018-01-2018/1/15
8 | */
9 | public interface HttpQueryParameterParser {
10 | /**
11 | * 解析QueryString,返回HttpQueryParameter集合
12 | *
13 | * @return
14 | */
15 | HttpQueryParameters parse();
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/parser/HttpRequestLineParser.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.parser;
2 |
3 | import com.ljm.server.protocol.http.RequestLine;
4 |
5 | /**
6 | * @author 李佳明 https://github.com/pkpk1234
7 | * @date 2018-01-2018/1/14
8 | */
9 | public interface HttpRequestLineParser {
10 | /**
11 | * 解析start line,并返回RequestLine对象
12 | *
13 | * Method SP Request-URI SP HTTP-Version CRLF
14 | *
15 | * @return
16 | */
17 | RequestLine parse();
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/parser/HttpRequestMessageParser.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.parser;
2 |
3 | import com.ljm.server.protocol.http.HttpMessage;
4 |
5 | import java.io.IOException;
6 | import java.io.InputStream;
7 |
8 | /**
9 | * @author 李佳明 https://github.com/pkpk1234
10 | * @date 2018-01-2018/1/14
11 | */
12 | public interface HttpRequestMessageParser {
13 | /**
14 | * 解析输入流中的内容,并构建Http Message对象
15 | *
16 | * @param inputStream
17 | * @return
18 | * @throws IOException
19 | */
20 | HttpMessage parse(InputStream inputStream) throws IOException;
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/parser/ParserException.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.parser;
2 |
3 | /**
4 | * @author 李佳明 https://github.com/pkpk1234
5 | * @date 2018-01-2018/1/14
6 | */
7 | public class ParserException extends RuntimeException {
8 | public ParserException() {
9 | super();
10 | }
11 |
12 | public ParserException(String message) {
13 | super(message);
14 | }
15 |
16 | public ParserException(String message, Throwable cause) {
17 | super(message, cause);
18 | }
19 |
20 | public ParserException(Throwable cause) {
21 | super(cause);
22 | }
23 |
24 | protected ParserException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
25 | super(message, cause, enableSuppression, writableStackTrace);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/response/HttpResponseConstants.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.response;
2 |
3 | import com.ljm.server.protocol.http.HttpResponseMessage;
4 | import com.ljm.server.protocol.http.header.HttpMessageHeaders;
5 |
6 | /**
7 | * @author 李佳明 https://github.com/pkpk1234
8 | * @date 2018/1/22
9 | */
10 | public interface HttpResponseConstants {
11 | HttpResponseMessage HTTP_404 = HttpResponseMessageBuilder.builder()
12 | .withResponseLine(ResponseLineConstants.RES_404)
13 | .withMessageHeaders(HttpMessageHeaders.newBuilder()
14 | .addHeader("Content-Type", "text/html").build())
15 | .build();
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/response/HttpResponseMessageBuilder.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.response;
2 |
3 | import com.ljm.server.protocol.http.HttpResponseMessage;
4 | import com.ljm.server.protocol.http.ResponseLine;
5 | import com.ljm.server.protocol.http.body.HttpBody;
6 | import com.ljm.server.protocol.http.header.IMessageHeaders;
7 |
8 | import java.util.Optional;
9 |
10 | /**
11 | * @author 李佳明 https://github.com/pkpk1234
12 | * @date 2018/1/22
13 | */
14 | public final class HttpResponseMessageBuilder {
15 | private ResponseLine responseLine;
16 | private IMessageHeaders messageHeaders;
17 | private Optional httpBody = Optional.empty();
18 |
19 | private HttpResponseMessageBuilder() {
20 | }
21 |
22 | public static HttpResponseMessageBuilder builder() {
23 | return new HttpResponseMessageBuilder();
24 | }
25 |
26 | public HttpResponseMessageBuilder withResponseLine(ResponseLine responseLine) {
27 | this.responseLine = responseLine;
28 | return this;
29 | }
30 |
31 | public HttpResponseMessageBuilder withMessageHeaders(IMessageHeaders messageHeaders) {
32 | this.messageHeaders = messageHeaders;
33 | return this;
34 | }
35 |
36 | public HttpResponseMessageBuilder withHttpBody(Optional httpBody) {
37 | this.httpBody = httpBody;
38 | return this;
39 | }
40 |
41 | public HttpResponseMessage build() {
42 | return new HttpResponseMessage(responseLine, messageHeaders, httpBody);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/response/HttpResponseMessageWriter.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.response;
2 |
3 | import com.ljm.server.io.connection.Connection;
4 | import com.ljm.server.protocol.http.HttpResponseMessage;
5 | import com.ljm.server.protocol.http.ResponseLine;
6 | import com.ljm.server.protocol.http.body.HttpBody;
7 | import com.ljm.server.protocol.http.header.IMessageHeaders;
8 | import org.apache.commons.io.IOUtils;
9 |
10 | import java.io.IOException;
11 | import java.io.OutputStream;
12 | import java.util.Optional;
13 |
14 | /**
15 | * @author 李佳明 https://github.com/pkpk1234
16 | * @date 2018-01-22
17 | */
18 | public class HttpResponseMessageWriter {
19 | public void write(HttpResponseMessage httpResponseMessage, Connection connection)
20 | throws IOException {
21 | OutputStream outputStream = connection.getOutputStream();
22 | ResponseLine responseLine = httpResponseMessage.getResponseLine();
23 | String responseLineString = responseLine.asString();
24 | write(outputStream, responseLineString);
25 |
26 | IMessageHeaders headers = httpResponseMessage.getMessageHeaders();
27 | String headersString = headers.asString();
28 | write(outputStream, headersString);
29 |
30 | Optional opHttpBody = httpResponseMessage.getHttpBody();
31 | if (opHttpBody.isPresent()) {
32 |
33 | IOUtils.copy(opHttpBody.get().getInputStream(), outputStream);
34 | }
35 | outputStream.flush();
36 | }
37 |
38 | private void write(OutputStream outputStream, String message) throws IOException {
39 | outputStream.write(message.getBytes("utf-8"));
40 | outputStream.write("\r\n".getBytes());
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/response/HttpStatus.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.response;
2 |
3 | /**
4 | * @author 李佳明 https://github.com/pkpk1234
5 | * @date 2018-01-2018/1/22
6 | */
7 | @SuppressWarnings("AlibabaEnumConstantsMustHaveComment")
8 | public enum HttpStatus {
9 | STATUS_404(404, "Not Found"), STATUS_200(200, "OK");
10 | private int statusCode;
11 | private String reason;
12 |
13 | HttpStatus(int statusCode, String reason) {
14 | this.statusCode = statusCode;
15 | this.reason = reason;
16 | }
17 |
18 | public int getStatusCode() {
19 | return statusCode;
20 | }
21 |
22 | public String getReason() {
23 | return reason;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/ljm/server/protocol/http/response/ResponseLineConstants.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.response;
2 |
3 | import com.ljm.server.protocol.http.HttpProtocol;
4 | import com.ljm.server.protocol.http.ResponseLine;
5 |
6 | /**
7 | * @author 李佳明 https://github.com/pkpk1234
8 | * @date 2018/1/22
9 | */
10 | public interface ResponseLineConstants {
11 | ResponseLine RES_404 = new ResponseLine(HttpProtocol.VERSION11.toString(),
12 | HttpStatus.STATUS_404.getStatusCode(), HttpStatus.STATUS_404.getReason());
13 |
14 | ResponseLine RES_200 = new ResponseLine(HttpProtocol.VERSION11.toString(),
15 | HttpStatus.STATUS_200.getStatusCode(), HttpStatus.STATUS_200.getReason());
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/resources/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | front.log
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/main/resources/mime-mapping.properties:
--------------------------------------------------------------------------------
1 | #拷贝自tomcat
2 | .123=application/vnd.lotus-1-2-3
3 | .3dml=text/vnd.in3d.3dml
4 | .3ds=image/x-3ds
5 | .3g2=video/3gpp2
6 | .3gp=video/3gpp
7 | .7z=application/x-7z-compressed
8 | .aab=application/x-authorware-bin
9 | .aac=audio/x-aac
10 | .aam=application/x-authorware-map
11 | .aas=application/x-authorware-seg
12 | .abs=audio/x-mpeg
13 | .abw=application/x-abiword
14 | .ac=application/pkix-attr-cert
15 | .acc=application/vnd.americandynamics.acc
16 | .ace=application/x-ace-compressed
17 | .acu=application/vnd.acucobol
18 | .acutc=application/vnd.acucorp
19 | .adp=audio/adpcm
20 | .aep=application/vnd.audiograph
21 | .afm=application/x-font-type1
22 | .afp=application/vnd.ibm.modcap
23 | .ahead=application/vnd.ahead.space
24 | .ai=application/postscript
25 | .aif=audio/x-aiff
26 | .aifc=audio/x-aiff
27 | .aiff=audio/x-aiff
28 | .aim=application/x-aim
29 | .air=application/vnd.adobe.air-application-installer-package+zip
30 | .ait=application/vnd.dvb.ait
31 | .ami=application/vnd.amiga.ami
32 | .anx=application/annodex
33 | .apk=application/vnd.android.package-archive
34 | .appcache=text/cache-manifest
35 | .application=application/x-ms-application
36 | .apr=application/vnd.lotus-approach
37 | .arc=application/x-freearc
38 | .art=image/x-jg
39 | .asc=application/pgp-signature
40 | .asf=video/x-ms-asf
41 | .asm=text/x-asm
42 | .aso=application/vnd.accpac.simply.aso
43 | .asx=video/x-ms-asf
44 | .atc=application/vnd.acucorp
45 | .atom=application/atom+xml
46 | .atomcat=application/atomcat+xml
47 | .atomsvc=application/atomsvc+xml
48 | .atx=application/vnd.antix.game-component
49 | .au=audio/basic
50 | .avi=video/x-msvideo
51 | .avx=video/x-rad-screenplay
52 | .aw=application/applixware
53 | .axa=audio/annodex
54 | .axv=video/annodex
55 | .azf=application/vnd.airzip.filesecure.azf
56 | .azs=application/vnd.airzip.filesecure.azs
57 | .azw=application/vnd.amazon.ebook
58 | .bat=application/x-msdownload
59 | .bcpio=application/x-bcpio
60 | .bdf=application/x-font-bdf
61 | .bdm=application/vnd.syncml.dm+wbxml
62 | .bed=application/vnd.realvnc.bed
63 | .bh2=application/vnd.fujitsu.oasysprs
64 | .bin=application/octet-stream
65 | .blb=application/x-blorb
66 | .blorb=application/x-blorb
67 | .bmi=application/vnd.bmi
68 | .bmp=image/bmp
69 | .body=text/html
70 | .book=application/vnd.framemaker
71 | .box=application/vnd.previewsystems.box
72 | .boz=application/x-bzip2
73 | .bpk=application/octet-stream
74 | .btif=image/prs.btif
75 | .bz=application/x-bzip
76 | .bz2=application/x-bzip2
77 | .c=text/x-c
78 | .c11amc=application/vnd.cluetrust.cartomobile-config
79 | .c11amz=application/vnd.cluetrust.cartomobile-config-pkg
80 | .c4d=application/vnd.clonk.c4group
81 | .c4f=application/vnd.clonk.c4group
82 | .c4g=application/vnd.clonk.c4group
83 | .c4p=application/vnd.clonk.c4group
84 | .c4u=application/vnd.clonk.c4group
85 | .cab=application/vnd.ms-cab-compressed
86 | .caf=audio/x-caf
87 | .cap=application/vnd.tcpdump.pcap
88 | .car=application/vnd.curl.car
89 | .cat=application/vnd.ms-pki.seccat
90 | .cb7=application/x-cbr
91 | .cba=application/x-cbr
92 | .cbr=application/x-cbr
93 | .cbt=application/x-cbr
94 | .cbz=application/x-cbr
95 | .cc=text/x-c
96 | .cct=application/x-director
97 | .ccxml=application/ccxml+xml
98 | .cdbcmsg=application/vnd.contact.cmsg
99 | .cdf=application/x-cdf
100 | .cdkey=application/vnd.mediastation.cdkey
101 | .cdmia=application/cdmi-capability
102 | .cdmic=application/cdmi-container
103 | .cdmid=application/cdmi-domain
104 | .cdmio=application/cdmi-object
105 | .cdmiq=application/cdmi-queue
106 | .cdx=chemical/x-cdx
107 | .cdxml=application/vnd.chemdraw+xml
108 | .cdy=application/vnd.cinderella
109 | .cer=application/pkix-cert
110 | .cfs=application/x-cfs-compressed
111 | .cgm=image/cgm
112 | .chat=application/x-chat
113 | .chm=application/vnd.ms-htmlhelp
114 | .chrt=application/vnd.kde.kchart
115 | .cif=chemical/x-cif
116 | .cii=application/vnd.anser-web-certificate-issue-initiation
117 | .cil=application/vnd.ms-artgalry
118 | .cla=application/vnd.claymore
119 | .class=application/java
120 | .clkk=application/vnd.crick.clicker.keyboard
121 | .clkp=application/vnd.crick.clicker.palette
122 | .clkt=application/vnd.crick.clicker.template
123 | .clkw=application/vnd.crick.clicker.wordbank
124 | .clkx=application/vnd.crick.clicker
125 | .clp=application/x-msclip
126 | .cmc=application/vnd.cosmocaller
127 | .cmdf=chemical/x-cmdf
128 | .cml=chemical/x-cml
129 | .cmp=application/vnd.yellowriver-custom-menu
130 | .cmx=image/x-cmx
131 | .cod=application/vnd.rim.cod
132 | .com=application/x-msdownload
133 | .conf=text/plain
134 | .cpio=application/x-cpio
135 | .cpp=text/x-c
136 | .cpt=application/mac-compactpro
137 | .crd=application/x-mscardfile
138 | .crl=application/pkix-crl
139 | .crt=application/x-x509-ca-cert
140 | .cryptonote=application/vnd.rig.cryptonote
141 | .csh=application/x-csh
142 | .csml=chemical/x-csml
143 | .csp=application/vnd.commonspace
144 | .css=text/css
145 | .cst=application/x-director
146 | .csv=text/csv
147 | .cu=application/cu-seeme
148 | .curl=text/vnd.curl
149 | .cww=application/prs.cww
150 | .cxt=application/x-director
151 | .cxx=text/x-c
152 | .dae=model/vnd.collada+xml
153 | .daf=application/vnd.mobius.daf
154 | .dart=application/vnd.dart
155 | .dataless=application/vnd.fdsn.seed
156 | .davmount=application/davmount+xml
157 | .dbk=application/docbook+xml
158 | .dcr=application/x-director
159 | .dcurl=text/vnd.curl.dcurl
160 | .dd2=application/vnd.oma.dd2+xml
161 | .ddd=application/vnd.fujixerox.ddd
162 | .deb=application/x-debian-package
163 | .def=text/plain
164 | .deploy=application/octet-stream
165 | .der=application/x-x509-ca-cert
166 | .dfac=application/vnd.dreamfactory
167 | .dgc=application/x-dgc-compressed
168 | .dib=image/bmp
169 | .dic=text/x-c
170 | .dir=application/x-director
171 | .dis=application/vnd.mobius.dis
172 | .dist=application/octet-stream
173 | .distz=application/octet-stream
174 | .djv=image/vnd.djvu
175 | .djvu=image/vnd.djvu
176 | .dll=application/x-msdownload
177 | .dmg=application/x-apple-diskimage
178 | .dmp=application/vnd.tcpdump.pcap
179 | .dms=application/octet-stream
180 | .dna=application/vnd.dna
181 | .doc=application/msword
182 | .docm=application/vnd.ms-word.document.macroenabled.12
183 | .docx=application/vnd.openxmlformats-officedocument.wordprocessingml.document
184 | .dot=application/msword
185 | .dotm=application/vnd.ms-word.template.macroenabled.12
186 | .dotx=application/vnd.openxmlformats-officedocument.wordprocessingml.template
187 | .dp=application/vnd.osgi.dp
188 | .dpg=application/vnd.dpgraph
189 | .dra=audio/vnd.dra
190 | .dsc=text/prs.lines.tag
191 | .dssc=application/dssc+der
192 | .dtb=application/x-dtbook+xml
193 | .dtd=application/xml-dtd
194 | .dts=audio/vnd.dts
195 | .dtshd=audio/vnd.dts.hd
196 | .dump=application/octet-stream
197 | .dv=video/x-dv
198 | .dvb=video/vnd.dvb.file
199 | .dvi=application/x-dvi
200 | .dwf=model/vnd.dwf
201 | .dwg=image/vnd.dwg
202 | .dxf=image/vnd.dxf
203 | .dxp=application/vnd.spotfire.dxp
204 | .dxr=application/x-director
205 | .ecelp4800=audio/vnd.nuera.ecelp4800
206 | .ecelp7470=audio/vnd.nuera.ecelp7470
207 | .ecelp9600=audio/vnd.nuera.ecelp9600
208 | .ecma=application/ecmascript
209 | .edm=application/vnd.novadigm.edm
210 | .edx=application/vnd.novadigm.edx
211 | .efif=application/vnd.picsel
212 | .ei6=application/vnd.pg.osasli
213 | .elc=application/octet-stream
214 | .emf=application/x-msmetafile
215 | .eml=message/rfc822
216 | .emma=application/emma+xml
217 | .emz=application/x-msmetafile
218 | .eol=audio/vnd.digital-winds
219 | .eot=application/vnd.ms-fontobject
220 | .eps=application/postscript
221 | .epub=application/epub+zip
222 | .es3=application/vnd.eszigno3+xml
223 | .esa=application/vnd.osgi.subsystem
224 | .esf=application/vnd.epson.esf
225 | .et3=application/vnd.eszigno3+xml
226 | .etx=text/x-setext
227 | .eva=application/x-eva
228 | .evy=application/x-envoy
229 | .exe=application/octet-stream
230 | .exi=application/exi
231 | .ext=application/vnd.novadigm.ext
232 | .ez=application/andrew-inset
233 | .ez2=application/vnd.ezpix-album
234 | .ez3=application/vnd.ezpix-package
235 | .f=text/x-fortran
236 | .f4v=video/x-f4v
237 | .f77=text/x-fortran
238 | .f90=text/x-fortran
239 | .fbs=image/vnd.fastbidsheet
240 | .fcdt=application/vnd.adobe.formscentral.fcdt
241 | .fcs=application/vnd.isac.fcs
242 | .fdf=application/vnd.fdf
243 | .fe_launch=application/vnd.denovo.fcselayout-link
244 | .fg5=application/vnd.fujitsu.oasysgp
245 | .fgd=application/x-director
246 | .fh=image/x-freehand
247 | .fh4=image/x-freehand
248 | .fh5=image/x-freehand
249 | .fh7=image/x-freehand
250 | .fhc=image/x-freehand
251 | .fig=application/x-xfig
252 | .flac=audio/flac
253 | .fli=video/x-fli
254 | .flo=application/vnd.micrografx.flo
255 | .flv=video/x-flv
256 | .flw=application/vnd.kde.kivio
257 | .flx=text/vnd.fmi.flexstor
258 | .fly=text/vnd.fly
259 | .fm=application/vnd.framemaker
260 | .fnc=application/vnd.frogans.fnc
261 | .for=text/x-fortran
262 | .fpx=image/vnd.fpx
263 | .frame=application/vnd.framemaker
264 | .fsc=application/vnd.fsc.weblaunch
265 | .fst=image/vnd.fst
266 | .ftc=application/vnd.fluxtime.clip
267 | .fti=application/vnd.anser-web-funds-transfer-initiation
268 | .fvt=video/vnd.fvt
269 | .fxp=application/vnd.adobe.fxp
270 | .fxpl=application/vnd.adobe.fxp
271 | .fzs=application/vnd.fuzzysheet
272 | .g2w=application/vnd.geoplan
273 | .g3=image/g3fax
274 | .g3w=application/vnd.geospace
275 | .gac=application/vnd.groove-account
276 | .gam=application/x-tads
277 | .gbr=application/rpki-ghostbusters
278 | .gca=application/x-gca-compressed
279 | .gdl=model/vnd.gdl
280 | .geo=application/vnd.dynageo
281 | .gex=application/vnd.geometry-explorer
282 | .ggb=application/vnd.geogebra.file
283 | .ggt=application/vnd.geogebra.tool
284 | .ghf=application/vnd.groove-help
285 | .gif=image/gif
286 | .gim=application/vnd.groove-identity-message
287 | .gml=application/gml+xml
288 | .gmx=application/vnd.gmx
289 | .gnumeric=application/x-gnumeric
290 | .gph=application/vnd.flographit
291 | .gpx=application/gpx+xml
292 | .gqf=application/vnd.grafeq
293 | .gqs=application/vnd.grafeq
294 | .gram=application/srgs
295 | .gramps=application/x-gramps-xml
296 | .gre=application/vnd.geometry-explorer
297 | .grv=application/vnd.groove-injector
298 | .grxml=application/srgs+xml
299 | .gsf=application/x-font-ghostscript
300 | .gtar=application/x-gtar
301 | .gtm=application/vnd.groove-tool-message
302 | .gtw=model/vnd.gtw
303 | .gv=text/vnd.graphviz
304 | .gxf=application/gxf
305 | .gxt=application/vnd.geonext
306 | .gz=application/x-gzip
307 | .h=text/x-c
308 | .h261=video/h261
309 | .h263=video/h263
310 | .h264=video/h264
311 | .hal=application/vnd.hal+xml
312 | .hbci=application/vnd.hbci
313 | .hdf=application/x-hdf
314 | .hh=text/x-c
315 | .hlp=application/winhlp
316 | .hpgl=application/vnd.hp-hpgl
317 | .hpid=application/vnd.hp-hpid
318 | .hps=application/vnd.hp-hps
319 | .hqx=application/mac-binhex40
320 | .htc=text/x-component
321 | .htke=application/vnd.kenameaapp
322 | .htm=text/html
323 | .html=text/html
324 | .hvd=application/vnd.yamaha.hv-dic
325 | .hvp=application/vnd.yamaha.hv-voice
326 | .hvs=application/vnd.yamaha.hv-script
327 | .i2g=application/vnd.intergeo
328 | .icc=application/vnd.iccprofile
329 | .ice=x-conference/x-cooltalk
330 | .icm=application/vnd.iccprofile
331 | .ico=image/x-icon
332 | .ics=text/calendar
333 | .ief=image/ief
334 | .ifb=text/calendar
335 | .ifm=application/vnd.shana.informed.formdata
336 | .iges=model/iges
337 | .igl=application/vnd.igloader
338 | .igm=application/vnd.insors.igm
339 | .igs=model/iges
340 | .igx=application/vnd.micrografx.igx
341 | .iif=application/vnd.shana.informed.interchange
342 | .imp=application/vnd.accpac.simply.imp
343 | .ims=application/vnd.ms-ims
344 | .in=text/plain
345 | .ink=application/inkml+xml
346 | .inkml=application/inkml+xml
347 | .install=application/x-install-instructions
348 | .iota=application/vnd.astraea-software.iota
349 | .ipfix=application/ipfix
350 | .ipk=application/vnd.shana.informed.package
351 | .irm=application/vnd.ibm.rights-management
352 | .irp=application/vnd.irepository.package+xml
353 | .iso=application/x-iso9660-image
354 | .itp=application/vnd.shana.informed.formtemplate
355 | .ivp=application/vnd.immervision-ivp
356 | .ivu=application/vnd.immervision-ivu
357 | .jad=text/vnd.sun.j2me.app-descriptor
358 | .jam=application/vnd.jam
359 | .jar=application/java-archive
360 | .java=text/x-java-source
361 | .jisp=application/vnd.jisp
362 | .jlt=application/vnd.hp-jlyt
363 | .jnlp=application/x-java-jnlp-file
364 | .joda=application/vnd.joost.joda-archive
365 | .jpe=image/jpeg
366 | .jpeg=image/jpeg
367 | .jpg=image/jpeg
368 | .jpgm=video/jpm
369 | .jpgv=video/jpeg
370 | .jpm=video/jpm
371 | .js=application/javascript
372 | .jsf=text/plain
373 | .json=application/json
374 | .jsonml=application/jsonml+json
375 | .jspf=text/plain
376 | .kar=audio/midi
377 | .karbon=application/vnd.kde.karbon
378 | .kfo=application/vnd.kde.kformula
379 | .kia=application/vnd.kidspiration
380 | .kml=application/vnd.google-earth.kml+xml
381 | .kmz=application/vnd.google-earth.kmz
382 | .kne=application/vnd.kinar
383 | .knp=application/vnd.kinar
384 | .kon=application/vnd.kde.kontour
385 | .kpr=application/vnd.kde.kpresenter
386 | .kpt=application/vnd.kde.kpresenter
387 | .kpxx=application/vnd.ds-keypoint
388 | .ksp=application/vnd.kde.kspread
389 | .ktr=application/vnd.kahootz
390 | .ktx=image/ktx
391 | .ktz=application/vnd.kahootz
392 | .kwd=application/vnd.kde.kword
393 | .kwt=application/vnd.kde.kword
394 | .lasxml=application/vnd.las.las+xml
395 | .latex=application/x-latex
396 | .lbd=application/vnd.llamagraphics.life-balance.desktop
397 | .lbe=application/vnd.llamagraphics.life-balance.exchange+xml
398 | .les=application/vnd.hhe.lesson-player
399 | .lha=application/x-lzh-compressed
400 | .link66=application/vnd.route66.link66+xml
401 | .list=text/plain
402 | .list3820=application/vnd.ibm.modcap
403 | .listafp=application/vnd.ibm.modcap
404 | .lnk=application/x-ms-shortcut
405 | .log=text/plain
406 | .lostxml=application/lost+xml
407 | .lrf=application/octet-stream
408 | .lrm=application/vnd.ms-lrm
409 | .ltf=application/vnd.frogans.ltf
410 | .lvp=audio/vnd.lucent.voice
411 | .lwp=application/vnd.lotus-wordpro
412 | .lzh=application/x-lzh-compressed
413 | .m13=application/x-msmediaview
414 | .m14=application/x-msmediaview
415 | .m1v=video/mpeg
416 | .m21=application/mp21
417 | .m2a=audio/mpeg
418 | .m2v=video/mpeg
419 | .m3a=audio/mpeg
420 | .m3u=audio/x-mpegurl
421 | .m3u8=application/vnd.apple.mpegurl
422 | .m4a=audio/mp4
423 | .m4b=audio/mp4
424 | .m4r=audio/mp4
425 | .m4u=video/vnd.mpegurl
426 | .m4v=video/mp4
427 | .ma=application/mathematica
428 | .mac=image/x-macpaint
429 | .mads=application/mads+xml
430 | .mag=application/vnd.ecowin.chart
431 | .maker=application/vnd.framemaker
432 | .man=text/troff
433 | .mar=application/octet-stream
434 | .mathml=application/mathml+xml
435 | .mb=application/mathematica
436 | .mbk=application/vnd.mobius.mbk
437 | .mbox=application/mbox
438 | .mc1=application/vnd.medcalcdata
439 | .mcd=application/vnd.mcd
440 | .mcurl=text/vnd.curl.mcurl
441 | .mdb=application/x-msaccess
442 | .mdi=image/vnd.ms-modi
443 | .me=text/troff
444 | .mesh=model/mesh
445 | .meta4=application/metalink4+xml
446 | .metalink=application/metalink+xml
447 | .mets=application/mets+xml
448 | .mfm=application/vnd.mfmp
449 | .mft=application/rpki-manifest
450 | .mgp=application/vnd.osgeo.mapguide.package
451 | .mgz=application/vnd.proteus.magazine
452 | .mid=audio/midi
453 | .midi=audio/midi
454 | .mie=application/x-mie
455 | .mif=application/x-mif
456 | .mime=message/rfc822
457 | .mj2=video/mj2
458 | .mjp2=video/mj2
459 | .mk3d=video/x-matroska
460 | .mka=audio/x-matroska
461 | .mks=video/x-matroska
462 | .mkv=video/x-matroska
463 | .mlp=application/vnd.dolby.mlp
464 | .mmd=application/vnd.chipnuts.karaoke-mmd
465 | .mmf=application/vnd.smaf
466 | .mmr=image/vnd.fujixerox.edmics-mmr
467 | .mng=video/x-mng
468 | .mny=application/x-msmoney
469 | .mobi=application/x-mobipocket-ebook
470 | .mods=application/mods+xml
471 | .mov=video/quicktime
472 | .movie=video/x-sgi-movie
473 | .mp1=audio/mpeg
474 | .mp2=audio/mpeg
475 | .mp21=application/mp21
476 | .mp2a=audio/mpeg
477 | .mp3=audio/mpeg
478 | .mp4=video/mp4
479 | .mp4a=audio/mp4
480 | .mp4s=application/mp4
481 | .mp4v=video/mp4
482 | .mpa=audio/mpeg
483 | .mpc=application/vnd.mophun.certificate
484 | .mpe=video/mpeg
485 | .mpeg=video/mpeg
486 | .mpega=audio/x-mpeg
487 | .mpg=video/mpeg
488 | .mpg4=video/mp4
489 | .mpga=audio/mpeg
490 | .mpkg=application/vnd.apple.installer+xml
491 | .mpm=application/vnd.blueice.multipass
492 | .mpn=application/vnd.mophun.application
493 | .mpp=application/vnd.ms-project
494 | .mpt=application/vnd.ms-project
495 | .mpv2=video/mpeg2
496 | .mpy=application/vnd.ibm.minipay
497 | .mqy=application/vnd.mobius.mqy
498 | .mrc=application/marc
499 | .mrcx=application/marcxml+xml
500 | .ms=text/troff
501 | .mscml=application/mediaservercontrol+xml
502 | .mseed=application/vnd.fdsn.mseed
503 | .mseq=application/vnd.mseq
504 | .msf=application/vnd.epson.msf
505 | .msh=model/mesh
506 | .msi=application/x-msdownload
507 | .msl=application/vnd.mobius.msl
508 | .msty=application/vnd.muvee.style
509 | .mts=model/vnd.mts
510 | .mus=application/vnd.musician
511 | .musicxml=application/vnd.recordare.musicxml+xml
512 | .mvb=application/x-msmediaview
513 | .mwf=application/vnd.mfer
514 | .mxf=application/mxf
515 | .mxl=application/vnd.recordare.musicxml
516 | .mxml=application/xv+xml
517 | .mxs=application/vnd.triscape.mxs
518 | .mxu=video/vnd.mpegurl
519 | .n-gage=application/vnd.nokia.n-gage.symbian.install
520 | .n3=text/n3
521 | .nb=application/mathematica
522 | .nbp=application/vnd.wolfram.player
523 | .nc=application/x-netcdf
524 | .ncx=application/x-dtbncx+xml
525 | .nfo=text/x-nfo
526 | .ngdat=application/vnd.nokia.n-gage.data
527 | .nitf=application/vnd.nitf
528 | .nlu=application/vnd.neurolanguage.nlu
529 | .nml=application/vnd.enliven
530 | .nnd=application/vnd.noblenet-directory
531 | .nns=application/vnd.noblenet-sealer
532 | .nnw=application/vnd.noblenet-web
533 | .npx=image/vnd.net-fpx
534 | .nsc=application/x-conference
535 | .nsf=application/vnd.lotus-notes
536 | .ntf=application/vnd.nitf
537 | .nzb=application/x-nzb
538 | .oa2=application/vnd.fujitsu.oasys2
539 | .oa3=application/vnd.fujitsu.oasys3
540 | .oas=application/vnd.fujitsu.oasys
541 | .obd=application/x-msbinder
542 | .obj=application/x-tgif
543 | .oda=application/oda
544 | .
545 | .odb=application/vnd.oasis.opendocument.database
546 | .
547 | .odc=application/vnd.oasis.opendocument.chart
548 | .
549 | .odf=application/vnd.oasis.opendocument.formula
550 | .odft=application/vnd.oasis.opendocument.formula-template
551 | .
552 | .odg=application/vnd.oasis.opendocument.graphics
553 | .
554 | .odi=application/vnd.oasis.opendocument.image
555 | .
556 | .odm=application/vnd.oasis.opendocument.text-master
557 | .
558 | .odp=application/vnd.oasis.opendocument.presentation
559 | .
560 | .ods=application/vnd.oasis.opendocument.spreadsheet
561 | .
562 | .odt=application/vnd.oasis.opendocument.text
563 | .oga=audio/ogg
564 | .ogg=audio/ogg
565 | .ogv=video/ogg
566 | .
567 | .ogx=application/ogg
568 | .omdoc=application/omdoc+xml
569 | .onepkg=application/onenote
570 | .onetmp=application/onenote
571 | .onetoc=application/onenote
572 | .onetoc2=application/onenote
573 | .opf=application/oebps-package+xml
574 | .opml=text/x-opml
575 | .oprc=application/vnd.palm
576 | .org=application/vnd.lotus-organizer
577 | .osf=application/vnd.yamaha.openscoreformat
578 | .osfpvg=application/vnd.yamaha.openscoreformat.osfpvg+xml
579 | .otc=application/vnd.oasis.opendocument.chart-template
580 | .otf=application/x-font-otf
581 | .
582 | .otg=application/vnd.oasis.opendocument.graphics-template
583 | .
584 | .oth=application/vnd.oasis.opendocument.text-web
585 | .oti=application/vnd.oasis.opendocument.image-template
586 | .
587 | .otp=application/vnd.oasis.opendocument.presentation-template
588 | .
589 | .ots=application/vnd.oasis.opendocument.spreadsheet-template
590 | .
591 | .ott=application/vnd.oasis.opendocument.text-template
592 | .oxps=application/oxps
593 | .oxt=application/vnd.openofficeorg.extension
594 | .p=text/x-pascal
595 | .p10=application/pkcs10
596 | .p12=application/x-pkcs12
597 | .p7b=application/x-pkcs7-certificates
598 | .p7c=application/pkcs7-mime
599 | .p7m=application/pkcs7-mime
600 | .p7r=application/x-pkcs7-certreqresp
601 | .p7s=application/pkcs7-signature
602 | .p8=application/pkcs8
603 | .pas=text/x-pascal
604 | .paw=application/vnd.pawaafile
605 | .pbd=application/vnd.powerbuilder6
606 | .pbm=image/x-portable-bitmap
607 | .pcap=application/vnd.tcpdump.pcap
608 | .pcf=application/x-font-pcf
609 | .pcl=application/vnd.hp-pcl
610 | .pclxl=application/vnd.hp-pclxl
611 | .pct=image/pict
612 | .pcurl=application/vnd.curl.pcurl
613 | .pcx=image/x-pcx
614 | .pdb=application/vnd.palm
615 | .pdf=application/pdf
616 | .pfa=application/x-font-type1
617 | .pfb=application/x-font-type1
618 | .pfm=application/x-font-type1
619 | .pfr=application/font-tdpfr
620 | .pfx=application/x-pkcs12
621 | .pgm=image/x-portable-graymap
622 | .pgn=application/x-chess-pgn
623 | .pgp=application/pgp-encrypted
624 | .pic=image/pict
625 | .pict=image/pict
626 | .pkg=application/octet-stream
627 | .pki=application/pkixcmp
628 | .pkipath=application/pkix-pkipath
629 | .plb=application/vnd.3gpp.pic-bw-large
630 | .plc=application/vnd.mobius.plc
631 | .plf=application/vnd.pocketlearn
632 | .pls=audio/x-scpls
633 | .pml=application/vnd.ctc-posml
634 | .png=image/png
635 | .pnm=image/x-portable-anymap
636 | .pnt=image/x-macpaint
637 | .portpkg=application/vnd.macports.portpkg
638 | .pot=application/vnd.ms-powerpoint
639 | .potm=application/vnd.ms-powerpoint.template.macroenabled.12
640 | .potx=application/vnd.openxmlformats-officedocument.presentationml.template
641 | .ppam=application/vnd.ms-powerpoint.addin.macroenabled.12
642 | .ppd=application/vnd.cups-ppd
643 | .ppm=image/x-portable-pixmap
644 | .pps=application/vnd.ms-powerpoint
645 | .ppsm=application/vnd.ms-powerpoint.slideshow.macroenabled.12
646 | .ppsx=application/vnd.openxmlformats-officedocument.presentationml.slideshow
647 | .ppt=application/vnd.ms-powerpoint
648 | .pptm=application/vnd.ms-powerpoint.presentation.macroenabled.12
649 | .pptx=application/vnd.openxmlformats-officedocument.presentationml.presentation
650 | .pqa=application/vnd.palm
651 | .prc=application/x-mobipocket-ebook
652 | .pre=application/vnd.lotus-freelance
653 | .prf=application/pics-rules
654 | .ps=application/postscript
655 | .psb=application/vnd.3gpp.pic-bw-small
656 | .psd=image/vnd.adobe.photoshop
657 | .psf=application/x-font-linux-psf
658 | .pskcxml=application/pskc+xml
659 | .ptid=application/vnd.pvi.ptid1
660 | .pub=application/x-mspublisher
661 | .pvb=application/vnd.3gpp.pic-bw-var
662 | .pwn=application/vnd.3m.post-it-notes
663 | .pya=audio/vnd.ms-playready.media.pya
664 | .pyv=video/vnd.ms-playready.media.pyv
665 | .qam=application/vnd.epson.quickanime
666 | .qbo=application/vnd.intu.qbo
667 | .qfx=application/vnd.intu.qfx
668 | .qps=application/vnd.publishare-delta-tree
669 | .qt=video/quicktime
670 | .qti=image/x-quicktime
671 | .qtif=image/x-quicktime
672 | .qwd=application/vnd.quark.quarkxpress
673 | .qwt=application/vnd.quark.quarkxpress
674 | .qxb=application/vnd.quark.quarkxpress
675 | .qxd=application/vnd.quark.quarkxpress
676 | .qxl=application/vnd.quark.quarkxpress
677 | .qxt=application/vnd.quark.quarkxpress
678 | .ra=audio/x-pn-realaudio
679 | .ram=audio/x-pn-realaudio
680 | .rar=application/x-rar-compressed
681 | .ras=image/x-cmu-raster
682 | .rcprofile=application/vnd.ipunplugged.rcprofile
683 | .rdf=application/rdf+xml
684 | .rdz=application/vnd.data-vision.rdz
685 | .rep=application/vnd.businessobjects
686 | .res=application/x-dtbresource+xml
687 | .rgb=image/x-rgb
688 | .rif=application/reginfo+xml
689 | .rip=audio/vnd.rip
690 | .ris=application/x-research-info-systems
691 | .rl=application/resource-lists+xml
692 | .rlc=image/vnd.fujixerox.edmics-rlc
693 | .rld=application/resource-lists-diff+xml
694 | .rm=application/vnd.rn-realmedia
695 | .rmi=audio/midi
696 | .rmp=audio/x-pn-realaudio-plugin
697 | .rms=application/vnd.jcp.javame.midlet-rms
698 | .rmvb=application/vnd.rn-realmedia-vbr
699 | .rnc=application/relax-ng-compact-syntax
700 | .roa=application/rpki-roa
701 | .roff=text/troff
702 | .rp9=application/vnd.cloanto.rp9
703 | .rpss=application/vnd.nokia.radio-presets
704 | .rpst=application/vnd.nokia.radio-preset
705 | .rq=application/sparql-query
706 | .rs=application/rls-services+xml
707 | .rsd=application/rsd+xml
708 | .rss=application/rss+xml
709 | .rtf=application/rtf
710 | .rtx=text/richtext
711 | .s=text/x-asm
712 | .s3m=audio/s3m
713 | .saf=application/vnd.yamaha.smaf-audio
714 | .sbml=application/sbml+xml
715 | .sc=application/vnd.ibm.secure-container
716 | .scd=application/x-msschedule
717 | .scm=application/vnd.lotus-screencam
718 | .scq=application/scvp-cv-request
719 | .scs=application/scvp-cv-response
720 | .scurl=text/vnd.curl.scurl
721 | .sda=application/vnd.stardivision.draw
722 | .sdc=application/vnd.stardivision.calc
723 | .sdd=application/vnd.stardivision.impress
724 | .sdkd=application/vnd.solent.sdkm+xml
725 | .sdkm=application/vnd.solent.sdkm+xml
726 | .sdp=application/sdp
727 | .sdw=application/vnd.stardivision.writer
728 | .see=application/vnd.seemail
729 | .seed=application/vnd.fdsn.seed
730 | .sema=application/vnd.sema
731 | .semd=application/vnd.semd
732 | .semf=application/vnd.semf
733 | .ser=application/java-serialized-object
734 | .setpay=application/set-payment-initiation
735 | .setreg=application/set-registration-initiation
736 | .sfd-hdstx=application/vnd.hydrostatix.sof-data
737 | .sfs=application/vnd.spotfire.sfs
738 | .sfv=text/x-sfv
739 | .sgi=image/sgi
740 | .sgl=application/vnd.stardivision.writer-global
741 | .sgm=text/sgml
742 | .sgml=text/sgml
743 | .sh=application/x-sh
744 | .shar=application/x-shar
745 | .shf=application/shf+xml
746 | .
749 | .sid=image/x-mrsid-image
750 | .sig=application/pgp-signature
751 | .sil=audio/silk
752 | .silo=model/mesh
753 | .sis=application/vnd.symbian.install
754 | .sisx=application/vnd.symbian.install
755 | .sit=application/x-stuffit
756 | .sitx=application/x-stuffitx
757 | .skd=application/vnd.koan
758 | .skm=application/vnd.koan
759 | .skp=application/vnd.koan
760 | .skt=application/vnd.koan
761 | .sldm=application/vnd.ms-powerpoint.slide.macroenabled.12
762 | .sldx=application/vnd.openxmlformats-officedocument.presentationml.slide
763 | .slt=application/vnd.epson.salt
764 | .sm=application/vnd.stepmania.stepchart
765 | .smf=application/vnd.stardivision.math
766 | .smi=application/smil+xml
767 | .smil=application/smil+xml
768 | .smv=video/x-smv
769 | .smzip=application/vnd.stepmania.package
770 | .snd=audio/basic
771 | .snf=application/x-font-snf
772 | .so=application/octet-stream
773 | .spc=application/x-pkcs7-certificates
774 | .spf=application/vnd.yamaha.smaf-phrase
775 | .spl=application/x-futuresplash
776 | .spot=text/vnd.in3d.spot
777 | .spp=application/scvp-vp-response
778 | .spq=application/scvp-vp-request
779 | .spx=audio/ogg
780 | .sql=application/x-sql
781 | .src=application/x-wais-source
782 | .srt=application/x-subrip
783 | .sru=application/sru+xml
784 | .srx=application/sparql-results+xml
785 | .ssdl=application/ssdl+xml
786 | .sse=application/vnd.kodak-descriptor
787 | .ssf=application/vnd.epson.ssf
788 | .ssml=application/ssml+xml
789 | .st=application/vnd.sailingtracker.track
790 | .stc=application/vnd.sun.xml.calc.template
791 | .std=application/vnd.sun.xml.draw.template
792 | .stf=application/vnd.wt.stf
793 | .sti=application/vnd.sun.xml.impress.template
794 | .stk=application/hyperstudio
795 | .stl=application/vnd.ms-pki.stl
796 | .str=application/vnd.pg.format
797 | .stw=application/vnd.sun.xml.writer.template
798 | .sub=text/vnd.dvb.subtitle
799 | .sus=application/vnd.sus-calendar
800 | .susp=application/vnd.sus-calendar
801 | .sv4cpio=application/x-sv4cpio
802 | .sv4crc=application/x-sv4crc
803 | .svc=application/vnd.dvb.service
804 | .svd=application/vnd.svd
805 | .svg=image/svg+xml
806 | .svgz=image/svg+xml
807 | .swa=application/x-director
808 | .swf=application/x-shockwave-flash
809 | .swi=application/vnd.aristanetworks.swi
810 | .sxc=application/vnd.sun.xml.calc
811 | .sxd=application/vnd.sun.xml.draw
812 | .sxg=application/vnd.sun.xml.writer.global
813 | .sxi=application/vnd.sun.xml.impress
814 | .sxm=application/vnd.sun.xml.math
815 | .sxw=application/vnd.sun.xml.writer
816 | .t=text/troff
817 | .t3=application/x-t3vm-image
818 | .taglet=application/vnd.mynfc
819 | .tao=application/vnd.tao.intent-module-archive
820 | .tar=application/x-tar
821 | .tcap=application/vnd.3gpp2.tcap
822 | .tcl=application/x-tcl
823 | .teacher=application/vnd.smart.teacher
824 | .tei=application/tei+xml
825 | .teicorpus=application/tei+xml
826 | .tex=application/x-tex
827 | .texi=application/x-texinfo
828 | .texinfo=application/x-texinfo
829 | .text=text/plain
830 | .tfi=application/thraud+xml
831 | .tfm=application/x-tex-tfm
832 | .tga=image/x-tga
833 | .thmx=application/vnd.ms-officetheme
834 | .tif=image/tiff
835 | .tiff=image/tiff
836 | .tmo=application/vnd.tmobile-livetv
837 | .torrent=application/x-bittorrent
838 | .tpl=application/vnd.groove-tool-template
839 | .tpt=application/vnd.trid.tpt
840 | .tr=text/troff
841 | .tra=application/vnd.trueapp
842 | .trm=application/x-msterminal
843 | .tsd=application/timestamped-data
844 | .tsv=text/tab-separated-values
845 | .ttc=application/x-font-ttf
846 | .ttf=application/x-font-ttf
847 | .ttl=text/turtle
848 | .twd=application/vnd.simtech-mindmapper
849 | .twds=application/vnd.simtech-mindmapper
850 | .txd=application/vnd.genomatix.tuxedo
851 | .txf=application/vnd.mobius.txf
852 | .txt=text/plain
853 | .u32=application/x-authorware-bin
854 | .udeb=application/x-debian-package
855 | .ufd=application/vnd.ufdl
856 | .ufdl=application/vnd.ufdl
857 | .ulw=audio/basic
858 | .ulx=application/x-glulx
859 | .umj=application/vnd.umajin
860 | .unityweb=application/vnd.unity
861 | .uoml=application/vnd.uoml+xml
862 | .uri=text/uri-list
863 | .uris=text/uri-list
864 | .urls=text/uri-list
865 | .ustar=application/x-ustar
866 | .utz=application/vnd.uiq.theme
867 | .uu=text/x-uuencode
868 | .uva=audio/vnd.dece.audio
869 | .uvd=application/vnd.dece.data
870 | .uvf=application/vnd.dece.data
871 | .uvg=image/vnd.dece.graphic
872 | .uvh=video/vnd.dece.hd
873 | .uvi=image/vnd.dece.graphic
874 | .uvm=video/vnd.dece.mobile
875 | .uvp=video/vnd.dece.pd
876 | .uvs=video/vnd.dece.sd
877 | .uvt=application/vnd.dece.ttml+xml
878 | .uvu=video/vnd.uvvu.mp4
879 | .uvv=video/vnd.dece.video
880 | .uvva=audio/vnd.dece.audio
881 | .uvvd=application/vnd.dece.data
882 | .uvvf=application/vnd.dece.data
883 | .uvvg=image/vnd.dece.graphic
884 | .uvvh=video/vnd.dece.hd
885 | .uvvi=image/vnd.dece.graphic
886 | .uvvm=video/vnd.dece.mobile
887 | .uvvp=video/vnd.dece.pd
888 | .uvvs=video/vnd.dece.sd
889 | .uvvt=application/vnd.dece.ttml+xml
890 | .uvvu=video/vnd.uvvu.mp4
891 | .uvvv=video/vnd.dece.video
892 | .uvvx=application/vnd.dece.unspecified
893 | .uvvz=application/vnd.dece.zip
894 | .uvx=application/vnd.dece.unspecified
895 | .uvz=application/vnd.dece.zip
896 | .vcard=text/vcard
897 | .vcd=application/x-cdlink
898 | .vcf=text/x-vcard
899 | .vcg=application/vnd.groove-vcard
900 | .vcs=text/x-vcalendar
901 | .vcx=application/vnd.vcx
902 | .vis=application/vnd.visionary
903 | .viv=video/vnd.vivo
904 | .vob=video/x-ms-vob
905 | .vor=application/vnd.stardivision.writer
906 | .vox=application/x-authorware-bin
907 | .vrml=model/vrml
908 | .vsd=application/vnd.visio
909 | .vsf=application/vnd.vsf
910 | .vss=application/vnd.visio
911 | .vst=application/vnd.visio
912 | .vsw=application/vnd.visio
913 | .vtu=model/vnd.vtu
914 | .vxml=application/voicexml+xml
915 | .w3d=application/x-director
916 | .wad=application/x-doom
917 | .wav=audio/x-wav
918 | .wax=audio/x-ms-wax
919 | .
920 | .wbmp=image/vnd.wap.wbmp
921 | .wbs=application/vnd.criticaltools.wbs+xml
922 | .wbxml=application/vnd.wap.wbxml
923 | .wcm=application/vnd.ms-works
924 | .wdb=application/vnd.ms-works
925 | .wdp=image/vnd.ms-photo
926 | .weba=audio/webm
927 | .webm=video/webm
928 | .webp=image/webp
929 | .wg=application/vnd.pmi.widget
930 | .wgt=application/widget
931 | .wks=application/vnd.ms-works
932 | .wm=video/x-ms-wm
933 | .wma=audio/x-ms-wma
934 | .wmd=application/x-ms-wmd
935 | .wmf=application/x-msmetafile
936 | .
937 | .wml=text/vnd.wap.wml
938 | .
939 | .wmlc=application/vnd.wap.wmlc
940 | .
941 | .wmls=text/vnd.wap.wmlscript
942 | .
943 | .wmlsc=application/vnd.wap.wmlscriptc
944 | .wmv=video/x-ms-wmv
945 | .wmx=video/x-ms-wmx
946 | .wmz=application/x-msmetafile
947 | .woff=application/x-font-woff
948 | .woff2=font/woff2
949 | .wpd=application/vnd.wordperfect
950 | .wpl=application/vnd.ms-wpl
951 | .wps=application/vnd.ms-works
952 | .wqd=application/vnd.wqd
953 | .wri=application/x-mswrite
954 | .wrl=model/vrml
955 | .wsdl=application/wsdl+xml
956 | .wspolicy=application/wspolicy+xml
957 | .wtb=application/vnd.webturbo
958 | .wvx=video/x-ms-wvx
959 | .x32=application/x-authorware-bin
960 | .x3d=model/x3d+xml
961 | .x3db=model/x3d+binary
962 | .x3dbz=model/x3d+binary
963 | .x3dv=model/x3d+vrml
964 | .x3dvz=model/x3d+vrml
965 | .x3dz=model/x3d+xml
966 | .xaml=application/xaml+xml
967 | .xap=application/x-silverlight-app
968 | .xar=application/vnd.xara
969 | .xbap=application/x-ms-xbap
970 | .xbd=application/vnd.fujixerox.docuworks.binder
971 | .xbm=image/x-xbitmap
972 | .xdf=application/xcap-diff+xml
973 | .xdm=application/vnd.syncml.dm+xml
974 | .xdp=application/vnd.adobe.xdp+xml
975 | .xdssc=application/dssc+xml
976 | .xdw=application/vnd.fujixerox.docuworks
977 | .xenc=application/xenc+xml
978 | .xer=application/patch-ops-error+xml
979 | .xfdf=application/vnd.adobe.xfdf
980 | .xfdl=application/vnd.xfdl
981 | .xht=application/xhtml+xml
982 | .xhtml=application/xhtml+xml
983 | .xhvml=application/xv+xml
984 | .xif=image/vnd.xiff
985 | .xla=application/vnd.ms-excel
986 | .xlam=application/vnd.ms-excel.addin.macroenabled.12
987 | .xlc=application/vnd.ms-excel
988 | .xlf=application/x-xliff+xml
989 | .xlm=application/vnd.ms-excel
990 | .xls=application/vnd.ms-excel
991 | .xlsb=application/vnd.ms-excel.sheet.binary.macroenabled.12
992 | .xlsm=application/vnd.ms-excel.sheet.macroenabled.12
993 | .xlsx=application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
994 | .xlt=application/vnd.ms-excel
995 | .xltm=application/vnd.ms-excel.template.macroenabled.12
996 | .xltx=application/vnd.openxmlformats-officedocument.spreadsheetml.template
997 | .xlw=application/vnd.ms-excel
998 | .xm=audio/xm
999 | .xml=application/xml
1000 | .xo=application/vnd.olpc-sugar
1001 | .xop=application/xop+xml
1002 | .xpi=application/x-xpinstall
1003 | .xpl=application/xproc+xml
1004 | .xpm=image/x-xpixmap
1005 | .xpr=application/vnd.is-xpr
1006 | .xps=application/vnd.ms-xpsdocument
1007 | .xpw=application/vnd.intercon.formnet
1008 | .xpx=application/vnd.intercon.formnet
1009 | .xsl=application/xml
1010 | .xslt=application/xslt+xml
1011 | .xsm=application/vnd.syncml+xml
1012 | .xspf=application/xspf+xml
1013 | .xul=application/vnd.mozilla.xul+xml
1014 | .xvm=application/xv+xml
1015 | .xvml=application/xv+xml
1016 | .xwd=image/x-xwindowdump
1017 | .xyz=chemical/x-xyz
1018 | .xz=application/x-xz
1019 | .yang=application/yang
1020 | .yin=application/yin+xml
1021 | .z=application/x-compress
1022 | .Z=application/x-compress
1023 | .z1=application/x-zmachine
1024 | .z2=application/x-zmachine
1025 | .z3=application/x-zmachine
1026 | .z4=application/x-zmachine
1027 | .z5=application/x-zmachine
1028 | .z6=application/x-zmachine
1029 | .z7=application/x-zmachine
1030 | .z8=application/x-zmachine
1031 | .zaz=application/vnd.zzazz.deck+xml
1032 | .zip=application/zip
1033 | .zir=application/vnd.zul
1034 | .zirz=application/vnd.zul
1035 | .zmm=application/vnd.handheld-entertainment+xml
1036 |
--------------------------------------------------------------------------------
/src/test/java/com/ljm/server/TestServer.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server;
2 |
3 | import com.ljm.server.config.ServerConfig;
4 | import org.junit.BeforeClass;
5 | import org.junit.Test;
6 |
7 | import static org.junit.Assert.assertTrue;
8 |
9 | /**
10 | * @author 李佳明 https://github.com/pkpk1234
11 | * @date 2018-01-2018/1/4
12 | */
13 | public class TestServer extends TestServerBase {
14 | private static Server server;
15 |
16 | @BeforeClass
17 | public static void init() {
18 |
19 | ServerConfig serverConfig = ServerConfig.builder().build();
20 | server = ServerFactory.getServer(serverConfig);
21 | }
22 |
23 | @Test
24 | public void testServerStart() {
25 | startServer(server);
26 | waitServerStart(server);
27 | assertTrue("服务器启动后,状态是STARTED", server.getStatus().equals(ServerStatus.STARTED));
28 | }
29 |
30 | @Test
31 | public void testServerStop() {
32 | server.stop();
33 | assertTrue("服务器关闭后,状态是STOPED", server.getStatus().equals(ServerStatus.STOPED));
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/src/test/java/com/ljm/server/TestServerAcceptRequest.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server;
2 |
3 | import com.ljm.server.config.ServerConfig;
4 | import com.ljm.server.demo.EchoEventHandler;
5 | import com.ljm.server.event.listener.EventListener;
6 | import com.ljm.server.io.connection.Connection;
7 | import com.ljm.server.io.connector.impl.socket.SocketConnector;
8 | import com.ljm.server.io.connector.impl.socket.SocketConnectorFactory;
9 | import com.ljm.server.io.event.listener.impl.ConnectionEventListener;
10 | import com.ljm.server.io.utils.IoUtils;
11 | import org.junit.AfterClass;
12 | import org.junit.BeforeClass;
13 | import org.junit.Test;
14 | import org.slf4j.Logger;
15 | import org.slf4j.LoggerFactory;
16 |
17 | import java.io.IOException;
18 | import java.net.InetSocketAddress;
19 | import java.net.Socket;
20 | import java.net.SocketAddress;
21 |
22 | import static org.junit.Assert.assertTrue;
23 |
24 | /**
25 | * @author 李佳明 https://github.com/pkpk1234
26 | * @date 2018-01-2018/1/5
27 | */
28 | public class TestServerAcceptRequest extends TestServerBase {
29 | private static Logger logger = LoggerFactory.getLogger(TestServerAcceptRequest.class);
30 | private static Server server;
31 | // 设置超时时间为500毫秒
32 | private static final int TIMEOUT = 500;
33 |
34 | @BeforeClass
35 | public static void init() {
36 | EventListener socketEventListener =
37 | new ConnectionEventListener(new EchoEventHandler());
38 | SocketConnector connector = SocketConnectorFactory.build(ServerConfig.DEFAULT_PORT, socketEventListener);
39 | ServerConfig serverConfig = ServerConfig.builder().addConnector(connector).build();
40 | server = ServerFactory.getServer(serverConfig);
41 | }
42 |
43 | @Test
44 | public void testServerAcceptRequest() {
45 | // 如果server没有启动,首先启动server
46 | if (server.getStatus().equals(ServerStatus.STOPED)) {
47 | startServer(server);
48 | waitServerStart(server);
49 | Socket socket = new Socket();
50 | SocketAddress endpoint = new InetSocketAddress("localhost",
51 | ServerConfig.DEFAULT_PORT);
52 | try {
53 | // 试图发送请求到服务器,超时时间为TIMEOUT
54 | socket.connect(endpoint, TIMEOUT);
55 | assertTrue("服务器启动后,能接受请求", socket.isConnected());
56 | } catch (IOException e) {
57 | logger.error(e.getMessage(), e);
58 | } finally {
59 | IoUtils.closeQuietly(socket);
60 | }
61 | }
62 | }
63 |
64 | @AfterClass
65 | public static void destroy() {
66 | server.stop();
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/test/java/com/ljm/server/TestServerBase.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 | import java.io.IOException;
7 |
8 | /**
9 | * @author 李佳明 https://github.com/pkpk1234
10 | * @date 2018-01-2018/1/6
11 | */
12 | public abstract class TestServerBase {
13 | private static Logger logger = LoggerFactory.getLogger(TestServerBase.class);
14 |
15 | /**
16 | * 在单独的线程中启动Server,如果启动不成功,抛出异常
17 | *
18 | * @param server
19 | */
20 | protected void startServer(Server server) {
21 | //在另外一个线程中启动server
22 | new Thread(() -> {
23 | try {
24 | server.start();
25 | } catch (IOException e) {
26 | //转为RuntimeException抛出,避免异常丢失
27 | throw new RuntimeException(e);
28 | }
29 | }).start();
30 | }
31 |
32 | /**
33 | * 等待Server启动
34 | *
35 | * @param server
36 | */
37 | protected void waitServerStart(Server server) {
38 | //如果server未启动,就sleep一下
39 | while (server.getStatus().equals(ServerStatus.STOPED)) {
40 | logger.info("等待server启动");
41 | try {
42 | Thread.sleep(500);
43 | } catch (InterruptedException e) {
44 | logger.error(e.getMessage(), e);
45 | }
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/test/java/com/ljm/server/TestServerSuite.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server;
2 |
3 | import org.junit.runner.RunWith;
4 | import org.junit.runners.Suite;
5 |
6 | /**
7 | * @author 李佳明 https://github.com/pkpk1234
8 | * @date 2018-01-2018/1/9
9 | */
10 | @RunWith(Suite.class)
11 | @Suite.SuiteClasses({
12 | TestServer.class,
13 | TestServerAcceptRequest.class
14 | })
15 | public class TestServerSuite {
16 | }
17 |
--------------------------------------------------------------------------------
/src/test/java/com/ljm/server/com/ljm/server/protocol/http/body/TestHttpBodyInputStream.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.com.ljm.server.protocol.http.body;
2 |
3 | import com.ljm.server.protocol.http.body.HttpBodyInputStream;
4 | import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
5 | import org.apache.commons.io.IOUtils;
6 | import org.junit.Test;
7 |
8 | import static org.junit.Assert.*;
9 |
10 | import org.slf4j.Logger;
11 | import org.slf4j.LoggerFactory;
12 |
13 | import java.io.ByteArrayInputStream;
14 | import java.io.IOException;
15 | import java.io.InputStream;
16 |
17 | /**
18 | * @author 李佳明 https://github.com/pkpk1234
19 | * @date 2018-01-2018/1/30
20 | */
21 | public class TestHttpBodyInputStream {
22 | private static final Logger LOGGER =
23 | LoggerFactory.getLogger(TestHttpBodyInputStream.class);
24 | /*
25 | 7
26 | Mozilla
27 | 9
28 | Developer
29 | 7
30 | Network
31 | 0
32 |
33 | */
34 | private static final String TEST_OTHER_MESSAGE = "testtest";
35 | private static final String CHUNKED_BODY = "7\r\n" +
36 | "Mozilla\r\n" +
37 | "9\r\n" +
38 | "Developer\r\n" +
39 | "7\r\n" +
40 | "Network\r\n" +
41 | "0\r\n" +
42 | "\r\n";
43 |
44 | private static final String BODY = CHUNKED_BODY + TEST_OTHER_MESSAGE;
45 |
46 | private static final String NORMAL_BODY = "Mozilla Developer Network";
47 |
48 | @Test
49 | public void testReadChunkedBody() throws IOException {
50 | InputStream in = new ByteArrayInputStream(BODY.getBytes());
51 | HttpBodyInputStream httpBodyInputStream = new HttpBodyInputStream(in, true);
52 | ByteOutputStream out = new ByteOutputStream();
53 | IOUtils.copy(httpBodyInputStream, out);
54 | LOGGER.info(out.toString());
55 | assertEquals(CHUNKED_BODY, out.toString());
56 | }
57 |
58 | @Test
59 | public void testReadBody() throws IOException {
60 | InputStream in = new ByteArrayInputStream(NORMAL_BODY.getBytes());
61 | HttpBodyInputStream httpBodyInputStream = new HttpBodyInputStream(in, false);
62 | httpBodyInputStream.setContentLength(25);
63 | ByteOutputStream out = new ByteOutputStream();
64 | IOUtils.copy(httpBodyInputStream, out);
65 | LOGGER.info(out.toString());
66 | assertEquals(NORMAL_BODY, out.toString());
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/test/java/com/ljm/server/com/ljm/server/protocol/http/response/TestHttpResponseMessageBuilder.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.com.ljm.server.protocol.http.response;
2 |
3 | import com.ljm.server.protocol.http.HttpResponseMessage;
4 | import com.ljm.server.protocol.http.ResponseLine;
5 | import com.ljm.server.protocol.http.header.HttpHeader;
6 | import com.ljm.server.protocol.http.header.HttpMessageHeaders;
7 | import com.ljm.server.protocol.http.header.IMessageHeaders;
8 | import com.ljm.server.protocol.http.response.HttpResponseMessageBuilder;
9 | import org.junit.Test;
10 | import sun.net.www.MimeTable;
11 |
12 | import javax.activation.MimetypesFileTypeMap;
13 |
14 | import java.io.IOException;
15 | import java.nio.file.Files;
16 | import java.nio.file.Path;
17 | import java.nio.file.Paths;
18 |
19 | import static org.junit.Assert.assertEquals;
20 | import static org.junit.Assert.assertNotNull;
21 |
22 | /**
23 | * @author 李佳明 https://github.com/pkpk1234
24 | * @date 2018/1/22
25 | */
26 | public class TestHttpResponseMessageBuilder {
27 | @Test
28 | public void test() {
29 | IMessageHeaders messageHeaders = new HttpMessageHeaders();
30 | messageHeaders.addHeader(new HttpHeader("Content-Type", "plain/html"));
31 | HttpResponseMessage result = HttpResponseMessageBuilder.builder()
32 | .withResponseLine(new ResponseLine("HTTP/1.1", 200, "OK"))
33 | .withMessageHeaders(messageHeaders).build();
34 | assertNotNull(result);
35 | assertEquals("plain/html",
36 | result.getMessageHeaders().getFirstHeader("Content-Type").getValue());
37 | }
38 |
39 | @Test
40 | public void test2() throws IOException {
41 | System.out.println(Files.probeContentType(Paths.get("", ".js")));
42 | System.out.println(Files.probeContentType(Paths.get("", ".html")));
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/test/java/com/ljm/server/io/connection/socket/TestSocketConnection.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.io.connection.socket;
2 |
3 | import org.junit.Test;
4 |
5 | import java.io.ByteArrayInputStream;
6 | import java.io.ByteArrayOutputStream;
7 | import java.io.IOException;
8 | import java.net.Socket;
9 |
10 | import static org.junit.Assert.assertEquals;
11 | import static org.mockito.Mockito.mock;
12 | import static org.mockito.Mockito.when;
13 |
14 | public class TestSocketConnection {
15 |
16 |
17 | @Test
18 | public void testRead() throws IOException {
19 | Socket socket = mock(Socket.class);
20 | when(socket.getInputStream()).thenReturn(new ByteArrayInputStream("Hello world".getBytes()));
21 | when(socket.getOutputStream()).thenReturn(new ByteArrayOutputStream(64));
22 |
23 | SocketConnection socketConnection = new SocketConnection(socket);
24 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(64);
25 | byte[] bytes = new byte[64];
26 | int readLength = -1;
27 | while ((readLength = socketConnection.read(bytes, 0, bytes.length)) != -1) {
28 | outputStream.write(bytes, 0, readLength);
29 | }
30 | assertEquals("Hello world", outputStream.toString("UTF-8"));
31 | }
32 |
33 | @Test
34 | public void testWrite() throws IOException {
35 | Socket socket = mock(Socket.class);
36 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(64);
37 | when(socket.getOutputStream()).thenReturn(byteArrayOutputStream);
38 |
39 | SocketConnection socketConnection = new SocketConnection(socket);
40 | String input = "Hello world";
41 | socketConnection.write(input.getBytes());
42 | assertEquals(input, byteArrayOutputStream.toString("UTF-8"));
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/test/java/com/ljm/server/protocol/http/parser/TestDefaultHttpBodyParser.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.parser;
2 |
3 | import com.ljm.server.protocol.http.body.HttpBody;
4 | import org.apache.commons.io.IOUtils;
5 | import org.junit.Test;
6 |
7 | import java.io.ByteArrayInputStream;
8 | import java.io.IOException;
9 | import java.io.UnsupportedEncodingException;
10 |
11 | import static org.junit.Assert.assertArrayEquals;
12 |
13 | /**
14 | * @author 李佳明 https://github.com/pkpk1234
15 | * @date 2018-01-2018/1/21
16 | */
17 | public class TestDefaultHttpBodyParser {
18 |
19 | private static final String BODY = "name=Professional%20Ajax&publisher=Wiley\r\n";
20 |
21 | @Test
22 | public void test() throws IOException {
23 | byte[] bytes = BODY.getBytes("utf-8");
24 | HttpParserContext.setInputStream(
25 | new ByteArrayInputStream(bytes));
26 | HttpParserContext.setBodyInfo(
27 | new BodyInfo("", "", true, bytes.length));
28 | DefaultHttpBodyParser httpBodyParser
29 | = new DefaultHttpBodyParser();
30 | HttpBody httpBody = httpBodyParser.parse();
31 | byte[] bodyContent =
32 | IOUtils.readFully(httpBody.getInputStream(), (int) httpBody.getContentLength());
33 | assertArrayEquals(BODY.getBytes(), bodyContent);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/test/java/com/ljm/server/protocol/http/parser/TestDefaultHttpHeaderParser.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.parser;
2 |
3 | import com.ljm.server.protocol.http.header.HttpMessageHeaders;
4 | import org.junit.Test;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 |
8 | import java.io.UnsupportedEncodingException;
9 | import java.util.regex.Matcher;
10 | import java.util.regex.Pattern;
11 |
12 | import static org.junit.Assert.assertEquals;
13 | import static org.junit.Assert.assertTrue;
14 |
15 | /**
16 | * @author 李佳明 https://github.com/pkpk1234
17 | * @date 2018-01-2018/1/16
18 | */
19 | public class TestDefaultHttpHeaderParser {
20 | private static final Logger LOGGER = LoggerFactory.getLogger(TestDefaultHttpHeaderParser.class);
21 | private static final String HTTP_MESSAGE =
22 | "POST / HTTP1.1\r\n" +
23 | "Host:www.wrox.com\r\n" +
24 | "User-Agent:Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)\r\n" +
25 | "Content-Type:application/x-www-form-urlencoded\r\n" +
26 | "Content-Length:40\r\n" +
27 | "Connection: Keep-Alive\r\n" +
28 | "\r\n" +
29 | "name=Professional%20Ajax&publisher=Wiley\r\n";
30 |
31 | @Test
32 | public void testRegex() {
33 | String regex = "(?m)^\r\n";
34 | Pattern pattern = Pattern.compile(regex);
35 | Matcher matcher = pattern.matcher(HTTP_MESSAGE);
36 | if (matcher.find()) {
37 | int end = matcher.end();
38 | LOGGER.info("" + end);
39 | LOGGER.info("rest {} ----", HTTP_MESSAGE.substring(0, end));
40 | }
41 |
42 | }
43 |
44 | @Test
45 | public void test() throws UnsupportedEncodingException {
46 | HttpParserContext context = new HttpParserContext();
47 | DefaultHttpHeaderParser httpHeaderParser = new DefaultHttpHeaderParser();
48 | context.setHttpMessageBytes(HTTP_MESSAGE.getBytes("utf-8"));
49 | HttpMessageHeaders httpHeaders = httpHeaderParser.parse();
50 | assertTrue(httpHeaders.hasHeader("Host"));
51 | assertEquals("40", httpHeaders.getFirstHeader("Content-Length").getValue());
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/test/java/com/ljm/server/protocol/http/parser/TestDefaultHttpQueryParameterParser.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.parser;
2 |
3 | import com.ljm.server.protocol.http.HttpQueryParameter;
4 | import com.ljm.server.protocol.http.HttpQueryParameters;
5 | import org.junit.Test;
6 |
7 | import java.util.List;
8 |
9 | import static org.junit.Assert.assertEquals;
10 | import static org.junit.Assert.assertNotNull;
11 |
12 | /**
13 | * @author 李佳明 https://github.com/pkpk1234
14 | * @date 2018-01-2018/1/15
15 | */
16 | public class TestDefaultHttpQueryParameterParser {
17 |
18 | @Test
19 | public void test() {
20 | String queryStr = "a=123&a1=1&b=456&a=321";
21 | HttpParserContext.setRequestQueryString(queryStr);
22 | DefaultHttpQueryParameterParser httpRequestParameterParser
23 | = new DefaultHttpQueryParameterParser();
24 | HttpQueryParameters result = httpRequestParameterParser.parse();
25 | List parameters = result.getQueryParameter("a");
26 | assertNotNull(parameters);
27 | assertEquals(2, parameters.size());
28 | assertEquals("123", parameters.get(0).getValue());
29 | assertEquals("321", parameters.get(1).getValue());
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/test/java/com/ljm/server/protocol/http/parser/TestDefaultHttpRequestLineParser.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.parser;
2 |
3 | import com.ljm.server.protocol.http.RequestLine;
4 | import org.junit.Test;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 |
8 | import java.net.URI;
9 |
10 | import static org.junit.Assert.assertEquals;
11 |
12 | /**
13 | * @author 李佳明 https://github.com/pkpk1234
14 | * @date 2018-01-2018/1/14
15 | */
16 | public class TestDefaultHttpRequestLineParser {
17 | private static final Logger
18 | LOGGER = LoggerFactory.getLogger(TestDefaultHttpRequestLineParser.class);
19 |
20 | @Test
21 | public void test() {
22 | HttpParserContext parserContext = new HttpParserContext();
23 | parserContext.setHttpMessageBytes("GET /hello.txt HTTP/1.1\r\n".getBytes());
24 | DefaultHttpRequestLineParser defaultRequestLineParser
25 | = new DefaultHttpRequestLineParser();
26 |
27 | RequestLine result = defaultRequestLineParser.parse();
28 | String method = result.getMethod();
29 | assertEquals("GET", method);
30 | final URI requestURI = result.getRequestURI();
31 | assertEquals(URI.create("/hello.txt"), requestURI);
32 | LOGGER.info(requestURI.getQuery());
33 | LOGGER.info(requestURI.getFragment());
34 | assertEquals("HTTP/1.1", result.getHttpVersion());
35 | assertEquals(requestURI.getQuery(), parserContext.getRequestQueryString());
36 | }
37 |
38 | @Test
39 | public void testQuery() {
40 | HttpParserContext parserContext = new HttpParserContext();
41 | parserContext.setHttpMessageBytes("GET /test?a=123&a1=1&b=456 HTTP/1.1\r\n".getBytes());
42 |
43 | DefaultHttpRequestLineParser defaultRequestLineParser
44 | = new DefaultHttpRequestLineParser();
45 | RequestLine result = defaultRequestLineParser.parse();
46 | String method = result.getMethod();
47 | assertEquals("GET", method);
48 | final URI requestURI = result.getRequestURI();
49 | assertEquals(URI.create("/test?a=123&a1=1&b=456"), requestURI);
50 | LOGGER.info(requestURI.getQuery());
51 | assertEquals("HTTP/1.1", result.getHttpVersion());
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/test/java/com/ljm/server/protocol/http/parser/TestDefaultHttpRequestMessageParser.java:
--------------------------------------------------------------------------------
1 | package com.ljm.server.protocol.http.parser;
2 |
3 | import com.ljm.server.protocol.http.HttpMessage;
4 | import com.ljm.server.protocol.http.HttpQueryParameters;
5 | import com.ljm.server.protocol.http.HttpRequestMessage;
6 | import com.ljm.server.protocol.http.RequestLine;
7 | import com.ljm.server.protocol.http.body.HttpBody;
8 | import com.ljm.server.protocol.http.header.IMessageHeaders;
9 | import org.apache.commons.io.IOUtils;
10 | import org.junit.Test;
11 |
12 | import java.io.ByteArrayInputStream;
13 | import java.io.IOException;
14 | import java.util.Optional;
15 |
16 | import static org.junit.Assert.*;
17 |
18 | /**
19 | * @author 李佳明 https://github.com/pkpk1234
20 | * @date 2018-01-2018/1/21
21 | */
22 | public class TestDefaultHttpRequestMessageParser {
23 | private static final String MESSAGE_NO_Body =
24 | "POST / HTTP/1.1\r\n" +
25 | "Host:www.wrox.com\r\n" +
26 | "User-Agent:Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)\r\n" +
27 | "Content-Type:application/x-www-form-urlencoded\r\n" +
28 | "Content-Length:40\r\n" +
29 | "Connection: Keep-Alive\r\n" +
30 | "\r\n";
31 | private static final String BODY = "name=Professional%20Ajax&publisher=Wiley";
32 | private static final String HTTP_MESSAGE = MESSAGE_NO_Body + BODY;
33 |
34 | @Test
35 | public void test() throws IOException {
36 | DefaultHttpRequestMessageParser httpRequestMessageParser
37 | = new DefaultHttpRequestMessageParser(new DefaultHttpRequestLineParser(),
38 | new DefaultHttpQueryParameterParser(),
39 | new DefaultHttpHeaderParser(),
40 | new DefaultHttpBodyParser());
41 | HttpMessage result
42 | = httpRequestMessageParser.parse(new ByteArrayInputStream(HTTP_MESSAGE.getBytes()));
43 | assertEquals("HTTP/1.1", result.getStartLine().getHttpVersion());
44 | HttpRequestMessage requestMessage
45 | = HttpRequestMessage.class.cast(result);
46 | RequestLine requestLine = requestMessage.getRequestLine();
47 | assertEquals("POST", requestLine.getMethod());
48 | HttpQueryParameters queryParameters = requestMessage.getHttpQueryParameters();
49 | queryParameters.hasRequestParameter("123");
50 |
51 | IMessageHeaders httpHeaders = requestMessage.getMessageHeaders();
52 | assertEquals(5, httpHeaders.getAllHeaders().size());
53 | assertEquals("Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)"
54 | , httpHeaders.getFirstHeader("User-Agent").getValue());
55 |
56 | Optional opBody = requestMessage.getHttpBody();
57 | assertTrue(opBody.isPresent());
58 | HttpBody httpBody = opBody.get();
59 | byte[] content = IOUtils.readFully(httpBody.getInputStream(), (int) opBody.get().getContentLength());
60 | assertArrayEquals(BODY.getBytes(), content);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/test/resources/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | front.log
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Hello Beggar Servlet Container
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/web/index.js:
--------------------------------------------------------------------------------
1 | var date = new Date();
2 | var content = "now is " + date;
3 | var div = document.querySelector("#content");
4 | div.innerHTML = content;
--------------------------------------------------------------------------------
/web/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: antiquewhite;
3 | }
4 |
5 | #content {
6 | background-color: cornflowerblue;
7 | }
--------------------------------------------------------------------------------
/web/spring.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pkpk1234/BeggarServletContainer/cfd2d32cc494beedeb74e357e7d4ce37c84e2904/web/spring.jpg
--------------------------------------------------------------------------------