2 * Copyright (c) 2010-2022 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.nest.internal.wwn.handler;
15 import java.util.Properties;
16 import java.util.concurrent.TimeUnit;
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;
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}.
36 * @author Wouter Born - Initial contribution
37 * @author Wouter Born - Extract resolving redirect URL from NestBridgeHandler into NestRedirectUrlSupplier
40 public class WWNRedirectUrlSupplier {
42 private final Logger logger = LoggerFactory.getLogger(WWNRedirectUrlSupplier.class);
44 protected String cachedUrl = "";
46 protected Properties httpHeaders;
48 public WWNRedirectUrlSupplier(Properties httpHeaders) {
49 this.httpHeaders = httpHeaders;
52 public String getRedirectUrl() throws FailedResolvingWWNUrlException {
53 if (cachedUrl.isEmpty()) {
54 cachedUrl = resolveRedirectUrl();
59 public void resetCache() {
64 * Resolves the redirect URL for calls using the {@link WWNBindingConstants#NEST_URL}.
66 * The Jetty client used by {@link HttpUtil} will not pass the Authorization header after a redirect resulting in
67 * "401 Unauthorized error" issues.
69 * Note that this workaround currently does not use any configured proxy like {@link HttpUtil} does.
71 * @see https://developers.nest.com/documentation/cloud/how-to-handle-redirects
73 private String resolveRedirectUrl() throws FailedResolvingWWNUrlException {
74 HttpClient httpClient = new HttpClient(new SslContextFactory.Client());
75 httpClient.setFollowRedirects(false);
77 Request request = httpClient.newRequest(WWNBindingConstants.NEST_URL).method(HttpMethod.GET).timeout(30,
79 for (String httpHeaderKey : httpHeaders.stringPropertyNames()) {
80 request.header(httpHeaderKey, httpHeaders.getProperty(httpHeaderKey));
83 ContentResponse response;
86 response = request.send();
88 } catch (Exception e) {
89 throw new FailedResolvingWWNUrlException("Failed to resolve redirect URL: " + e.getMessage(), e);
92 int status = response.getStatus();
93 String redirectUrl = response.getHeaders().get(HttpHeader.LOCATION);
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");
104 redirectUrl = redirectUrl.endsWith("/") ? redirectUrl.substring(0, redirectUrl.length() - 1) : redirectUrl;
105 logger.debug("Redirect URL: {}", redirectUrl);