View Javadoc
1   /*
2    * DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway
3    * Copyright (C) 2010  Mickael Guessant
4    *
5    * This program is free software; you can redistribute it and/or
6    * modify it under the terms of the GNU General Public License
7    * as published by the Free Software Foundation; either version 2
8    * of the License, or (at your option) any later version.
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with this program; if not, write to the Free Software
17   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18   */
19  
20  package davmail.http;
21  
22  import davmail.Settings;
23  import davmail.exception.*;
24  import davmail.http.request.*;
25  import org.apache.http.Header;
26  import org.apache.http.HttpResponse;
27  import org.apache.http.HttpStatus;
28  import org.apache.http.StatusLine;
29  import org.apache.http.auth.AuthSchemeProvider;
30  import org.apache.http.auth.AuthScope;
31  import org.apache.http.auth.NTCredentials;
32  import org.apache.http.client.CredentialsProvider;
33  import org.apache.http.client.HttpResponseException;
34  import org.apache.http.client.config.AuthSchemes;
35  import org.apache.http.client.config.RequestConfig;
36  import org.apache.http.client.methods.CloseableHttpResponse;
37  import org.apache.http.client.methods.HttpRequestBase;
38  import org.apache.http.client.protocol.HttpClientContext;
39  import org.apache.http.client.utils.URIUtils;
40  import org.apache.http.config.Registry;
41  import org.apache.http.config.RegistryBuilder;
42  import org.apache.http.conn.HttpClientConnectionManager;
43  import org.apache.http.conn.socket.ConnectionSocketFactory;
44  import org.apache.http.conn.socket.PlainConnectionSocketFactory;
45  import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
46  import org.apache.http.cookie.Cookie;
47  import org.apache.http.impl.auth.BasicSchemeFactory;
48  import org.apache.http.impl.auth.DigestSchemeFactory;
49  import org.apache.http.impl.client.BasicCookieStore;
50  import org.apache.http.impl.client.BasicCredentialsProvider;
51  import org.apache.http.impl.client.CloseableHttpClient;
52  import org.apache.http.impl.client.HttpClientBuilder;
53  import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
54  import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
55  import org.apache.http.impl.conn.SystemDefaultRoutePlanner;
56  import org.apache.jackrabbit.webdav.DavException;
57  import org.apache.jackrabbit.webdav.MultiStatus;
58  import org.apache.jackrabbit.webdav.MultiStatusResponse;
59  import org.apache.jackrabbit.webdav.client.methods.BaseDavRequest;
60  import org.apache.jackrabbit.webdav.client.methods.HttpCopy;
61  import org.apache.jackrabbit.webdav.client.methods.HttpMove;
62  import org.apache.log4j.Logger;
63  import org.codehaus.jettison.json.JSONObject;
64  
65  import java.io.Closeable;
66  import java.io.IOException;
67  import java.net.*;
68  import java.security.Security;
69  import java.util.HashSet;
70  import java.util.List;
71  
72  public class HttpClientAdapter implements Closeable {
73      static final Logger LOGGER = Logger.getLogger("davmail.http.HttpClientAdapter");
74  
75      static final String[] SUPPORTED_PROTOCOLS = new String[]{"TLSv1", "TLSv1.1", "TLSv1.2"};
76      static final Registry<ConnectionSocketFactory> SCHEME_REGISTRY;
77      static String WORKSTATION_NAME = "UNKNOWN";
78      static final int MAX_REDIRECTS = 10;
79  
80      static {
81          // disable Client-initiated TLS renegotiation
82          System.setProperty("jdk.tls.rejectClientInitiatedRenegotiation", "true");
83          // force strong ephemeral Diffie-Hellman parameter
84          System.setProperty("jdk.tls.ephemeralDHKeySize", "2048");
85  
86          Security.setProperty("ssl.SocketFactory.provider", "davmail.http.DavGatewaySSLSocketFactory");
87  
88          // DavMail is Kerberos configuration provider
89          Security.setProperty("login.configuration.provider", "davmail.http.KerberosLoginConfiguration");
90  
91          // reenable basic proxy authentication on Java >= 1.8.111
92          System.setProperty("jdk.http.auth.tunneling.disabledSchemes", "");
93  
94          RegistryBuilder<ConnectionSocketFactory> schemeRegistry = RegistryBuilder.create();
95          schemeRegistry.register("http", new PlainConnectionSocketFactory());
96          schemeRegistry.register("https", new SSLConnectionSocketFactory(new DavGatewaySSLSocketFactory(),
97                  SUPPORTED_PROTOCOLS, null,
98                  SSLConnectionSocketFactory.getDefaultHostnameVerifier()));
99  
100         SCHEME_REGISTRY = schemeRegistry.build();
101 
102         try {
103             WORKSTATION_NAME = InetAddress.getLocalHost().getHostName();
104         } catch (Throwable t) {
105             // ignore
106         }
107 
108         // set system property *before* calling ProxySelector.getDefault()
109         if (Settings.getBooleanProperty("davmail.useSystemProxies", Boolean.FALSE)) {
110             System.setProperty("java.net.useSystemProxies", "true");
111         }
112         ProxySelector.setDefault(new DavGatewayProxySelector(ProxySelector.getDefault()));
113     }
114 
115     /**
116      * Test if the response is gzip encoded
117      *
118      * @param response http response
119      * @return true if response is gzip encoded
120      */
121     public static boolean isGzipEncoded(HttpResponse response) {
122         Header header = response.getFirstHeader("Content-Encoding");
123         return header != null && "gzip".equals(header.getValue());
124     }
125 
126     HttpClientConnectionManager connectionManager;
127     CloseableHttpClient httpClient;
128     CredentialsProvider provider = new BasicCredentialsProvider();
129     BasicCookieStore cookieStore = new BasicCookieStore() {
130         @Override
131         public void addCookie(final Cookie cookie) {
132             LOGGER.debug("Add cookie " + cookie);
133             super.addCookie(cookie);
134         }
135     };
136     // current URI
137     URI uri;
138     String domain;
139     String userid;
140     String userEmail;
141 
142     public HttpClientAdapter(String url) {
143         this(URI.create(url));
144     }
145 
146     public HttpClientAdapter(String url, String username, String password) {
147         this(URI.create(url), username, password, false);
148     }
149 
150     public HttpClientAdapter(String url, boolean enablePool) {
151         this(URI.create(url), null, null, enablePool);
152     }
153 
154     public HttpClientAdapter(String url, String username, String password, boolean enablePool) {
155         this(URI.create(url), username, password, enablePool);
156     }
157 
158     public HttpClientAdapter(URI uri) {
159         this(uri, null, null, false);
160     }
161 
162     public HttpClientAdapter(URI uri, boolean enablePool) {
163         this(uri, null, null, enablePool);
164     }
165 
166     public HttpClientAdapter(URI uri, String username, String password) {
167         this(uri, username, password, false);
168     }
169 
170     public HttpClientAdapter(URI uri, String username, String password, boolean enablePool) {
171         // init current uri
172         this.uri = uri;
173 
174         if (enablePool) {
175             connectionManager = new PoolingHttpClientConnectionManager(SCHEME_REGISTRY);
176             ((PoolingHttpClientConnectionManager) connectionManager).setDefaultMaxPerRoute(5);
177             startEvictorThread();
178         } else {
179             connectionManager = new BasicHttpClientConnectionManager(SCHEME_REGISTRY);
180         }
181         HttpClientBuilder clientBuilder = HttpClientBuilder.create()
182                 .disableRedirectHandling()
183                 .setDefaultRequestConfig(getRequestConfig())
184                 .setUserAgent(getUserAgent())
185                 .setDefaultAuthSchemeRegistry(getAuthSchemeRegistry())
186                 // httpClient is not shared between clients, do not track connection state
187                 .disableConnectionState()
188                 .setConnectionManager(connectionManager);
189 
190         SystemDefaultRoutePlanner routePlanner = new SystemDefaultRoutePlanner(ProxySelector.getDefault());
191         clientBuilder.setRoutePlanner(routePlanner);
192 
193         clientBuilder.setDefaultCookieStore(cookieStore);
194 
195         setCredentials(username, password);
196 
197         boolean enableProxy = Settings.getBooleanProperty("davmail.enableProxy");
198         boolean useSystemProxies = Settings.getBooleanProperty("davmail.useSystemProxies", Boolean.FALSE);
199         String proxyHost = null;
200         int proxyPort = 0;
201         String proxyUser = null;
202         String proxyPassword = null;
203 
204         if (useSystemProxies) {
205             // get proxy for url from system settings
206             System.setProperty("java.net.useSystemProxies", "true");
207             List<Proxy> proxyList = getProxyForURI(uri);
208             if (!proxyList.isEmpty() && proxyList.get(0).address() != null) {
209                 InetSocketAddress inetSocketAddress = (InetSocketAddress) proxyList.get(0).address();
210                 proxyHost = inetSocketAddress.getHostName();
211                 proxyPort = inetSocketAddress.getPort();
212 
213                 // we may still need authentication credentials
214                 proxyUser = Settings.getProperty("davmail.proxyUser");
215                 proxyPassword = Settings.getProperty("davmail.proxyPassword");
216             }
217         } else if (isNoProxyFor(uri)) {
218             LOGGER.debug("no proxy for " + uri.getHost());
219         } else if (enableProxy) {
220             proxyHost = Settings.getProperty("davmail.proxyHost");
221             proxyPort = Settings.getIntProperty("davmail.proxyPort");
222             proxyUser = Settings.getProperty("davmail.proxyUser");
223             proxyPassword = Settings.getProperty("davmail.proxyPassword");
224         }
225 
226         if (proxyHost != null && !proxyHost.isEmpty()) {
227             if (proxyUser != null && !proxyUser.isEmpty()) {
228 
229                 AuthScope authScope = new AuthScope(proxyHost, proxyPort, AuthScope.ANY_REALM);
230                 if (provider == null) {
231                     provider = new BasicCredentialsProvider();
232                 }
233 
234                 // detect ntlm authentication (windows domain name in user name)
235                 int backslashindex = proxyUser.indexOf('\\');
236                 if (backslashindex > 0) {
237                     provider.setCredentials(authScope, new NTCredentials(proxyUser.substring(backslashindex + 1),
238                             proxyPassword, WORKSTATION_NAME,
239                             proxyUser.substring(0, backslashindex)));
240                 } else {
241                     provider.setCredentials(authScope, new NTCredentials(proxyUser, proxyPassword, WORKSTATION_NAME, ""));
242                 }
243             }
244         }
245 
246         clientBuilder.setDefaultCredentialsProvider(provider);
247 
248         httpClient = clientBuilder.build();
249     }
250 
251     /**
252      * Get current uri host
253      *
254      * @return current host
255      */
256     public String getHost() {
257         return uri.getHost();
258     }
259 
260     /**
261      * Force current uri.
262      *
263      * @param uri new uri
264      */
265     public void setUri(URI uri) {
266         this.uri = uri;
267     }
268 
269     /**
270      * Current uri.
271      *
272      * @return current uri
273      */
274     public URI getUri() {
275         return uri;
276     }
277 
278     private Registry<AuthSchemeProvider> getAuthSchemeRegistry() {
279         final RegistryBuilder<AuthSchemeProvider> registryBuilder = RegistryBuilder.create();
280         registryBuilder.register(AuthSchemes.NTLM, new JCIFSNTLMSchemeFactory())
281                 .register(AuthSchemes.BASIC, new BasicSchemeFactory())
282                 .register(AuthSchemes.DIGEST, new DigestSchemeFactory());
283         if (Settings.getBooleanProperty("davmail.enableKerberos")) {
284             registryBuilder.register(AuthSchemes.SPNEGO, new DavMailSPNegoSchemeFactory());
285         }
286 
287         return registryBuilder.build();
288     }
289 
290     private RequestConfig getRequestConfig() {
291         HashSet<String> authSchemes = new HashSet<>();
292         if (Settings.getBooleanProperty("davmail.enableKerberos")) {
293             authSchemes.add(AuthSchemes.SPNEGO);
294             authSchemes.add(AuthSchemes.KERBEROS);
295         } else {
296             authSchemes.add(AuthSchemes.NTLM);
297             authSchemes.add(AuthSchemes.BASIC);
298             authSchemes.add(AuthSchemes.DIGEST);
299         }
300         return RequestConfig.custom()
301                 // socket connect timeout
302                 .setConnectTimeout(Settings.getIntProperty("davmail.exchange.connectionTimeout", 10) * 1000)
303                 // inactivity timeout
304                 .setSocketTimeout(Settings.getIntProperty("davmail.exchange.soTimeout", 120) * 1000)
305                 .setTargetPreferredAuthSchemes(authSchemes)
306                 .build();
307     }
308 
309     private void parseUserName(String username) {
310         if (username != null) {
311             int pipeIndex = username.indexOf("|");
312             if (pipeIndex >= 0) {
313                 userid = username.substring(0, pipeIndex);
314                 userEmail = username.substring(pipeIndex + 1);
315             } else {
316                 userid = username;
317                 userEmail = username;
318             }
319             // separate domain name
320             int backSlashIndex = userid.indexOf('\\');
321             if (backSlashIndex >= 0) {
322                 // separate domain from username in credentials
323                 domain = userid.substring(0, backSlashIndex);
324                 userid = userid.substring(backSlashIndex + 1);
325             } else {
326                 domain = Settings.getProperty("davmail.defaultDomain", "");
327             }
328         }
329     }
330 
331     /**
332      * Retrieve Proxy Selector
333      *
334      * @param uri target uri
335      * @return proxy selector
336      */
337     private static List<Proxy> getProxyForURI(java.net.URI uri) {
338         LOGGER.debug("get Default proxy selector");
339         ProxySelector proxySelector = ProxySelector.getDefault();
340         LOGGER.debug("getProxyForURI(" + uri + ')');
341         List<Proxy> proxies = proxySelector.select(uri);
342         LOGGER.debug("got system proxies:" + proxies);
343         return proxies;
344     }
345 
346     protected static boolean isNoProxyFor(java.net.URI uri) {
347         final String noProxyFor = Settings.getProperty("davmail.noProxyFor");
348         if (noProxyFor != null) {
349             final String urihost = uri.getHost().toLowerCase();
350             final String[] domains = noProxyFor.toLowerCase().split(",\\s*");
351             for (String domain : domains) {
352                 if (urihost.endsWith(domain)) {
353                     return true; //break;
354                 }
355             }
356         }
357         return false;
358     }
359 
360     public void startEvictorThread() {
361         DavMailIdleConnectionEvictor.addConnectionManager(connectionManager);
362     }
363 
364     @Override
365     public void close() {
366         DavMailIdleConnectionEvictor.removeConnectionManager(connectionManager);
367         try {
368             httpClient.close();
369         } catch (IOException e) {
370             LOGGER.warn("Exception closing http client", e);
371         }
372     }
373 
374     public static void close(HttpClientAdapter httpClientAdapter) {
375         if (httpClientAdapter != null) {
376             httpClientAdapter.close();
377         }
378     }
379 
380     /**
381      * Execute request, do not follow redirects.
382      * if request is an instance of ResponseHandler, process and close response
383      *
384      * @param request Http request
385      * @return Http response
386      * @throws IOException on error
387      */
388     public CloseableHttpResponse execute(HttpRequestBase request) throws IOException {
389         return execute(request, null);
390     }
391 
392     /**
393      * Execute request, do not follow redirects.
394      * if request is an instance of ResponseHandler, process and close response
395      *
396      * @param request Http request
397      * @param context Http request context
398      * @return Http response
399      * @throws IOException on error
400      */
401     public CloseableHttpResponse execute(HttpRequestBase request, HttpClientContext context) throws IOException {
402         // make sure request path is absolute
403         handleURI(request);
404         // execute request and return response
405         return httpClient.execute(request, context);
406     }
407 
408     /**
409      * fix relative uri and update current uri.
410      *
411      * @param request http request
412      */
413     private void handleURI(HttpRequestBase request) {
414         URI requestURI = request.getURI();
415         if (!requestURI.isAbsolute()) {
416             request.setURI(URIUtils.resolve(uri, requestURI));
417         }
418         uri = request.getURI();
419     }
420 
421     public ResponseWrapper executeFollowRedirect(PostRequest request) throws IOException {
422         ResponseWrapper responseWrapper = request;
423         LOGGER.debug(request.getMethod() + " " + request.getURI().toString());
424         LOGGER.debug(request.getParameters());
425 
426         int count = 0;
427         int maxRedirect = Settings.getIntProperty("davmail.httpMaxRedirects", MAX_REDIRECTS);
428 
429         executePostRequest(request);
430         URI redirectLocation = request.getRedirectLocation();
431 
432         while (count++ < maxRedirect && redirectLocation != null) {
433             LOGGER.debug("Redirect " + request.getURI() + " to " + redirectLocation);
434             // replace uri with target location
435             responseWrapper = new GetRequest(redirectLocation);
436             executeGetRequest((GetRequest) responseWrapper);
437             redirectLocation = ((GetRequest) responseWrapper).getRedirectLocation();
438         }
439 
440         return responseWrapper;
441     }
442 
443     public GetRequest executeFollowRedirect(GetRequest request) throws IOException {
444         GetRequest result = request;
445         LOGGER.debug(request.getMethod() + " " + request.getURI().toString());
446 
447         int count = 0;
448         int maxRedirect = Settings.getIntProperty("davmail.httpMaxRedirects", MAX_REDIRECTS);
449 
450         executeGetRequest(request);
451         URI redirectLocation = request.getRedirectLocation();
452 
453         while (count++ < maxRedirect && redirectLocation != null) {
454             LOGGER.debug("Redirect " + request.getURI() + " to " + redirectLocation);
455             // replace uri with target location
456             result = new GetRequest(redirectLocation);
457             executeGetRequest(result);
458             redirectLocation = result.getRedirectLocation();
459         }
460 
461         return result;
462     }
463 
464     /**
465      * Execute get request and return response body as string.
466      *
467      * @param getRequest get request
468      * @return response body
469      * @throws IOException on error
470      */
471     public String executeGetRequest(GetRequest getRequest) throws IOException {
472         handleURI(getRequest);
473         String responseBodyAsString;
474         try (CloseableHttpResponse response = execute(getRequest)) {
475             responseBodyAsString = getRequest.handleResponse(response);
476         }
477         return responseBodyAsString;
478     }
479 
480     /**
481      * Execute post request and return response body as string.
482      *
483      * @param postRequest post request
484      * @return response body
485      * @throws IOException on error
486      */
487     public String executePostRequest(PostRequest postRequest) throws IOException {
488         handleURI(postRequest);
489         String responseBodyAsString;
490         try (CloseableHttpResponse response = execute(postRequest)) {
491             responseBodyAsString = postRequest.handleResponse(response);
492         }
493         return responseBodyAsString;
494     }
495 
496     public JSONObject executeRestRequest(RestRequest restRequest) throws IOException {
497         handleURI(restRequest);
498         JSONObject responseBody;
499         try (CloseableHttpResponse response = execute(restRequest)) {
500             responseBody = restRequest.handleResponse(response);
501         }
502         return responseBody;
503     }
504 
505     /**
506      * Execute WebDav request
507      *
508      * @param request WebDav request
509      * @return multistatus response
510      * @throws IOException on error
511      */
512     public MultiStatus executeDavRequest(BaseDavRequest request) throws IOException {
513         handleURI(request);
514         MultiStatus multiStatus = null;
515         try (CloseableHttpResponse response = execute(request)) {
516             request.checkSuccess(response);
517             if (response.getStatusLine().getStatusCode() == HttpStatus.SC_MULTI_STATUS) {
518                 multiStatus = request.getResponseBodyAsMultiStatus(response);
519             }
520         } catch (DavException e) {
521             LOGGER.error(e.getMessage(), e);
522             throw new IOException(e.getErrorCode() + " " + e.getStatusPhrase(), e);
523         }
524         return multiStatus;
525     }
526 
527     /**
528      * Execute Exchange WebDav request
529      *
530      * @param request WebDav request
531      * @return multistatus response
532      * @throws IOException on error
533      */
534     public MultiStatusResponse[] executeDavRequest(ExchangeDavRequest request) throws IOException {
535         handleURI(request);
536         MultiStatusResponse[] responses;
537         try (CloseableHttpResponse response = execute(request)) {
538             List<MultiStatusResponse> responseList = request.handleResponse(response);
539             // TODO check error handling
540             //request.checkSuccess(response);
541             responses = responseList.toArray(new MultiStatusResponse[0]);
542         }
543         return responses;
544     }
545 
546 
547     /**
548      * Execute webdav search method.
549      *
550      * @param path            <i>encoded</i> searched folder path
551      * @param searchStatement (SQL like) search statement
552      * @param maxCount        max item count
553      * @return Responses enumeration
554      * @throws IOException on error
555      */
556     public MultiStatusResponse[] executeSearchRequest(String path, String searchStatement, int maxCount) throws IOException {
557         ExchangeSearchRequest searchRequest = new ExchangeSearchRequest(path, searchStatement);
558         if (maxCount > 0) {
559             searchRequest.setHeader("Range", "rows=0-" + (maxCount - 1));
560         }
561         return executeDavRequest(searchRequest);
562     }
563 
564     public static boolean isRedirect(HttpResponse response) {
565         return isRedirect(response.getStatusLine().getStatusCode());
566     }
567 
568     /**
569      * Check if status is a redirect (various 30x values).
570      *
571      * @param status Http status
572      * @return true if status is a redirect
573      */
574     public static boolean isRedirect(int status) {
575         return status == HttpStatus.SC_MOVED_PERMANENTLY
576                 || status == HttpStatus.SC_MOVED_TEMPORARILY
577                 || status == HttpStatus.SC_SEE_OTHER
578                 || status == HttpStatus.SC_TEMPORARY_REDIRECT;
579     }
580 
581     /**
582      * Get redirect location from header.
583      *
584      * @param response Http response
585      * @return URI target location
586      */
587     public static URI getRedirectLocation(HttpResponse response) {
588         Header location = response.getFirstHeader("Location");
589         if (isRedirect(response.getStatusLine().getStatusCode()) && location != null) {
590             return URI.create(location.getValue());
591         }
592         return null;
593     }
594 
595     public void setCredentials(String username, String password) {
596         parseUserName(username);
597         if (userid != null && password != null) {
598             LOGGER.debug("Creating NTCredentials for user " + userid + " workstation " + WORKSTATION_NAME + " domain " + domain);
599             NTCredentials credentials = new NTCredentials(userid, password, WORKSTATION_NAME, domain);
600             provider.setCredentials(AuthScope.ANY, credentials);
601         }
602     }
603 
604     public List<Cookie> getCookies() {
605         return cookieStore.getCookies();
606     }
607 
608     public void addCookie(Cookie cookie) {
609         cookieStore.addCookie(cookie);
610     }
611 
612     public String getUserAgent() {
613         return Settings.getUserAgent();
614     }
615 
616     public static HttpResponseException buildHttpResponseException(HttpRequestBase request, HttpResponse response) {
617         return buildHttpResponseException(request, response.getStatusLine());
618     }
619 
620     /**
621      * Build Http Exception from method status
622      *
623      * @param method Http Method
624      * @return Http Exception
625      */
626     public static HttpResponseException buildHttpResponseException(HttpRequestBase method, StatusLine statusLine) {
627         int status = statusLine.getStatusCode();
628         StringBuilder message = new StringBuilder();
629         message.append(status).append(' ').append(statusLine.getReasonPhrase());
630         message.append(" at ").append(method.getURI());
631         if (method instanceof HttpCopy || method instanceof HttpMove) {
632             message.append(" to ").append(method.getFirstHeader("Destination"));
633         }
634         // 440 means forbidden on Exchange
635         if (status == 440) {
636             return new LoginTimeoutException(message.toString());
637         } else if (status == HttpStatus.SC_FORBIDDEN) {
638             return new HttpForbiddenException(message.toString());
639         } else if (status == HttpStatus.SC_NOT_FOUND) {
640             return new HttpNotFoundException(message.toString());
641         } else if (status == HttpStatus.SC_PRECONDITION_FAILED) {
642             return new HttpPreconditionFailedException(message.toString());
643         } else if (status == HttpStatus.SC_INTERNAL_SERVER_ERROR) {
644             return new HttpServerErrorException(message.toString());
645         } else {
646             return new HttpResponseException(status, message.toString());
647         }
648     }
649 
650 }