1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 package org.waarp.openr66.protocol.http.rest.client;
37
38 import com.fasterxml.jackson.databind.JsonNode;
39 import com.fasterxml.jackson.databind.node.ObjectNode;
40 import io.netty.buffer.ByteBuf;
41 import io.netty.buffer.Unpooled;
42 import io.netty.channel.Channel;
43 import io.netty.channel.ChannelHandlerContext;
44 import io.netty.channel.SimpleChannelInboundHandler;
45 import io.netty.handler.codec.http.FullHttpResponse;
46 import io.netty.handler.codec.http.HttpContent;
47 import io.netty.handler.codec.http.HttpHeaderNames;
48 import io.netty.handler.codec.http.HttpObject;
49 import io.netty.handler.codec.http.HttpResponse;
50 import io.netty.handler.codec.http.HttpResponseStatus;
51 import io.netty.handler.codec.http.LastHttpContent;
52 import org.waarp.common.crypto.ssl.WaarpSslUtility;
53 import org.waarp.common.json.JsonHandler;
54 import org.waarp.common.logging.WaarpLogger;
55 import org.waarp.common.logging.WaarpLoggerFactory;
56 import org.waarp.common.utility.WaarpNettyUtil;
57 import org.waarp.common.utility.WaarpStringUtils;
58 import org.waarp.gateway.kernel.exception.HttpIncorrectRequestException;
59 import org.waarp.gateway.kernel.exception.HttpInvalidAuthenticationException;
60 import org.waarp.gateway.kernel.rest.DataModelRestMethodHandler.COMMAND_TYPE;
61 import org.waarp.gateway.kernel.rest.RestArgument;
62 import org.waarp.gateway.kernel.rest.client.HttpRestClientSimpleResponseHandler;
63 import org.waarp.gateway.kernel.rest.client.RestFuture;
64 import org.waarp.openr66.protocol.http.rest.handler.HttpRestAbstractR66Handler.ACTIONS_TYPE;
65
66 import java.net.ConnectException;
67 import java.nio.channels.ClosedChannelException;
68 import java.nio.charset.UnsupportedCharsetException;
69
70
71
72
73
74
75
76
77 public abstract class HttpRestR66ClientResponseHandler
78 extends SimpleChannelInboundHandler<HttpObject> {
79
80
81
82 private static final WaarpLogger logger =
83 WaarpLoggerFactory.getLogger(HttpRestR66ClientResponseHandler.class);
84
85 private ByteBuf cumulativeBody;
86 protected JsonNode jsonObject;
87
88 protected final void addContent(final FullHttpResponse response)
89 throws HttpIncorrectRequestException {
90 final ByteBuf content = response.content();
91 if (content != null && content.isReadable()) {
92 WaarpNettyUtil.retain(content);
93 if (cumulativeBody != null) {
94 cumulativeBody = Unpooled.wrappedBuffer(cumulativeBody, content);
95 } else {
96 cumulativeBody = content;
97 }
98
99 try {
100 final String json = cumulativeBody.toString(WaarpStringUtils.UTF8);
101 jsonObject = JsonHandler.getFromString(json);
102 } catch (final UnsupportedCharsetException e2) {
103 logger.warn("Error" + " : {}", e2.getMessage());
104 throw new HttpIncorrectRequestException(e2);
105 }
106 cumulativeBody = null;
107 }
108 }
109
110
111
112
113
114
115
116
117 protected final void actionFromResponse(final Channel channel)
118 throws HttpInvalidAuthenticationException {
119 boolean includeValidation = false;
120 final RestArgument ra = new RestArgument((ObjectNode) jsonObject);
121 if (jsonObject == null) {
122 logger.debug("Recv: EMPTY");
123 }
124 final RestFuture restFuture =
125 channel.attr(HttpRestClientSimpleResponseHandler.RESTARGUMENT).get();
126 restFuture.setRestArgument(ra);
127 switch (ra.getMethod()) {
128 case DELETE:
129 includeValidation = delete(channel, ra);
130 break;
131 case GET:
132 includeValidation = get(channel, ra);
133 break;
134 case OPTIONS:
135 includeValidation = options(channel, ra);
136 break;
137 case POST:
138 includeValidation = post(channel, ra);
139 break;
140 case PUT:
141 includeValidation = put(channel, ra);
142 break;
143 default:
144 break;
145 }
146 if (!includeValidation) {
147
148 restFuture.setSuccess();
149 }
150 }
151
152
153
154
155
156
157
158
159
160
161
162
163 protected abstract boolean action(Channel channel, RestArgument ra,
164 ACTIONS_TYPE act)
165 throws HttpInvalidAuthenticationException;
166
167
168
169
170
171
172
173
174
175
176
177 protected abstract boolean afterDbGet(Channel channel, RestArgument ra)
178 throws HttpInvalidAuthenticationException;
179
180
181
182
183
184
185
186
187
188
189
190 protected abstract boolean afterDbPost(Channel channel, RestArgument ra)
191 throws HttpInvalidAuthenticationException;
192
193
194
195
196
197
198
199
200
201
202
203 protected abstract boolean afterDbPut(Channel channel, RestArgument ra)
204 throws HttpInvalidAuthenticationException;
205
206
207
208
209
210
211
212
213
214
215
216 protected abstract boolean afterDbDelete(Channel channel, RestArgument ra)
217 throws HttpInvalidAuthenticationException;
218
219
220
221
222
223
224
225
226
227
228
229 protected abstract boolean afterDbGetMultiple(Channel channel,
230 RestArgument ra)
231 throws HttpInvalidAuthenticationException;
232
233
234
235
236
237
238
239
240
241
242
243 protected abstract boolean afterDbOptions(Channel channel, RestArgument ra)
244 throws HttpInvalidAuthenticationException;
245
246
247
248
249
250
251
252
253
254
255
256 protected abstract boolean afterError(Channel channel, RestArgument ra)
257 throws HttpInvalidAuthenticationException;
258
259 protected final boolean get(final Channel channel, final RestArgument ra)
260 throws HttpInvalidAuthenticationException {
261 if (logger.isDebugEnabled()) {
262 logger.debug(ra.prettyPrint());
263 }
264 if (ra.getCommand() == COMMAND_TYPE.GET) {
265 return afterDbGet(channel, ra);
266 } else if (ra.getCommand() == COMMAND_TYPE.MULTIGET) {
267 return afterDbGetMultiple(channel, ra);
268 } else {
269 final String cmd = ra.getCommandField();
270 try {
271 final ACTIONS_TYPE act = ACTIONS_TYPE.valueOf(cmd);
272 return action(channel, ra, act);
273 } catch (final Exception e) {
274 return false;
275 }
276 }
277 }
278
279 protected final boolean put(final Channel channel, final RestArgument ra)
280 throws HttpInvalidAuthenticationException {
281 if (logger.isDebugEnabled()) {
282 logger.debug(ra.prettyPrint());
283 }
284 if (ra.getCommand() == COMMAND_TYPE.UPDATE) {
285 return afterDbPut(channel, ra);
286 } else {
287 final String cmd = ra.getCommandField();
288 try {
289 final ACTIONS_TYPE act = ACTIONS_TYPE.valueOf(cmd);
290 return action(channel, ra, act);
291 } catch (final Exception e) {
292 return false;
293 }
294 }
295 }
296
297 protected final boolean post(final Channel channel, final RestArgument ra)
298 throws HttpInvalidAuthenticationException {
299 if (logger.isDebugEnabled()) {
300 logger.debug(ra.prettyPrint());
301 }
302 if (ra.getCommand() == COMMAND_TYPE.CREATE) {
303 return afterDbPost(channel, ra);
304 } else {
305 final String cmd = ra.getCommandField();
306 try {
307 final ACTIONS_TYPE act = ACTIONS_TYPE.valueOf(cmd);
308 return action(channel, ra, act);
309 } catch (final Exception e) {
310 return false;
311 }
312 }
313 }
314
315 protected final boolean delete(final Channel channel, final RestArgument ra)
316 throws HttpInvalidAuthenticationException {
317 if (logger.isDebugEnabled()) {
318 logger.debug(ra.prettyPrint());
319 }
320 if (ra.getCommand() == COMMAND_TYPE.DELETE) {
321 return afterDbDelete(channel, ra);
322 } else {
323 final String cmd = ra.getCommandField();
324 try {
325 final ACTIONS_TYPE act = ACTIONS_TYPE.valueOf(cmd);
326 return action(channel, ra, act);
327 } catch (final Exception e) {
328 return false;
329 }
330 }
331 }
332
333 protected final boolean options(final Channel channel, final RestArgument ra)
334 throws HttpInvalidAuthenticationException {
335 if (logger.isDebugEnabled()) {
336 logger.debug(ra.prettyPrint());
337 }
338 if (ra.getCommand() == COMMAND_TYPE.OPTIONS) {
339 return afterDbOptions(channel, ra);
340 } else {
341 final String cmd = ra.getCommandField();
342 try {
343 final ACTIONS_TYPE act = ACTIONS_TYPE.valueOf(cmd);
344 return action(channel, ra, act);
345 } catch (final Exception e) {
346 return false;
347 }
348 }
349 }
350
351 @Override
352 protected void channelRead0(final ChannelHandlerContext ctx,
353 final HttpObject msg) throws Exception {
354 if (msg instanceof HttpResponse) {
355 final HttpResponse response = (HttpResponse) msg;
356 final HttpResponseStatus status = response.status();
357 logger.debug("{}: {} STATUS: {}", HttpHeaderNames.REFERER,
358 response.headers().get(HttpHeaderNames.REFERER), status);
359 if (response.status().code() != 200) {
360 if (response instanceof FullHttpResponse) {
361 addContent((FullHttpResponse) response);
362 }
363 RestArgument ra = null;
364 if (jsonObject != null) {
365 ra = new RestArgument((ObjectNode) jsonObject);
366 final RestFuture restFuture = ctx.channel().attr(
367 HttpRestClientSimpleResponseHandler.RESTARGUMENT).get();
368 restFuture.setRestArgument(ra);
369 logger.error("Error: " + response.status().code() + ' ' +
370 response.status().reasonPhrase() + '\n' +
371 ra.prettyPrint());
372 } else {
373 logger.error("Error: " + response.status().code() + ' ' +
374 response.status().reasonPhrase());
375 }
376 if (!afterError(ctx.channel(), ra)) {
377 final RestFuture restFuture = ctx.channel().attr(
378 HttpRestClientSimpleResponseHandler.RESTARGUMENT).get();
379 restFuture.cancel();
380 }
381 if (ctx.channel().isActive()) {
382 logger.debug("Will close");
383 WaarpSslUtility.closingSslChannel(ctx.channel());
384 }
385 } else {
386 if (response instanceof FullHttpResponse) {
387 addContent((FullHttpResponse) response);
388 actionFromResponse(ctx.channel());
389 }
390 }
391 } else {
392 final HttpContent chunk = (HttpContent) msg;
393 if (chunk instanceof LastHttpContent) {
394 final ByteBuf content = chunk.content();
395 if (content != null && content.isReadable()) {
396 WaarpNettyUtil.retain(content);
397 if (cumulativeBody != null) {
398 cumulativeBody = Unpooled.wrappedBuffer(cumulativeBody, content);
399 } else {
400 cumulativeBody = content;
401 }
402 }
403
404 if (cumulativeBody == null) {
405 jsonObject = JsonHandler.createObjectNode();
406 } else {
407 try {
408 final String json = cumulativeBody.toString(WaarpStringUtils.UTF8);
409 jsonObject = JsonHandler.getFromString(json);
410 } catch (final Throwable e2) {
411 logger.warn("Error" + " : {}", e2.getMessage());
412 throw new HttpIncorrectRequestException(e2);
413 }
414 WaarpNettyUtil.release(cumulativeBody);
415 cumulativeBody = null;
416 }
417 actionFromResponse(ctx.channel());
418 } else {
419 final ByteBuf content = chunk.content();
420 if (content != null && content.isReadable()) {
421 WaarpNettyUtil.retain(content);
422 if (cumulativeBody != null) {
423 cumulativeBody = Unpooled.wrappedBuffer(cumulativeBody, content);
424 } else {
425 cumulativeBody = content;
426 }
427 }
428 }
429 }
430 }
431
432 @Override
433 public void exceptionCaught(final ChannelHandlerContext ctx,
434 final Throwable cause) {
435 final RestFuture restFuture =
436 ctx.channel().attr(HttpRestClientSimpleResponseHandler.RESTARGUMENT)
437 .get();
438 if (cause instanceof ClosedChannelException) {
439 logger.debug("Close before ending");
440 restFuture.setFailure(cause);
441 return;
442 } else if (cause instanceof ConnectException) {
443 if (ctx.channel().isActive()) {
444 logger.debug("Will close");
445 restFuture.setFailure(cause);
446 WaarpSslUtility.closingSslChannel(ctx.channel());
447 }
448 return;
449 }
450 logger.warn("Error: {}", cause.getMessage());
451 if (ctx.channel() != null && restFuture != null) {
452 restFuture.setFailure(cause);
453 }
454 logger.debug("Will close");
455 WaarpSslUtility.closingSslChannel(ctx.channel());
456 }
457
458 }