Skip to content

Commit 491e863

Browse files
committed
Add flag to control when headers are reset on error
- fix #1383 - fix #1299
1 parent 91404a2 commit 491e863

14 files changed

Lines changed: 172 additions & 18 deletions

File tree

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,13 @@ public interface Context extends Registry {
751751
*/
752752
@Nonnull Context removeResponseHeader(@Nonnull String name);
753753

754+
/**
755+
* Clear/reset all the headers, including cookies.
756+
*
757+
* @return This context.
758+
*/
759+
@Nonnull Context removeResponseHeaders();
760+
754761
/**
755762
* Set response content length header.
756763
*
@@ -1068,4 +1075,20 @@ public interface Context extends Registry {
10681075
*/
10691076
boolean isResponseStarted();
10701077

1078+
/**
1079+
* True if response headers are cleared on application error. If none set it uses the
1080+
* default/global value specified by {@link RouterOptions#getResetHeadersOnError()}.
1081+
*
1082+
* @return True if response headers are cleared on application error. If none set it uses the
1083+
* default/global value specified by {@link RouterOptions#getResetHeadersOnError()}.
1084+
*/
1085+
boolean getResetHeadersOnError();
1086+
1087+
/**
1088+
* Set whenever reset/clear headers on application error.
1089+
*
1090+
* @param value True for reset/clear headers.
1091+
* @return This context.
1092+
*/
1093+
@Nonnull Context setResetHeadersOnError(boolean value);
10711094
}

