Skip to content

Commit 72a370a

Browse files
committed
Added removeCookie() method and error classes, implemented encoding, better form data
1 parent 8b92b7c commit 72a370a

26 files changed

+487
-66
lines changed

src/main/java/dev/latvian/apps/tinyserver/HTTPServer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ private void handleClient(Socket socket) {
288288
builder.setStatus(HTTPStatus.NO_CONTENT);
289289
builder.addHeader("Allow", allowed.stream().map(HTTPMethod::name).collect(Collectors.joining(",")));
290290
out = new BufferedOutputStream(socket.getOutputStream(), bufferSize);
291-
builder.write(out, writeBody);
291+
builder.write(req, out, writeBody);
292292
out.flush();
293293
} else if (method == HTTPMethod.TRACE) {
294294
// no-op
@@ -345,7 +345,7 @@ private void handleClient(Socket socket) {
345345
}
346346

347347
out = new BufferedOutputStream(socket.getOutputStream(), bufferSize);
348-
builder.write(out, writeBody);
348+
builder.write(req, out, writeBody);
349349
out.flush();
350350

351351
upgradedToWebSocket = (WSSession) builder.getWSSession();

src/main/java/dev/latvian/apps/tinyserver/ServerRegistry.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import dev.latvian.apps.tinyserver.http.HTTPMethod;
55
import dev.latvian.apps.tinyserver.http.HTTPRequest;
66
import dev.latvian.apps.tinyserver.http.PathFileHandler;
7-
import dev.latvian.apps.tinyserver.http.RootPathFileHandler;
87
import dev.latvian.apps.tinyserver.http.response.HTTPResponse;
98
import dev.latvian.apps.tinyserver.ws.WSHandler;
109
import dev.latvian.apps.tinyserver.ws.WSSession;
@@ -56,12 +55,14 @@ default void redirect(String path, String redirect) {
5655
get(path, req -> res);
5756
}
5857

59-
default void files(String path, Path directory, Duration cacheDuration, boolean autoInedx) {
60-
if (autoInedx) {
61-
get(path, new RootPathFileHandler<>(path, directory));
58+
default void files(String path, Path directory, Duration cacheDuration, boolean autoIndex) {
59+
var handler = new PathFileHandler<REQ>(path, directory, cacheDuration, autoIndex);
60+
61+
if (autoIndex) {
62+
get(path, handler);
6263
}
6364

64-
get(path + "/<path>", new PathFileHandler<>(path, directory, cacheDuration, autoInedx));
65+
get(path + "/<path>", handler);
6566
}
6667

6768
<WSS extends WSSession<REQ>> WSHandler<REQ, WSS> ws(String path, WSSessionFactory<REQ, WSS> factory);

src/main/java/dev/latvian/apps/tinyserver/content/ByteContent.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package dev.latvian.apps.tinyserver.content;
22

3+
import java.io.IOException;
34
import java.io.OutputStream;
45
import java.net.http.HttpRequest;
56

@@ -10,10 +11,15 @@ public long length() {
1011
}
1112

1213
@Override
13-
public void write(OutputStream out) throws Exception {
14+
public void write(OutputStream out) throws IOException {
1415
out.write(bytes);
1516
}
1617

18+
@Override
19+
public byte[] toBytes() throws IOException {
20+
return bytes;
21+
}
22+
1723
@Override
1824
public HttpRequest.BodyPublisher bodyPublisher() {
1925
return HttpRequest.BodyPublishers.ofByteArray(bytes);

src/main/java/dev/latvian/apps/tinyserver/content/FileContent.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,15 @@ public String type() {
3030
}
3131

3232
@Override
33-
public void write(OutputStream out) throws Exception {
33+
public void write(OutputStream out) throws IOException {
3434
Files.copy(file, out);
3535
}
3636

37+
@Override
38+
public byte[] toBytes() throws IOException {
39+
return Files.readAllBytes(file);
40+
}
41+
3742
@Override
3843
public HttpRequest.BodyPublisher bodyPublisher() throws IOException {
3944
return HttpRequest.BodyPublishers.ofByteArray(Files.readAllBytes(file));

src/main/java/dev/latvian/apps/tinyserver/content/ResponseContent.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package dev.latvian.apps.tinyserver.content;
22

3+
import java.io.ByteArrayOutputStream;
34
import java.io.IOException;
45
import java.io.OutputStream;
56
import java.net.http.HttpRequest;
@@ -13,7 +14,13 @@ default String type() {
1314
return "";
1415
}
1516

16-
void write(OutputStream out) throws Exception;
17+
void write(OutputStream out) throws IOException;
18+
19+
default byte[] toBytes() throws IOException {
20+
var out = new ByteArrayOutputStream();
21+
write(out);
22+
return out.toByteArray();
23+
}
1724

1825
default HttpRequest.BodyPublisher bodyPublisher() throws IOException {
1926
throw new IllegalStateException("Body publisher not supported");

src/main/java/dev/latvian/apps/tinyserver/http/HTTPRequest.java

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,15 @@
1010

1111
import java.io.IOException;
1212
import java.io.InputStream;
13+
import java.net.URLDecoder;
1314
import java.nio.charset.StandardCharsets;
15+
import java.util.Arrays;
1416
import java.util.Collections;
1517
import java.util.HashMap;
18+
import java.util.HashSet;
1619
import java.util.List;
1720
import java.util.Map;
21+
import java.util.Set;
1822

1923
public class HTTPRequest {
2024
private HTTPServer<?> server;
@@ -29,6 +33,7 @@ public class HTTPRequest {
2933
private InputStream bodyStream = null;
3034
private Map<String, String> cookies = null;
3135
private Map<String, String> formData = null;
36+
private Set<String> acceptedEncodings = null;
3237

3338
@ApiStatus.Internal
3439
public void init(HTTPServer<?> server, HTTPMethod method, long startTime, String path, String[] pathParts, CompiledPath compiledPath, List<Header> headers, String queryString, Map<String, String> query, InputStream bodyStream) {
@@ -94,13 +99,21 @@ public Map<String, String> query() {
9499
return query;
95100
}
96101

102+
public String query(String key, String def) {
103+
return query.getOrDefault(key, def);
104+
}
105+
106+
public String query(String key) {
107+
return query(key, "");
108+
}
109+
97110
public List<Header> headers() {
98111
return Collections.unmodifiableList(headers);
99112
}
100113

101114
public String header(String name) {
102115
for (var header : headers) {
103-
if (header.key().equalsIgnoreCase(name)) {
116+
if (header.is(name)) {
104117
return header.value();
105118
}
106119
}
@@ -148,7 +161,7 @@ public Map<String, String> cookies() {
148161
cookies = new HashMap<>(4);
149162

150163
for (var header : headers) {
151-
if (header.key().equalsIgnoreCase("Cookie")) {
164+
if (header.is("Cookie")) {
152165
for (var part : header.value().split("; ")) {
153166
var parts = part.split("=", 2);
154167

@@ -169,6 +182,10 @@ public String cookie(String key) {
169182
}
170183

171184
public Map<String, String> formData() {
185+
if (method == HTTPMethod.GET || method == HTTPMethod.HEAD) {
186+
return query;
187+
}
188+
172189
if (formData == null) {
173190
formData = new HashMap<>(4);
174191

@@ -179,7 +196,7 @@ public Map<String, String> formData() {
179196
var parts = part.split("=", 2);
180197

181198
if (parts.length == 2) {
182-
formData.put(parts[0], parts[1]);
199+
formData.put(URLDecoder.decode(parts[0], StandardCharsets.UTF_8), URLDecoder.decode(parts[1], StandardCharsets.UTF_8));
183200
}
184201
}
185202
} catch (IOException e) {
@@ -190,11 +207,31 @@ public Map<String, String> formData() {
190207
return formData;
191208
}
192209

210+
public Set<String> acceptedEncodings() {
211+
if (acceptedEncodings == null) {
212+
acceptedEncodings = new HashSet<>(2);
213+
214+
for (var header : headers) {
215+
if (header.is("Accept-Encoding")) {
216+
Arrays.stream(header.value().split(",")).map(s -> s.trim().split(";")).forEach(s -> acceptedEncodings.add(s[0]));
217+
}
218+
}
219+
220+
acceptedEncodings = Set.of(header("Accept-Encoding").split(","));
221+
}
222+
223+
return acceptedEncodings;
224+
}
225+
193226
@Nullable
194227
public String formData(String key) {
195228
return formData().get(key);
196229
}
197230

231+
public String userAgent() {
232+
return header("User-Agent");
233+
}
234+
198235
public void beforeResponse(HTTPPayload payload, HTTPResponse response) {
199236
}
200237

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
package dev.latvian.apps.tinyserver.http;
22

33
public record Header(String key, String value) {
4+
public boolean is(String name) {
5+
return key.equalsIgnoreCase(name);
6+
}
47
}

src/main/java/dev/latvian/apps/tinyserver/http/PathFileHandler.java

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,42 +5,51 @@
55
import dev.latvian.apps.tinyserver.http.response.HTTPStatus;
66

77
import java.io.IOException;
8-
import java.nio.charset.StandardCharsets;
98
import java.nio.file.Files;
109
import java.nio.file.Path;
1110
import java.time.Duration;
1211

13-
public record PathFileHandler<REQ extends HTTPRequest>(String httpPath, Path directory, Duration cacheDuration, boolean autoInedx) implements HTTPHandler<REQ> {
12+
public record PathFileHandler<REQ extends HTTPRequest>(String httpPath, Path directory, Duration cacheDuration, boolean autoIndex) implements HTTPHandler<REQ> {
1413
@Override
1514
public HTTPResponse handle(REQ req) throws IOException {
16-
var path = directory.resolve(req.variable("path"));
15+
var pathVar = req.variables().get("path");
16+
17+
if (pathVar == null) {
18+
if (autoIndex && Files.exists(directory) && Files.isDirectory(directory) && Files.isReadable(directory)) {
19+
return PathFileHandler.index(httpPath, directory, directory);
20+
}
21+
22+
return HTTPStatus.NOT_FOUND;
23+
}
24+
25+
var path = directory.resolve(pathVar);
1726

1827
if (path.startsWith(directory) && Files.exists(path) && Files.isReadable(path)) {
1928
if (Files.isRegularFile(path)) {
2029
return HTTPResponse.ok().publicCache(cacheDuration).content(path);
21-
} else if (autoInedx && Files.isDirectory(path)) {
30+
} else if (autoIndex && Files.isDirectory(path)) {
2231
return index(httpPath, directory, path);
2332
}
2433
}
2534

2635
return HTTPStatus.NOT_FOUND;
2736
}
2837

29-
public static HTTPResponse index(String httpPath, Path rootDirectory, Path directory) throws IOException {
38+
private static HTTPResponse index(String httpPath, Path rootDirectory, Path directory) throws IOException {
3039
var sb = new StringBuilder();
3140
sb.append("<ul>");
3241

3342
if (!rootDirectory.equals(directory)) {
34-
sb.append("<li><a href=\"" + httpPath + "/" + rootDirectory.relativize(directory.getParent()) + "\">..</a></li>");
43+
sb.append("<li><a href=\"").append(httpPath).append('/').append(rootDirectory.relativize(directory.getParent())).append("\">..</a></li>");
3544
}
3645

3746
for (var file : Files.list(directory).sorted().toList()) {
3847
var name = file.getFileName().toString();
39-
sb.append("<li><a href=\"" + httpPath + "/" + rootDirectory.relativize(file) + "\">" + name + "</a></li>");
48+
sb.append("<li><a href=\"").append(httpPath).append('/').append(rootDirectory.relativize(file)).append("\">").append(name).append("</a></li>");
4049
}
4150

4251
sb.append("</ul>");
4352

44-
return HTTPResponse.ok().content(sb.toString().getBytes(StandardCharsets.UTF_8), MimeType.HTML);
53+
return HTTPResponse.ok().content(sb, MimeType.HTML);
4554
}
4655
}

src/main/java/dev/latvian/apps/tinyserver/http/RootPathFileHandler.java

Lines changed: 0 additions & 19 deletions
This file was deleted.

0 commit comments

Comments
 (0)