1 2 /** 3 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 * SPDX-License-Identifier: Apache-2.0. 5 */ 6 package software.amazon.awssdk.crt.s3; 7 8 import java.nio.charset.Charset; 9 import java.util.concurrent.CompletableFuture; 10 import software.amazon.awssdk.crt.CrtResource; 11 import software.amazon.awssdk.crt.CrtRuntimeException; 12 import software.amazon.awssdk.crt.http.HttpMonitoringOptions; 13 import software.amazon.awssdk.crt.http.HttpProxyEnvironmentVariableSetting; 14 import software.amazon.awssdk.crt.http.HttpProxyOptions; 15 import software.amazon.awssdk.crt.http.HttpRequestBodyStream; 16 import software.amazon.awssdk.crt.io.TlsConnectionOptions; 17 import software.amazon.awssdk.crt.io.TlsContext; 18 import software.amazon.awssdk.crt.io.StandardRetryOptions; 19 import software.amazon.awssdk.crt.Log; 20 import software.amazon.awssdk.crt.auth.signing.AwsSigningConfig; 21 22 import java.net.URI; 23 24 public class S3Client extends CrtResource { 25 26 private final static Charset UTF8 = java.nio.charset.StandardCharsets.UTF_8; 27 private final CompletableFuture<Void> shutdownComplete = new CompletableFuture<>(); 28 private final String region; 29 S3Client(S3ClientOptions options)30 public S3Client(S3ClientOptions options) throws CrtRuntimeException { 31 TlsContext tlsCtx = options.getTlsContext(); 32 region = options.getRegion(); 33 34 int proxyConnectionType = 0; 35 String proxyHost = null; 36 int proxyPort = 0; 37 TlsContext proxyTlsContext = null; 38 int proxyAuthorizationType = 0; 39 String proxyAuthorizationUsername = null; 40 String proxyAuthorizationPassword = null; 41 42 HttpProxyOptions proxyOptions = options.getProxyOptions(); 43 if (proxyOptions != null) { 44 proxyConnectionType = proxyOptions.getConnectionType().getValue(); 45 proxyHost = proxyOptions.getHost(); 46 proxyPort = proxyOptions.getPort(); 47 proxyTlsContext = proxyOptions.getTlsContext(); 48 proxyAuthorizationType = proxyOptions.getAuthorizationType().getValue(); 49 proxyAuthorizationUsername = proxyOptions.getAuthorizationUsername(); 50 proxyAuthorizationPassword = proxyOptions.getAuthorizationPassword(); 51 } 52 53 int environmentVariableProxyConnectionType = 0; 54 TlsConnectionOptions environmentVariableProxyTlsConnectionOptions = null; 55 int environmentVariableType = 1; 56 HttpProxyEnvironmentVariableSetting environmentVariableSetting = options.getHttpProxyEnvironmentVariableSetting(); 57 if (environmentVariableSetting != null) { 58 environmentVariableProxyConnectionType = environmentVariableSetting.getConnectionType().getValue(); 59 environmentVariableProxyTlsConnectionOptions = environmentVariableSetting.getTlsConnectionOptions(); 60 environmentVariableType = environmentVariableSetting.getEnvironmentVariableType().getValue(); 61 } 62 63 HttpMonitoringOptions monitoringOptions = options.getMonitoringOptions(); 64 long monitoringThroughputThresholdInBytesPerSecond = 0; 65 int monitoringFailureIntervalInSeconds = 0; 66 if (monitoringOptions != null) { 67 monitoringThroughputThresholdInBytesPerSecond = monitoringOptions.getMinThroughputBytesPerSecond(); 68 monitoringFailureIntervalInSeconds = monitoringOptions.getAllowableThroughputFailureIntervalSeconds(); 69 } 70 AwsSigningConfig signingConfig = options.getSigningConfig(); 71 boolean didCreateSigningConfig = false; 72 if(signingConfig == null && options.getCredentialsProvider()!= null) { 73 /* Create the signing config from credentials provider */ 74 signingConfig = AwsSigningConfig.getDefaultS3SigningConfig(region, options.getCredentialsProvider()); 75 didCreateSigningConfig = true; 76 } 77 78 acquireNativeHandle(s3ClientNew(this, 79 region.getBytes(UTF8), 80 options.getClientBootstrap().getNativeHandle(), 81 tlsCtx != null ? tlsCtx.getNativeHandle() : 0, 82 signingConfig, 83 options.getPartSize(), 84 options.getMultiPartUploadThreshold(), 85 options.getThroughputTargetGbps(), 86 options.getReadBackpressureEnabled(), 87 options.getInitialReadWindowSize(), 88 options.getMaxConnections(), 89 options.getStandardRetryOptions(), 90 options.getComputeContentMd5(), 91 proxyConnectionType, 92 proxyHost != null ? proxyHost.getBytes(UTF8) : null, 93 proxyPort, 94 proxyTlsContext != null ? proxyTlsContext.getNativeHandle() : 0, 95 proxyAuthorizationType, 96 proxyAuthorizationUsername != null ? proxyAuthorizationUsername.getBytes(UTF8) : null, 97 proxyAuthorizationPassword != null ? proxyAuthorizationPassword.getBytes(UTF8) : null, 98 environmentVariableProxyConnectionType, 99 environmentVariableProxyTlsConnectionOptions != null 100 ? environmentVariableProxyTlsConnectionOptions.getNativeHandle() 101 : 0, 102 environmentVariableType, 103 options.getConnectTimeoutMs(), 104 options.getTcpKeepAliveOptions(), 105 monitoringThroughputThresholdInBytesPerSecond, 106 monitoringFailureIntervalInSeconds, 107 options.getEnableS3Express(), 108 options.getS3ExpressCredentialsProviderFactory(), 109 options.getMemoryLimitInBytes())); 110 111 addReferenceTo(options.getClientBootstrap()); 112 if(didCreateSigningConfig) { 113 /* The native code will keep the needed resource around */ 114 signingConfig.close(); 115 } 116 } 117 onShutdownComplete()118 private void onShutdownComplete() { 119 releaseReferences(); 120 121 this.shutdownComplete.complete(null); 122 } 123 makeMetaRequest(S3MetaRequestOptions options)124 public S3MetaRequest makeMetaRequest(S3MetaRequestOptions options) { 125 126 if(isNull()) { 127 Log.log(Log.LogLevel.Error, Log.LogSubject.S3Client, 128 "S3Client.makeMetaRequest has invalid client. The client can not be used after it is closed."); 129 throw new IllegalStateException("S3Client.makeMetaRequest has invalid client. The client can not be used after it is closed."); 130 } 131 132 if (options.getHttpRequest() == null) { 133 Log.log(Log.LogLevel.Error, Log.LogSubject.S3Client, 134 "S3Client.makeMetaRequest has invalid options; Http Request cannot be null."); 135 throw new IllegalArgumentException("S3Client.makeMetaRequest has invalid options; Http Request cannot be null."); 136 } 137 138 if (options.getResponseHandler() == null) { 139 Log.log(Log.LogLevel.Error, Log.LogSubject.S3Client, 140 "S3Client.makeMetaRequest has invalid options; Response Handler cannot be null."); 141 throw new IllegalArgumentException("S3Client.makeMetaRequest has invalid options; Response Handler cannot be null."); 142 } 143 144 S3MetaRequest metaRequest = new S3MetaRequest(); 145 S3MetaRequestResponseHandlerNativeAdapter responseHandlerNativeAdapter = new S3MetaRequestResponseHandlerNativeAdapter( 146 options.getResponseHandler()); 147 148 byte[] httpRequestBytes = options.getHttpRequest().marshalForJni(); 149 byte[] requestFilePath = null; 150 if (options.getRequestFilePath() != null) { 151 requestFilePath = options.getRequestFilePath().toString().getBytes(UTF8); 152 } 153 154 AwsSigningConfig signingConfig = options.getSigningConfig(); 155 boolean didCreateSigningConfig = false; 156 if(signingConfig == null && options.getCredentialsProvider()!= null) { 157 signingConfig = AwsSigningConfig.getDefaultS3SigningConfig(region, options.getCredentialsProvider()); 158 didCreateSigningConfig = true; 159 } 160 URI endpoint = options.getEndpoint(); 161 162 ChecksumConfig checksumConfig = options.getChecksumConfig() != null ? options.getChecksumConfig() 163 : new ChecksumConfig(); 164 165 long metaRequestNativeHandle = s3ClientMakeMetaRequest(getNativeHandle(), metaRequest, region.getBytes(UTF8), 166 options.getMetaRequestType().getNativeValue(), checksumConfig.getChecksumLocation().getNativeValue(), 167 checksumConfig.getChecksumAlgorithm().getNativeValue(), checksumConfig.getValidateChecksum(), 168 ChecksumAlgorithm.marshallAlgorithmsForJNI(checksumConfig.getValidateChecksumAlgorithmList()), 169 httpRequestBytes, options.getHttpRequest().getBodyStream(), requestFilePath, signingConfig, 170 responseHandlerNativeAdapter, endpoint == null ? null : endpoint.toString().getBytes(UTF8), 171 options.getResumeToken()); 172 173 metaRequest.setMetaRequestNativeHandle(metaRequestNativeHandle); 174 175 if(didCreateSigningConfig) { 176 /* The native code will keep the needed resource around */ 177 signingConfig.close(); 178 } 179 return metaRequest; 180 } 181 182 /** 183 * Determines whether a resource releases its dependencies at the same time the 184 * native handle is released or if it waits. Resources that wait are responsible 185 * for calling releaseReferences() manually. 186 */ 187 @Override canReleaseReferencesImmediately()188 protected boolean canReleaseReferencesImmediately() { 189 return false; 190 } 191 192 /** 193 * Cleans up the native resources associated with this client. The client is 194 * unusable after this call 195 */ 196 @Override releaseNativeHandle()197 protected void releaseNativeHandle() { 198 if (!isNull()) { 199 s3ClientDestroy(getNativeHandle()); 200 } 201 } 202 getShutdownCompleteFuture()203 public CompletableFuture<Void> getShutdownCompleteFuture() { 204 return shutdownComplete; 205 } 206 207 /******************************************************************************* 208 * native methods 209 ******************************************************************************/ s3ClientNew(S3Client thisObj, byte[] region, long clientBootstrap, long tlsContext, AwsSigningConfig signingConfig, long partSize, long multipartUploadThreshold, double throughputTargetGbps, boolean enableReadBackpressure, long initialReadWindow, int maxConnections, StandardRetryOptions standardRetryOptions, boolean computeContentMd5, int proxyConnectionType, byte[] proxyHost, int proxyPort, long proxyTlsContext, int proxyAuthorizationType, byte[] proxyAuthorizationUsername, byte[] proxyAuthorizationPassword, int environmentVariableProxyConnectionType, long environmentVariableProxyTlsConnectionOptions, int environmentVariableSetting, int connectTimeoutMs, S3TcpKeepAliveOptions tcpKeepAliveOptions, long monitoringThroughputThresholdInBytesPerSecond, int monitoringFailureIntervalInSeconds, boolean enableS3Express, S3ExpressCredentialsProviderFactory s3expressCredentialsProviderFactory, long memoryLimitInBytes)210 private static native long s3ClientNew(S3Client thisObj, byte[] region, long clientBootstrap, 211 long tlsContext, AwsSigningConfig signingConfig, long partSize, long multipartUploadThreshold, double throughputTargetGbps, 212 boolean enableReadBackpressure, long initialReadWindow, int maxConnections, 213 StandardRetryOptions standardRetryOptions, boolean computeContentMd5, 214 int proxyConnectionType, 215 byte[] proxyHost, 216 int proxyPort, 217 long proxyTlsContext, 218 int proxyAuthorizationType, 219 byte[] proxyAuthorizationUsername, 220 byte[] proxyAuthorizationPassword, 221 int environmentVariableProxyConnectionType, 222 long environmentVariableProxyTlsConnectionOptions, 223 int environmentVariableSetting, 224 int connectTimeoutMs, 225 S3TcpKeepAliveOptions tcpKeepAliveOptions, 226 long monitoringThroughputThresholdInBytesPerSecond, 227 int monitoringFailureIntervalInSeconds, 228 boolean enableS3Express, 229 S3ExpressCredentialsProviderFactory s3expressCredentialsProviderFactory, 230 long memoryLimitInBytes) throws CrtRuntimeException; 231 s3ClientDestroy(long client)232 private static native void s3ClientDestroy(long client); 233 s3ClientMakeMetaRequest(long clientId, S3MetaRequest metaRequest, byte[] region, int metaRequestType, int checksumLocation, int checksumAlgorithm, boolean validateChecksum, int[] validateAlgorithms, byte[] httpRequestBytes, HttpRequestBodyStream httpRequestBodyStream, byte[] requestFilePath, AwsSigningConfig signingConfig, S3MetaRequestResponseHandlerNativeAdapter responseHandlerNativeAdapter, byte[] endpoint, ResumeToken resumeToken)234 private static native long s3ClientMakeMetaRequest(long clientId, S3MetaRequest metaRequest, byte[] region, 235 int metaRequestType, int checksumLocation, int checksumAlgorithm, boolean validateChecksum, 236 int[] validateAlgorithms, byte[] httpRequestBytes, 237 HttpRequestBodyStream httpRequestBodyStream, byte[] requestFilePath, 238 AwsSigningConfig signingConfig, S3MetaRequestResponseHandlerNativeAdapter responseHandlerNativeAdapter, 239 byte[] endpoint, ResumeToken resumeToken); 240 } 241