]> git.basschouten.com Git - openhab-addons.git/blob
c03f589611599b0b681e9a08ed7692d7e7b04518
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.homeconnect.internal.client;
14
15 import static io.github.bucket4j.Bandwidth.classic;
16 import static io.github.bucket4j.Refill.intervally;
17
18 import java.io.IOException;
19 import java.time.Duration;
20 import java.time.LocalDateTime;
21 import java.time.format.DateTimeFormatter;
22 import java.util.HashMap;
23 import java.util.Map;
24 import java.util.concurrent.ExecutionException;
25 import java.util.concurrent.TimeoutException;
26
27 import org.eclipse.jdt.annotation.NonNullByDefault;
28 import org.eclipse.jdt.annotation.Nullable;
29 import org.eclipse.jetty.client.api.ContentResponse;
30 import org.eclipse.jetty.client.api.Request;
31 import org.eclipse.jetty.http.HttpMethod;
32 import org.openhab.binding.homeconnect.internal.client.exception.AuthorizationException;
33 import org.openhab.binding.homeconnect.internal.client.exception.CommunicationException;
34 import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
35 import org.openhab.core.auth.client.oauth2.OAuthClientService;
36 import org.openhab.core.auth.client.oauth2.OAuthException;
37 import org.openhab.core.auth.client.oauth2.OAuthResponseException;
38 import org.slf4j.LoggerFactory;
39
40 import com.google.gson.Gson;
41 import com.google.gson.GsonBuilder;
42 import com.google.gson.JsonElement;
43 import com.google.gson.JsonObject;
44 import com.google.gson.JsonParser;
45
46 import io.github.bucket4j.Bucket;
47 import io.github.bucket4j.Bucket4j;
48
49 /**
50  * okHttp helper.
51  *
52  * @author Jonas BrĂ¼stel - Initial contribution
53  * @author Laurent Garnier - Removed okhttp
54  *
55  */
56 @NonNullByDefault
57 public class HttpHelper {
58     private static final String BEARER = "Bearer ";
59     private static final int OAUTH_EXPIRE_BUFFER = 10;
60     private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
61     private static final JsonParser JSON_PARSER = new JsonParser();
62     private static final Map<String, Bucket> BUCKET_MAP = new HashMap<>();
63
64     private static @Nullable String lastAccessToken = null;
65
66     public static ContentResponse sendRequest(Request request, String clientId)
67             throws InterruptedException, TimeoutException, ExecutionException {
68         if (HttpMethod.GET.name().equals(request.getMethod())) {
69             try {
70                 getBucket(clientId).asScheduler().consume(1);
71             } catch (InterruptedException e) {
72                 LoggerFactory.getLogger(HttpHelper.class).debug("Could not consume from bucket! clientId={}, error={}",
73                         clientId, e.getMessage());
74             }
75         }
76         return request.send();
77     }
78
79     public static String formatJsonBody(@Nullable String jsonString) {
80         if (jsonString == null) {
81             return "";
82         }
83         try {
84             JsonObject json = parseString(jsonString).getAsJsonObject();
85             return GSON.toJson(json);
86         } catch (Exception e) {
87             return jsonString;
88         }
89     }
90
91     public static String getAuthorizationHeader(OAuthClientService oAuthClientService)
92             throws AuthorizationException, CommunicationException {
93         try {
94             AccessTokenResponse accessTokenResponse = oAuthClientService.getAccessTokenResponse();
95             // refresh the token if it's about to expire
96             if (accessTokenResponse != null
97                     && accessTokenResponse.isExpired(LocalDateTime.now(), OAUTH_EXPIRE_BUFFER)) {
98                 LoggerFactory.getLogger(HttpHelper.class).debug("Requesting a refresh of the access token.");
99                 accessTokenResponse = oAuthClientService.refreshToken();
100             }
101
102             if (accessTokenResponse != null) {
103                 String lastToken = lastAccessToken;
104                 if (lastToken == null) {
105                     LoggerFactory.getLogger(HttpHelper.class).debug("The used access token was created at {}",
106                             accessTokenResponse.getCreatedOn().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
107                 } else if (!lastToken.equals(accessTokenResponse.getAccessToken())) {
108                     LoggerFactory.getLogger(HttpHelper.class).debug("The access token changed. New one created at {}",
109                             accessTokenResponse.getCreatedOn().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
110                 }
111                 lastAccessToken = accessTokenResponse.getAccessToken();
112                 return BEARER + accessTokenResponse.getAccessToken();
113             } else {
114                 LoggerFactory.getLogger(HttpHelper.class).error("No access token available! Fatal error.");
115                 throw new AuthorizationException("No access token available!");
116             }
117         } catch (IOException e) {
118             String errorMessage = e.getMessage();
119             throw new CommunicationException(errorMessage != null ? errorMessage : "IOException", e);
120         } catch (OAuthException | OAuthResponseException e) {
121             String errorMessage = e.getMessage();
122             throw new AuthorizationException(errorMessage != null ? errorMessage : "oAuth exception", e);
123         }
124     }
125
126     public static JsonElement parseString(String json) {
127         return JSON_PARSER.parse(json);
128     }
129
130     private static synchronized Bucket getBucket(String clientId) {
131         Bucket bucket = null;
132         if (BUCKET_MAP.containsKey(clientId)) {
133             bucket = BUCKET_MAP.get(clientId);
134         }
135
136         if (bucket == null) {
137             bucket = Bucket4j.builder()
138                     // allows 50 tokens per minute (added 10 second buffer)
139                     .addLimit(classic(50, intervally(50, Duration.ofSeconds(70))).withInitialTokens(40))
140                     // but not often then 50 tokens per second
141                     .addLimit(classic(10, intervally(10, Duration.ofSeconds(1)))).build();
142             BUCKET_MAP.put(clientId, bucket);
143         }
144         return bucket;
145     }
146 }