├── README.md
├── sample
├── style.css
├── がぞう.png
├── script.js
├── sample.html
└── page2.html
├── .gitignore
├── src
├── main
│ └── java
│ │ └── gl8080
│ │ └── http
│ │ ├── Constant.java
│ │ ├── HttpMethod.java
│ │ ├── EmptyRequestException.java
│ │ ├── Main.java
│ │ ├── Status.java
│ │ ├── ContentType.java
│ │ ├── HttpResponse.java
│ │ ├── IOUtil.java
│ │ ├── HttpRequest.java
│ │ ├── HttpHeader.java
│ │ └── SimpleHttpServer.java
└── test
│ └── java
│ └── gl8080
│ └── http
│ ├── ContentTypeTest.java
│ ├── HttpResponseTest.java
│ ├── HttpRequestTest.java
│ └── HttpHeaderTest.java
├── にほんご.html
├── hello.html
└── LICENSE
/README.md:
--------------------------------------------------------------------------------
1 | # simple-http-server
2 |
--------------------------------------------------------------------------------
/sample/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: skyblue;
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .classpath
2 | .project
3 | .gradle/
4 | .settings/
5 | bin/
--------------------------------------------------------------------------------
/sample/がぞう.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opengl-8080/simple-http-server/HEAD/sample/がぞう.png
--------------------------------------------------------------------------------
/src/main/java/gl8080/http/Constant.java:
--------------------------------------------------------------------------------
1 | package gl8080.http;
2 |
3 | public class Constant {
4 | public static final String CRLF = "\r\n";
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/gl8080/http/HttpMethod.java:
--------------------------------------------------------------------------------
1 | package gl8080.http;
2 |
3 | public enum HttpMethod {
4 | GET,
5 | POST,
6 | PUT,
7 | DELETE,
8 | }
9 |
--------------------------------------------------------------------------------
/にほんご.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | しんぷる HTTP さーばー
6 |
7 |
8 | しんぷる HTTP さーばー!!
9 |
10 |
11 |
--------------------------------------------------------------------------------
/sample/script.js:
--------------------------------------------------------------------------------
1 | window.addEventListener('load', function() {
2 | var backButton = document.getElementById('back');
3 |
4 | backButton.addEventListener('click', function() {
5 | history.back();
6 | });
7 | });
--------------------------------------------------------------------------------
/hello.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Simple HTTP Server
6 |
7 |
8 | Hello Simple HTTP Server
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/main/java/gl8080/http/EmptyRequestException.java:
--------------------------------------------------------------------------------
1 | package gl8080.http;
2 |
3 | /**
4 | * 何故か空のリクエストが飛んでくるときがあるので、それを回避するための例外
5 | */
6 | public class EmptyRequestException extends RuntimeException {
7 | private static final long serialVersionUID = 1L;
8 | }
9 |
--------------------------------------------------------------------------------
/sample/sample.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Sample Page
6 |
7 |
8 |
9 |
10 |
11 |
12 | ページ2
13 |
14 |
--------------------------------------------------------------------------------
/src/main/java/gl8080/http/Main.java:
--------------------------------------------------------------------------------
1 | package gl8080.http;
2 |
3 | public class Main {
4 |
5 | public static void main(String[] args) throws Exception {
6 | System.out.println("start >>>");
7 |
8 | SimpleHttpServer server = new SimpleHttpServer();
9 | server.start();
10 | }
11 | }
--------------------------------------------------------------------------------
/sample/page2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Sample Page 2
6 |
7 |
8 |
9 |
10 | Page 2
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/main/java/gl8080/http/Status.java:
--------------------------------------------------------------------------------
1 | package gl8080.http;
2 |
3 | public enum Status {
4 | OK("200 OK"),
5 | NOT_FOUND("404 Not Found")
6 | ;
7 |
8 | private final String text;
9 |
10 | private Status(String text) {
11 | this.text = text;
12 | }
13 |
14 | @Override
15 | public String toString() {
16 | return this.text;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 opengl-8080
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/src/test/java/gl8080/http/ContentTypeTest.java:
--------------------------------------------------------------------------------
1 | package gl8080.http;
2 |
3 | import static org.hamcrest.CoreMatchers.*;
4 | import static org.junit.Assert.*;
5 |
6 | import org.junit.Test;
7 |
8 | public class ContentTypeTest {
9 |
10 | @Test
11 | public void html() {
12 | // exercise
13 | ContentType contentType = ContentType.toContentType("html");
14 |
15 | // verify
16 | assertThat(contentType, is(ContentType.TEXT_HTML));
17 | }
18 |
19 | @Test
20 | public void htm() {
21 | // exercise
22 | ContentType contentType = ContentType.toContentType("htm");
23 |
24 | // verify
25 | assertThat(contentType, is(ContentType.TEXT_HTML));
26 | }
27 |
28 | @Test
29 | public void css() {
30 | // exercise
31 | ContentType contentType = ContentType.toContentType("css");
32 |
33 | // verify
34 | assertThat(contentType, is(ContentType.TEXT_CSS));
35 | }
36 |
37 | @Test
38 | public void jpeg() {
39 | // exercise
40 | ContentType contentType = ContentType.toContentType("jpeg");
41 |
42 | // verify
43 | assertThat(contentType, is(ContentType.IMAGE_JPEG));
44 | }
45 |
46 | @Test
47 | public void unknown() {
48 | // exercise
49 | ContentType contentType = ContentType.toContentType("xxxx");
50 |
51 | // verify
52 | assertThat(contentType, is(ContentType.TEXT_PLAIN));
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/gl8080/http/ContentType.java:
--------------------------------------------------------------------------------
1 | package gl8080.http;
2 |
3 | import java.util.Arrays;
4 | import java.util.HashMap;
5 | import java.util.HashSet;
6 | import java.util.Map;
7 | import java.util.Objects;
8 | import java.util.Set;
9 | import java.util.stream.Stream;
10 |
11 | public enum ContentType {
12 | TEXT_PLAIN("text/plain", "txt"),
13 | TEXT_HTML("text/html", "html,htm"),
14 | TEXT_CSS("text/css", "css"),
15 | TEXT_XML("text/xml", "xml"),
16 | APPLICATION_JAVASCRIPT("application/javascript", "js"),
17 | APPLICATION_JSON("application/json", "json"),
18 | IMAGE_JPEG("image/jpeg", "jpg,jpeg"),
19 | IMAGE_PNG("image/png", "png"),
20 | IMAGE_GIF("image/gif", "gif"),
21 | ;
22 |
23 | private static final Map EXTENSION_CONTENT_TYPE_MAP = new HashMap<>();
24 |
25 | static {
26 | Stream.of(ContentType.values())
27 | .forEach(contentType -> {
28 | contentType.extensions.forEach(extension -> {
29 | EXTENSION_CONTENT_TYPE_MAP.put(extension.toUpperCase(), contentType);
30 | });
31 | });
32 | }
33 |
34 | private final String text;
35 | private final Set extensions = new HashSet<>();
36 |
37 | private ContentType(String text, String extensions) {
38 | this.text = text;
39 | this.extensions.addAll(Arrays.asList(extensions.split(",")));
40 | }
41 |
42 | @Override
43 | public String toString() {
44 | return this.text;
45 | }
46 |
47 | public static ContentType toContentType(String extension) {
48 | Objects.requireNonNull(extension);
49 | return EXTENSION_CONTENT_TYPE_MAP.getOrDefault(extension.toUpperCase(), TEXT_PLAIN);
50 | };
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/gl8080/http/HttpResponse.java:
--------------------------------------------------------------------------------
1 | package gl8080.http;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.io.OutputStream;
6 | import java.nio.file.Files;
7 | import java.util.HashMap;
8 | import java.util.Map;
9 | import java.util.Objects;
10 |
11 | public class HttpResponse {
12 |
13 | private final Status status;
14 | private Map headers = new HashMap<>();
15 | private String body;
16 | private File bodyFile;
17 |
18 | public HttpResponse(Status status) {
19 | Objects.requireNonNull(status);
20 | this.status = status;
21 | }
22 |
23 | public void addHeader(String string, Object value) {
24 | this.headers.put(string, value.toString());
25 | }
26 |
27 | public void setBody(String body) {
28 | this.body = body;
29 | }
30 |
31 | public void writeTo(OutputStream out) throws IOException {
32 | IOUtil.println(out, "HTTP/1.1 " + this.status);
33 |
34 | this.headers.forEach((key, value) -> {
35 | IOUtil.println(out, key + ": " + value);
36 | });
37 |
38 | if (this.body != null) {
39 | IOUtil.println(out, "");
40 | IOUtil.print(out, this.body);
41 | } else if (this.bodyFile != null) {
42 | IOUtil.println(out, "");
43 | Files.copy(this.bodyFile.toPath(), out);
44 | }
45 | }
46 |
47 | public void setBody(File file) {
48 | Objects.requireNonNull(file);
49 | this.bodyFile = file;
50 |
51 | String fileName = this.bodyFile.getName();
52 | String extension = fileName.substring(fileName.lastIndexOf('.') + 1);
53 |
54 | this.addHeader("Content-Type", ContentType.toContentType(extension));
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/test/java/gl8080/http/HttpResponseTest.java:
--------------------------------------------------------------------------------
1 | package gl8080.http;
2 |
3 | import static gl8080.http.Constant.*;
4 | import static org.hamcrest.CoreMatchers.*;
5 | import static org.junit.Assert.*;
6 |
7 | import java.io.ByteArrayOutputStream;
8 | import java.io.File;
9 | import java.nio.file.Files;
10 | import java.nio.file.StandardOpenOption;
11 |
12 | import org.junit.Rule;
13 | import org.junit.Test;
14 | import org.junit.rules.TemporaryFolder;
15 |
16 | public class HttpResponseTest {
17 |
18 | @Rule
19 | public TemporaryFolder tmpDir = new TemporaryFolder();
20 |
21 | @Test
22 | public void file_html() throws Exception {
23 | // setup
24 | File tmpFile = new File(tmpDir.getRoot(), "test.html");
25 | Files.write(tmpFile.toPath(), "hello!!".getBytes("UTF-8"), StandardOpenOption.CREATE);
26 |
27 | HttpResponse response = new HttpResponse(Status.OK);
28 | response.setBody(tmpFile);
29 |
30 | ByteArrayOutputStream out = new ByteArrayOutputStream();
31 |
32 | // exercise
33 | response.writeTo(out);
34 |
35 | // verify
36 | String actual = out.toString("UTF-8");
37 |
38 | assertThat(actual, is(
39 | "HTTP/1.1 200 OK" + CRLF +
40 | "Content-Type: text/html" + CRLF +
41 | CRLF +
42 | "hello!!"
43 | ));
44 | }
45 |
46 | @Test
47 | public void basic() throws Exception {
48 | // setup
49 | HttpResponse response = new HttpResponse(Status.OK);
50 | response.addHeader("Content-Type", ContentType.TEXT_HTML);
51 | response.setBody("Hello World!!
");
52 |
53 | ByteArrayOutputStream out = new ByteArrayOutputStream();
54 |
55 | // exercise
56 | response.writeTo(out);
57 |
58 | // verify
59 | String actual = out.toString("UTF-8");
60 | assertThat(actual, is(
61 | "HTTP/1.1 200 OK" + CRLF +
62 | "Content-Type: text/html" + CRLF +
63 | CRLF +
64 | "Hello World!!
"
65 | ));
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/java/gl8080/http/IOUtil.java:
--------------------------------------------------------------------------------
1 | package gl8080.http;
2 |
3 | import static gl8080.http.Constant.*;
4 |
5 | import java.io.ByteArrayInputStream;
6 | import java.io.IOException;
7 | import java.io.InputStream;
8 | import java.io.OutputStream;
9 | import java.io.UncheckedIOException;
10 | import java.nio.charset.Charset;
11 | import java.nio.charset.StandardCharsets;
12 | import java.util.ArrayList;
13 | import java.util.List;
14 |
15 | public class IOUtil {
16 |
17 | private static final Charset UTF_8 = StandardCharsets.UTF_8;
18 |
19 | public static void println(OutputStream out, String line) {
20 | print(out, line + CRLF);
21 | }
22 |
23 | public static void print(OutputStream out, String line) {
24 | try {
25 | out.write(line.getBytes(UTF_8));
26 | } catch (IOException e) {
27 | throw new UncheckedIOException(e);
28 | }
29 | }
30 |
31 | public static String readLine(InputStream in) throws IOException {
32 | List list = new ArrayList<>();
33 |
34 | while (true) {
35 | byte b = (byte)in.read();
36 |
37 | if (b == -1) {
38 | throw new EmptyRequestException();
39 | }
40 |
41 | list.add(b);
42 |
43 | int size = list.size();
44 | if (2 <= size) {
45 | char cr = (char)list.get(size - 2).byteValue();
46 | char lf = (char)list.get(size - 1).byteValue();
47 |
48 | if (cr == '\r' && lf == '\n') {
49 | break;
50 | }
51 | }
52 | }
53 |
54 | byte[] buffer = new byte[list.size() - 2]; // CRLF の分減らす
55 | for (int i = 0; i < list.size() - 2; i++) {
56 | buffer[i] = list.get(i);
57 | }
58 |
59 | return new String(buffer, UTF_8);
60 | }
61 |
62 | public static InputStream toInputStream(String string) {
63 | return new ByteArrayInputStream(string.getBytes(UTF_8));
64 | }
65 |
66 | public static String toString(byte[] buffer) {
67 | return new String(buffer, UTF_8);
68 | }
69 |
70 | private IOUtil() {}
71 | }
72 |
--------------------------------------------------------------------------------
/src/main/java/gl8080/http/HttpRequest.java:
--------------------------------------------------------------------------------
1 | package gl8080.http;
2 |
3 | import java.io.IOException;
4 | import java.io.InputStream;
5 | import java.io.UncheckedIOException;
6 |
7 | public class HttpRequest {
8 |
9 | private final HttpHeader header;
10 | private final String bodyText;
11 |
12 | public HttpRequest(InputStream input) {
13 | try {
14 | this.header = new HttpHeader(input);
15 | this.bodyText = this.readBody(input);
16 |
17 | } catch (IOException e) {
18 | throw new UncheckedIOException(e);
19 | }
20 | }
21 |
22 | private String readBody(InputStream in) throws IOException {
23 | if (this.header.isChunkedTransfer()) {
24 | return this.readBodyByChunkedTransfer(in);
25 | } else {
26 | return this.readBodyByContentLength(in);
27 | }
28 | }
29 |
30 | private String readBodyByChunkedTransfer(InputStream in) throws IOException {
31 | StringBuilder body = new StringBuilder();
32 |
33 | int chunkSize = Integer.parseInt(IOUtil.readLine(in), 16);
34 |
35 | while (chunkSize != 0) {
36 | byte[] buffer = new byte[chunkSize];
37 | in.read(buffer);
38 |
39 | body.append(IOUtil.toString(buffer));
40 |
41 | IOUtil.readLine(in); // chunk-body の末尾にある CRLF を読み飛ばす
42 | chunkSize = Integer.parseInt(IOUtil.readLine(in), 16);
43 | }
44 |
45 | return body.toString();
46 | }
47 |
48 | private String readBodyByContentLength(InputStream in) throws IOException {
49 | final int contentLength = this.header.getContentLength();
50 |
51 | if (contentLength <= 0) {
52 | return null;
53 | }
54 |
55 | byte[] buffer = new byte[contentLength];
56 | in.read(buffer);
57 |
58 | return IOUtil.toString(buffer);
59 | }
60 |
61 | public String getHeaderText() {
62 | return this.header.getText();
63 | }
64 |
65 | public String getBodyText() {
66 | return this.bodyText;
67 | }
68 |
69 | public HttpHeader getHeader() {
70 | return this.header;
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/src/main/java/gl8080/http/HttpHeader.java:
--------------------------------------------------------------------------------
1 | package gl8080.http;
2 |
3 | import static gl8080.http.Constant.*;
4 |
5 | import java.io.IOException;
6 | import java.io.InputStream;
7 | import java.net.URLDecoder;
8 | import java.util.HashMap;
9 | import java.util.Map;
10 |
11 | public class HttpHeader {
12 |
13 | private HttpMethod method;
14 | private String path;
15 | private final String headerText;
16 | private Map messageHeaders = new HashMap<>();
17 |
18 | public HttpHeader(InputStream in) throws IOException {
19 | StringBuilder header = new StringBuilder();
20 |
21 | header.append(this.readRequestLine(in))
22 | .append(this.readMessageLine(in));
23 |
24 | this.headerText = header.toString();
25 | }
26 |
27 | private String readRequestLine(InputStream in) throws IOException {
28 | String requestLine = IOUtil.readLine(in);
29 |
30 | String[] tmp = requestLine.split(" ");
31 | this.method = HttpMethod.valueOf(tmp[0].toUpperCase());
32 | this.path = URLDecoder.decode(tmp[1], "UTF-8");
33 |
34 | return requestLine + CRLF;
35 | }
36 |
37 | private StringBuilder readMessageLine(InputStream in) throws IOException {
38 | StringBuilder sb = new StringBuilder();
39 |
40 | String messageLine = IOUtil.readLine(in);
41 |
42 | while (messageLine != null && !messageLine.isEmpty()) {
43 | this.putMessageLine(messageLine);
44 |
45 | sb.append(messageLine + CRLF);
46 | messageLine = IOUtil.readLine(in);
47 | }
48 |
49 | return sb;
50 | }
51 |
52 | private void putMessageLine(String messageLine) {
53 | String[] tmp = messageLine.split(":");
54 | String key = tmp[0].trim();
55 | String value = tmp[1].trim();
56 | this.messageHeaders.put(key, value);
57 | }
58 |
59 | public String getText() {
60 | return this.headerText;
61 | }
62 |
63 | public int getContentLength() {
64 | return Integer.parseInt(this.messageHeaders.getOrDefault("Content-Length", "0"));
65 | }
66 |
67 | public boolean isChunkedTransfer() {
68 | return this.messageHeaders.getOrDefault("Transfer-Encoding", "-").equals("chunked");
69 | }
70 |
71 | public String getPath() {
72 | return this.path;
73 | }
74 |
75 | public boolean isGetMethod() {
76 | return this.method == HttpMethod.GET;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/java/gl8080/http/SimpleHttpServer.java:
--------------------------------------------------------------------------------
1 | package gl8080.http;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 | import java.io.OutputStream;
7 | import java.io.UncheckedIOException;
8 | import java.net.ServerSocket;
9 | import java.net.Socket;
10 | import java.util.concurrent.ExecutorService;
11 | import java.util.concurrent.Executors;
12 |
13 | public class SimpleHttpServer {
14 |
15 | private ExecutorService service = Executors.newCachedThreadPool();
16 |
17 | public void start() {
18 | try (ServerSocket server = new ServerSocket(80)) {
19 | while (true) {
20 | this.serverProcess(server);
21 | }
22 | } catch (Exception e) {
23 | e.printStackTrace(System.err);
24 | }
25 | }
26 |
27 | private void serverProcess(ServerSocket server) throws IOException {
28 | Socket socket = server.accept();
29 |
30 | this.service.execute(() -> {
31 | try (
32 | InputStream in = socket.getInputStream();
33 | OutputStream out = socket.getOutputStream();
34 | ) {
35 |
36 | HttpRequest request = new HttpRequest(in);
37 |
38 | HttpHeader header = request.getHeader();
39 |
40 | if (header.isGetMethod()) {
41 | File file = new File(".", header.getPath());
42 |
43 | if (file.exists() && file.isFile()) {
44 | this.respondLocalFile(file, out);
45 | } else {
46 | this.respondNotFoundError(out);
47 | }
48 | } else {
49 | this.respondOk(out);
50 | }
51 | } catch (EmptyRequestException e) {
52 | // ignore
53 | } catch (IOException e) {
54 | throw new UncheckedIOException(e);
55 | } finally {
56 | try {
57 | socket.close();
58 | } catch (IOException e) {
59 | e.printStackTrace();
60 | }
61 | }
62 | });
63 | }
64 |
65 | private void respondNotFoundError(OutputStream out) throws IOException {
66 | HttpResponse response = new HttpResponse(Status.NOT_FOUND);
67 | response.addHeader("Content-Type", ContentType.TEXT_PLAIN);
68 | response.setBody("404 Not Found");
69 | response.writeTo(out);
70 | }
71 |
72 | private void respondLocalFile(File file, OutputStream out) throws IOException {
73 | HttpResponse response = new HttpResponse(Status.OK);
74 | response.setBody(file);
75 | response.writeTo(out);
76 | }
77 |
78 | private void respondOk(OutputStream out) throws IOException {
79 | HttpResponse response = new HttpResponse(Status.OK);
80 | response.writeTo(out);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/test/java/gl8080/http/HttpRequestTest.java:
--------------------------------------------------------------------------------
1 | package gl8080.http;
2 |
3 | import static org.hamcrest.CoreMatchers.*;
4 | import static org.junit.Assert.*;
5 |
6 | import java.io.ByteArrayInputStream;
7 | import java.io.InputStream;
8 |
9 | import org.junit.Test;
10 |
11 | import static gl8080.http.Constant.*;
12 |
13 | public class HttpRequestTest {
14 |
15 | @Test
16 | public void chunk() throws Exception {
17 | // setup
18 | String header =
19 | "POST /chunk.txt HTTP/1.1" + CRLF
20 | + "User-Agent: curl/7.37.1" + CRLF
21 | + "Host: localhost" + CRLF
22 | + "Accept: */*" + CRLF
23 | + "Transfer-Encoding: chunked" + CRLF
24 | + "Expect: 100-continue" + CRLF;
25 |
26 | String body =
27 | "5" + CRLF
28 | + "start" + CRLF
29 | + "c" + CRLF
30 | + "123456789" + CRLF
31 | + "0" + CRLF
32 | + "3" + CRLF
33 | + "end" + CRLF
34 | + "0" + CRLF
35 | + CRLF;
36 |
37 | String httpRequestMessage = header + CRLF + body;
38 |
39 | InputStream in = new ByteArrayInputStream(httpRequestMessage.getBytes());
40 | HttpRequest request = new HttpRequest(in);
41 |
42 | // exercise
43 | String headerText = request.getHeaderText();
44 | String bodyText = request.getBodyText();
45 |
46 | // verify
47 | assertThat(headerText, is(header));
48 | assertThat(bodyText, is("start123456789" + CRLF + "0end"));
49 | }
50 |
51 | @Test
52 | public void contentLength() {
53 | // setup
54 | String header =
55 | "POST / HTTP/1.1" + CRLF
56 | + "User-Agent: curl/7.37.1" + CRLF
57 | + "Host: localhost" + CRLF
58 | + "Accept: */*" + CRLF
59 | + "Content-Length: 14" + CRLF
60 | + "Content-Type: application/x-www-form-urlencoded" + CRLF;
61 |
62 | String body = "Message Body!!";
63 |
64 | String httpRequestMessage = header + CRLF + body;
65 |
66 | InputStream in = new ByteArrayInputStream(httpRequestMessage.getBytes());
67 | HttpRequest request = new HttpRequest(in);
68 |
69 | // exercise
70 | String headerText = request.getHeaderText();
71 | String bodyText = request.getBodyText();
72 |
73 | // verify
74 | assertThat(headerText, is(header));
75 | assertThat(bodyText, is(body));
76 | }
77 |
78 | @Test
79 | public void contentLength_日本語() {
80 | // setup
81 | String header =
82 | "POST / HTTP/1.1" + CRLF
83 | + "User-Agent: curl/7.37.1" + CRLF
84 | + "Host: localhost" + CRLF
85 | + "Accept: */*" + CRLF
86 | + "Content-Length: 15" + CRLF
87 | + "Content-Type: application/x-www-form-urlencoded" + CRLF;
88 |
89 | String body = "あいうえお";
90 |
91 | String httpRequestMessage = header + CRLF + body;
92 |
93 | InputStream in = new ByteArrayInputStream(httpRequestMessage.getBytes());
94 | HttpRequest request = new HttpRequest(in);
95 |
96 | // exercise
97 | String headerText = request.getHeaderText();
98 | String bodyText = request.getBodyText();
99 |
100 | // verify
101 | assertThat(headerText, is(header));
102 | assertThat(bodyText, is(body));
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/test/java/gl8080/http/HttpHeaderTest.java:
--------------------------------------------------------------------------------
1 | package gl8080.http;
2 |
3 | import static gl8080.http.Constant.*;
4 | import static org.hamcrest.CoreMatchers.*;
5 | import static org.hamcrest.MatcherAssert.*;
6 |
7 | import org.junit.Test;
8 |
9 | public class HttpHeaderTest {
10 |
11 | @Test
12 | public void method_get() throws Exception {
13 | // setup
14 | String headerText = "GET /foo/bar HTTP/1.1" + CRLF
15 | + CRLF;
16 |
17 | // exercise
18 | HttpHeader header = new HttpHeader(IOUtil.toInputStream(headerText));
19 |
20 | // verify
21 | assertThat(header.isGetMethod(), is(true));
22 | }
23 |
24 | @Test
25 | public void path() throws Exception {
26 | // setup
27 | String headerText = "GET /foo/bar HTTP/1.1" + CRLF
28 | + CRLF;
29 |
30 | // exercise
31 | HttpHeader header = new HttpHeader(IOUtil.toInputStream(headerText));
32 |
33 | // verify
34 | assertThat(header.getPath(), is("/foo/bar"));
35 | }
36 |
37 | @Test
38 | public void path_url_encoding() throws Exception {
39 | // setup
40 | String headerText = "GET /foo/%e3%83%86%e3%82%b9%e3%83%88/bar HTTP/1.1" + CRLF
41 | + CRLF;
42 |
43 | // exercise
44 | HttpHeader header = new HttpHeader(IOUtil.toInputStream(headerText));
45 |
46 | // verify
47 | assertThat(header.getPath(), is("/foo/テスト/bar"));
48 | }
49 |
50 | @Test
51 | public void text() throws Exception {
52 | // setup
53 | String headerText =
54 | "POST /chunk.txt HTTP/1.1" + CRLF
55 | + "User-Agent: curl/7.37.1" + CRLF
56 | + "Host: localhost" + CRLF
57 | + "Accept: */*" + CRLF
58 | + "Transfer-Encoding: chunked" + CRLF
59 | + "Expect: 100-continue" + CRLF
60 | + CRLF;
61 |
62 | // exercise
63 | HttpHeader header = new HttpHeader(IOUtil.toInputStream(headerText));
64 |
65 | // verify
66 | assertThat(header.getText() + CRLF, is(headerText));
67 | }
68 |
69 |
70 | @Test
71 | public void contentLength() throws Exception {
72 | // setup
73 | String headerText =
74 | "POST / HTTP/1.1" + CRLF
75 | + "User-Agent: curl/7.37.1" + CRLF
76 | + "Host: localhost" + CRLF
77 | + "Accept: */*" + CRLF
78 | + "Content-Length: 14" + CRLF
79 | + "Content-Type: application/x-www-form-urlencoded" + CRLF
80 | + CRLF;
81 |
82 | // exercise
83 | HttpHeader header = new HttpHeader(IOUtil.toInputStream(headerText));
84 |
85 | // verify
86 | assertThat(header.getContentLength(), is(14));
87 | }
88 |
89 |
90 | @Test
91 | public void chunked() throws Exception {
92 | // setup
93 | String headerText =
94 | "POST /chunk.txt HTTP/1.1" + CRLF
95 | + "User-Agent: curl/7.37.1" + CRLF
96 | + "Host: localhost" + CRLF
97 | + "Accept: */*" + CRLF
98 | + "Transfer-Encoding: chunked" + CRLF
99 | + "Expect: 100-continue" + CRLF
100 | + CRLF;
101 |
102 | // exercise
103 | HttpHeader header = new HttpHeader(IOUtil.toInputStream(headerText));
104 |
105 | // verify
106 | assertThat(header.isChunkedTransfer(), is(true));
107 | }
108 |
109 | }
110 |
--------------------------------------------------------------------------------