├── 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 | --------------------------------------------------------------------------------