d8e93bf8b933c8dfe7668d345f4122ac9d85b63e
[iotivity.git] / cloud / stack / src / main / java / org / iotivity / cloud / base / protocols / http / HCProxyHandler.java
1 /*
2  * //******************************************************************
3  * //
4  * // Copyright 2016 Samsung Electronics All Rights Reserved.
5  * //
6  * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
7  * //
8  * // Licensed under the Apache License, Version 2.0 (the "License");
9  * // you may not use this file except in compliance with the License.
10  * // You may obtain a copy of the License at
11  * //
12  * //      http://www.apache.org/licenses/LICENSE-2.0
13  * //
14  * // Unless required by applicable law or agreed to in writing, software
15  * // distributed under the License is distributed on an "AS IS" BASIS,
16  * // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * // See the License for the specific language governing permissions and
18  * // limitations under the License.
19  * //
20  * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
21  */
22 package org.iotivity.cloud.base.protocols.http;
23
24 import java.util.concurrent.ConcurrentHashMap;
25
26 import org.iotivity.cloud.base.protocols.IResponse;
27 import org.iotivity.cloud.base.protocols.Message;
28 import org.iotivity.cloud.util.Log;
29
30 import io.netty.channel.ChannelDuplexHandler;
31 import io.netty.channel.ChannelFuture;
32 import io.netty.channel.ChannelFutureListener;
33 import io.netty.channel.ChannelHandlerContext;
34 import io.netty.channel.ChannelPromise;
35 import io.netty.handler.codec.http.HttpContent;
36 import io.netty.handler.codec.http.HttpHeaderNames;
37 import io.netty.handler.codec.http.HttpHeaders;
38 import io.netty.handler.codec.http.HttpRequest;
39 import io.netty.handler.codec.http.HttpResponse;
40 import io.netty.handler.codec.http.LastHttpContent;
41 import io.netty.util.CharsetUtil;
42
43 /**
44  * This channel handler intercepts HTTP request and CoAP response
45  * and translate them into CoAP request and HTTP response accordingly.
46  */
47 public class HCProxyHandler extends ChannelDuplexHandler {
48
49     private static ConcurrentHashMap<String, HCProxyProcessor> hcProxyProcessorMap
50     = new ConcurrentHashMap<String, HCProxyProcessor>();
51
52     @Override
53     public void channelRead(ChannelHandlerContext ctx, Object msg)
54             throws Exception {
55
56         // Wait translating the request until the end of content is received
57         if (msg instanceof HttpRequest) {
58
59             StringBuilder contentStrBuilder = new StringBuilder();
60             ctx.channel().attr(HCProxyProcessor.ctxStrContent)
61                     .set(contentStrBuilder);
62
63             HttpRequest httpRequest = (HttpRequest) msg;
64
65             HCProxyProcessor hcProxyProcessor = new HCProxyProcessor(ctx);
66
67             HCProxyHandler.hcProxyProcessorMap
68                     .put(ctx.channel().id().asLongText(), hcProxyProcessor);
69
70             // Set the values for required attributes in hcProxyProcessor
71             hcProxyProcessor.setHttpMethod(httpRequest.method());
72             hcProxyProcessor.setHostingHttpUri(httpRequest.uri());
73             HttpHeaders httpHeaders = httpRequest.headers();
74             hcProxyProcessor.setContentType(
75                     httpHeaders.get(HttpHeaderNames.CONTENT_TYPE));
76
77             // Check Session-ID for the already signed-in transaction
78             hcProxyProcessor.checkSessionId(
79                     httpHeaders.get(HttpHeaderNames.COOKIE));
80         }
81
82         // Do not hand over the message to next handler until the end of content
83         if (msg instanceof HttpContent) {
84
85             HttpContent content = (HttpContent) msg;
86
87             StringBuilder contentStrBuilder = ctx.channel()
88                     .attr(HCProxyProcessor.ctxStrContent).get();
89             contentStrBuilder
90                     .append(content.content().toString(CharsetUtil.UTF_8));
91
92             if (content instanceof LastHttpContent) {
93
94                 HCProxyProcessor hcProxyProcessor = HCProxyHandler.hcProxyProcessorMap
95                         .get(ctx.channel().id().asLongText());
96
97                 if (hcProxyProcessor != null
98                         && hcProxyProcessor.getContentType() != null) {
99                     hcProxyProcessor.setContent(contentStrBuilder.toString());
100                 }
101
102                 contentStrBuilder.setLength(0);
103
104                 // Check HTTP request whether there is an error or not
105                 String errorStatusCode = hcProxyProcessor.checkHttpRequest();
106
107                 if (errorStatusCode != null) {
108
109                     Log.v("HTTP Error: " + errorStatusCode);
110
111                     HttpResponse httpResponse = hcProxyProcessor
112                             .getErrorResponse(errorStatusCode);
113
114                     ctx.writeAndFlush(httpResponse);
115                 }
116
117                 // Create a message request from HTTP request
118                 Message message = null;
119
120                 if (hcProxyProcessor != null) {
121                     message = hcProxyProcessor.getRequestMessage();
122                 }
123
124                 if (message != null) {
125                     ctx.fireChannelRead(message);
126                 } else {
127                     errorStatusCode = "500 Internal Server Error: "
128                             + "HTTP-Method does not recognized.";
129
130                     Log.v("HTTP Error: " + errorStatusCode);
131
132                     HttpResponse httpResponse = hcProxyProcessor
133                             .getErrorResponse(errorStatusCode);
134
135                     ctx.writeAndFlush(httpResponse);
136                 }
137             }
138         }
139     }
140
141     @Override
142     public void write(ChannelHandlerContext ctx, Object msg,
143             ChannelPromise promise) {
144
145         // Create HTTP response from the response
146         HttpResponse httpResponse = null;
147
148         if (msg instanceof IResponse) {
149
150             IResponse response = (IResponse) msg;
151
152             HCProxyProcessor hcProxyProcessor = HCProxyHandler.hcProxyProcessorMap
153                     .get(ctx.channel().id().asLongText());
154
155             if (hcProxyProcessor != null) {
156
157                 /*
158                  * If HTTP request was sign-in and HTTP response is 200 OK,
159                  * then set HTTP Cookie in the response for the session.
160                  */
161                 httpResponse = hcProxyProcessor.getHttpResponse(response);
162             }
163
164             if (httpResponse == null) {
165                 String errorStatusCode = "500 Internal Server Error: "
166                         + "HTTP response could not be generated.";
167
168                 Log.v("HTTP Error: " + errorStatusCode);
169
170                 httpResponse = hcProxyProcessor
171                         .getErrorResponse(errorStatusCode);
172             }
173
174             msg = httpResponse;
175         }
176
177         ChannelFuture future = ctx.writeAndFlush(msg);
178
179         // Close the http connection after sending response
180         future.addListener(new ChannelFutureListener() {
181
182             @Override
183             public void operationComplete(ChannelFuture future)
184                     throws Exception {
185
186                 if (future.isSuccess()) {
187                     future.channel().close();
188                 } else {
189                     Log.v(ctx.channel().id().asLongText().substring(26)
190                             + " HTTP Disconnected (Unexpectedly), Address: "
191                             + ctx.channel().remoteAddress().toString());
192                 }
193             }
194         });
195     }
196 }