Nhu Nguyen 6 年之前
當前提交
a3a2f5fa1e

+ 155
- 0
.gitignore 查看文件

@@ -0,0 +1,155 @@
1
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
2
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
3
+
4
+# User-specific stuff:
5
+.idea/**/workspace.xml
6
+.idea/**/tasks.xml
7
+.idea/dictionaries
8
+
9
+# Sensitive or high-churn files:
10
+.idea/**/dataSources/
11
+.idea/**/dataSources.ids
12
+.idea/**/dataSources.xml
13
+.idea/**/dataSources.local.xml
14
+.idea/**/sqlDataSources.xml
15
+.idea/**/dynamic.xml
16
+.idea/**/uiDesigner.xml
17
+
18
+# Gradle:
19
+.idea/**/gradle.xml
20
+.idea/**/libraries
21
+
22
+# CMake
23
+cmake-build-debug/
24
+
25
+# Mongo Explorer plugin:
26
+.idea/**/mongoSettings.xml
27
+
28
+## File-based project format:
29
+*.iws
30
+
31
+## Plugin-specific files:
32
+
33
+# IntelliJ
34
+/out/
35
+
36
+# mpeltonen/sbt-idea plugin
37
+.idea_modules/
38
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
39
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
40
+
41
+# User-specific stuff:
42
+*.iml
43
+.idea/**
44
+.idea/**/workspace.xml
45
+.idea/**/tasks.xml
46
+.idea/dictionaries
47
+
48
+# Sensitive or high-churn files:
49
+.idea/**/dataSources/
50
+.idea/**/dataSources.ids
51
+.idea/**/dataSources.xml
52
+.idea/**/dataSources.local.xml
53
+.idea/**/sqlDataSources.xml
54
+.idea/**/dynamic.xml
55
+.idea/**/uiDesigner.xml
56
+
57
+# Gradle:
58
+.idea/**/gradle.xml
59
+.idea/**/libraries
60
+
61
+# CMake
62
+cmake-build-debug/
63
+
64
+# Mongo Explorer plugin:
65
+.idea/**/mongoSettings.xml
66
+
67
+## File-based project format:
68
+*.iws
69
+
70
+## Plugin-specific files:
71
+
72
+# IntelliJ
73
+/out/
74
+
75
+# mpeltonen/sbt-idea plugin
76
+.idea_modules/
77
+
78
+# JIRA plugin
79
+atlassian-ide-plugin.xml
80
+
81
+# Cursive Clojure plugin
82
+.idea/replstate.xml
83
+
84
+# Crashlytics plugin (for Android Studio and IntelliJ)
85
+com_crashlytics_export_strings.xml
86
+crashlytics.properties
87
+crashlytics-build.properties
88
+fabric.properties# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
89
+                 # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
90
+
91
+                 # User-specific stuff:
92
+                 .idea/**/workspace.xml
93
+                 .idea/**/tasks.xml
94
+                 .idea/dictionaries
95
+
96
+                 # Sensitive or high-churn files:
97
+                 .idea/**/dataSources/
98
+                 .idea/**/dataSources.ids
99
+                 .idea/**/dataSources.xml
100
+                 .idea/**/dataSources.local.xml
101
+                 .idea/**/sqlDataSources.xml
102
+                 .idea/**/dynamic.xml
103
+                 .idea/**/uiDesigner.xml
104
+
105
+                 # Gradle:
106
+                 .idea/**/gradle.xml
107
+                 .idea/**/libraries
108
+
109
+                 # CMake
110
+                 cmake-build-debug/
111
+
112
+                 # Mongo Explorer plugin:
113
+                 .idea/**/mongoSettings.xml
114
+
115
+                 ## File-based project format:
116
+                 *.iws
117
+
118
+                 ## Plugin-specific files:
119
+
120
+                 # IntelliJ
121
+                 /out/
122
+
123
+                 # mpeltonen/sbt-idea plugin
124
+                 .idea_modules/
125
+
126
+                 # JIRA plugin
127
+                 atlassian-ide-plugin.xml
128
+
129
+                 # Cursive Clojure plugin
130
+                 .idea/replstate.xml
131
+
132
+                 # Crashlytics plugin (for Android Studio and IntelliJ)
133
+                 com_crashlytics_export_strings.xml
134
+                 crashlytics.properties
135
+                 crashlytics-build.properties
136
+                 fabric.properties
137
+# JIRA plugin
138
+atlassian-ide-plugin.xml
139
+
140
+# Cursive Clojure plugin
141
+.idea/replstate.xml
142
+
143
+# Crashlytics plugin (for Android Studio and IntelliJ)
144
+com_crashlytics_export_strings.xml
145
+crashlytics.properties
146
+crashlytics-build.properties
147
+fabric.properties
148
+
149
+.project
150
+.classpath
151
+.settings
152
+
153
+
154
+#maven build target
155
+target/

+ 30
- 0
pom.xml 查看文件

@@ -0,0 +1,30 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<project xmlns="http://maven.apache.org/POM/4.0.0"
3
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5
+    <modelVersion>4.0.0</modelVersion>
6
+
7
+    <groupId>com.zipcoder</groupId>
8
+    <artifactId>server</artifactId>
9
+    <version>1.0-SNAPSHOT</version>
10
+
11
+    <properties>
12
+        <maven.compiler.source>1.8</maven.compiler.source>
13
+        <maven.compiler.target>1.8</maven.compiler.target>
14
+    </properties>
15
+
16
+    <dependencies>
17
+        <dependency>
18
+            <groupId>junit</groupId>
19
+            <artifactId>junit</artifactId>
20
+            <version>4.12</version>
21
+        </dependency>
22
+        <!-- https://mvnrepository.com/artifact/org.easymock/easymock -->
23
+        <dependency>
24
+            <groupId>org.easymock</groupId>
25
+            <artifactId>easymock</artifactId>
26
+            <version>3.6</version>
27
+            <scope>test</scope>
28
+        </dependency>
29
+    </dependencies>
30
+</project>

+ 18
- 0
src/main/java/Main.java 查看文件

@@ -0,0 +1,18 @@
1
+import com.zipcoder.server.Server;
2
+
3
+public class Main {
4
+
5
+    public static void main(String[] args) {
6
+        try {
7
+            int port = 5000;
8
+            Server server = new Server(port);
9
+            System.out.println("\n***** Started server on port http://localhost:5000");
10
+
11
+            server.start();
12
+        } catch (Exception e) {
13
+            System.err.println("Failed to start the server.");
14
+            e.printStackTrace();
15
+            System.exit(1);
16
+        }
17
+    }
18
+}

+ 20
- 0
src/main/java/com/zipcoder/server/Config.java 查看文件

@@ -0,0 +1,20 @@
1
+package com.zipcoder.server;
2
+
3
+public class Config {
4
+    public static final String PUBLIC_DIRECTORY = getRootDirectory();
5
+
6
+    private static String getRootDirectory() {
7
+        String directory = System.getProperty("resource_directory");
8
+        if (directory == null) {
9
+            directory = Config.class.getResource("/resources").getPath();
10
+        }
11
+        return directory;
12
+    }
13
+
14
+    public static final String LOG_PATH = "/logs";
15
+    public static final String LOG_USERNAME = "admin";
16
+    public static final String LOG_PASSWORD = "hunter2";
17
+
18
+    public static final String ENCODE = "UTF-8";
19
+
20
+}

+ 75
- 0
src/main/java/com/zipcoder/server/Logger.java 查看文件

