Skip to content

Commit 012e67d

Browse files
committed
Merge remote-tracking branch 'origin/main' into test/HttpResponse
2 parents 3d72223 + 7453592 commit 012e67d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1840
-319
lines changed

Dockerfile

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ COPY pom.xml pom.xml
66
RUN mvn dependency:go-offline -B
77

88
COPY src ./src
9-
RUN mvn clean package -DskipTests
9+
RUN mvn clean package -DskipTests -Dmaven.shade.skip=true
10+
RUN mvn dependency:copy-dependencies -DoutputDirectory=target/deps -DincludeScope=runtime
1011

1112
FROM eclipse-temurin:25-jre-alpine
1213

1314
WORKDIR /app
1415

15-
# might need to update this later when we have our explicit class names
16-
COPY --from=build /app/target/app.jar app.jar
17-
ENTRYPOINT ["java", "-jar", "app.jar"]
16+
COPY --from=build /app/target/deps/ libs/
17+
COPY --from=build /app/target/classes/ classes/
18+
19+
ENTRYPOINT ["java", "-cp", "classes:libs/*","org.juv25d.App"]

src/main/java/org/juv25d/App.java

Lines changed: 10 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,26 @@
11
package org.juv25d;
22

3+
import org.juv25d.di.Container;
34
import org.juv25d.filter.*;
45
import org.juv25d.logging.ServerLogging;
56
import org.juv25d.http.HttpParser;
6-
import org.juv25d.plugin.HealthCheckPlugin;
7-
import org.juv25d.plugin.MetricPlugin;
8-
import org.juv25d.plugin.NotFoundPlugin; // New import
9-
import org.juv25d.plugin.StaticFilesPlugin;
107
import org.juv25d.router.SimpleRouter;
118
import org.juv25d.util.ConfigLoader;
129

13-
import java.util.List;
14-
15-
import java.util.Set;
1610
import java.util.logging.Logger;
1711

1812
public class App {
1913
public static void main(String[] args) {
14+
2015
ConfigLoader config = ConfigLoader.getInstance();
2116
Logger logger = ServerLogging.getLogger();
2217
HttpParser httpParser = new HttpParser();
23-
Pipeline pipeline = new Pipeline();
24-
25-
pipeline.addGlobalFilter(new SecurityHeadersFilter(), 0);
2618

27-
pipeline.addGlobalFilter(new LoggingFilter(), 1);
19+
Container container = new Container("org.juv25d");
2820

29-
pipeline.addGlobalFilter(new IpFilter(Set.of(), Set.of()), 2);
30-
31-
if (config.isRateLimitingEnabled()) {pipeline.addGlobalFilter(new RateLimitingFilter(
32-
config.getRequestsPerMinute(), config.getBurstCapacity()), 3);}
33-
34-
List<RedirectRule> redirectRules = List.of(
35-
new RedirectRule("/old-page", "/new-page", 301),
36-
new RedirectRule("/temp", "https://example.com/temporary", 302),
37-
new RedirectRule("/docs/*", "/documentation/", 301)
38-
);
21+
container.bind(org.juv25d.router.Router.class, SimpleRouter.class);
3922

40-
pipeline.addGlobalFilter(new RedirectFilter(redirectRules), 4);
41-
42-
43-
SimpleRouter router = new SimpleRouter();
44-
router.registerPlugin("/metric", new MetricPlugin()); //Register MetricPlugin for a specified path
45-
router.registerPlugin("/health", new HealthCheckPlugin()); //Register HealthCheckPlugin for a specified path
46-
router.registerPlugin("/", new StaticFilesPlugin()); // Register StaticFilesPlugin for the root path
47-
router.registerPlugin("/*", new StaticFilesPlugin()); // Register StaticFilesPlugin for all paths
48-
router.registerPlugin("/notfound", new NotFoundPlugin()); // Example: Register NotFoundPlugin for a specific path
49-
50-
pipeline.setRouter(router);
23+
Pipeline pipeline = Bootstrap.init(container, "org.juv25d");
5124

5225
DefaultConnectionHandlerFactory handlerFactory =
5326
new DefaultConnectionHandlerFactory(httpParser, logger, pipeline);
@@ -58,6 +31,11 @@ public static void main(String[] args) {
5831
handlerFactory,
5932
pipeline
6033
);
34+
35+
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
36+
logger.info("Server shutting down...");
37+
}));
38+
6139
server.start();
6240
}
6341
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package org.juv25d;
2+
3+
import org.juv25d.di.Container;
4+
import org.juv25d.filter.*;
5+
import org.juv25d.router.Router;
6+
7+
import java.util.List;
8+
import java.util.logging.Logger;
9+
10+
public class Bootstrap {
11+
12+
private static final Logger logger = Logger.getLogger(Bootstrap.class.getName());
13+
14+
public static Pipeline init(Container container, String basePackage) {
15+
16+
FilterRegistry registry = new FilterRegistry();
17+
FilterFactory factory = new FilterFactory(container);
18+
19+
FilterScanner.scan(basePackage, registry, factory);
20+
21+
logger.info("Filters initialized:");
22+
logger.info(" Global: " + registry.getGlobalFilters().stream()
23+
.sorted()
24+
.map(fr -> fr.filter().getClass().getSimpleName())
25+
.toList());
26+
logger.info(" Route-specific: " + registry.getRouteFilters().values().stream()
27+
.flatMap(List::stream)
28+
.distinct()
29+
.sorted()
30+
.map(fr -> fr.filter().getClass().getSimpleName())
31+
.toList());
32+
33+
Router router = container.get(Router.class);
34+
container.get(org.juv25d.router.RouterConfig.class);
35+
36+
return new Pipeline(new FilterMatcher(registry), router);
37+
}
38+
}
39+

