Skip to content
This repository was archived by the owner on Mar 3, 2026. It is now read-only.

Commit defbf17

Browse files
committed
add a response.end() method that force and release any existing resources (if any) fix jooby-project#30
1 parent d0b98de commit defbf17

File tree

6 files changed

+141
-42
lines changed

6 files changed

+141
-42
lines changed

jooby/src/main/java/org/jooby/Response.java

Lines changed: 61 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -53,214 +53,219 @@ public interface Response {
5353
class Forwarding implements Response {
5454

5555
/** The target response. */
56-
private Response response;
56+
private Response rsp;
5757

5858
/**
5959
* Creates a new {@link Forwarding} response.
6060
*
6161
* @param response A response object.
6262
*/
6363
public Forwarding(final Response response) {
64-
this.response = requireNonNull(response, "A response is required.");
64+
this.rsp = requireNonNull(response, "A response is required.");
6565
}
6666

6767
@Override
6868
public void download(final String filename, final InputStream stream) throws Exception {
69-
response.download(filename, stream);
69+
rsp.download(filename, stream);
7070
}
7171

7272
@Override
7373
public void download(final String filename, final Reader reader) throws Exception {
74-
response.download(filename, reader);
74+
rsp.download(filename, reader);
7575
}
7676

7777
@Override
7878
public void download(final File file) throws Exception {
79-
response.download(file);
79+
rsp.download(file);
8080
}
8181

8282
@Override
8383
public void download(final String filename, final File file) throws Exception {
84-
response.download(filename, file);
84+
rsp.download(filename, file);
8585
}
8686

8787
@Override
8888
public void download(final String filename) throws Exception {
89-
response.download(filename);
89+
rsp.download(filename);
9090
}
9191

9292
@Override
9393
public void download(final String filename, final String location) throws Exception {
94-
response.download(filename, location);
94+
rsp.download(filename, location);
9595
}
9696

9797
@Override
9898
public Response cookie(final String name, final String value) {
99-
response.cookie(name, value);
99+
rsp.cookie(name, value);
100100
return this;
101101
}
102102

103103
@Override
104104
public Response cookie(final Cookie cookie) {
105-
response.cookie(cookie);
105+
rsp.cookie(cookie);
106106
return this;
107107
}
108108

109109
@Override
110110
public Response cookie(final Definition cookie) {
111-
response.cookie(cookie);
111+
rsp.cookie(cookie);
112112
return this;
113113
}
114114

115115
@Override
116116
public Response clearCookie(final String name) {
117-
response.clearCookie(name);
117+
rsp.clearCookie(name);
118118
return this;
119119
}
120120

121121
@Override
122122
public Mutant header(final String name) {
123-
return response.header(name);
123+
return rsp.header(name);
124124
}
125125

126126
@Override
127127
public Response header(final String name, final byte value) {
128-
response.header(name, value);
128+
rsp.header(name, value);
129129
return this;
130130
}
131131

132132
@Override
133133
public Response header(final String name, final char value) {
134-
response.header(name, value);
134+
rsp.header(name, value);
135135
return this;
136136
}
137137

138138
@Override
139139
public Response header(final String name, final Date value) {
140-
response.header(name, value);
140+
rsp.header(name, value);
141141
return this;
142142
}
143143

144144
@Override
145145
public Response header(final String name, final double value) {
146-
response.header(name, value);
146+
rsp.header(name, value);
147147
return this;
148148
}
149149

150150
@Override
151151
public Response header(final String name, final float value) {
152-
response.header(name, value);
152+
rsp.header(name, value);
153153
return this;
154154
}
155155

156156
@Override
157157
public Response header(final String name, final int value) {
158-
response.header(name, value);
158+
rsp.header(name, value);
159159
return this;
160160
}
161161

162162
@Override
163163
public Response header(final String name, final long value) {
164-
response.header(name, value);
164+
rsp.header(name, value);
165165
return this;
166166
}
167167

168168
@Override
169169
public Response header(final String name, final short value) {
170-
response.header(name, value);
170+
rsp.header(name, value);
171171
return this;
172172
}
173173

174174
@Override
175175
public Response header(final String name, final CharSequence value) {
176-
response.header(name, value);
176+
rsp.header(name, value);
177177
return this;
178178
}
179179

180180
@Override
181181
public Charset charset() {
182-
return response.charset();
182+
return rsp.charset();
183183
}
184184

185185
@Override
186186
public Response charset(final Charset charset) {
187-
response.charset(charset);
187+
rsp.charset(charset);
188188
return this;
189189
}
190190

191191
@Override
192192
public Response length(final long length) {
193-
response.length(length);
193+
rsp.length(length);
194194
return this;
195195
}
196196

197197
@Override
198198
public Optional<MediaType> type() {
199-
return response.type();
199+
return rsp.type();
200200
}
201201

202202
@Override
203203
public Response type(final MediaType type) {
204-
response.type(type);
204+
rsp.type(type);
205205
return this;
206206
}
207207

208208
@Override
209209
public Response type(final String type) {
210-
response.type(type);
210+
rsp.type(type);
211211
return this;
212212
}
213213

214214
@Override
215215
public void send(final Object body) throws Exception {
216-
response.send(body);
216+
rsp.send(body);
217217
}
218218

219219
@Override
220220
public void send(final Body body) throws Exception {
221-
response.send(body);
221+
rsp.send(body);
222+
}
223+
224+
@Override
225+
public void end() {
226+
rsp.end();
222227
}
223228

224229
@Override
225230
public Formatter format() {
226-
return response.format();
231+
return rsp.format();
227232
}
228233

229234
@Override
230235
public void redirect(final String location) throws Exception {
231-
response.redirect(location);
236+
rsp.redirect(location);
232237
}
233238

234239
@Override
235240
public void redirect(final Status status, final String location) throws Exception {
236-
response.redirect(status, location);
241+
rsp.redirect(status, location);
237242
}
238243

239244
@Override
240245
public Optional<Status> status() {
241-
return response.status();
246+
return rsp.status();
242247
}
243248

244249
@Override
245250
public Response status(final Status status) {
246-
response.status(status);
251+
rsp.status(status);
247252
return this;
248253
}
249254

250255
@Override
251256
public Response status(final int status) {
252-
response.status(status);
257+
rsp.status(status);
253258
return this;
254259
}
255260

256261
@Override
257262
public boolean committed() {
258-
return response.committed();
263+
return rsp.committed();
259264
}
260265

261266
@Override
262267
public String toString() {
263-
return response.toString();
268+
return rsp.toString();
264269
}
265270

266271
/**
@@ -273,7 +278,7 @@ public static Response unwrap(final @Nonnull Response rsp) {
273278
requireNonNull(rsp, "A response is required.");
274279
Response root = rsp;
275280
while (root instanceof Forwarding) {
276-
root = ((Forwarding) root).response;
281+
root = ((Forwarding) root).rsp;
277282
}
278283
return root;
279284
}
@@ -820,4 +825,21 @@ default Response status(final int status) {
820825
*/
821826
boolean committed();
822827

828+
/**
829+
* Ends current request/response cycle by releasing any existing resources and committing the
830+
* response into the channel.
831+
*
832+
* This method is automatically call it from a send method, so you are not force to call this
833+
* method per each request/response cycle.
834+
*
835+
* It's recommended for quickly ending the response without any data:
836+
* <pre>
837+
* rsp.status(304).end();
838+
* </pre>
839+
*
840+
* Keep in mind that an explicit call to this method will stop the execution of handlers. So,
841+
* any handler further in the chain won't be executed once end has been called.
842+
*/
843+
void end();
844+
823845
}

jooby/src/main/java/org/jooby/internal/AssetHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public void handle(final Request req, final Response rsp, final Route.Chain chai
4343
if (lastModified > 0) {
4444
long ifModified = req.header("If-Modified-Since").toOptional(Long.class).orElse(-1l);
4545
if (ifModified > 0 && lastModified / 1000 <= ifModified / 1000) {
46-
rsp.status(Status.NOT_MODIFIED);
46+
rsp.status(Status.NOT_MODIFIED).end();
4747
return;
4848
}
4949
rsp.header("Last-Modified", new Date(lastModified));

jooby/src/main/java/org/jooby/internal/RouteHandler.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,12 @@ public void handle(final HttpServerExchange exchange, final String method, final
219219
defaultErrorPage(reqerr, rsperr, err.err(reqerr, rsperr, ex));
220220
}
221221
} finally {
222+
// mark request/response as done.
223+
Response rspdone = rsp.get(injector, notFound);
224+
rspdone.end();
225+
222226
long end = System.currentTimeMillis();
223-
log.debug(" status -> {} in {}ms", exchange.getResponseCode(), end - start);
227+
log.debug(" status -> {} in {}ms", rspdone.status().get(), end - start);
224228

225229
saveSession(req.get(injector, notFound));
226230
}

jooby/src/main/java/org/jooby/internal/undertow/UndertowResponse.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,17 @@ public List<MediaType> viewableTypes() {
333333
return selector.viewableTypes();
334334
}
335335

336+
@Override
337+
public void end() {
338+
if (!committed()) {
339+
if (status == null) {
340+
status(200);
341+
}
342+
}
343+
// this is a noop when response has been set, still call it...
344+
exchange.endExchange();
345+
}
346+
336347
public void send(final Body body, final Body.Formatter formatter) throws Exception {
337348
requireNonNull(body, "A response message is required.");
338349
requireNonNull(formatter, "A converter is required.");
@@ -376,9 +387,11 @@ && status().map(s -> s.value() >= 300 && s.value() < 400).orElse(false)) {
376387
formatter.format(message, new BodyWriterImpl(charset, ImmutableMap.copyOf(locals),
377388
stream, writer));
378389
} else {
379-
// close output
390+
// noop, but we apply headers.
380391
stream.get().close();
381392
}
393+
// end response
394+
end();
382395
}
383396

384397
private io.undertow.server.handlers.Cookie toUndertowCookie(final Cookie cookie) {

jooby/src/test/java/org/jooby/ResponseTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@ public Response length(final long length) {
104104
throw new UnsupportedOperationException();
105105
}
106106

107+
@Override
108+
public void end() {
109+
throw new UnsupportedOperationException();
110+
}
111+
107112
@Override
108113
public Optional<MediaType> type() {
109114
throw new UnsupportedOperationException();

0 commit comments

Comments
 (0)