jooby/src/main/java/io/jooby/CorsHandler.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public CorsHandler() {
7070
return ctx.send(StatusCode.FORBIDDEN);
7171
}
7272
} else if (isSimple(ctx)) {
73+
ctx.setResetHeadersOnError(false);
7374
simple(ctx, options, origin);
7475
} else {
7576
return ctx.send(StatusCode.FORBIDDEN);

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

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -339,11 +339,13 @@ public interface DefaultContext extends Context {
339339
return setResponseHeader(name, RFC1123.format(Instant.ofEpochMilli(value.getTime())));
340340
}
341341

342-
@Override @Nonnull default Context setResponseHeader(@Nonnull String name, @Nonnull Instant value) {
342+
@Override @Nonnull
343+
default Context setResponseHeader(@Nonnull String name, @Nonnull Instant value) {
343344
return setResponseHeader(name, RFC1123.format(value));
344345
}
345346

346-
@Override @Nonnull default Context setResponseHeader(@Nonnull String name, @Nonnull Object value) {
347+
@Override @Nonnull
348+
default Context setResponseHeader(@Nonnull String name, @Nonnull Object value) {
347349
if (value instanceof Date) {
348350
return setResponseHeader(name, (Date) value);
349351
}
@@ -390,7 +392,8 @@ public interface DefaultContext extends Context {
390392
return responseStream(consumer);
391393
}
392394

393-
@Override default @Nonnull Context responseStream(@Nonnull SneakyThrows.Consumer<OutputStream> consumer)
395+
@Override default @Nonnull Context responseStream(
396+
@Nonnull SneakyThrows.Consumer<OutputStream> consumer)
394397
throws Exception {
395398
try (OutputStream out = responseStream()) {
396399
consumer.accept(out);
@@ -406,7 +409,8 @@ public interface DefaultContext extends Context {
406409
return responseWriter(contentType, contentType.getCharset());
407410
}
408411

409-
@Override default @Nonnull Context responseWriter(@Nonnull SneakyThrows.Consumer<PrintWriter> consumer)
412+
@Override default @Nonnull Context responseWriter(
413+
@Nonnull SneakyThrows.Consumer<PrintWriter> consumer)
410414
throws Exception {
411415
return responseWriter(MediaType.text, consumer);
412416
}
@@ -416,7 +420,8 @@ public interface DefaultContext extends Context {
416420
return responseWriter(contentType, contentType.getCharset(), consumer);
417421
}
418422

419-
@Override default @Nonnull Context responseWriter(@Nonnull MediaType contentType, @Nullable Charset charset,
423+
@Override default @Nonnull Context responseWriter(@Nonnull MediaType contentType,
424+
@Nullable Charset charset,
420425
@Nonnull SneakyThrows.Consumer<PrintWriter> consumer) throws Exception {
421426
try (PrintWriter writer = responseWriter(contentType, charset)) {
422427
consumer.accept(writer);
@@ -428,7 +433,8 @@ public interface DefaultContext extends Context {
428433
return sendRedirect(StatusCode.FOUND, location);
429434
}
430435

431-
@Override default @Nonnull Context sendRedirect(@Nonnull StatusCode redirect, @Nonnull String location) {
436+
@Override default @Nonnull Context sendRedirect(@Nonnull StatusCode redirect,
437+
@Nonnull String location) {
432438
setResponseHeader("location", location);
433439
return send(redirect);
434440
}
@@ -474,21 +480,25 @@ public interface DefaultContext extends Context {
474480
* @param statusCode Status code.
475481
* @return This context.
476482
*/
477-
@Override @Nonnull default Context sendError(@Nonnull Throwable cause, @Nonnull StatusCode statusCode) {
483+
@Override @Nonnull default Context sendError(@Nonnull Throwable cause,
484+
@Nonnull StatusCode statusCode) {
478485
Router router = getRouter();
479486
if (isResponseStarted()) {
480487
router.getLog().error(ErrorHandler.errorMessage(this, statusCode), cause);
481488
} else {
482489
try {
490+
if (getResetHeadersOnError()) {
491+
removeResponseHeaders();
492+
}
483493
router.getErrorHandler().apply(this, cause, statusCode);
484494
} catch (Exception x) {
485495
router.getLog()
486496
.error("error handler resulted in exception {} {}", getMethod(), pathString(), x);
487497
}
488-
/** rethrow fatal exceptions: */
489-
if (SneakyThrows.isFatal(cause)) {
490-
throw SneakyThrows.propagate(cause);
491-
}
498+
}
499+
/** rethrow fatal exceptions: */
500+
if (SneakyThrows.isFatal(cause)) {
501+
throw SneakyThrows.propagate(cause);
492502
}
493503
return this;
494504
}

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,11 @@ public Context setResponseHeader(@Nonnull String name, @Nonnull Instant value) {
330330
return this;
331331
}
332332

333+
@Nonnull @Override public Context removeResponseHeaders() {
334+
ctx.removeResponseHeaders();
335+
return this;
336+
}
337+
333338
@Override public long getResponseLength() {
334339
return ctx.getResponseLength();
335340
}
@@ -517,6 +522,15 @@ public Context sendError(@Nonnull Throwable cause, @Nonnull StatusCode statusCod
517522
return ctx.isResponseStarted();
518523
}
519524

525+
@Override public boolean getResetHeadersOnError() {
526+
return ctx.getResetHeadersOnError();
527+
}
528+
529+
@Override public Context setResetHeadersOnError(boolean value) {
530+
ctx.setResetHeadersOnError(value);
531+
return this;
532+
}
533+
520534
@Nonnull @Override public <T> T require(@Nonnull Class<T> type) throws RegistryException {
521535
return ctx.require(type);
522536
}

jooby/src/main/java/io/jooby/Route.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ public interface Handler extends Serializable {
270270
* Handler for {@link StatusCode#METHOD_NOT_ALLOWED} responses.
271271
*/
272272
public static final Handler METHOD_NOT_ALLOWED = ctx -> {
273+
ctx.setResetHeadersOnError(false);
273274
// Allow header was generated by routing algorithm
274275
if (ctx.getMethod().equals(Router.OPTIONS)) {
275276
return ctx.send(StatusCode.OK);

jooby/src/main/java/io/jooby/Router.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ interface Match {
509509
* Find a matching route using the given context.
510510
*
511511
* If no match exists this method returns a route with a <code>404</code> handler.
512-
* See {@link Route.Handler#NOT_FOUND}.
512+
* See {@link Route#NOT_FOUND}.
513513
*
514514
* @param ctx Web Context.
515515
* @return A route match result.

jooby/src/main/java/io/jooby/RouterOptions.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
*/
66
package io.jooby;
77

8+
import javax.annotation.Nonnull;
9+
810
/**
911
* Router matching options. Specify whenever ignore case and trailing slash. Options:
1012
*
@@ -30,6 +32,8 @@ public class RouterOptions {
3032

3133
private boolean ignoreTrailingSlash;
3234

35+
private boolean resetHeadersOnError = true;
36+
3337
/**
3438
* Indicates whenever routing algorithm does case-sensitive matching or not on incoming request
3539
* path.
@@ -72,4 +76,24 @@ public RouterOptions setIgnoreTrailingSlash(boolean ignoreTrailingSlash) {
7276
this.ignoreTrailingSlash = ignoreTrailingSlash;
7377
return this;
7478
}
79+
80+
/**
81+
* True if response headers are cleared on application error. Reset is enabled by default.
82+
*
83+
* @return True if response headers are cleared on application error. Reset is enabled by default.
84+
*/
85+
public boolean getResetHeadersOnError() {
86+
return resetHeadersOnError;
87+
}
88+
89+
/**
90+
* Set whenever reset/clear headers on application error.
91+
*
92+
* @param resetHeadersOnError True for reset/clear headers.
93+
* @return This context.
94+
*/
95+
public @Nonnull RouterOptions setResetHeadersOnError(boolean resetHeadersOnError) {
96+
this.resetHeadersOnError = resetHeadersOnError;
97+
return this;
98+
}
7599
}

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,7 @@ public FlashMapImpl(Context ctx, Cookie template) {
3737
this.template = template;
3838
this.initialScope = seed;
3939
if (seed.size() > 0) {
40-
Cookie sync = toCookie();
41-
if (sync != null) {
42-
ctx.setResponseCookie(sync);
43-
}
40+
syncCookie();
4441
}
4542
}
4643

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ public class JettyContext implements Callback, DefaultContext {
8383
private Map<String, String> cookies;
8484
private HashMap<String, String> responseCookies;
8585
private boolean responseStarted;
86+
private Boolean resetHeadersOnError;
8687

8788
public JettyContext(Request request, Router router, int bufferSize, long maxRequestSize) {
8889
this.request = request;
@@ -294,6 +295,11 @@ public Context setResponseType(@Nonnull MediaType contentType, @Nullable Charset
294295
return this;
295296
}
296297

298+
@Nonnull @Override public Context removeResponseHeaders() {
299+
response.reset();
300+
return this;
301+
}
302+
297303
@Nonnull @Override public Context setResponseLength(long length) {
298304
response.setContentLengthLong(length);
299305
return this;
@@ -424,6 +430,17 @@ private Context sendStreamInternal(@Nonnull InputStream in) {
424430
return responseStarted;
425431
}
426432

433+
@Override public boolean getResetHeadersOnError() {
434+
return resetHeadersOnError == null
435+
? getRouter().getRouterOptions().getResetHeadersOnError()
436+
: resetHeadersOnError.booleanValue();
437+
}
438+
439+
@Override public Context setResetHeadersOnError(boolean resetHeadersOnError) {
440+
this.resetHeadersOnError = resetHeadersOnError;
441+
return this;
442+
}
443+
427444
@Override public void succeeded() {
428445
complete(null);
429446
}

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ public class NettyContext implements DefaultContext, ChannelFutureListener {
111111
private boolean needsFlush;
112112
private Map<String, String> cookies;
113113
private Map<String, String> responseCookies;
114+
private Boolean resetHeadersOnError;
114115

115116
public NettyContext(ChannelHandlerContext ctx, HttpRequest req, Router router, String path,
116117
int bufferSize) {
@@ -284,6 +285,11 @@ public NettyContext(ChannelHandlerContext ctx, HttpRequest req, Router router, S
284285
return this;
285286
}
286287

288+
@Nonnull @Override public Context removeResponseHeaders() {
289+
setHeaders.clear();
290+
return this;
291+
}
292+
287293
@Nonnull @Override public MediaType getResponseType() {
288294
return responseType == null ? MediaType.text : responseType;
289295
}
@@ -457,6 +463,17 @@ public void flush() {
457463
return responseStarted;
458464
}
459465

466+
@Override public boolean getResetHeadersOnError() {
467+
return resetHeadersOnError == null
468+
? getRouter().getRouterOptions().getResetHeadersOnError()
469+
: resetHeadersOnError.booleanValue();
470+
}
471+
472+
@Override public Context setResetHeadersOnError(boolean value) {
473+
this.resetHeadersOnError = value;
474+
return this;
475+
}
476+
460477
@Nonnull @Override public Context send(StatusCode statusCode) {
461478
responseStarted = true;
462479
if (!setHeaders.contains(CONTENT_LENGTH)) {

0 commit comments

Comments
 (0)