@@ -0,0 +1,75 @@
1
+package com.zipcoder.server;
2
+
3
+import java.io.*;
4
+import java.net.URISyntaxException;
5
+
6
+public class Logger {
7
+
8
+    private enum Type{
9
+        INFO, ERROR;
10
+    }
11
+
12
+    public static void info(String message) {
13
+        log(Type.INFO, message);
14
+    }
15
+
16
+    public static void error(String message, Exception e) {
17
+        log(Type.ERROR, message);
18
+
19
+        PrintWriter writer = getWriter();
20
+        e.printStackTrace(writer);
21
+        closeWriter(writer);
22
+    }
23
+
24
+    private static void log(Type type, String message) {
25
+        Writer writer = null;
26
+        try {
27
+            writer = writeMessage(type, message);
28
+        } catch (IOException e) {
29
+            System.err.println("Unable to write message");
30
+            e.printStackTrace();
31
+        } finally {
32
+            closeWriter(writer);
33
+        }
34
+    }
35
+
36
+    private static Writer writeMessage(Type type, String message) throws IOException {
37
+        Writer writer = getWriter();
38
+        writer.append(type.name() + ": " + message + "\n");
39
+        return writer;
40
+    }
41
+
42
+    private static void closeWriter(Writer writer) {
43
+        try {
44
+            if (writer != null){
45
+                writer.close();
46
+            }
47
+        } catch (IOException e) {
48
+            System.err.println("Unable to close log file.");
49
+        }
50
+    }
51
+
52
+    private static PrintWriter getWriter() {
53
+        PrintWriter writer = null;
54
+        try {
55
+            writer = new PrintWriter(new FileWriter(getLogFile(), true));
56
+        } catch (Exception e) {
57
+            handleException(e);
58
+            writer = new PrintWriter(System.out);
59
+        }
60
+        return writer;
61
+    }
62
+
63
+    private static File getLogFile() throws URISyntaxException, IOException {
64
+        File file = new File(Config.PUBLIC_DIRECTORY + Config.LOG_PATH);
65
+        if (!file.exists()){
66
+            file.createNewFile();
67
+        }
68
+        return file;
69
+    }
70
+
71
+    private static void handleException(Exception e) {
72
+        System.err.println("Unable to open log file.");
73
+        e.printStackTrace();
74
+    }
75
+}

+ 40
- 0
src/main/java/com/zipcoder/server/RequestHandler.java 查看文件

@@ -0,0 +1,40 @@
1
+package com.zipcoder.server;
2
+
3
+import sun.jvm.hotspot.HelloWorld;
4
+
5
+import java.io.IOException;
6
+import java.net.Socket;
7
+
8
+public class RequestHandler {
9
+    private final Socket clientSocket;
10
+
11
+
12
+    public RequestHandler(Socket clientSocket){
13
+        this.clientSocket = clientSocket;
14
+    }
15
+
16
+    public void run() {
17
+        try {
18
+            handleRequest();
19
+        } catch (IOException e) {
20
+            Logger.error("Cannot handle request because " + e.getMessage(), e);
21
+        } finally {
22
+            closeSocket();
23
+        }
24
+    }
25
+
26
+    private void closeSocket() {
27
+        if (clientSocket != null){
28
+            try {
29
+                clientSocket.close();
30
+            } catch (IOException e) {
31
+                Logger.error("Cannot close socket because " + e.getMessage(), e);
32
+            }
33
+        }
34
+    }
35
+
36
+    private void handleRequest() throws IOException {
37
+        ResponseHello response = new ResponseHello(this.clientSocket.getOutputStream());
38
+        response.writeResponse();
39
+    }
40
+}

+ 34
- 0
src/main/java/com/zipcoder/server/ResponseHello.java 查看文件

