Skip to content

Commit 7f89196

Browse files
committed
Add raw data hanlders
1 parent 6b11bf1 commit 7f89196

File tree

15 files changed

+284
-52
lines changed

15 files changed

+284
-52
lines changed

docs/body.adoc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
=== Request Body
22

3-
Parsing of `request body` is available via javadoc:Context[body] method:
3+
Raw `request body` is available via javadoc:Context[body] method:
44

55
[source, java]
66
----
@@ -26,7 +26,7 @@ Parsing of `request body` is available via javadoc:Context[body] method:
2626
<2> `HTTP Body` as `byte`
2727
<3> `HTTP Body` as `InputStream`
2828

29-
This give us the `raw body`. For custom object you need to provide a `Parser`.
29+
This give us the `raw body`.
3030

3131
==== Parser
3232

@@ -51,7 +51,7 @@ and returns a single result of the given type.
5151
5252
parser(MediaType.json, (ctx, type) -> { // <2>
5353
54-
String body = ctx.body().value(); // <3>
54+
byte[] body = ctx.body().bytes(); // <3>
5555
5656
return lib.fromJson(body, type); // <4>
5757
});
@@ -64,7 +64,7 @@ and returns a single result of the given type.
6464

6565
<1> Choose your favorite `json` library
6666
<2> Check if the `Content-Type` header matches `application/json`
67-
<3> Ready the body as `String`
67+
<3> Ready the body as `byte[]`
6868
<4> Parse the `body` and use the requested type
6969
<5> Route handler now call the `body(Type)` function to trigger the parser function
7070

jooby/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,12 @@
150150
<version>1</version>
151151
</dependency>
152152

153+
<!-- netty-buffer -->
154+
<dependency>
155+
<groupId>io.netty</groupId>
156+
<artifactId>netty-buffer</artifactId>
157+
</dependency>
158+
153159
<!-- rxjava -->
154160
<dependency>
155161
<groupId>io.reactivex.rxjava2</groupId>

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package io.jooby;
1717

1818
import io.jooby.internal.UrlParser;
19+
import io.netty.buffer.ByteBuf;
1920

