Skip to content

Commit 97b8e4a

Browse files
committed
Add Context.forward fix jooby-project#1637
1 parent be73d0d commit 97b8e4a

File tree

11 files changed

+114
-14
lines changed

11 files changed

+114
-14
lines changed

jooby/src/main/java/io/jooby/Context.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,16 @@ public interface Context extends Registry {
9595
*/
9696
@Nonnull Router getRouter();
9797

98+
/**
99+
* Forward executing to another route. We use the given path to find a matching route.
100+
*
101+
* NOTE: the entire handler pipeline is executed (filter, decorator, etc.).
102+
*
103+
* @param path Path to forward the request.
104+
* @return This context.
105+
*/
106+
@Nonnull Context forward(@Nonnull String path);
107+
98108
/**
99109
* Converts a value (single or hash) into the given type.
100110
*
@@ -219,6 +229,14 @@ public interface Context extends Registry {
219229
*/
220230
@Nonnull String getRequestPath();
221231

232+
/**
233+
* Set request path. This is usually done by Web Server or framework, but by user.
234+
*
235+
* @param path Request path.
236+
* @return This context.
237+
*/
238+
@Nonnull Context setRequestPath(@Nonnull String path);
239+
222240
/**
223241
* Path variable. Value is decoded.
224242
*

jooby/src/main/java/io/jooby/DefaultContext.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,12 @@ public interface DefaultContext extends Context {
131131
return session;
132132
}
133133

134+
@Override default @Nonnull Context forward(@Nonnull String path) {
135+
setRequestPath(path);
136+
getRouter().match(this).execute(this);
137+
return this;
138+
}
139+
134140
@Override default @Nonnull Value cookie(@Nonnull String name) {
135141
String value = cookieMap().get(name);
136142
return value == null ? Value.missing(name) : Value.value(this, name, value);

jooby/src/main/java/io/jooby/ForwardingContext.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ public ForwardingContext(@Nonnull Context context) {
5151
return this;
5252
}
5353

54+
@Nonnull @Override public Context forward(@Nonnull String path) {
55+
ctx.forward(path);
56+
return this;
57+
}
58+
5459
@Override public boolean matches(@Nonnull String pattern) {
5560
return ctx.matches(pattern);
5661
}
@@ -124,6 +129,11 @@ public ForwardingContext(@Nonnull Context context) {
124129
return ctx.getRequestPath();
125130
}
126131

132+
@Nonnull @Override public Context setRequestPath(@Nonnull String path) {
133+
ctx.setRequestPath(path);
134+
return this;
135+
}
136+
127137
@Nonnull @Override public Value path(@Nonnull String name) {
128138
return ctx.path(name);
129139
}

jooby/src/main/java/io/jooby/internal/RouterMatch.java

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,6 @@ public class RouterMatch implements Router.Match {
2727

2828
private Route.Handler handler;
2929

30-
public RouterMatch(Route route) {
31-
this.route = route;
32-
this.matches = true;
33-
}
34-
3530
public RouterMatch() {
3631
}
3732

@@ -41,10 +36,6 @@ public void key(List<String> keys) {
4136
}
4237
}
4338

44-
public void value(String value) {
45-
46-
}
47-
4839
public void value(Chi.ZeroCopyString value) {
4940
if (vars == Collections.EMPTY_MAP) {
5041
vars = new LinkedHashMap();

modules/jooby-jetty/src/main/java/io/jooby/internal/jetty/JettyContext.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public class JettyContext implements DefaultContext {
9595
private boolean responseStarted;
9696
private Boolean resetHeadersOnError;
9797
private final String method;
98-
private final String requestPath;
98+
private String requestPath;
9999
private CompletionListeners listeners;
100100

101101
public JettyContext(Request request, Router router, int bufferSize, long maxRequestSize) {
@@ -164,6 +164,11 @@ public JettyContext(Request request, Router router, int bufferSize, long maxRequ
164164
return requestPath;
165165
}
166166

167+
@Nonnull @Override public Context setRequestPath(@Nonnull String path) {
168+
this.requestPath = path;
169+
return this;
170+
}
171+
167172
@Nonnull @Override public Map<String, String> pathMap() {
168173
return pathMap;
169174
}

modules/jooby-netty/src/main/java/io/jooby/internal/netty/NettyContext.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ public class NettyContext implements DefaultContext, ChannelFutureListener {
112112
private Route route;
113113
ChannelHandlerContext ctx;
114114
private HttpRequest req;
115-
private final String path;
115+
private String path;
116116
private HttpResponseStatus status = HttpResponseStatus.OK;
117117
private boolean responseStarted;
118118
private QueryString query;
@@ -176,6 +176,11 @@ public NettyContext(ChannelHandlerContext ctx, HttpRequest req, Router router, S
176176
return path;
177177
}
178178

179+
@Nonnull @Override public Context setRequestPath(String path) {
180+
this.path = path;
181+
return this;
182+
}
183+
179184
@Nonnull @Override public Map<String, String> pathMap() {
180185
return pathMap;
181186
}

modules/jooby-test/src/main/java/io/jooby/MockContext.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.List;
3030
import java.util.Map;
3131
import java.util.concurrent.Executor;
32+
import java.util.function.Consumer;
3233
import java.util.stream.Collectors;
3334
import java.util.stream.IntStream;
3435

@@ -81,6 +82,10 @@ public class MockContext implements DefaultContext {
8182

8283
private CompletionListeners listeners = new CompletionListeners();
8384

85+
private Consumer<MockResponse> consumer;
86+
87+
private MockRouter mockRouter;
88+
8489
@Nonnull @Override public String getMethod() {
8590
return method;
8691
}
@@ -122,6 +127,14 @@ public class MockContext implements DefaultContext {
122127
return cookies;
123128
}
124129

130+
@Nonnull @Override public Context forward(@Nonnull String path) {
131+
setRequestPath(path);
132+
if (mockRouter != null) {
133+
mockRouter.call(getMethod(), path, this, consumer);
134+
}
135+
return this;
136+
}
137+
125138
/**
126139
* Set cookie map.
127140
*
@@ -680,4 +693,11 @@ public MockContext sendError(@Nonnull Throwable cause, @Nonnull StatusCode statu
680693
return method + " " + requestPath;
681694
}
682695

696+
void setConsumer(Consumer<MockResponse> consumer) {
697+
this.consumer = consumer;
698+
}
699+
700+
void setMockRouter(MockRouter mockRouter) {
701+
this.mockRouter = mockRouter;
702+
}
683703
}

modules/jooby-test/src/main/java/io/jooby/MockRouter.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package io.jooby;
77

88
import javax.annotation.Nonnull;
9+
import java.util.Optional;
910
import java.util.function.Consumer;
1011
import java.util.function.Supplier;
1112

@@ -410,6 +411,8 @@ private MockValue call(Jooby router, String method, String path, Context ctx,
410411
findContext.setMethod(method.toUpperCase());
411412
findContext.setRequestPath(path);
412413
findContext.setRouter(router);
414+
findContext.setConsumer(consumer);
415+
findContext.setMockRouter(this);
413416

414417
Router.Match match = router.match(findContext);
415418
Route route = match.route();
@@ -426,13 +429,14 @@ private MockValue call(Jooby router, String method, String path, Context ctx,
426429
value = handler.apply(ctx);
427430
if (ctx instanceof MockContext) {
428431
MockResponse response = ((MockContext) ctx).getResponse();
429-
if (!(value instanceof Context)) {
432+
if (value != null && !(value instanceof Context)) {
430433
response.setResult(value);
431434
}
432435
if (response.getContentLength() <= 0) {
433436
response.setContentLength(contentLength(value));
434437
}
435438
consumer.accept(response);
439+
return new SingleMockValue(Optional.ofNullable(response.value()).orElse(value));
436440
}
437441
return new SingleMockValue(value);
438442
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package issues;
2+
3+
import io.jooby.Jooby;
4+
import io.jooby.MockRouter;
5+
import org.junit.jupiter.api.Test;
6+
7+
import static org.junit.jupiter.api.Assertions.assertEquals;
8+
9+
public class Issue1637 {
10+
11+
@Test
12+
public void shouldForwardRoute() {
13+
Jooby app = new Jooby();
14+
15+
app.get("/foo/{var}", ctx -> ctx.forward("/bar/" + ctx.path("var").value()));
16+
17+
app.get("/bar/{id}", ctx -> ctx.path("id").intValue());
18+
19+
MockRouter router = new MockRouter(app);
20+
21+
assertEquals(123, router.get("/foo/123").value());
22+
}
23+
}

modules/jooby-utow/src/main/java/io/jooby/internal/utow/UtowContext.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public class UtowContext implements DefaultContext, IoCallback {
8484
private long responseLength = -1;
8585
private Boolean resetHeadersOnError;
8686
private final String method;
87-
private final String requestPath;
87+
private String requestPath;
8888
private UtowCompletionListener completionListener;
8989

9090
public UtowContext(HttpServerExchange exchange, Router router) {
@@ -146,6 +146,11 @@ public UtowContext(HttpServerExchange exchange, Router router) {
146146
return requestPath;
147147
}
148148

149+
@Nonnull @Override public Context setRequestPath(@Nonnull String path) {
150+
this.requestPath = path;
151+
return this;
152+
}
153+
149154
@Nonnull @Override public Map<String, String> pathMap() {
150155
return pathMap;
151156
}

0 commit comments

Comments
 (0)