From 12e2206f9d24b29c8276b2743603635620643444 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Wed, 18 Jan 2023 20:39:41 +0300 Subject: RestTemplate -> OkHttpClient --- .../java/com/juick/service/ActivityPubService.java | 75 ++++++++++---------- .../java/com/juick/service/WebfingerService.java | 82 +++++++++++++--------- 2 files changed, 86 insertions(+), 71 deletions(-) (limited to 'src/main/java/com/juick/service') diff --git a/src/main/java/com/juick/service/ActivityPubService.java b/src/main/java/com/juick/service/ActivityPubService.java index 9b93cab2..7eff123f 100644 --- a/src/main/java/com/juick/service/ActivityPubService.java +++ b/src/main/java/com/juick/service/ActivityPubService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2020, Juick + * Copyright (C) 2008-2023, Juick * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,16 +17,18 @@ package com.juick.service; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.juick.KeystoreManager; +import com.juick.model.AnonymousUser; import com.juick.model.User; import com.juick.service.activities.DeleteUserEvent; import com.juick.util.DateFormattersHolder; import com.juick.www.api.activity.model.Context; import com.juick.www.api.activity.model.objects.Actor; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.juick.KeystoreManager; -import com.juick.model.AnonymousUser; - -import org.apache.commons.codec.binary.Base64; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,20 +37,18 @@ import org.springframework.cache.annotation.Cacheable; import org.springframework.context.ApplicationEventPublisher; import org.springframework.core.convert.ConversionService; import org.springframework.dao.DuplicateKeyException; -import org.springframework.http.*; +import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.client.RestClientException; -import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; +import org.tomitribe.auth.signatures.Base64; import org.tomitribe.auth.signatures.MissingRequiredHeaderException; import org.tomitribe.auth.signatures.Signature; import org.tomitribe.auth.signatures.Verifier; import javax.annotation.Nonnull; import javax.inject.Inject; - import java.io.IOException; import java.net.URI; import java.security.Key; @@ -56,7 +56,6 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SignatureException; import java.time.Instant; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; @@ -71,7 +70,7 @@ public class ActivityPubService extends BaseJdbcService implements SocialService @Inject private UserService userService; @Inject - private RestTemplate restClient; + private OkHttpClient httpClient; @Inject private ObjectMapper jsonMapper; @Inject @@ -138,28 +137,26 @@ public class ActivityPubService extends BaseJdbcService implements SocialService try { String signatureString = signatureService.addSignature(from, host, "get", contextUri.getPath(), requestDate, ""); - HttpHeaders requestHeaders = new HttpHeaders(); - requestHeaders.add(HttpHeaders.DATE, requestDate); - requestHeaders.add(HttpHeaders.HOST, host); - requestHeaders.add("Signature", signatureString); - requestHeaders.setAccept(Collections.singletonList(MediaType.valueOf(Context.ACTIVITY_MEDIA_TYPE))); - HttpEntity activityRequest = new HttpEntity<>(requestHeaders); - var response = restClient.exchange(contextUri, HttpMethod.GET, activityRequest, Context.class); - if (response.getStatusCode().is2xxSuccessful()) { - var context = response.getBody(); - if (context == null) { - logger.warn("Cannot identify {}", contextUri); - return Optional.empty(); + var request = new Request.Builder() + .url(contextUri.toURL()) + .addHeader(HttpHeaders.DATE, requestDate) + .addHeader(HttpHeaders.HOST, host) + .addHeader("Signature", signatureString) + .addHeader(HttpHeaders.ACCEPT, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE) + .build(); + try (var response = httpClient.newCall(request).execute()) { + if (response.isSuccessful() && response.body() != null) { + var context = jsonMapper.readValue(response.body().string(), Context.class); + return Optional.of(context); } - return Optional.of(context); } - } catch (IOException | RestClientException e) { + } catch (IOException e) { logger.warn("HTTP Signature exception: {}", e.getMessage()); } return Optional.empty(); } - public HttpStatusCode post(Actor from, Actor to, Context data) throws IOException, NoSuchAlgorithmException { + public int post(Actor from, Actor to, Context data) throws IOException, NoSuchAlgorithmException, InterruptedException { UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(to.getInbox()); URI inbox = uriComponentsBuilder.build().toUri(); Instant now = Instant.now(); @@ -171,18 +168,20 @@ public class ActivityPubService extends BaseJdbcService implements SocialService final String digestHeader = "SHA-256=" + new String(Base64.encodeBase64(digest)); String signatureString = signatureService.addSignature(from, host, "post", inbox.getPath(), requestDate, digestHeader); - - HttpHeaders requestHeaders = new HttpHeaders(); - requestHeaders.add(HttpHeaders.CONTENT_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE); - requestHeaders.add(HttpHeaders.DATE, requestDate); - requestHeaders.add(HttpHeaders.HOST, host); - requestHeaders.add("Digest", digestHeader); - requestHeaders.add("Signature", signatureString); - HttpEntity request = new HttpEntity<>(payload, requestHeaders); + var body = RequestBody.create(payload, MediaType.get(Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE)); + var request = new Request.Builder() + .url(inbox.toASCIIString()) + .post(body) + .addHeader(HttpHeaders.DATE, requestDate) + .addHeader(HttpHeaders.CONTENT_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE) + .addHeader("Digest", digestHeader) + .addHeader("Signature", signatureString) + .build(); logger.debug("Sending context to {}: {}", to.getId(), payload); - ResponseEntity response = restClient.postForEntity(inbox, request, String.class); - logger.debug("Remote response: {} {}", response.getStatusCode(), response.getBody()); - return response.getStatusCode(); + try (var response = httpClient.newCall(request).execute()) { + logger.debug("Remote response: {} {}", response.code(), response.body()); + return response.code(); + } } public User verifyActor(String method, String path, Map headers) { diff --git a/src/main/java/com/juick/service/WebfingerService.java b/src/main/java/com/juick/service/WebfingerService.java index dc978763..ca49bd51 100644 --- a/src/main/java/com/juick/service/WebfingerService.java +++ b/src/main/java/com/juick/service/WebfingerService.java @@ -1,57 +1,73 @@ -package com.juick.service; +/* + * Copyright (C) 2008-2023, Juick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ -import java.net.URI; -import java.util.Collections; - -import javax.inject.Inject; +package com.juick.service; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.juick.www.api.webfinger.Resource; +import com.juick.www.api.webfinger.model.Account; +import com.juick.www.api.webfinger.model.Link; +import okhttp3.OkHttpClient; +import okhttp3.Request; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; +import org.springframework.http.*; import org.springframework.stereotype.Component; import org.springframework.web.client.RestClientException; -import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; - -import com.juick.www.api.webfinger.model.Account; -import com.juick.www.api.webfinger.model.Link; - import rocks.xmpp.addr.Jid; +import javax.inject.Inject; +import java.io.IOException; +import java.net.URI; +import java.util.Collections; + @Component public class WebfingerService { private static final Logger logger = LoggerFactory.getLogger("ActivityPub"); - - private final RestTemplate restClient; + private final OkHttpClient httpClient; + private final ObjectMapper jsonMapper; @Inject - public WebfingerService(final RestTemplate restClient) { - this.restClient = restClient; + public WebfingerService(final OkHttpClient httpClient, final ObjectMapper jsonMapper) { + this.httpClient = httpClient; + this.jsonMapper = jsonMapper; } public URI discoverAccountURI(String acct, MediaType linkType) { Jid acctId = Jid.of(acct); - URI resourceUri = UriComponentsBuilder.fromPath("/.well-known/webfinger").host(acctId.getDomain()) - .scheme("https").queryParam("resource", "acct:" + acct).build().toUri(); - HttpHeaders headers = new HttpHeaders(); - headers.setAccept(Collections.singletonList(MediaType.valueOf("application/jrd+json"))); - HttpEntity webfingerRequest = new HttpEntity<>(headers); + var resourceUri = UriComponentsBuilder.fromPath("/.well-known/webfinger").host(acctId.getDomain()) + .scheme("https").queryParam("resource", "acct:" + acct).build().toUriString(); + var request = new Request.Builder() + .url(resourceUri) + .addHeader(HttpHeaders.ACCEPT, Resource.MEDIA_TYPE) + .build(); try { - ResponseEntity response = restClient.exchange(resourceUri, HttpMethod.GET, webfingerRequest, - Account.class); - if (response.getStatusCode().is2xxSuccessful()) { - var account = response.getBody(); - for (Link l : account.links()) { - if (l.rel().equals("self") && l.type().equals(linkType.toString())) { - return URI.create(l.href()); + try(var response = httpClient.newCall(request).execute()) { + if (response.isSuccessful() && response.body() != null) { + var account = jsonMapper.readValue(response.body().string(), Account.class); + for (Link l : account.links()) { + if (l.rel().equals("self") && l.type().equals(linkType.toString())) { + return URI.create(l.href()); + } } } } - } catch (RestClientException e) { - logger.warn("Cannot discover person {}: {}", acct, e.getMessage()); + } catch (IOException e) { + return URI.create(StringUtils.EMPTY); } return URI.create(StringUtils.EMPTY); } -- cgit v1.2.3