2021
import javax.annotation.Nonnull;
2122
import javax.annotation.Nullable;
@@ -361,8 +362,10 @@ default long requestLength() {
361362

362363
default @Nonnull Context render(@Nonnull Object result) {
363364
try {
364-
byte[] encode = route().renderer().encode(this, result);
365-
sendBytes(encode);
365+
Route route = route();
366+
Renderer renderer = route.renderer();
367+
byte[] bytes = renderer.encode(this, result);
368+
sendBytes(bytes);
366369
return this;
367370
} catch (Exception x) {
368371
throw Throwing.sneakyThrow(x);
@@ -425,8 +428,7 @@ default long requestLength() {
425428

426429
default @Nonnull Context sendRedirect(@Nonnull StatusCode redirect, @Nonnull String location) {
427430
header("location", location);
428-
sendStatusCode(redirect);
429-
return this;
431+
return sendStatusCode(redirect);
430432
}
431433

432434
default @Nonnull Context sendString(@Nonnull String data) {
@@ -439,6 +441,10 @@ default long requestLength() {
439441

440442
@Nonnull Context sendBytes(@Nonnull ByteBuffer data);
441443

444+
default @Nonnull Context sendBytes(@Nonnull ByteBuf data) {
445+
return sendBytes(data.nioBuffer());
446+
}
447+
442448
@Nonnull Context sendStream(@Nonnull InputStream input);
443449

444450
default @Nonnull Context sendFile(@Nonnull Path file) {

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

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
import io.jooby.Reified;
2121
import io.jooby.Route;
2222
import io.jooby.Route.Handler;
23+
import io.jooby.internal.handler.ByteArrayHandler;
24+
import io.jooby.internal.handler.ByteBufHandler;
25+
import io.jooby.internal.handler.ByteBufferHandler;
26+
import io.jooby.internal.handler.CharSequenceHandler;
2327
import io.jooby.internal.handler.CompletionStageHandler;
2428
import io.jooby.internal.handler.DefaultHandler;
2529
import io.jooby.internal.handler.DetachHandler;
@@ -36,9 +40,11 @@
3640
import io.jooby.internal.handler.WorkerHandler;
3741
import io.jooby.internal.handler.reactive.RxFlowableHandler;
3842
import io.jooby.internal.handler.reactive.RxSingleHandler;
43+
import io.netty.buffer.ByteBuf;
3944

4045
import java.io.File;
4146
import java.io.InputStream;
47+
import java.nio.ByteBuffer;
4248
import java.nio.channels.FileChannel;
4349
import java.nio.file.Path;
4450
import java.util.Optional;
@@ -116,17 +122,32 @@ public static Handler compute(ClassLoader loader, Route route, ExecutionMode mod
116122
if (Flow.Publisher.class.isAssignableFrom(type)) {
117123
return javaFlowPublisher(mode, route, executor);
118124
}
125+
/** Context: */
119126
if (Context.class.isAssignableFrom(type)) {
120127
return next(mode, executor, new NoopHandler(route.pipeline()), true);
121128
}
122-
129+
/** InputStream: */
123130
if (InputStream.class.isAssignableFrom(type)) {
124131
return next(mode, executor, new InputStreamHandler(route.pipeline()), true);
125132
}
126-
133+
/** FileChannel: */
127134
if (FileChannel.class.isAssignableFrom(type) || Path.class.isAssignableFrom(type) || File.class
128135
.isAssignableFrom(type)) {
129-
return next(mode, executor, new FileChannelHandler(route.pipeline()), false);
136+
return next(mode, executor, new FileChannelHandler(route.pipeline()), true);
137+
}
138+
/** Strings: */
139+
if (CharSequence.class.isAssignableFrom(type)) {
140+
return next(mode, executor, new CharSequenceHandler(route.pipeline()), true);
141+
}
142+
/** RawByte: */
143+
if (byte[].class == type) {
144+
return next(mode, executor, new ByteArrayHandler(route.pipeline()), true);
145+
}
146+
if (ByteBuffer.class.isAssignableFrom(type)) {
147+
return next(mode, executor, new ByteBufferHandler(route.pipeline()), true);
148+
}
149+
if (ByteBuf.class.isAssignableFrom(type)) {
150+
return next(mode, executor, new ByteBufHandler(route.pipeline()), true);
130151
}
131152

132153
return next(mode, executor, new DefaultHandler(route.pipeline()), true);
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*
14+
* Copyright 2014 Edgar Espina
15+
*/
16+
package io.jooby.internal.handler;
17+
18+
import io.jooby.Context;
19+
import io.jooby.Route;
20+
21+
import javax.annotation.Nonnull;
22+
23+
public class ByteArrayHandler implements ChainedHandler {
24+
private Route.Handler next;
25+
26+
public ByteArrayHandler(Route.Handler next) {
27+
this.next = next;
28+
}
29+
30+
@Nonnull @Override public Object apply(@Nonnull Context ctx) {
31+
try {
32+
byte[] result = (byte[]) next.apply(ctx);
33+
return ctx.sendBytes(result);
34+
} catch (Throwable x) {
35+
return ctx.sendError(x);
36+
}
37+
}
38+
39+
@Override public Route.Handler next() {
40+
return next;
41+
}
42+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*
14+
* Copyright 2014 Edgar Espina
15+
*/
16+
package io.jooby.internal.handler;
17+
18+
import io.jooby.Context;
19+
import io.jooby.Route;
20+
import io.netty.buffer.ByteBuf;
21+
22+
import javax.annotation.Nonnull;
23+
24+
public class ByteBufHandler implements ChainedHandler {
25+
private Route.Handler next;
26+
27+
public ByteBufHandler(Route.Handler next) {
28+
this.next = next;
29+
}
30+
31+
@Nonnull @Override public Object apply(@Nonnull Context ctx) {
32+
try {
33+
ByteBuf result = (ByteBuf) next.apply(ctx);
34+
return ctx.sendBytes(result);
35+
} catch (Throwable x) {
36+
return ctx.sendError(x);
37+
}
38+
}
39+
40+
@Override public Route.Handler next() {
41+
return next;
42+
}
43+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*
14+
* Copyright 2014 Edgar Espina
15+
*/
16+
package io.jooby.internal.handler;
17+
18+
import io.jooby.Context;
19+
import io.jooby.Route;
20+
21+
import javax.annotation.Nonnull;
22+
import java.nio.ByteBuffer;
23+
24+
public class ByteBufferHandler implements ChainedHandler {
25+
private Route.Handler next;
26+
27+
public ByteBufferHandler(Route.Handler next) {
28+
this.next = next;
29+
}
30+
31+
@Nonnull @Override public Object apply(@Nonnull Context ctx) {
32+
try {
33+
ByteBuffer result = (ByteBuffer) next.apply(ctx);
34+
return ctx.sendBytes(result);
35+
} catch (Throwable x) {
36+
return ctx.sendError(x);
37+
}
38+
}
39+
40+
@Override public Route.Handler next() {
41+
return next;
42+
}
43+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*
14+
* Copyright 2014 Edgar Espina
15+
*/
16+
package io.jooby.internal.handler;
17+
18+
import io.jooby.Context;
19+
import io.jooby.Route;
20+
21+
import javax.annotation.Nonnull;
22+
23+
public class CharSequenceHandler implements ChainedHandler {
24+
private Route.Handler next;
25+
26+
public CharSequenceHandler(Route.Handler next) {
27+
this.next = next;
28+
}
29+
30+
@Nonnull @Override public Object apply(@Nonnull Context ctx) {
31+
try {
32+
CharSequence result = (CharSequence) next.apply(ctx);
33+
return ctx.sendString(result.toString());
34+
} catch (Throwable x) {
35+
return ctx.sendError(x);
36+
}
37+
}
38+
39+
@Override public Route.Handler next() {
40+
return next;
41+
}
42+
}

jooby/src/test/java/io/jooby/MockContext.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.jooby;
22

33
import io.jooby.internal.UrlParser;
4+
import io.netty.buffer.ByteBuf;
45

56
import javax.annotation.Nonnull;
67
import javax.annotation.Nullable;
@@ -303,6 +304,13 @@ public String getResultText() {
303304
return this;
304305
}
305306

307+
@Nonnull @Override public Context sendBytes(@Nonnull ByteBuf data) {
308+
responseStarted = true;
309+
result = data;
310+
length = data.readableBytes();
311+
return this;
312+
}
313+
306314
@Nonnull @Override public MockContext sendBytes(@Nonnull ByteBuffer data) {
307315
result = data;
308316
length = data.remaining();

jooby/src/test/java/io/jooby/internal/PipelineTest.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import io.jooby.ExecutionMode;
44
import io.jooby.Renderer;
55
import io.jooby.Route;
6+
import io.jooby.internal.handler.CharSequenceHandler;
67
import io.jooby.internal.handler.CompletionStageHandler;
78
import io.jooby.internal.handler.DefaultHandler;
89
import io.jooby.internal.handler.DetachHandler;
@@ -32,7 +33,7 @@ public class PipelineTest {
3233
public void eventLoopDoesNothingOnSimpleTypes() {
3334
Route.Handler h = ctx -> "OK";
3435
ChainedHandler pipeline = pipeline(route(String.class, h), ExecutionMode.EVENT_LOOP);
35-
assertTrue(pipeline instanceof DefaultHandler);
36+
assertTrue(pipeline instanceof CharSequenceHandler, pipeline.toString());
3637
assertTrue(pipeline.next() == h,
3738
"found: " + pipeline.next() + ", expected: " + h.getClass());
3839
}
@@ -45,8 +46,8 @@ public void eventLoopAlwaysDispatchToExecutorOnSimpleTypes() {
4546
ChainedHandler pipeline = pipeline(route(String.class, h), ExecutionMode.EVENT_LOOP, executor);
4647
assertTrue(pipeline instanceof WorkerExecHandler, "found: " + pipeline);
4748
Route.Handler next = pipeline.next();
48-
assertTrue(next instanceof DefaultHandler);
49-
next = ((DefaultHandler) next).next();
49+
assertTrue(next instanceof CharSequenceHandler);
50+
next = ((ChainedHandler) next).next();
5051
assertTrue(next == h, "found: " + next + ", expected: " + h.getClass());
5152
}
5253

@@ -120,8 +121,8 @@ public void workerDoesNothingOnSimpleTypes() {
120121
ChainedHandler pipeline = pipeline(route(String.class, h), ExecutionMode.WORKER);
121122
assertTrue(pipeline instanceof WorkerHandler, "found: " + pipeline);
122123
Route.Handler next = pipeline.next();
123-
assertTrue(next instanceof DefaultHandler);
124-
next = ((DefaultHandler) next).next();
124+
assertTrue(next instanceof CharSequenceHandler);
125+
next = ((ChainedHandler) next).next();
125126
assertTrue(next == h, "found: " + next + ", expected: " + h.getClass());
126127
}
127128

@@ -185,8 +186,8 @@ public void workerAlwaysDispatchToExecutorOnSimpleTypes() {
185186
ChainedHandler pipeline = pipeline(route(String.class, h), ExecutionMode.WORKER, executor);
186187
assertTrue(pipeline instanceof WorkerExecHandler, "found: " + pipeline);
187188
Route.Handler next = pipeline.next();
188-
assertTrue(next instanceof DefaultHandler);
189-
next = ((DefaultHandler) next).next();
189+
assertTrue(next instanceof CharSequenceHandler);
190+
next = ((ChainedHandler) next).next();
190191
assertTrue(next == h, "found: " + next + ", expected: " + h.getClass());
191192
}
192193

0 commit comments

Comments
 (0)