src/main/java/org/juv25d/ConnectionHandler.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ public ConnectionHandler(Socket socket, HttpParser httpParser, Logger logger, Pi
2525

2626
@Override
2727
public void run() {
28+
String connectionId = java.util.UUID.randomUUID().toString().substring(0, 8);
29+
org.juv25d.logging.LogContext.setConnectionId(connectionId);
2830
try (socket) {
2931
var in = socket.getInputStream();
3032
var out = socket.getOutputStream();
@@ -56,6 +58,8 @@ public void run() {
5658

5759
} catch (IOException e) {
5860
logger.log(Level.SEVERE, "Error while handling request", e);
61+
} finally {
62+
org.juv25d.logging.LogContext.clear();
5963
}
6064
}
6165
}

src/main/java/org/juv25d/Pipeline.java

Lines changed: 8 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2,73 +2,24 @@
22

33
import org.juv25d.filter.Filter;
44
import org.juv25d.filter.FilterChainImpl;
5+
import org.juv25d.filter.FilterMatcher;
56
import org.juv25d.http.HttpRequest;
6-
import org.juv25d.router.Router; // New import
7+
import org.juv25d.router.Router;
78

8-
import java.util.ArrayList;
9-
import java.util.Collections;
109
import java.util.List;
11-
import java.util.Map;
12-
import java.util.concurrent.ConcurrentHashMap;
13-
import java.util.concurrent.CopyOnWriteArrayList;
14-
import java.util.stream.Collectors;
1510

1611
public class Pipeline {
1712

18-
private final List<FilterRegistration> globalFilters = new CopyOnWriteArrayList<>();
19-
private final Map<String, List<FilterRegistration>> routeFilters = new ConcurrentHashMap<>();
20-
private volatile List<Filter> sortedGlobalFilters = List.of();
21-
private volatile Router router; // Changed from Plugin plugin;
13+
private final FilterMatcher matcher;
14+
private final Router router;
2215

23-
public void addGlobalFilter(Filter filter, int order) {
24-
globalFilters.add(new FilterRegistration(filter, order, null));
25-
sortedGlobalFilters = globalFilters.stream()
26-
.sorted()
27-
.map(FilterRegistration::filter)
28-
.collect(Collectors.toUnmodifiableList());
29-
}
30-
31-
public void addRouteFilter(Filter filter, int order, String pattern) {
32-
routeFilters.computeIfAbsent(pattern, k -> new CopyOnWriteArrayList<>())
33-
.add(new FilterRegistration(filter, order, pattern));
34-
}
35-
36-
public void setRouter(Router router) {
37-
if (router == null) {
38-
throw new IllegalArgumentException("Router cannot be null");
39-
}
16+
public Pipeline(FilterMatcher matcher, Router router) {
17+
this.matcher = matcher;
4018
this.router = router;
4119
}
4220

4321
public FilterChainImpl createChain(HttpRequest request) {
44-
List<Filter> filters = new ArrayList<>();
45-
filters.addAll(sortedGlobalFilters);
46-
String path = request.path();
47-
List<FilterRegistration> exactMatches = routeFilters.get(path);
48-
if (exactMatches != null) {
49-
exactMatches.stream()
50-
.sorted()
51-
.map(FilterRegistration::filter)
52-
.forEach(filters::add);
53-
}
54-
55-
for (Map.Entry<String, List<FilterRegistration>> entry : routeFilters.entrySet()) {
56-
String pattern = entry.getKey();
57-
if (pattern.endsWith("*") && path.startsWith(pattern.substring(0, pattern.length() - 1))) {
58-
entry.getValue().stream()
59-
.sorted()
60-
.map(FilterRegistration::filter)
61-
.forEach(filters::add);
62-
}
63-
}
64-
return new FilterChainImpl(filters, router); // Pass router instead of plugin
65-
}
66-
67-
public List<Filter> getFilters() {
68-
return Collections.unmodifiableList(sortedGlobalFilters);
69-
}
70-
71-
public Router getRouter() { // Renamed from getPlugin
72-
return router;
22+
List<Filter> filters = matcher.match(request);
23+
return new FilterChainImpl(filters, router);
7324
}
7425
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.juv25d.config;
2+
3+
import java.util.Map;
4+
5+
public class FilterConfig {
6+
7+
private final Map<String, String> params;
8+
9+
public FilterConfig(Map<String, String> params) {
10+
this.params = params;
11+
}
12+
13+
public String get(String key) {
14+
return params.get(key);
15+
}
16+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package org.juv25d.config;
2+
3+
import java.util.Set;
4+
5+
public class IpFilterConfig {
6+
7+
public Set<String> whitelist() {
8+
return Set.of();
9+
}
10+
11+
public Set<String> blacklist() {
12+
return Set.of();
13+
}
14+
15+
private final boolean allowByDefault = true;
16+
17+
public boolean allowByDefault() {
18+
return allowByDefault;
19+
}
20+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package org.juv25d.config;
2+
3+
import org.juv25d.util.ConfigLoader;
4+
5+
public class RateLimitConfig {
6+
7+
private final long rpm;
8+
private final long burst;
9+
private final boolean enabled;
10+
11+
public RateLimitConfig() {
12+
ConfigLoader config = ConfigLoader.getInstance();
13+
this.rpm = config.getRequestsPerMinute();
14+
this.burst = config.getBurstCapacity();
15+
this.enabled = config.isRateLimitingEnabled();
16+
}
17+
18+
public long rpm() { return rpm; }
19+
public long burst() { return burst; }
20+
public boolean isEnabled() { return enabled; }
21+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.juv25d.config;
2+
3+
import org.juv25d.filter.RedirectRule;
4+
5+
import java.util.List;
6+
7+
public class RedirectConfig {
8+
9+
public List<RedirectRule> rules() {
10+
return List.of(
11+
new RedirectRule("/old-page", "/new-page", 301),
12+
new RedirectRule("/temp", "https://example.com/temporary", 302),
13+
new RedirectRule("/docs/*", "/documentation/", 301)
14+
);
15+
}
16+
}

0 commit comments

Comments
 (0)