Skip to content

Commit 34ecee6

Browse files
committed
Add handlers doc + fix bugs around HEAD request implementation
1 parent 3b48773 commit 34ecee6

File tree

14 files changed

+163
-7
lines changed

14 files changed

+163
-7
lines changed

docs/asciidoc/handlers.adoc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
== Handlers
2+
3+
This section describes some built-in handler provided by Jooby.
4+
5+
include::handlers/cors.adoc[]
6+
7+
include::handlers/head.adoc[]
8+
9+
include::handlers/trace.adoc[]
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
== CORS
1+
=== CorsHandler
22

33
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS[Cross-Origin Resource Sharing (CORS)] is a mechanism that uses additional HTTP headers to tell a
44
browser to let a web application running at one origin (domain) have permission to access selected

docs/asciidoc/handlers/head.adoc

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
=== HeadHandler
2+
3+
Jooby doesn't support `HTTP HEAD` requests by default. To support them you have two options:
4+
5+
- Use the built-in `HeadHandler`
6+
- Write your own head handler
7+
8+
The javadoc:HeadHandler[] supports `HEAD` requests over existing `GET` handlers.
9+
10+
.Head Example
11+
[source, java, role = "primary"]
12+
----
13+
import io.jooby.Jooby;
14+
import io.jooby.HeadHandler;
15+
...
16+
{
17+
18+
decorator(new HeadHandler()); <1>
19+
20+
get("/", ctx -> {
21+
...
22+
});
23+
}
24+
----
25+
26+
.Kotlin
27+
[source, kotlin, role = "secondary"]
28+
----
29+
import io.jooby.Jooby
30+
import io.jooby.HeadHandler
31+
...
32+
{
33+
decorator(HeadHandler()) <1>
34+
35+
get("/") {
36+
...
37+
}
38+
}
39+
----
40+
41+
<1> Install HeadHandler
42+
43+
`HEAD /` produces an empty response with a `Content-Length` header (when possible) and any other
44+
header produce it by the `GET` handler.
45+
46+
The `GET` handler is executed but produces an empty response.

docs/asciidoc/handlers/trace.adoc

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
=== TraceHandler
2+
3+
Jooby doesn't support `HTTP Trace` requests by default. To support them you have two options:
4+
5+
- Use the built-in `TraceHandler`
6+
- Write your own trace handler
7+
8+
The javadoc:TraceHandler[] supports `TRACE` requests over existing handlers.
9+
10+
.Head Example
11+
[source, java, role = "primary"]
12+
----
13+
import io.jooby.Jooby;
14+
import io.jooby.TraceHandler;
15+
...
16+
{
17+
18+
decorator(new TraceHandler()); <1>
19+
20+
get("/", ctx -> {
21+
...
22+
});
23+
}
24+
----
25+
26+
.Kotlin
27+
[source, kotlin, role = "secondary"]
28+
----
29+
import io.jooby.Jooby
30+
import io.jooby.TraceHandler
31+
...
32+
{
33+
decorator(TraceHandler()) <1>
34+
35+
get("/") {
36+
...
37+
}
38+
}
39+
----
40+
41+
<1> Install TraceHandler
42+
43+
`TRACE /` performs a message loop-back test along the path to the target resource, providing a
44+
useful debugging mechanism.

docs/asciidoc/index.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ include::mvc-api.adoc[]
226226

227227
include::error-handler.adoc[]
228228

229-
include::cors.adoc[]
229+
include::handlers.adoc[]
230230

231231
include::configuration.adoc[]
232232

examples/src/main/java/examples/HelloApp.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@
77

88
import io.jooby.Jooby;
99
import io.jooby.RouterOptions;
10+
import io.jooby.TraceHandler;
1011
import io.jooby.annotations.QueryParam;
1112

1213
import java.util.stream.Stream;
1314

1415
public class HelloApp extends Jooby {
1516

1617
{
18+
decorator(new TraceHandler());
19+
1720
setRouterOptions(new RouterOptions().setIgnoreCase(false).setIgnoreTrailingSlash(true));
1821

1922
get("/", ctx -> {

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,13 @@ public interface Context extends Registry {
759759
*/
760760
@Nonnull Context setResponseLength(long length);
761761

762+
/**
763+
* Get response content length or <code>-1</code> when none was set.
764+
*
765+
* @return Response content length or <code>-1</code> when none was set.
766+
*/
767+
long getResponseLength();
768+
762769
/**
763770
* Set/add a cookie to response.
764771
*

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

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

333+
@Override public long getResponseLength() {
334+
return ctx.getResponseLength();
335+
}
336+
333337
@Override @Nonnull public Context setResponseLength(long length) {
334338
ctx.setResponseLength(length);
335339
return this;

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

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
import io.jooby.AttachedFile;
99
import io.jooby.Context;
10-
import io.jooby.DefaultContext;
1110
import io.jooby.ForwardingContext;
1211
import io.jooby.MediaType;
1312
import io.jooby.MessageEncoder;
@@ -43,6 +42,7 @@ public HeadContext(@Nonnull Context context) {
4342
@Nonnull @Override public Context send(@Nonnull Path file) {
4443
try {
4544
ctx.setResponseLength(Files.size(file));
45+
checkSizeHeaders();
4646
ctx.setResponseType(MediaType.byFile(file));
4747
ctx.send(StatusCode.OK);
4848
return this;
@@ -53,6 +53,7 @@ public HeadContext(@Nonnull Context context) {
5353

5454
@Nonnull @Override public Context send(@Nonnull byte[] data) {
5555
ctx.setResponseLength(data.length);
56+
checkSizeHeaders();
5657
ctx.send(StatusCode.OK);
5758
return this;
5859
}
@@ -63,13 +64,15 @@ public HeadContext(@Nonnull Context context) {
6364

6465
@Nonnull @Override public Context send(@Nonnull ByteBuffer data) {
6566
ctx.setResponseLength(data.remaining());
67+
checkSizeHeaders();
6668
ctx.send(StatusCode.OK);
6769
return this;
6870
}
6971

7072
@Nonnull @Override public Context send(@Nonnull FileChannel file) {
7173
try {
7274
ctx.setResponseLength(file.size());
75+
checkSizeHeaders();
7376
ctx.send(StatusCode.OK);
7477
return this;
7578
} catch (IOException x) {
@@ -80,12 +83,13 @@ public HeadContext(@Nonnull Context context) {
8083
@Nonnull @Override public Context send(@Nonnull AttachedFile file) {
8184
ctx.setResponseLength(file.getFileSize());
8285
ctx.setResponseType(file.getContentType());
86+
checkSizeHeaders();
8387
ctx.send(StatusCode.OK);
8488
return this;
8589
}
8690

8791
@Nonnull @Override public Context send(@Nonnull InputStream input) {
88-
ctx.setResponseHeader("Transfer-Encoding", "chunked");
92+
checkSizeHeaders();
8993
ctx.send(input);
9094
ctx.send(StatusCode.OK);
9195
return this;
@@ -97,13 +101,14 @@ public HeadContext(@Nonnull Context context) {
97101
}
98102

99103
@Nonnull @Override public Context send(@Nonnull ReadableByteChannel channel) {
100-
ctx.setResponseHeader("Transfer-Encoding", "chunked");
104+
checkSizeHeaders();
101105
ctx.send(StatusCode.OK);
102106
return this;
103107
}
104108

105109
@Nonnull @Override public Context send(@Nonnull String data, @Nonnull Charset charset) {
106110
ctx.setResponseLength(data.getBytes(charset).length);
111+
checkSizeHeaders();
107112
ctx.send(StatusCode.OK);
108113
return this;
109114
}
@@ -127,13 +132,13 @@ public HeadContext(@Nonnull Context context) {
127132
}
128133

129134
@Nonnull @Override public Sender responseSender() {
130-
ctx.setResponseHeader("Transfer-Encoding", "chunked");
135+
checkSizeHeaders();
131136
ctx.send(StatusCode.OK);
132137
return new NoopSender();
133138
}
134139

135140
@Nonnull @Override public OutputStream responseStream() {
136-
ctx.setResponseHeader("Transfer-Encoding", "chunked");
141+
checkSizeHeaders();
137142
ctx.send(StatusCode.OK);
138143
return new NoopOutputStream();
139144
}
@@ -142,6 +147,14 @@ public HeadContext(@Nonnull Context context) {
142147
return new PrintWriter(responseStream());
143148
}
144149

150+
private void checkSizeHeaders() {
151+
if (ctx.getResponseLength() < 0) {
152+
ctx.setResponseHeader("Transfer-Encoding", "chunked");
153+
} else {
154+
ctx.removeResponseHeader("Transfer-Encoding");
155+
}
156+
}
157+
145158
private static class NoopOutputStream extends OutputStream {
146159
@Override public void write(@NotNull byte[] b) throws IOException {
147160
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,10 @@ public Context setResponseType(@Nonnull MediaType contentType, @Nullable Charset
298298
return this;
299299
}
300300

301+
@Override public long getResponseLength() {
302+
return response.getContentLength();
303+
}
304+
301305
@Nonnull public Context setResponseCookie(@Nonnull Cookie cookie) {
302306
if (responseCookies == null) {
303307
responseCookies = new HashMap<>();

0 commit comments

Comments
 (0)