Skip to content

Commit 1ffa7b3

Browse files
committed
Add PatchResponder
1 parent 83f3b64 commit 1ffa7b3

File tree

2 files changed

+180
-0
lines changed

2 files changed

+180
-0
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package httpserver.responder;
2+
3+
import httpserver.AppConfig;
4+
import httpserver.Hasher;
5+
import httpserver.Request;
6+
import httpserver.file.FileOperator;
7+
import httpserver.file.PathExaminer;
8+
import httpserver.response.*;
9+
10+
import java.io.IOException;
11+
import java.nio.file.Path;
12+
13+
public class PatchResponder implements Responder {
14+
private PathExaminer pathExaminer;
15+
private final FileOperator fileOperator;
16+
private final Hasher hasher;
17+
18+
public PatchResponder(PathExaminer pathExaminer,
19+
FileOperator fileOperator, Hasher hasher) {
20+
this.pathExaminer = pathExaminer;
21+
this.fileOperator = fileOperator;
22+
this.hasher = hasher;
23+
}
24+
25+
@Override
26+
public Response respond(AppConfig appConfig, Request request) throws IOException {
27+
if (allowed(request.getPathString())) {
28+
Path fullPath = pathExaminer.getFullPath(appConfig.getRoot(), request.getPathString());
29+
if (pathExaminer.pathExists(fullPath)) {
30+
if (noIfMatchHeader(request)) {
31+
return new ConflictResponse();
32+
}
33+
if (matchingHash(fullPath, request)) {
34+
byte[] newFileContents = request.getBody().getBytes();
35+
fileOperator.appendToFile(fullPath, newFileContents);
36+
return new NoContentResponse(hasher.getHash(newFileContents));
37+
}
38+
return new ConflictResponse();
39+
} else {
40+
return new NotFoundResponse();
41+
}
42+
}
43+
return new MethodNotAllowedResponse();
44+
}
45+
46+
private boolean noIfMatchHeader(Request request) {
47+
return !request.hasHeader("If-Match");
48+
}
49+
50+
private boolean matchingHash(Path fullPath, Request request) {
51+
byte[] fileContents = pathExaminer.fileContents(fullPath);
52+
String ifMatchHash = request.getHeaderValue("If-Match");
53+
return hasher.matches(fileContents, ifMatchHash);
54+
}
55+
56+
public boolean allowed(String pathString) {
57+
return pathString.equals("/patch-content.txt");
58+
}
59+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package httpserver.responder;
2+
3+
import httpserver.AppConfig;
4+
import httpserver.Hasher;
5+
import httpserver.Request;
6+
import httpserver.file.FileOperator;
7+
import httpserver.file.PathExaminer;
8+
import httpserver.header.Header;
9+
import httpserver.response.Response;
10+
import org.junit.Ignore;
11+
import org.junit.Test;
12+
13+
import java.nio.file.Path;
14+
import java.util.Arrays;
15+
16+
import static org.junit.Assert.*;
17+
import static org.mockito.ArgumentMatchers.any;
18+
import static org.mockito.Mockito.*;
19+
20+
public class PatchResponderTest {
21+
private final PathExaminer pathExaminerMock;
22+
private final FileOperator fileOperatorMock;
23+
private final AppConfig appConfigMock;
24+
private final Path rootMock;
25+
private final Path fullPathMock;
26+
private final byte[] fileContentsMock;
27+
private final Hasher hasherMock;
28+
private PatchResponder patchResponder;
29+
30+
public PatchResponderTest() {
31+
pathExaminerMock = mock(PathExaminer.class);
32+
fileOperatorMock = mock(FileOperator.class);
33+
hasherMock = mock(Hasher.class);
34+
this.patchResponder = new PatchResponder(pathExaminerMock, fileOperatorMock, hasherMock);
35+
rootMock = mock(Path.class);
36+
appConfigMock = mock(AppConfig.class);
37+
when(appConfigMock.getRoot()).thenReturn(rootMock);
38+
fullPathMock = mock(Path.class);
39+
fileContentsMock = new byte[0];
40+
}
41+
42+
@Test
43+
public void returns409IfNoIfMatchHeader() throws Exception {
44+
String pathString = "/patch-content.txt";
45+
when(pathExaminerMock.pathExists(any())).thenReturn(true);
46+
when(pathExaminerMock.getFullPath(rootMock, pathString)).thenReturn(fullPathMock);
47+
when(hasherMock.matches(any(), any())).thenReturn(true);
48+
Header[] headers = new Header[0];
49+
Request request = new Request("PATCH", pathString, headers, "", "patch contents");
50+
51+
Response response = patchResponder.respond(appConfigMock, request);
52+
53+
assertEquals(409, response.getStatusCode());
54+
}
55+
56+
@Test
57+
public void callsHasherReturns204AndUpdatesFileWithRequestBodyIfHashesMatch() throws Exception {
58+
String pathString = "/patch-content.txt";
59+
when(pathExaminerMock.pathExists(any())).thenReturn(true);
60+
when(pathExaminerMock.getFullPath(rootMock, pathString)).thenReturn(fullPathMock);
61+
when(pathExaminerMock.fileContents(fullPathMock)).thenReturn("mock text".getBytes());
62+
when(hasherMock.matches(any(), any())).thenReturn(true);
63+
when(hasherMock.getHash(any())).thenReturn("hash code");
64+
String hashMock = "bfc13a";
65+
Header[] headers = new Header[]{new Header("If-Match", hashMock)};
66+
Request request = new Request("PATCH", pathString, headers, "", "patch contents");
67+
68+
Response response = patchResponder.respond(appConfigMock, request);
69+
70+
verify(pathExaminerMock).fileContents(fullPathMock);
71+
verify(hasherMock).matches("mock text".getBytes(), hashMock);
72+
verify(fileOperatorMock).appendToFile(fullPathMock, "patch contents".getBytes());
73+
assertEquals(204, response.getStatusCode());
74+
Header[] expected = new Header[]{new Header("ETag", "hash code")};
75+
assertTrue(Arrays.equals(expected, response.getHeaders()));
76+
}
77+
78+
@Test
79+
public void returns409IfHashesDontMatch() throws Exception {
80+
String pathString = "/patch-content.txt";
81+
when(pathExaminerMock.pathExists(any())).thenReturn(true);
82+
when(pathExaminerMock.getFullPath(rootMock, pathString)).thenReturn(fullPathMock);
83+
when(pathExaminerMock.fileContents(fullPathMock)).thenReturn("mock text".getBytes());
84+
when(hasherMock.matches(any(), any())).thenReturn(false);
85+
String hashMock = "bfc13a";
86+
Header[] headers = new Header[]{new Header("If-Match", hashMock)};
87+
Request request = new Request("PATCH", pathString, headers, "", "patch contents");
88+
89+
Response response = patchResponder.respond(appConfigMock, request);
90+
91+
verify(hasherMock).matches("mock text".getBytes(), hashMock);
92+
assertEquals(409, response.getStatusCode());
93+
}
94+
95+
@Test
96+
public void returns404IfAllowedButDoesntExist() throws Exception {
97+
String pathString = "/patch-content.txt";
98+
when(pathExaminerMock.pathExists(any())).thenReturn(false);
99+
when(pathExaminerMock.getFullPath(rootMock, pathString)).thenReturn(fullPathMock);
100+
Request request = new Request("PATCH", pathString, new Header[0], "", "data=example");
101+
102+
Response response = patchResponder.respond(appConfigMock, request);
103+
104+
assertEquals(404, response.getStatusCode());
105+
}
106+
107+
@Test
108+
public void returns405IfNotAllowed() throws Exception {
109+
Request request = new Request("PATCH", "/not_allowed", new Header[0], "", "data=example");
110+
111+
Response response = patchResponder.respond(appConfigMock, request);
112+
113+
assertEquals(405, response.getStatusCode());
114+
}
115+
116+
@Test
117+
public void PathContentTxtIsAllowed() throws Exception {
118+
assertTrue(patchResponder.allowed("/patch-content.txt"));
119+
assertFalse(patchResponder.allowed("/other"));
120+
}
121+
}

0 commit comments

Comments
 (0)