SUPPORT-8597: handle av errors
This commit is contained in:
parent
d1f592223a
commit
3b585487af
4 changed files with 111 additions and 84 deletions
|
|
@ -4,4 +4,8 @@ package ru.micord.ervu.av.exception;
|
|||
* @author r.latypov
|
||||
*/
|
||||
public class RetryableException extends RuntimeException {
|
||||
|
||||
public RetryableException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,4 +16,7 @@ public record FileStatus(String code, String status, String description) {
|
|||
public static final FileStatus FILE_STATUS_04 = new FileStatus("04", "Получен ЕРВУ",
|
||||
"Файл был принят в обработку"
|
||||
);
|
||||
public static final FileStatus FILE_STATUS_11 = new FileStatus("11", "Невозможно проверить файл ЛК РП",
|
||||
"Невозможно проверить файл по причине недоступности или ошибки в работе антивируса"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,22 +9,16 @@ import java.nio.file.Path;
|
|||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpDelete;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.conn.HttpHostConnectException;
|
||||
import org.apache.http.entity.mime.MultipartEntityBuilder;
|
||||
import org.apache.http.entity.mime.content.FileBody;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.apache.kafka.clients.admin.NewTopic;
|
||||
import org.apache.kafka.clients.producer.ProducerRecord;
|
||||
import org.slf4j.Logger;
|
||||
|
|
@ -42,10 +36,10 @@ import org.springframework.messaging.handler.annotation.Header;
|
|||
import org.springframework.stereotype.Service;
|
||||
import ru.micord.ervu.av.exception.FileUploadException;
|
||||
import ru.micord.ervu.av.exception.InvalidHttpFileUrlException;
|
||||
import ru.micord.ervu.av.exception.RetryableException;
|
||||
import ru.micord.ervu.av.kafka.dto.DownloadRequest;
|
||||
import ru.micord.ervu.av.kafka.dto.DownloadResponse;
|
||||
import ru.micord.ervu.av.kafka.dto.FileStatus;
|
||||
import ru.micord.ervu.av.response.AvFileSendResponse;
|
||||
import ru.micord.ervu.av.response.AvResponse;
|
||||
import ru.micord.ervu.av.s3.S3Service;
|
||||
|
||||
|
|
@ -57,10 +51,6 @@ public class FileUploadService {
|
|||
private static final Logger logger = LoggerFactory.getLogger(FileUploadService.class);
|
||||
@Value("${av.check.enabled}")
|
||||
private boolean avCheckEnabled;
|
||||
@Value("${av.rest.address}")
|
||||
private String avRestAddress;
|
||||
@Value("${av.first.timeout.milliseconds}")
|
||||
private Long avFirstTimeoutMilliseconds;
|
||||
@Value("${file.saving.path}")
|
||||
private String fileSavingPath;
|
||||
private final KafkaTemplate<String, String> kafkaTemplate;
|
||||
|
|
@ -104,21 +94,28 @@ public class FileUploadService {
|
|||
Path filePath = Paths.get(fileSavingPath, fileUrl.fileName());
|
||||
String downloadUrl = fileUrl.fileUrl();
|
||||
downloadFile(downloadUrl, filePath);
|
||||
boolean isAvError = false;
|
||||
boolean clean = true;
|
||||
boolean infected = false;
|
||||
|
||||
if (avCheckEnabled) {
|
||||
AvResponse avResponse = checkFile(filePath);
|
||||
|
||||
clean = Arrays.stream(avResponse.verdicts())
|
||||
.anyMatch(verdict -> verdict.equalsIgnoreCase(AvResponse.Scan.VERDICT_CLEAN));
|
||||
infected = Arrays.stream(avResponse.verdicts())
|
||||
.anyMatch(verdict -> verdict.equalsIgnoreCase(AvResponse.Scan.VERDICT_INFECTED));
|
||||
try {
|
||||
AvResponse avResponse = receiveScanReportRetryable.checkFile(filePath);
|
||||
clean = Arrays.stream(avResponse.verdicts())
|
||||
.anyMatch(verdict -> verdict.equalsIgnoreCase(AvResponse.Scan.VERDICT_CLEAN));
|
||||
infected = Arrays.stream(avResponse.verdicts())
|
||||
.anyMatch(verdict -> verdict.equalsIgnoreCase(AvResponse.Scan.VERDICT_INFECTED));
|
||||
}
|
||||
catch (FileUploadException | RetryableException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
isAvError = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (infected || !clean) {
|
||||
if (isAvError || infected || !clean) {
|
||||
downloadRequest.fileInfo().setFileUrl(null);
|
||||
downloadRequest.fileInfo().setFileStatus(FileStatus.FILE_STATUS_02);
|
||||
FileStatus fileStatus = isAvError ? FileStatus.FILE_STATUS_11 : FileStatus.FILE_STATUS_02;
|
||||
downloadRequest.fileInfo().setFileStatus(fileStatus);
|
||||
sendMessage(outErrorTopic.name(), downloadRequest, messageId, kafkaTemplate);
|
||||
}
|
||||
else {
|
||||
|
|
@ -221,68 +218,6 @@ public class FileUploadService {
|
|||
}
|
||||
}
|
||||
|
||||
private AvResponse checkFile(Path filePath) throws FileUploadException {
|
||||
File file = filePath.toFile();
|
||||
|
||||
try (CloseableHttpClient client = HttpClients.createDefault()) {
|
||||
HttpPost post = new HttpPost(avRestAddress);
|
||||
HttpEntity entity = MultipartEntityBuilder.create()
|
||||
.addPart("file", new FileBody(file))
|
||||
.build();
|
||||
post.setEntity(entity);
|
||||
|
||||
try (CloseableHttpResponse postResponse = client.execute(post)) {
|
||||
int postStatusCode = postResponse.getStatusLine().getStatusCode();
|
||||
String postResponseJson = EntityUtils.toString(postResponse.getEntity());
|
||||
|
||||
AvFileSendResponse avFileSendResponse;
|
||||
try {
|
||||
avFileSendResponse = new Gson().fromJson(postResponseJson, AvFileSendResponse.class);
|
||||
}
|
||||
catch (JsonSyntaxException e) {
|
||||
throw new FileUploadException("error json: " + postResponseJson, e);
|
||||
}
|
||||
|
||||
if (postStatusCode != HttpStatus.CREATED.value()) {
|
||||
StringBuilder stringBuilder = new StringBuilder(
|
||||
"http status code " + postStatusCode + " for " + avRestAddress + " post request.");
|
||||
|
||||
String status = avFileSendResponse.status();
|
||||
if (status != null) {
|
||||
stringBuilder.append(" Status: ").append(status).append(".");
|
||||
}
|
||||
if (avFileSendResponse.error() != null) {
|
||||
stringBuilder.append(" Error code: ")
|
||||
.append(avFileSendResponse.error().code())
|
||||
.append(". Error message: ")
|
||||
.append(avFileSendResponse.error().message())
|
||||
.append(". ");
|
||||
}
|
||||
throw new FileUploadException(stringBuilder.toString());
|
||||
}
|
||||
|
||||
String id = avFileSendResponse.id();
|
||||
String reportRequestUri = avRestAddress + "/" + id;
|
||||
HttpGet get = new HttpGet(reportRequestUri);
|
||||
|
||||
// waiting for timeout time before first request
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(
|
||||
avFirstTimeoutMilliseconds == null ? 1000L : avFirstTimeoutMilliseconds);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
throw new FileUploadException(e);
|
||||
}
|
||||
|
||||
return receiveScanReportRetryable.receiveScanReport(client, get);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
// непредусмотренная ошибка доступа через http-клиент
|
||||
throw new FileUploadException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteFile(String fileUrl) throws FileUploadException {
|
||||
try (CloseableHttpClient client = HttpClients.createDefault()) {
|
||||
HttpDelete delete = new HttpDelete(fileUrl);
|
||||
|
|
|
|||
|
|
@ -1,19 +1,30 @@
|
|||
package ru.micord.ervu.av.service;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.mime.MultipartEntityBuilder;
|
||||
import org.apache.http.entity.mime.content.FileBody;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.micord.ervu.av.exception.FileUploadException;
|
||||
import ru.micord.ervu.av.exception.RetryableException;
|
||||
import ru.micord.ervu.av.response.AvFileSendResponse;
|
||||
import ru.micord.ervu.av.response.AvResponse;
|
||||
|
||||
/**
|
||||
|
|
@ -21,19 +32,93 @@ import ru.micord.ervu.av.response.AvResponse;
|
|||
*/
|
||||
@Service
|
||||
public class ReceiveScanReportRetryable {
|
||||
|
||||
@Value("${av.rest.address}")
|
||||
private String avRestAddress;
|
||||
@Value("${av.first.timeout.milliseconds}")
|
||||
private Long avFirstTimeoutMilliseconds;
|
||||
|
||||
@Retryable(retryFor = {RetryableException.class},
|
||||
maxAttemptsExpression = "${av.retry.max.attempts.count}",
|
||||
backoff = @Backoff(delayExpression = "${av.retry.delay.milliseconds}"))
|
||||
public AvResponse receiveScanReport(CloseableHttpClient client, HttpGet get)
|
||||
public AvResponse checkFile(Path filePath) throws FileUploadException {
|
||||
File file = filePath.toFile();
|
||||
|
||||
try (CloseableHttpClient client = HttpClients.createDefault()) {
|
||||
HttpPost post = new HttpPost(avRestAddress);
|
||||
HttpEntity entity = MultipartEntityBuilder.create()
|
||||
.addPart("file", new FileBody(file))
|
||||
.build();
|
||||
post.setEntity(entity);
|
||||
|
||||
try (CloseableHttpResponse postResponse = client.execute(post)) {
|
||||
int postStatusCode = postResponse.getStatusLine().getStatusCode();
|
||||
String postResponseJson = EntityUtils.toString(postResponse.getEntity());
|
||||
AvFileSendResponse avFileSendResponse;
|
||||
|
||||
try {
|
||||
avFileSendResponse = new Gson().fromJson(postResponseJson, AvFileSendResponse.class);
|
||||
}
|
||||
catch (JsonSyntaxException e) {
|
||||
throw new FileUploadException("error json: " + postResponseJson, e);
|
||||
}
|
||||
|
||||
if (postStatusCode != HttpStatus.CREATED.value()) {
|
||||
StringBuilder stringBuilder = new StringBuilder(
|
||||
"http status code " + postStatusCode + " for " + avRestAddress + " post request.");
|
||||
String status = avFileSendResponse.status();
|
||||
|
||||
if (status != null) {
|
||||
stringBuilder.append(" Status: ").append(status).append(".");
|
||||
}
|
||||
|
||||
if (avFileSendResponse.error() != null) {
|
||||
stringBuilder.append(" Error code: ")
|
||||
.append(avFileSendResponse.error().code())
|
||||
.append(". Error message: ")
|
||||
.append(avFileSendResponse.error().message())
|
||||
.append(". ");
|
||||
}
|
||||
throw new FileUploadException(stringBuilder.toString());
|
||||
}
|
||||
|
||||
String id = avFileSendResponse.id();
|
||||
String reportRequestUri = avRestAddress + "/" + id;
|
||||
HttpGet get = new HttpGet(reportRequestUri);
|
||||
|
||||
// waiting for timeout time before first request
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(
|
||||
avFirstTimeoutMilliseconds == null ? 1000L : avFirstTimeoutMilliseconds);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
throw new FileUploadException(e);
|
||||
}
|
||||
|
||||
return receiveScanReport(client, get);
|
||||
}
|
||||
}
|
||||
catch (ClientProtocolException e) {
|
||||
throw new RetryableException("Failed to check file");
|
||||
}
|
||||
catch (IOException e) {
|
||||
// непредусмотренная ошибка доступа через http-клиент
|
||||
throw new FileUploadException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private AvResponse receiveScanReport(CloseableHttpClient client, HttpGet get)
|
||||
throws FileUploadException {
|
||||
|
||||
try (CloseableHttpResponse getResponse = client.execute(get)) {
|
||||
int getStatusCode = getResponse.getStatusLine().getStatusCode();
|
||||
|
||||
if (getStatusCode == HttpStatus.OK.value()) {
|
||||
String getResponseJson = EntityUtils.toString(getResponse.getEntity());
|
||||
AvResponse avResponse = new Gson().fromJson(getResponseJson, AvResponse.class);
|
||||
|
||||
if (avResponse.completed() == null) {
|
||||
throw new RetryableException();
|
||||
throw new RetryableException("Failed to complete file scan");
|
||||
}
|
||||
return avResponse;
|
||||
}
|
||||
|
|
@ -44,7 +129,7 @@ public class ReceiveScanReportRetryable {
|
|||
}
|
||||
catch (ClientProtocolException e) {
|
||||
// непредусмотренная ошибка доступа через http-клиент
|
||||
throw new RuntimeException(e);
|
||||
throw new RetryableException("Failed to receive scan report");
|
||||
}
|
||||
catch (IOException e) {
|
||||
// непредусмотренная ошибка доступа через http-клиент
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue