Refactor logging to use slf4j
[iotivity.git] / cloud / stack / src / main / java / org / iotivity / cloud / base / protocols / http / HCProxyProcessor.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.io.IOException;
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.UUID;
28 import java.util.concurrent.atomic.AtomicInteger;
29 import java.util.regex.Pattern;
30
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33 import org.iotivity.cloud.base.ServerSystem;
34 import org.iotivity.cloud.base.device.Device;
35 import org.iotivity.cloud.base.device.HttpDevice;
36 import org.iotivity.cloud.base.protocols.IResponse;
37 import org.iotivity.cloud.base.protocols.Message;
38 import org.iotivity.cloud.base.protocols.coap.CoapRequest;
39 import org.iotivity.cloud.base.protocols.coap.CoapResponse;
40 import org.iotivity.cloud.base.protocols.enums.ContentFormat;
41 import org.iotivity.cloud.base.protocols.enums.RequestMethod;
42 import org.iotivity.cloud.base.protocols.enums.ResponseStatus;
43 import org.iotivity.cloud.base.server.HttpServer;
44 import org.iotivity.cloud.util.Cbor;
45
46 import com.fasterxml.jackson.core.JsonProcessingException;
47 import com.fasterxml.jackson.core.type.TypeReference;
48 import com.fasterxml.jackson.databind.ObjectMapper;
49
50 import io.netty.buffer.ByteBuf;
51 import io.netty.buffer.Unpooled;
52 import io.netty.channel.ChannelHandlerContext;
53 import io.netty.handler.codec.http.DefaultFullHttpResponse;
54 import io.netty.handler.codec.http.DefaultHttpResponse;
55 import io.netty.handler.codec.http.HttpHeaderNames;
56 import io.netty.handler.codec.http.HttpHeaderValues;
57 import io.netty.handler.codec.http.HttpMethod;
58 import io.netty.handler.codec.http.HttpResponse;
59 import io.netty.handler.codec.http.HttpResponseStatus;
60 import io.netty.handler.codec.http.HttpVersion;
61 import io.netty.util.AttributeKey;
62
63 /**
64  * This class actually translates HTTP request to CoAP request
65  * and CoAP response to HTTP response.
66  * The translation complyes Default Mapping (scheme omission) Technique
67  * described in https://tools.ietf.org/html/draft-ietf-core-http-mapping-13.
68  */
69 public class HCProxyProcessor {
70     private final static Logger Log             = LoggerFactory.getLogger(HCProxyProcessor.class);
71     public final static String COAP_FORMAT_TYPE = "application/coap-payload;cf=";
72     public final static String APPLICATION_JSON = "application/json";
73     public final static String APPLICATION_CBOR = "application/cbor";
74     public final static String DISCOVER_URI     = "/oic/res";
75
76     public final static String HC_PROXY_PATH    = "/hc/";
77
78     public final static String SIGNIN_URI       = "/oic/account/session";
79     public final static String COOKIE_SESSION   = "SID";
80     public final static String COOKIE_MAX_AGE   = "Max-Age";
81
82     public final static long SESSION_TIMEOUT    = 3600L * 24L; //1 Day
83
84     public static AttributeKey<StringBuilder> ctxStrContent = AttributeKey
85             .newInstance("strContent");
86
87     public static AttributeKey<String> ctxSessionId = AttributeKey
88             .newInstance("sessionId");
89
90     // To encode/decode JSON with one root element
91     private Cbor<HashMap<String, Object>> mStackCborMap = new Cbor<>();
92
93     // To encode/decode JSON with array of root elements: Discover response
94     private Cbor<ArrayList<Object>> mStackCborArray = new Cbor<>();
95
96     private static int mRandomSeed = (int)(System.currentTimeMillis() % 10000);
97     private static AtomicInteger mTokenCounter = new AtomicInteger(mRandomSeed);
98
99     private ChannelHandlerContext mCtx = null;
100
101     /**
102      * This function is the constructor of this class.
103      * 
104      * @param ctx The message can be sent/received through this channel.
105      */
106     public HCProxyProcessor(ChannelHandlerContext ctx) {
107         this.mCtx = ctx;
108     }
109
110     /**
111      * This function returns a channel of the HTTP connection.
112      * 
113      * @return ctx The message can be sent/received through this channel.
114      */
115     public ChannelHandlerContext getCtx() {
116         return this.mCtx;
117     }
118
119     /*
120      * HC Proxy Path: A path addressing the HC proxy function, normally
121      * following HC Proxy host address
122      */
123     private String     mHCProxyPath     = HC_PROXY_PATH;
124
125     /*
126      * Target CoAP Path: A path addressing the cloud-interface function,
127      * normally CoAP host address is CI server address
128      */
129     private String     mTargetCoapPath  = null;
130     private String     mTargetCoapQuery = null;
131
132     /*
133      * Hosting HTTP URI: A default map-able URI refers to a HC proxy, whereas
134      * path (and query) component(s) embed the information used by a HC proxy to
135      * extract the Target CoAP URI
136      */
137     private String     mHostingHttpUri  = null;
138
139     /*
140      * HTTP Request Content
141      */
142     private String     mContentType     = null;
143     private String     mContent         = null;
144
145     /*
146      * CoAP Request Content
147      */
148     private String     mContentFormat   = null;
149     private byte[]     mCborContent     = null;
150
151     private HttpMethod mHttpMethod      = null;
152
153     private String     mSessionId       = null;
154
155     /**
156      * This function returns Session-ID of the request.
157      * 
158      * @return sessionId
159      */
160     public String getSessionId() {
161         return this.mSessionId;
162     }
163
164     /**
165      * This function returns HTTP Session ID if exists,
166      * and put session id value in the channel attribute.
167      * 
168      * @return sessionId, null if there is no session id
169      */
170     public String checkSessionId(String cookie) {
171         if (cookie == null) { return null; }
172         // e.g. SID=31d4d96e407aad42
173         String[] sidKeyValue = cookie.split(Pattern.quote("="), 2);
174         if (sidKeyValue.length > 1) {
175             mSessionId = sidKeyValue[1];
176         }
177         if (mSessionId != null) {
178             mCtx.channel().attr(HCProxyProcessor.ctxSessionId).set(mSessionId);
179         }
180         return mSessionId;
181     }
182
183     /**
184      * This function returns HC Proxy Path string.
185      * 
186      * @return hcProxyPath A path addressing the HC proxy function, normally
187      *         following HC Proxy host address
188      */
189     public String getHcProxyPath() {
190         return this.mHCProxyPath;
191     }
192
193     /**
194      * This function returns Target CoAP Path string.
195      * 
196      * @return A path addressing the cloud-interface function, normally CoAP
197      *         host address is CI server address
198      */
199     public String getTargetCoapPath() {
200         return this.mTargetCoapPath;
201     }
202
203     /**
204      * This function returns Hosting HTTP URI string.
205      * Hosting HTTP URI complyes Default Mapping (scheme omission) Technique
206      * described in https://tools.ietf.org/html/draft-ietf-core-http-mapping-13.
207      * 
208      * @return hostingHttpUri A default map-able URI refers to a HC proxy,
209      *         whereas path (and query) component(s) embed the information used
210      *         by a HC proxy to extract the Target CoAP URI
211      */
212     public String getHostingHttpUri() {
213         return this.mHostingHttpUri;
214     }
215
216     /**
217      * This function sets Hosting HTTP URI and Target CoAP Path/Query strings.
218      * Hosting HTTP URI complyes Default Mapping (scheme omission) Technique
219      * described in https://tools.ietf.org/html/draft-ietf-core-http-mapping-13.
220      * 
221      * @param hostingHttpUri
222      */
223     public void setHostingHttpUri(String hostingHttpUri) {
224         this.mHostingHttpUri = hostingHttpUri;
225         // hostingHttpUri = /hc/<ip>:<port>/<path>[?]<query>
226         String[] tempUriArray = hostingHttpUri
227                 .split(Pattern.quote(mHCProxyPath));
228         if (tempUriArray.length > 1) {
229             // tempUriArray[1] = <ip>:<port>/<path>[?]<query>
230             String[] tempPathArray = tempUriArray[1]
231                     .split(Pattern.quote("/"), 2);
232             if (tempPathArray.length > 1) {
233                 // tempPathArray[1] = <path>[?]<query>
234                 String[] tempPathQuery = tempPathArray[1]
235                         .split(Pattern.quote("?"), 2);
236                 this.mTargetCoapPath = "/" + tempPathQuery[0];
237                 if (tempPathQuery.length > 1) {
238                     this.mTargetCoapQuery = tempPathQuery[1];
239                 }
240             }
241         }
242     }
243
244     /**
245      * This function returns Content-Type of HTTP request.
246      * 
247      * @return contentType
248      */
249     public String getContentType() {
250         return this.mContentType;
251     }
252
253     /**
254      * This function sets HTTP Content-Type and CoAP Content-Format.
255      * 
256      * @param contentType
257      */
258     public void setContentType(String contentType) {
259         this.mContentType = contentType;
260         if (contentType != null) {
261             if (contentType.equalsIgnoreCase(APPLICATION_JSON)) {
262                 this.mContentFormat = APPLICATION_CBOR;
263             }
264         }
265     }
266
267     /**
268      * This function returns content of HTTP request.
269      * 
270      * @return content HTTP request
271      */
272     public String getContent() {
273         return this.mContent;
274     }
275
276     /**
277      * This function sets HTTP Content and CoAP Cbor Content.
278      * 
279      * @param content HTTP request
280      */
281     public void setContent(String content) {
282         this.mContent = content;
283         if (mContentType != null && !content.trim().equals("")) {
284             if (mContentType.equalsIgnoreCase(APPLICATION_JSON)) {
285                 byte[] cborData = null;
286                 try { // Cbor: JSON string to Map
287                     Log.trace("Cbor encoding using HashMap.class");
288                     ObjectMapper cborMapper = new ObjectMapper();
289                     HashMap<String, Object> cborMap
290                     = cborMapper.readValue(content,
291                             new TypeReference<HashMap<String, Object>>() {});
292                     cborData = mStackCborMap.encodingPayloadToCbor(cborMap);
293                 } catch (IOException e) {
294                     e.printStackTrace();
295                 }
296                 this.mCborContent = cborData;
297             }
298         }
299     }
300
301     /**
302      * This function returns HTTP request method.
303      * 
304      * @return httpMethod
305      */
306     public HttpMethod getHttpMethod() {
307         return this.mHttpMethod;
308     }
309
310     /**
311      * This function sets HTTP request method.
312      * 
313      * @param httpMethod
314      */
315     public void setHttpMethod(HttpMethod httpMethod) {
316         this.mHttpMethod = httpMethod;
317     }
318
319     /**
320      * This function returns a message created by the request,
321      * which implements IRequest(used in the cloud)
322      * translated from the HTTP request.
323      * 
324      * @return requestMessage
325      */
326     public Message getRequestMessage() {
327
328         Message requestMessage = null;
329
330         String coapUriPath = mTargetCoapPath;
331         String coapUriQuery = mTargetCoapQuery;
332         ContentFormat coapContentFormat = null;
333         if (mContentFormat != null) {
334             if (mContentFormat.equalsIgnoreCase(APPLICATION_CBOR)) {
335                 coapContentFormat = ContentFormat.APPLICATION_CBOR;
336             }
337         }
338         byte[] coapPayload = mCborContent;
339
340         if (mHttpMethod == HttpMethod.POST) {
341
342             CoapRequest coapRequest = new CoapRequest(RequestMethod.POST);
343             coapRequest.setToken(createToken());
344             coapRequest.setUriPath(coapUriPath);
345             if (coapUriQuery != null) {
346                 coapRequest.setUriQuery(coapUriQuery);
347             }
348             if (coapPayload != null) {
349                 coapRequest.setContentFormat(coapContentFormat);
350                 coapRequest.setPayload(coapPayload);
351             }
352             requestMessage = coapRequest;
353
354         } else if (mHttpMethod == HttpMethod.PUT) {
355
356             CoapRequest coapRequest = new CoapRequest(RequestMethod.PUT);
357             coapRequest.setToken(createToken());
358             coapRequest.setUriPath(coapUriPath);
359             if (coapUriQuery != null) {
360                 coapRequest.setUriQuery(coapUriQuery);
361             }
362             if (coapPayload != null) {
363                 coapRequest.setContentFormat(coapContentFormat);
364                 coapRequest.setPayload(coapPayload);
365             }
366             requestMessage = coapRequest;
367
368         } else if (mHttpMethod == HttpMethod.GET) {
369
370             CoapRequest coapRequest = new CoapRequest(RequestMethod.GET);
371             coapRequest.setToken(createToken());
372             coapRequest.setUriPath(coapUriPath);
373             if (coapUriQuery != null) {
374                 coapRequest.setUriQuery(coapUriQuery);
375             }
376             requestMessage = coapRequest;
377
378         } else if (mHttpMethod == HttpMethod.DELETE) {
379
380             CoapRequest coapRequest = new CoapRequest(RequestMethod.DELETE);
381             coapRequest.setToken(createToken());
382             coapRequest.setUriPath(coapUriPath);
383             if (coapUriQuery != null) {
384                 coapRequest.setUriQuery(coapUriQuery);
385             }
386             requestMessage = coapRequest;
387         }
388
389         return requestMessage;
390     }
391
392     private byte[] createToken() {
393         int token = mTokenCounter.incrementAndGet();
394         return new byte[] {
395                 (byte) (token >>> 24),
396                 (byte) (token >>> 16),
397                 (byte) (token >>> 8),
398                 (byte) token
399         };
400     }
401
402     /**
403      * This function returns an error information
404      * (HTTP status code and reason phrase) if there is an error.
405      * 
406      * @return errorStatusCode
407      */
408     public String checkHttpRequest() {
409
410         String errorStatusCode = null;
411
412         if (mHttpMethod == HttpMethod.POST) {
413             if (mContentFormat == null) {
414                 errorStatusCode = "500 Internal Server Error: "
415                         + "Content-Type does not recognized.";
416             } else if (mCborContent == null) {
417                 errorStatusCode = "400 Bad Request: Payload is empty.";
418             }
419         }
420
421         if (mHostingHttpUri == null || mTargetCoapPath == null) {
422             errorStatusCode = "400 Bad Request: "
423                     + "Please use proper URI Path "
424                     + HC_PROXY_PATH + "<Host, e.g. ip:port>/<Path>.";
425         }
426
427         return errorStatusCode;
428     }
429
430     /**
431      * This function returns a created HTTP error response
432      * created by an error information.
433      * 
434      * @param errorStatusCode HTTP status code and reason phrase
435      * @return httpResponse
436      */
437     public HttpResponse getErrorResponse(String errorStatusCode) {
438
439         String[] responseStatus = errorStatusCode.split(Pattern.quote(" "), 2);
440         int httpCode = Integer.valueOf(responseStatus[0]);
441         String reasonPhrase = responseStatus[1];
442
443         HttpVersion httpVersion = HttpVersion.HTTP_1_1;
444         HttpResponseStatus httpStatus = new HttpResponseStatus(httpCode,
445                 reasonPhrase);
446         HttpResponse httpResponse = new DefaultHttpResponse(httpVersion,
447                 httpStatus);
448
449         // Default: Non-persistent connection
450         httpResponse.headers().set(HttpHeaderNames.CONNECTION,
451                 HttpHeaderValues.CLOSE);
452
453         // Some clients requires content-length=0
454         // to decide a proper size of one response message.
455         httpResponse.headers().set(HttpHeaderNames.CONTENT_TYPE,
456                 HttpHeaderValues.NONE);
457         httpResponse.headers().set(HttpHeaderNames.CONTENT_LENGTH,
458                 HttpHeaderValues.ZERO);
459
460         return httpResponse;
461     }
462
463     /**
464      * This function returns a created HTTP response
465      * translated from the CoAP response.
466      * 
467      * @param response
468      * @return httpResponse
469      */
470     public HttpResponse getHttpResponse(IResponse response) {
471
472         HttpResponse httpResponse = null;
473
474         ResponseStatus coapResponseStatus = response.getStatus();
475         ContentFormat coapContentFormat = response.getContentFormat();
476         byte[] coapPayload = response.getPayload();
477         boolean isPayload = false;
478
479         HttpVersion httpVersion = HttpVersion.HTTP_1_1;
480
481         if (coapPayload != null && coapPayload.length > 0) {
482
483             isPayload = true;
484
485             HttpResponseStatus httpStatus = getHttpResponseStatus(
486                     coapResponseStatus, isPayload);
487
488             String httpContentType = null;
489
490             ByteBuf httpContent = Unpooled.EMPTY_BUFFER;
491
492             if (coapContentFormat == ContentFormat.APPLICATION_CBOR) {
493
494                 // Content-Type: application/json
495                 httpContentType = APPLICATION_JSON;
496
497                 String payloadString = null;
498                 try { // Cbor: Map to JSON string
499
500                     if (getTargetCoapPath().contains(DISCOVER_URI)) {
501                         Log.trace("Cbor decoding using ArrayList.class");
502                         ArrayList<Object> cborMap = mStackCborArray
503                                 .parsePayloadFromCbor(coapPayload, ArrayList.class);
504                         ObjectMapper cborMapper = new ObjectMapper();
505                         payloadString = cborMapper.writerWithDefaultPrettyPrinter()
506                                 .writeValueAsString(cborMap);
507                     } else {
508                         Log.trace("Cbor decoding using HashMap.class");
509                         HashMap<String, Object> cborMap = mStackCborMap
510                                 .parsePayloadFromCbor(coapPayload, HashMap.class);
511                         ObjectMapper cborMapper = new ObjectMapper();
512                         payloadString = cborMapper.writerWithDefaultPrettyPrinter()
513                                 .writeValueAsString(cborMap);
514                     }
515
516                 } catch (JsonProcessingException e) {
517                     e.printStackTrace();
518                 }
519
520                 if (payloadString == null
521                         || payloadString.trim().equalsIgnoreCase("null")) {
522                     payloadString = "";
523                 }
524
525                 byte[] payloadBytes = payloadString.getBytes();
526
527                 httpContent = Unpooled.wrappedBuffer(payloadBytes);
528
529             } else {
530
531                 if (response instanceof CoapResponse) {
532                     // Content-Type:
533                     // application/coap-payload;cf=<Content-Format value>
534                     httpContentType = COAP_FORMAT_TYPE
535                             + ((CoapResponse) response)
536                                     .getContentFormatValue();
537                 } else {
538                     httpContentType = coapContentFormat.toString();
539                 }
540
541                 httpContent = Unpooled.wrappedBuffer(coapPayload);
542             }
543
544             httpResponse = new DefaultFullHttpResponse(httpVersion, httpStatus,
545                     httpContent);
546
547             // Default: Non-persistent connection
548             httpResponse.headers().set(HttpHeaderNames.CONNECTION,
549                     HttpHeaderValues.CLOSE);
550
551             httpResponse.headers().set(HttpHeaderNames.CONTENT_TYPE,
552                     httpContentType);
553             httpResponse.headers().set(HttpHeaderNames.CONTENT_LENGTH,
554                     httpContent.readableBytes());
555
556             byte[] bytes = new byte[httpContent.readableBytes()];
557             httpContent.getBytes(httpContent.readerIndex(), bytes);
558
559             StringBuilder contentStrBuilder = new StringBuilder(
560                     new String(bytes));
561             mCtx.channel().attr(HCProxyProcessor.ctxStrContent)
562                     .set(contentStrBuilder);
563
564         } else {
565
566             isPayload = false;
567
568             HttpResponseStatus httpStatus = getHttpResponseStatus(
569                     coapResponseStatus, isPayload);
570
571             httpResponse = new DefaultFullHttpResponse(httpVersion, httpStatus);
572
573             // Default: Non-persistent connection
574             httpResponse.headers().set(HttpHeaderNames.CONNECTION,
575                     HttpHeaderValues.CLOSE);
576
577             // Some clients requires content-length=0
578             // to decide a proper size of one response message.
579             httpResponse.headers().set(HttpHeaderNames.CONTENT_TYPE,
580                     HttpHeaderValues.NONE);
581             httpResponse.headers().set(HttpHeaderNames.CONTENT_LENGTH,
582                     HttpHeaderValues.ZERO);
583         }
584
585         /*
586          * If HTTP request was sign-in and HTTP response is 200 OK,
587          * then set HTTP Cookie in the response for the session.
588          */
589         if (mTargetCoapPath != null && mTargetCoapPath.contains(SIGNIN_URI)
590                 && httpResponse.status() == HttpResponseStatus.OK) {
591             long timeoutSeconds = SESSION_TIMEOUT;
592             String createdSid = setSessionId(httpResponse, timeoutSeconds);
593
594             // Set SessionExpiredTime in HttpDevice
595             // and put it into httpDeviceMap using the session id as a key
596             Device device = mCtx.channel().attr(ServerSystem.keyDevice).get();
597             if (device != null) {
598                 ((HttpDevice) device).setSessionExpiredTime(timeoutSeconds);
599                 HttpServer.httpDeviceMap.put(createdSid, (HttpDevice) device);
600                 mCtx.channel().attr(ServerSystem.keyDevice).set(device);
601             }
602         }
603
604         // Put HttpDevice into httpDeviceMap using the session id as a key
605         HttpDevice httpDevice = (HttpDevice) mCtx.channel()
606                 .attr(ServerSystem.keyDevice).get();
607         if (httpDevice != null && mSessionId != null) {
608             HttpServer.httpDeviceMap.put(mSessionId, httpDevice);
609         }
610
611         return httpResponse;
612     }
613
614     private HttpResponseStatus getHttpResponseStatus(
615             ResponseStatus coapResponseStatus, boolean isPayload) {
616
617         HttpResponseStatus httpStatusCode = HttpResponseStatus.NOT_IMPLEMENTED;
618
619         if (coapResponseStatus == ResponseStatus.CREATED) {
620
621             httpStatusCode = HttpResponseStatus.CREATED;
622
623         } else if (coapResponseStatus == ResponseStatus.DELETED
624                 || coapResponseStatus == ResponseStatus.CHANGED
625                 || coapResponseStatus == ResponseStatus.CONTENT) {
626
627             if (isPayload) {
628                 httpStatusCode = HttpResponseStatus.OK;
629             } else {
630                 httpStatusCode = HttpResponseStatus.NO_CONTENT;
631             }
632
633         } else if (coapResponseStatus == ResponseStatus.VALID) {
634
635             if (isPayload) {
636                 httpStatusCode = HttpResponseStatus.OK;
637             } else {
638                 httpStatusCode = HttpResponseStatus.NOT_MODIFIED;
639             }
640
641         } else if (coapResponseStatus == ResponseStatus.UNAUTHORIZED
642                 || coapResponseStatus == ResponseStatus.FORBIDDEN) {
643
644             httpStatusCode = HttpResponseStatus.FORBIDDEN;
645
646         } else if (coapResponseStatus == ResponseStatus.BAD_REQUEST
647                 || coapResponseStatus == ResponseStatus.BAD_OPTION) {
648
649             httpStatusCode = HttpResponseStatus.BAD_REQUEST;
650
651         } else if (coapResponseStatus == ResponseStatus.METHOD_NOT_ALLOWED) {
652
653             // The HC Proxy should return a HTTP reason-phrase
654             // in the HTTP status line that starts with the string
655             // "CoAP server returned 4.05"
656             // in order to facilitate troubleshooting.
657             httpStatusCode = new HttpResponseStatus(400,
658                     "CoAP server returned 4.05");
659
660         } else if (coapResponseStatus == ResponseStatus.NOT_ACCEPTABLE) {
661
662             httpStatusCode = HttpResponseStatus.NOT_ACCEPTABLE;
663
664         } else if (coapResponseStatus == ResponseStatus.PRECONDITION_FAILED) {
665
666             httpStatusCode = HttpResponseStatus.PRECONDITION_FAILED;
667
668         } else if (coapResponseStatus == ResponseStatus.UNSUPPORTED_CONTENT_FORMAT) {
669
670             httpStatusCode = HttpResponseStatus.UNSUPPORTED_MEDIA_TYPE;
671
672         } else if (coapResponseStatus == ResponseStatus.BAD_GATEWAY
673                 || coapResponseStatus == ResponseStatus.PROXY_NOT_SUPPORTED) {
674
675             httpStatusCode = HttpResponseStatus.BAD_GATEWAY;
676
677         } else if (coapResponseStatus == ResponseStatus.SERVICE_UNAVAILABLE) {
678
679             httpStatusCode = HttpResponseStatus.SERVICE_UNAVAILABLE;
680
681         } else if (coapResponseStatus == ResponseStatus.GATEWAY_TIMEOUT) {
682
683             httpStatusCode = HttpResponseStatus.GATEWAY_TIMEOUT;
684
685         } else if (coapResponseStatus == ResponseStatus.NOT_FOUND) {
686
687             httpStatusCode = HttpResponseStatus.NOT_FOUND;
688
689         } else if (coapResponseStatus == ResponseStatus.INTERNAL_SERVER_ERROR) {
690
691             httpStatusCode = HttpResponseStatus.INTERNAL_SERVER_ERROR;
692         }
693
694         return httpStatusCode;
695     }
696
697     private String setSessionId(HttpResponse httpResponse, long timeoutSeconds) {
698
699         String randomSid = UUID.randomUUID().toString().replaceAll("-", "");
700
701         // e.g. SID=31d4d96e407aad42; Max-Age=86400
702         StringBuilder sessionBuilder = new StringBuilder();
703         sessionBuilder.append(COOKIE_SESSION);
704         sessionBuilder.append("=");
705         sessionBuilder.append(randomSid);
706         sessionBuilder.append("; ");
707         sessionBuilder.append(COOKIE_MAX_AGE);
708         sessionBuilder.append("=");
709         sessionBuilder.append(timeoutSeconds);
710
711         httpResponse.headers().set(HttpHeaderNames.SET_COOKIE,
712                 sessionBuilder.toString());
713
714         return randomSid;
715     }
716 }