]> git.basschouten.com Git - openhab-addons.git/blob
47051bae7fb387e0675257e3d204f2114aa0d8a6
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.nest.internal.wwn.handler;
14
15 import java.util.Properties;
16 import java.util.concurrent.TimeUnit;
17
18 import org.eclipse.jdt.annotation.NonNullByDefault;
19 import org.eclipse.jetty.client.HttpClient;
20 import org.eclipse.jetty.client.api.ContentResponse;
21 import org.eclipse.jetty.client.api.Request;
22 import org.eclipse.jetty.http.HttpHeader;
23 import org.eclipse.jetty.http.HttpMethod;
24 import org.eclipse.jetty.http.HttpStatus;
25 import org.eclipse.jetty.util.ssl.SslContextFactory;
26 import org.openhab.binding.nest.internal.wwn.WWNBindingConstants;
27 import org.openhab.binding.nest.internal.wwn.exceptions.FailedResolvingWWNUrlException;
28 import org.openhab.core.io.net.http.HttpUtil;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 /**
33  * Supplies resolved redirect URLs of {@link WWNBindingConstants#NEST_URL} so they can be used with HTTP clients that
34  * do not pass Authorization headers after redirects like the Jetty client used by {@link HttpUtil}.
35  *
36  * @author Wouter Born - Initial contribution
37  * @author Wouter Born - Extract resolving redirect URL from NestBridgeHandler into NestRedirectUrlSupplier
38  */
39 @NonNullByDefault
40 public class WWNRedirectUrlSupplier {
41
42     private final Logger logger = LoggerFactory.getLogger(WWNRedirectUrlSupplier.class);
43
44     protected String cachedUrl = "";
45
46     protected Properties httpHeaders;
47
48     public WWNRedirectUrlSupplier(Properties httpHeaders) {
49         this.httpHeaders = httpHeaders;
50     }
51
52     public String getRedirectUrl() throws FailedResolvingWWNUrlException {
53         if (cachedUrl.isEmpty()) {
54             cachedUrl = resolveRedirectUrl();
55         }
56         return cachedUrl;
57     }
58
59     public void resetCache() {
60         cachedUrl = "";
61     }
62
63     /**
64      * Resolves the redirect URL for calls using the {@link WWNBindingConstants#NEST_URL}.
65      *
66      * The Jetty client used by {@link HttpUtil} will not pass the Authorization header after a redirect resulting in
67      * "401 Unauthorized error" issues.
68      *
69      * Note that this workaround currently does not use any configured proxy like {@link HttpUtil} does.
70      *
71      * @see https://developers.nest.com/documentation/cloud/how-to-handle-redirects
72      */
73     private String resolveRedirectUrl() throws FailedResolvingWWNUrlException {
74         HttpClient httpClient = new HttpClient(new SslContextFactory.Client());
75         httpClient.setFollowRedirects(false);
76
77         Request request = httpClient.newRequest(WWNBindingConstants.NEST_URL).method(HttpMethod.GET).timeout(30,
78                 TimeUnit.SECONDS);
79         for (String httpHeaderKey : httpHeaders.stringPropertyNames()) {
80             request.header(httpHeaderKey, httpHeaders.getProperty(httpHeaderKey));
81         }
82
83         ContentResponse response;
84         try {
85             httpClient.start();
86             response = request.send();
87             httpClient.stop();
88         } catch (Exception e) {
89             throw new FailedResolvingWWNUrlException("Failed to resolve redirect URL: " + e.getMessage(), e);
90         }
91
92         int status = response.getStatus();
93         String redirectUrl = response.getHeaders().get(HttpHeader.LOCATION);
94
95         if (status != HttpStatus.TEMPORARY_REDIRECT_307) {
96             logger.debug("Redirect status: {}", status);
97             logger.debug("Redirect response: {}", response.getContentAsString());
98             throw new FailedResolvingWWNUrlException("Failed to get redirect URL, expected status "
99                     + HttpStatus.TEMPORARY_REDIRECT_307 + " but was " + status);
100         } else if (redirectUrl == null || redirectUrl.isEmpty()) {
101             throw new FailedResolvingWWNUrlException("Redirect URL is empty");
102         }
103
104         redirectUrl = redirectUrl.endsWith("/") ? redirectUrl.substring(0, redirectUrl.length() - 1) : redirectUrl;
105         logger.debug("Redirect URL: {}", redirectUrl);
106         return redirectUrl;
107     }
108 }