diff --git a/src/main/java/ru/soune/nocopy/handler/ImageFoundRequestHandler.java b/src/main/java/ru/soune/nocopy/handler/ImageFoundRequestHandler.java index 614ab06..e02d034 100644 --- a/src/main/java/ru/soune/nocopy/handler/ImageFoundRequestHandler.java +++ b/src/main/java/ru/soune/nocopy/handler/ImageFoundRequestHandler.java @@ -63,8 +63,7 @@ public class ImageFoundRequestHandler implements RequestHandler { String yandexReverseSearchResponse; try { - yandexReverseSearchResponse = yandexSearchService.searchReverseByPublicUrlWithTimeout(fileEntity, - 45); + yandexReverseSearchResponse = yandexSearchService.searchReverseByPublicUrlWithTimeout(fileEntity); } catch (TimeoutException e) { log.warn("Search timeout for file {}, returning empty results", fileId); return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), diff --git a/src/main/java/ru/soune/nocopy/service/search/YandexSearchService.java b/src/main/java/ru/soune/nocopy/service/search/YandexSearchService.java index e37206a..0240a9a 100644 --- a/src/main/java/ru/soune/nocopy/service/search/YandexSearchService.java +++ b/src/main/java/ru/soune/nocopy/service/search/YandexSearchService.java @@ -16,6 +16,7 @@ import ru.soune.nocopy.exception.NotValidFieldException; import java.io.*; import java.net.HttpURLConnection; +import java.net.SocketTimeoutException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; @@ -33,10 +34,10 @@ public class YandexSearchService { public YandexSearchService() { this.httpClient = new OkHttpClient.Builder() - .connectTimeout(30, TimeUnit.SECONDS) - .readTimeout(120, TimeUnit.SECONDS) - .writeTimeout(30, TimeUnit.SECONDS) - .callTimeout(180, TimeUnit.SECONDS) + .connectTimeout(5, TimeUnit.SECONDS) + .writeTimeout(5, TimeUnit.SECONDS) + .readTimeout(15, TimeUnit.SECONDS) + .callTimeout(20, TimeUnit.SECONDS) .retryOnConnectionFailure(true) .build(); @@ -102,44 +103,44 @@ public class YandexSearchService { return callReverseImageApiByUrl(publicUrl); } - private String callReverseImageApiByUrl(String imageUrl) throws IOException { - if (searchApiKey == null || searchApiKey.isEmpty()) { - throw new IllegalStateException("SearchAPI key not configured. Set searchapi.api-key property"); - } - - HttpUrl url = HttpUrl.parse("https://searchapi.io/api/v1/search") - .newBuilder() - .addQueryParameter("engine", "yandex_reverse_image") - .addQueryParameter("api_key", searchApiKey) - .addQueryParameter("url", imageUrl) - .addQueryParameter("wait", "true") - .addQueryParameter("timeout", "30000") - .build(); - - log.info("Calling Yandex Reverse Image API: {}", url); - - Request request = new Request.Builder() - .url(url) - .header("Accept", "application/json") - .header("User-Agent", "Mozilla/5.0") - .build(); - - try (Response response = httpClient.newCall(request).execute()) { - log.info("Response code: {}", response.code()); - log.info("Response headers: {}", response.headers()); - - if (!response.isSuccessful()) { - String errorBody = response.body() != null ? response.body().string() : "No error body"; - log.error("API request failed: {} - {}", response.code(), errorBody); - throw new IOException("API request failed: " + response.code() + " - " + response.message()); - } - - String responseBody = response.body().string(); - log.info("Response body length: {} characters", responseBody.length()); - - return responseBody; - } - } +// private String callReverseImageApiByUrl(String imageUrl) throws IOException { +// if (searchApiKey == null || searchApiKey.isEmpty()) { +// throw new IllegalStateException("SearchAPI key not configured. Set searchapi.api-key property"); +// } +// +// HttpUrl url = HttpUrl.parse("https://searchapi.io/api/v1/search") +// .newBuilder() +// .addQueryParameter("engine", "yandex_reverse_image") +// .addQueryParameter("api_key", searchApiKey) +// .addQueryParameter("url", imageUrl) +// .addQueryParameter("wait", "true") +// .addQueryParameter("timeout", "30000") +// .build(); +// +// log.info("Calling Yandex Reverse Image API: {}", url); +// +// Request request = new Request.Builder() +// .url(url) +// .header("Accept", "application/json") +// .header("User-Agent", "Mozilla/5.0") +// .build(); +// +// try (Response response = httpClient.newCall(request).execute()) { +// log.info("Response code: {}", response.code()); +// log.info("Response headers: {}", response.headers()); +// +// if (!response.isSuccessful()) { +// String errorBody = response.body() != null ? response.body().string() : "No error body"; +// log.error("API request failed: {} - {}", response.code(), errorBody); +// throw new IOException("API request failed: " + response.code() + " - " + response.message()); +// } +// +// String responseBody = response.body().string(); +// log.info("Response body length: {} characters", responseBody.length()); +// +// return responseBody; +// } +// } private boolean isImageFile(FileEntity fileEntity) { @@ -207,79 +208,62 @@ public class YandexSearchService { } } - public String searchReverseByPublicUrlWithTimeout(FileEntity fileEntity, int timeoutSeconds) + public String searchReverseByPublicUrlWithTimeout(FileEntity fileEntity) throws IOException, TimeoutException { + if (!isImageFile(fileEntity)) { - log.error("File not image: {}", fileEntity.getMimeType()); - throw new NotValidFieldException("File not image", new BaseResponse(20007, - MessageCode.INVALID_FIELD.getCode(), MessageCode.INVALID_FIELD.getDescription(), - Map.of("file_type", fileEntity.getMimeType()))); + throw new NotValidFieldException( + "File not image", + new BaseResponse( + 20007, + MessageCode.INVALID_FIELD.getCode(), + MessageCode.INVALID_FIELD.getDescription(), + Map.of("file_type", fileEntity.getMimeType()) + ) + ); } String publicUrl = String.format("%s/api/files/public/%s", appBaseUrl, fileEntity.getId()); + log.info("Searching reverse for image: {}", publicUrl); - ExecutorService executor = Executors.newSingleThreadExecutor(); - try { - Future future = executor.submit(() -> - callReverseImageApiByUrlWithTimeout(publicUrl, timeoutSeconds) - ); - - return future.get(timeoutSeconds, TimeUnit.SECONDS); - - } catch (TimeoutException e) { - log.error("Yandex search timeout after {} seconds for file: {}", timeoutSeconds, fileEntity.getId()); - throw e; - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new IOException("Search interrupted", e); - } catch (ExecutionException e) { - Throwable cause = e.getCause(); - if (cause instanceof IOException) { - throw (IOException) cause; - } - throw new IOException("Search failed", cause); - } finally { - executor.shutdownNow(); + return callReverseImageApiByUrl(publicUrl); + } catch (SocketTimeoutException e) { + log.error("Yandex search timeout after {}", fileEntity.getId()); + throw new TimeoutException("Search timeout"); } } - private String callReverseImageApiByUrlWithTimeout(String imageUrl, int timeoutSeconds) throws IOException { - if (searchApiKey == null || searchApiKey.isEmpty()) { + private String callReverseImageApiByUrl(String imageUrl) throws IOException { + + if (searchApiKey == null || searchApiKey.isBlank()) { throw new IllegalStateException("SearchAPI key not configured"); } - int apiTimeout = Math.max(1, timeoutSeconds - 2) * 1000; - HttpUrl url = HttpUrl.parse("https://searchapi.io/api/v1/search") .newBuilder() .addQueryParameter("engine", "yandex_reverse_image") .addQueryParameter("api_key", searchApiKey) .addQueryParameter("url", imageUrl) .addQueryParameter("wait", "true") - .addQueryParameter("timeout", String.valueOf(apiTimeout)) .build(); - log.info("Calling Yandex API with timeout {}ms: {}", apiTimeout, imageUrl); - Request request = new Request.Builder() .url(url) .header("Accept", "application/json") .header("User-Agent", "Mozilla/5.0") .build(); - long startTime = System.currentTimeMillis(); + long start = System.currentTimeMillis(); try (Response response = httpClient.newCall(request).execute()) { - long duration = System.currentTimeMillis() - startTime; - log.info("Response code: {}, duration: {}ms", response.code(), duration); + long duration = System.currentTimeMillis() - start; + log.info("Yandex response code={}, duration={}ms", response.code(), duration); if (!response.isSuccessful()) { - String errorBody = response.body() != null ? response.body().string() : "No error body"; - log.error("API request failed: {} - {}", response.code(), errorBody); - throw new IOException("API request failed: " + response.code()); + throw new IOException("API error: " + response.code()); } ResponseBody body = response.body(); @@ -287,13 +271,97 @@ public class YandexSearchService { throw new IOException("Empty response body"); } - String responseBody = body.string(); - log.info("Response body length: {} characters", responseBody.length()); - - return responseBody; + return body.string(); } } +// public String searchReverseByPublicUrlWithTimeout(FileEntity fileEntity, int timeoutSeconds) +// throws IOException, TimeoutException { +// if (!isImageFile(fileEntity)) { +// log.error("File not image: {}", fileEntity.getMimeType()); +// throw new NotValidFieldException("File not image", new BaseResponse(20007, +// MessageCode.INVALID_FIELD.getCode(), MessageCode.INVALID_FIELD.getDescription(), +// Map.of("file_type", fileEntity.getMimeType()))); +// } +// +// String publicUrl = String.format("%s/api/files/public/%s", appBaseUrl, fileEntity.getId()); +// log.info("Searching reverse for image: {}", publicUrl); +// +// ExecutorService executor = Executors.newSingleThreadExecutor(); +// +// try { +// Future future = executor.submit(() -> +// callReverseImageApiByUrlWithTimeout(publicUrl, timeoutSeconds) +// ); +// +// return future.get(timeoutSeconds, TimeUnit.SECONDS); +// +// } catch (TimeoutException e) { +// log.error("Yandex search timeout after {} seconds for file: {}", timeoutSeconds, fileEntity.getId()); +// throw e; +// } catch (InterruptedException e) { +// Thread.currentThread().interrupt(); +// throw new IOException("Search interrupted", e); +// } catch (ExecutionException e) { +// Throwable cause = e.getCause(); +// if (cause instanceof IOException) { +// throw (IOException) cause; +// } +// throw new IOException("Search failed", cause); +// } finally { +// executor.shutdownNow(); +// } +// } + +// private String callReverseImageApiByUrlWithTimeout(String imageUrl, int timeoutSeconds) throws IOException { +// if (searchApiKey == null || searchApiKey.isEmpty()) { +// throw new IllegalStateException("SearchAPI key not configured"); +// } +// +// int apiTimeout = Math.max(1, timeoutSeconds - 2) * 1000; +// +// HttpUrl url = HttpUrl.parse("https://searchapi.io/api/v1/search") +// .newBuilder() +// .addQueryParameter("engine", "yandex_reverse_image") +// .addQueryParameter("api_key", searchApiKey) +// .addQueryParameter("url", imageUrl) +// .addQueryParameter("wait", "true") +// .addQueryParameter("timeout", String.valueOf(apiTimeout)) +// .build(); +// +// log.info("Calling Yandex API with timeout {}ms: {}", apiTimeout, imageUrl); +// +// Request request = new Request.Builder() +// .url(url) +// .header("Accept", "application/json") +// .header("User-Agent", "Mozilla/5.0") +// .build(); +// +// long startTime = System.currentTimeMillis(); +// +// try (Response response = httpClient.newCall(request).execute()) { +// long duration = System.currentTimeMillis() - startTime; +// +// log.info("Response code: {}, duration: {}ms", response.code(), duration); +// +// if (!response.isSuccessful()) { +// String errorBody = response.body() != null ? response.body().string() : "No error body"; +// log.error("API request failed: {} - {}", response.code(), errorBody); +// throw new IOException("API request failed: " + response.code()); +// } +// +// ResponseBody body = response.body(); +// if (body == null) { +// throw new IOException("Empty response body"); +// } +// +// String responseBody = body.string(); +// log.info("Response body length: {} characters", responseBody.length()); +// +// return responseBody; +// } +// } + private String readAll(BufferedReader reader) throws IOException { StringBuilder sb = new StringBuilder(); String line;