YahooFinanceSessionService.java

package com.tdmconsult.ete.services.yahoo;

import java.net.URI;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Service;
import org.springframework.web.client.DefaultResponseErrorHandler;

@Service
@RequiredArgsConstructor
public class YahooFinanceSessionService {

    private final RestTemplateBuilder builder;
    private final YahooFinanceProperties yahooProperties;

    @Nullable
    private YahooSession session;

    public record YahooSession(
            String crumb,
            String cookie) {
    }

    public synchronized YahooSession getSession() {

        if (session != null) {
            return session;
        }

        final var restTemplateWithErrorHandlerDeactivated = builder
                .errorHandler(new DefaultResponseErrorHandler() {
                    @Override
                    public boolean hasError(final ClientHttpResponse response) {
                        return false;
                    }
                })
                .build();

        final var authHeaders = new HttpHeaders();
        authHeaders.set("User-Agent", "Mozilla/5.0");

        final var cookieResponse =
                restTemplateWithErrorHandlerDeactivated.exchange(
                        URI.create(yahooProperties.authurl()),
                        HttpMethod.GET,
                        new HttpEntity<>(authHeaders),
                        Void.class);

        final var cookies = cookieResponse.getHeaders().get(HttpHeaders.SET_COOKIE);

        if (cookies == null || cookies.isEmpty()) {
            throw new IllegalStateException("Yahoo %s did not return a cookie".formatted(yahooProperties.authurl()));
        }

        final var cookie = cookies.getFirst().split(";", 2)[0];

        final var headers = new HttpHeaders();
        headers.set("User-Agent", "Mozilla/5.0");
        headers.add(HttpHeaders.COOKIE, cookie);

        final var crumbResponse =
                restTemplateWithErrorHandlerDeactivated.exchange(
                        URI.create(yahooProperties.baseurl() + "/v1/test/getcrumb"),
                        HttpMethod.GET,
                        new HttpEntity<>(headers),
                        String.class);

        session = new YahooSession(
                Objects.requireNonNull(crumbResponse.getBody(), "expecting crumb response body to be non-empty"),
                cookie
        );

        return session;
    }

    public synchronized void invalidateSession() {
        session = null;
    }
}