20
20
21
21
#include " fdbclient/S3BlobStore.h"
22
22
23
+ #include < sstream>
24
+ #include " fdbrpc/HTTP.h"
23
25
#include " fdbclient/ClientKnobs.h"
24
26
#include " fdbclient/Knobs.h"
25
27
#include " flow/FastRef.h"
@@ -76,9 +78,6 @@ S3BlobStoreEndpoint::Stats S3BlobStoreEndpoint::s_stats;
76
78
std::unique_ptr<S3BlobStoreEndpoint::BlobStats> S3BlobStoreEndpoint::blobStats;
77
79
Future<Void> S3BlobStoreEndpoint::statsLogger = Never();
78
80
79
- std::unordered_map<BlobStoreConnectionPoolKey, Reference<S3BlobStoreEndpoint::ConnectionPoolData>>
80
- S3BlobStoreEndpoint::globalConnectionPool;
81
-
82
81
S3BlobStoreEndpoint::BlobKnobs::BlobKnobs () {
83
82
secure_connection = 1 ;
84
83
connect_tries = CLIENT_KNOBS->BLOBSTORE_CONNECT_TRIES ;
@@ -199,6 +198,11 @@ std::string S3BlobStoreEndpoint::BlobKnobs::getURLParameters() const {
199
198
}
200
199
201
200
std::string guessRegionFromDomain (std::string domain) {
201
+ // Special case for localhost/127.0.0.1 to prevent basic_string exception
202
+ if (domain == " 127.0.0.1" || domain == " localhost" ) {
203
+ return " us-east-1" ;
204
+ }
205
+
202
206
static const std::vector<const char *> knownServices = { " s3." , " cos." , " oss-" , " obs." };
203
207
boost::algorithm::to_lower (domain);
204
208
@@ -843,6 +847,10 @@ ACTOR Future<S3BlobStoreEndpoint::ReusableConnection> connect_impl(Reference<S3B
843
847
} else {
844
848
wait (store (conn, INetworkConnections::net ()->connect (host, service, isTLS)));
845
849
}
850
+
851
+ // Ensure connection is valid before handshake
852
+ ASSERT (conn.isValid ());
853
+
846
854
wait (conn->connectHandshake ());
847
855
848
856
TraceEvent (" S3BlobStoreEndpointNewConnectionSuccess" )
@@ -1030,6 +1038,12 @@ ACTOR Future<Reference<HTTP::IncomingResponse>> doRequest_impl(Reference<S3BlobS
1030
1038
req->data .headers [" Host" ] = bstore->host ;
1031
1039
req->data .headers [" Accept" ] = " application/xml" ;
1032
1040
1041
+ // In simulation, disable connection pooling for MockS3 to prevent NetSAV use-after-free crashes
1042
+ // This forces connection closure after each request, preventing race conditions during coordinator shutdown
1043
+ if (g_network->isSimulated () && bstore->host == " 127.0.0.1" ) {
1044
+ req->data .headers [" Connection" ] = " close" ;
1045
+ }
1046
+
1033
1047
// Avoid to send request with an empty resource.
1034
1048
if (resource.empty ()) {
1035
1049
resource = " /" ;
@@ -1140,7 +1154,11 @@ ACTOR Future<Reference<HTTP::IncomingResponse>> doRequest_impl(Reference<S3BlobS
1140
1154
rconn.conn , dryrunRequest, bstore->sendRate , &bstore->s_stats .bytes_sent , bstore->recvRate );
1141
1155
Reference<HTTP::IncomingResponse> _dryrunR = wait (timeoutError (dryrunResponse, requestTimeout));
1142
1156
dryrunR = _dryrunR;
1143
- std::string s3Error = parseErrorCodeFromS3 (dryrunR->data .content );
1157
+ // Only parse S3 error code for error responses (4xx/5xx), not successful responses (2xx)
1158
+ std::string s3Error;
1159
+ if (dryrunR->code >= 400 ) {
1160
+ s3Error = parseErrorCodeFromS3 (dryrunR->data .content );
1161
+ }
1144
1162
if (dryrunR->code == badRequestCode && isS3TokenError (s3Error)) {
1145
1163
// authentication fails and s3 token error persists, retry with a HEAD dryrun request
1146
1164
// to avoid sending duplicate data indefinitly to save network bandwidth
@@ -1263,7 +1281,12 @@ ACTOR Future<Reference<HTTP::IncomingResponse>> doRequest_impl(Reference<S3BlobS
1263
1281
1264
1282
if (!err.present ()) {
1265
1283
event.detail (" ResponseCode" , r->code );
1266
- std::string s3Error = parseErrorCodeFromS3 (r->data .content );
1284
+ // Only parse S3 error code for real error responses (4xx/5xx), not successful responses (2xx)
1285
+ // Skip parsing for simulated errors where response content is still binary data
1286
+ std::string s3Error;
1287
+ if (r->code >= 400 && !simulateS3TokenError) {
1288
+ s3Error = parseErrorCodeFromS3 (r->data .content );
1289
+ }
1267
1290
event.detail (" S3ErrorCode" , s3Error);
1268
1291
if (r->code == badRequestCode) {
1269
1292
if (isS3TokenError (s3Error) || simulateS3TokenError) {
@@ -1460,7 +1483,8 @@ ACTOR Future<Void> listObjectsStream_impl(Reference<S3BlobStoreEndpoint> bstore,
1460
1483
if (key == nullptr ) {
1461
1484
throw http_bad_response ();
1462
1485
}
1463
- object.name = key->value ();
1486
+ // URL decode the object name since S3 XML responses contain URL-encoded names
1487
+ object.name = HTTP::urlDecode (key->value ());
1464
1488
1465
1489
xml_node<>* size = n->first_node (" Size" );
1466
1490
if (size == nullptr ) {
@@ -2035,8 +2059,11 @@ ACTOR Future<int> readObject_impl(Reference<S3BlobStoreEndpoint> bstore,
2035
2059
try {
2036
2060
// Copy the output bytes, server could have sent more or less bytes than requested so copy at most length
2037
2061
// bytes
2038
- memcpy (data, r->data .content .data (), std::min<int64_t >(r->data .contentLen , length));
2039
- return r->data .contentLen ;
2062
+ int bytesToCopy = std::min<int64_t >(r->data .contentLen , length);
2063
+ memcpy (data, r->data .content .data (), bytesToCopy);
2064
+ // Return the number of bytes actually copied, not the contentLen
2065
+ // This ensures AsyncFileEncrypted gets blocks of the correct size (4KB)
2066
+ return bytesToCopy;
2040
2067
} catch (Error& e) {
2041
2068
TraceEvent (SevWarn, " S3BlobStoreReadObjectMemcpyError" ).detail (" Error" , e.what ());
2042
2069
throw io_error ();
@@ -2441,4 +2468,4 @@ TEST_CASE("/backup/s3/guess_region") {
2441
2468
ASSERT_EQ (e.code (), error_code_backup_invalid_url);
2442
2469
}
2443
2470
return Void ();
2444
- }
2471
+ }
0 commit comments