SUPPORT-8662: use synchronous request-reply; fix file manager

This commit is contained in:
gulnaz 2024-11-28 12:42:58 +03:00 committed by Zaripov Emil
parent 755e0e8789
commit 25798eeb40
5 changed files with 48 additions and 91 deletions

View file

@ -30,3 +30,6 @@ S3_ACCESS_KEY=rlTdTvkmSXu9FsLhfecw
S3_SECRET_KEY=NUmY0wwRIEyAd98GCKd1cOgJWvLQYAcMMul5Ulu0 S3_SECRET_KEY=NUmY0wwRIEyAd98GCKd1cOgJWvLQYAcMMul5Ulu0
S3_BUCKET_NAME=default-out-bucket S3_BUCKET_NAME=default-out-bucket
S3_PATH_STYLE_ACCESS_ENABLED=true S3_PATH_STYLE_ACCESS_ENABLED=true
WEBDAV_USERNAME=test
WEBDAV_PASSWORD=test

View file

@ -1,11 +0,0 @@
package ru.micord.ervu.av.response;
/**
* @author r.latypov
*/
public record AvFileSendResponse(String id, String location, Error error, String status) {
public static final String STATUS_ERROR = "error";
public record Error(String code, String message) {
}
}

View file

@ -6,12 +6,17 @@ import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import org.apache.http.HttpEntity; import org.apache.http.HttpEntity;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.HttpHostConnectException; import org.apache.http.conn.HttpHostConnectException;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.retry.annotation.Backoff; import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable; import org.springframework.retry.annotation.Retryable;
@ -20,12 +25,19 @@ import ru.micord.ervu.av.exception.FileUploadException;
import ru.micord.ervu.av.exception.InvalidHttpFileUrlException; import ru.micord.ervu.av.exception.InvalidHttpFileUrlException;
import ru.micord.ervu.av.exception.RetryableException; import ru.micord.ervu.av.exception.RetryableException;
import static org.springframework.util.StringUtils.hasText;
/** /**
* @author gulnaz * @author gulnaz
*/ */
@Component @Component
public class FileManager { public class FileManager {
@Value("${webdav.username}")
private String username;
@Value("${webdav.password}")
private String password;
@Retryable(retryFor = {InvalidHttpFileUrlException.class}, @Retryable(retryFor = {InvalidHttpFileUrlException.class},
maxAttemptsExpression = "${av.retry.max.attempts.count}", maxAttemptsExpression = "${av.retry.max.attempts.count}",
backoff = @Backoff(delayExpression = "${av.retry.delay.milliseconds}")) backoff = @Backoff(delayExpression = "${av.retry.delay.milliseconds}"))
@ -34,7 +46,7 @@ public class FileManager {
File file = filePath.toFile(); File file = filePath.toFile();
HttpGet request = new HttpGet(fileUrl); HttpGet request = new HttpGet(fileUrl);
try (CloseableHttpClient client = HttpClients.createDefault(); try (CloseableHttpClient client = getClientBuilder().build();
CloseableHttpResponse response = client.execute(request)) { CloseableHttpResponse response = client.execute(request)) {
int statusCode = response.getStatusLine().getStatusCode(); int statusCode = response.getStatusLine().getStatusCode();
@ -67,7 +79,7 @@ public class FileManager {
maxAttemptsExpression = "${av.retry.max.attempts.count}", maxAttemptsExpression = "${av.retry.max.attempts.count}",
backoff = @Backoff(delayExpression = "${av.retry.delay.milliseconds}")) backoff = @Backoff(delayExpression = "${av.retry.delay.milliseconds}"))
public void deleteFile(String fileUrl) throws FileUploadException { public void deleteFile(String fileUrl) throws FileUploadException {
try (CloseableHttpClient client = HttpClients.createDefault()) { try (CloseableHttpClient client = getClientBuilder().build()) {
HttpDelete delete = new HttpDelete(fileUrl); HttpDelete delete = new HttpDelete(fileUrl);
try (CloseableHttpResponse response = client.execute(delete)) { try (CloseableHttpResponse response = client.execute(delete)) {
@ -83,4 +95,18 @@ public class FileManager {
throw new FileUploadException(e); throw new FileUploadException(e);
} }
} }
private HttpClientBuilder getClientBuilder() {
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
if (hasText(username) && hasText(password)) {
CredentialsProvider provider = new BasicCredentialsProvider();
provider.setCredentials(
AuthScope.ANY,
new UsernamePasswordCredentials(username, password)
);
httpClientBuilder.setDefaultCredentialsProvider(provider);
}
return httpClientBuilder;
}
} }

View file

@ -2,16 +2,16 @@ package ru.micord.ervu.av.service;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.concurrent.TimeUnit;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import org.apache.http.HttpEntity; import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException; import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse; 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.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody; import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.CloseableHttpClient;
@ -24,7 +24,6 @@ import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import ru.micord.ervu.av.exception.FileUploadException; import ru.micord.ervu.av.exception.FileUploadException;
import ru.micord.ervu.av.exception.RetryableException; import ru.micord.ervu.av.exception.RetryableException;
import ru.micord.ervu.av.response.AvFileSendResponse;
import ru.micord.ervu.av.response.AvResponse; import ru.micord.ervu.av.response.AvResponse;
/** /**
@ -35,8 +34,6 @@ public class ReceiveScanReportRetryable {
@Value("${av.rest.address}") @Value("${av.rest.address}")
private String avRestAddress; private String avRestAddress;
@Value("${av.first.timeout.milliseconds:1000}")
private Long avFirstTimeoutMilliseconds;
@Retryable(retryFor = {RetryableException.class}, @Retryable(retryFor = {RetryableException.class},
maxAttemptsExpression = "${av.retry.max.attempts.count}", maxAttemptsExpression = "${av.retry.max.attempts.count}",
@ -45,7 +42,10 @@ public class ReceiveScanReportRetryable {
File file = filePath.toFile(); File file = filePath.toFile();
try (CloseableHttpClient client = HttpClients.createDefault()) { try (CloseableHttpClient client = HttpClients.createDefault()) {
HttpPost post = new HttpPost(avRestAddress); URI uri = new URIBuilder(avRestAddress)
.addParameter("wait", "1")
.build();
HttpPost post = new HttpPost(uri);
HttpEntity entity = MultipartEntityBuilder.create() HttpEntity entity = MultipartEntityBuilder.create()
.addPart("file", new FileBody(file)) .addPart("file", new FileBody(file))
.build(); .build();
@ -54,84 +54,21 @@ public class ReceiveScanReportRetryable {
try (CloseableHttpResponse postResponse = client.execute(post)) { try (CloseableHttpResponse postResponse = client.execute(post)) {
int postStatusCode = postResponse.getStatusLine().getStatusCode(); int postStatusCode = postResponse.getStatusLine().getStatusCode();
String postResponseJson = EntityUtils.toString(postResponse.getEntity()); String postResponseJson = EntityUtils.toString(postResponse.getEntity());
AvFileSendResponse avFileSendResponse;
try { if (postStatusCode == HttpStatus.OK.value()) {
avFileSendResponse = new Gson().fromJson(postResponseJson, AvFileSendResponse.class); return new Gson().fromJson(postResponseJson, AvResponse.class);
} }
catch (JsonSyntaxException e) { else {
throw new FileUploadException("error json: " + postResponseJson, e); throw new FileUploadException(
"http status code " + postStatusCode + " for " + post.getURI()
+ " get request.");
} }
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);
}
catch (InterruptedException e) {
throw new FileUploadException(e);
}
return receiveScanReport(client, get);
} }
} }
catch (ClientProtocolException e) { catch (ClientProtocolException e) {
throw new RetryableException("Failed to check file"); throw new RetryableException("Failed to check file");
} }
catch (IOException e) { catch (IOException | URISyntaxException e) {
// непредусмотренная ошибка доступа через http-клиент
throw new FileUploadException(e);
}
}
private AvResponse receiveScanReport(CloseableHttpClient client, HttpGet get)
throws RetryableException, 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("Failed to complete file scan");
}
return avResponse;
}
else {
throw new FileUploadException("http status code " + getStatusCode + " for " + get.getURI()
+ " get request.");
}
}
catch (ClientProtocolException e) {
// непредусмотренная ошибка доступа через http-клиент
throw new RetryableException("Failed to receive scan report");
}
catch (IOException e) {
// непредусмотренная ошибка доступа через http-клиент
throw new FileUploadException(e); throw new FileUploadException(e);
} }
} }

View file

@ -63,6 +63,8 @@ s3.out.secret_key=${S3_SECRET_KEY}
s3.out.bucket_name=${S3_OUT_BUCKET_NAME} s3.out.bucket_name=${S3_OUT_BUCKET_NAME}
s3.out.path.style.access.enabled=${S3_OUT_PATH_STYLE_ACCESS_ENABLED} s3.out.path.style.access.enabled=${S3_OUT_PATH_STYLE_ACCESS_ENABLED}
# #
webdav.username=${WEBDAV_USERNAME}
webdav.password=${WEBDAV_PASSWORD}
# endpoints management # endpoints management
management.endpoints.web.exposure.include = health, info, metrics management.endpoints.web.exposure.include = health, info, metrics