@@ -0,0 +1,34 @@
1
+package com.zipcoder.server;
2
+
3
+import java.io.IOException;
4
+import java.io.OutputStream;
5
+
6
+public class ResponseHello {
7
+
8
+
9
+    private final OutputStream outputStream;
10
+
11
+    public ResponseHello(OutputStream outputStream){
12
+        this.outputStream = outputStream;
13
+    }
14
+
15
+    public void writeResponse() throws IOException {
16
+        String responseMessage = "Hello, world!";
17
+        StringBuilder httpResponse = new StringBuilder();
18
+
19
+        buildHeader(httpResponse, responseMessage);
20
+        httpResponse.append("\r\n");
21
+        httpResponse.append(responseMessage);
22
+
23
+        System.out.println(httpResponse.toString());
24
+
25
+        this.outputStream.write(httpResponse.toString().getBytes());
26
+    }
27
+
28
+
29
+    private void buildHeader(StringBuilder httpResponse, String responseMessage) {
30
+        httpResponse.append("HTTP/1.1 200 OK\n");
31
+        httpResponse.append("text/html\n");
32
+        httpResponse.append("Content-Length: " + responseMessage.length() + "\n");
33
+    }
34
+}

+ 26
- 0
src/main/java/com/zipcoder/server/Server.java 查看文件

@@ -0,0 +1,26 @@
1
+package com.zipcoder.server;
2
+
3
+
4
+import java.io.IOException;
5
+import java.net.ServerSocket;
6
+import java.net.Socket;
7
+
8
+public class Server {
9
+    private ServerSocket serverSocket;
10
+
11
+    public Server(int port) throws IOException {
12
+        this.serverSocket = new ServerSocket(5000);
13
+    }
14
+
15
+    public Server(ServerSocket socket) {
16
+        this.serverSocket = socket;
17
+    }
18
+
19
+    public void start() throws IOException {
20
+        while(!serverSocket.isClosed()){
21
+            Socket clientSocket = serverSocket.accept();
22
+            RequestHandler handler = new RequestHandler(clientSocket);
23
+            handler.run();
24
+        }
25
+    }
26
+}

+ 1
- 0
src/main/resources/file1 查看文件

@@ -0,0 +1 @@
1
+file1 contents

+ 1
- 0
src/main/resources/file2 查看文件

@@ -0,0 +1 @@
1
+file2 contents

二進制
src/main/resources/high5.gif 查看文件


二進制
src/main/resources/hug_monster_inc.gif 查看文件


二進制
src/main/resources/hug_random.gif 查看文件


二進制
src/main/resources/hug_six_there_there.gif 查看文件


+ 1
- 0
src/main/resources/partial_content.txt 查看文件

@@ -0,0 +1 @@
1
+This is a file that contains text to read part of in order to fulfill a 206.

二進制
src/main/resources/so_happy.gif 查看文件


+ 1
- 0
src/main/resources/text-file.txt 查看文件

@@ -0,0 +1 @@
1
+file1 contents

二進制
src/main/resources/whale_hello.gif 查看文件


+ 33
- 0
src/test/java/com/zipcoder/server/RequestHandlerTest.java 查看文件

@@ -0,0 +1,33 @@
1
+package com.zipcoder.server;
2
+
3
+import com.zipcoder.server.mocks.MockSocket;
4
+import org.junit.Assert;
5
+import org.junit.Before;
6
+import org.junit.Test;
7
+
8
+public class RequestHandlerTest {
9
+    private MockSocket socket;
10
+    private RequestHandler handler;
11
+
12
+    @Before
13
+    public void setUp(){
14
+        socket = new MockSocket();
15
+        handler = new RequestHandler(socket);
16
+    }
17
+
18
+    @Test
19
+    public void testRun() {
20
+        socket.setRequest("GET / HTTP/1.1");
21
+
22
+        handler.run();
23
+        Assert.assertTrue(socket.getResponse().contains("HTTP/1.1 200 OK"));
24
+    }
25
+
26
+    @Test
27
+    public void testRun_whenExceptionIsThrownSocketIsClosed() {
28
+        socket.setRequest("GET /nonexistence HTTP/1.1");
29
+
30
+        handler.run();
31
+        Assert.assertTrue(socket.isClosed());
32
+    }
33
+}

+ 41
- 0
src/test/java/com/zipcoder/server/ServerTest.java 查看文件

@@ -0,0 +1,41 @@
1
+package com.zipcoder.server;
2
+
3
+import org.easymock.EasyMock;
4
+import org.junit.Test;
5
+
6
+import java.io.IOException;
7
+import java.net.ServerSocket;
8
+
9
+public class ServerTest {
10
+
11
+    @Test
12
+    public void testStart_whenSocketIsClosed() throws IOException {
13
+        ServerSocket serverSocket = getMockServerSocket(true);
14
+        EasyMock.replay(serverSocket);
15
+
16
+        Server server = new Server(serverSocket);
17
+        server.start();
18
+
19
+        EasyMock.verify(serverSocket);
20
+    }
21
+
22
+    @Test
23
+    public void testStart_whenSocketIsOpen() throws IOException {
24
+        ServerSocket serverSocket = getMockServerSocket(false, true);
25
+        EasyMock.expect(serverSocket.accept()).andReturn(null);
26
+        EasyMock.replay(serverSocket);
27
+
28
+        Server server = new Server(serverSocket);
29
+        server.start();
30
+
31
+        EasyMock.verify(serverSocket);
32
+    }
33
+
34
+    private ServerSocket getMockServerSocket(boolean... returnValues) {
35
+        ServerSocket serverSocket = EasyMock.createMock(ServerSocket.class);
36
+        for (boolean value : returnValues){
37
+            EasyMock.expect(serverSocket.isClosed()).andReturn(value);
38
+        }
39
+        return serverSocket;
40
+    }
41
+}

+ 18
- 0
src/test/java/com/zipcoder/server/mocks/MockInputStream.java 查看文件

@@ -0,0 +1,18 @@
1
+package com.zipcoder.server.mocks;
2
+
3
+import java.io.IOException;
4
+import java.io.InputStream;
5
+
6
+public class MockInputStream extends InputStream{
7
+
8
+    private String request;
9
+
10
+    public MockInputStream(String input) {
11
+
12
+    }
13
+
14
+    @Override
15
+    public int read() throws IOException {
16
+        return 0;  //To change body of implemented methods use File | Settings | File Templates.
17
+    }
18
+}

+ 30
- 0
src/test/java/com/zipcoder/server/mocks/MockOutputStream.java 查看文件

@@ -0,0 +1,30 @@
1
+package com.zipcoder.server.mocks;
2
+
3
+import java.io.IOException;
4
+import java.io.OutputStream;
5
+
6
+public class MockOutputStream extends OutputStream {
7
+    private String value = "";
8
+    private boolean close;
9
+
10
+    @Override
11
+    public void write(int i) throws IOException {
12
+    }
13
+
14
+    @Override
15
+    public void write(byte[] bytes, int off, int len) throws IOException {
16
+        value += (new String(bytes));
17
+    }
18
+
19
+    public String getValue(){
20
+        return value;
21
+    }
22
+
23
+    public void close(){
24
+        close = true;
25
+    }
26
+
27
+    public boolean isClosed(){
28
+        return close;
29
+    }
30
+}

+ 29
- 0
src/test/java/com/zipcoder/server/mocks/MockSocket.java 查看文件

@@ -0,0 +1,29 @@
1
+package com.zipcoder.server.mocks;
2
+
3
+import java.io.*;
4
+import java.net.Socket;
5
+
6
+public class MockSocket extends Socket{
7
+    private MockOutputStream output;
8
+    private InputStream input;
9
+
10
+    public MockOutputStream getOutputStream(){
11
+        if (output == null){
12
+            output = new MockOutputStream();
13
+        }
14
+        return output;
15
+    }
16
+
17
+    public InputStream getInputStream(){
18
+        return input;
19
+    }
20
+
21
+    public void setRequest(String request) {
22
+        input = new ByteArrayInputStream(request.getBytes());
23
+    }
24
+
25
+    public String getResponse() {
26
+        return output.getValue();
27
+    }
28
+
29
+}

+ 1
- 0
src/test/resources/file1 查看文件

@@ -0,0 +1 @@
1
+I am a test file

二進制
src/test/resources/image.gif 查看文件