@@ -1109,13 +1109,13 @@ func NewS3ReplicaClientFromConfig(c *ReplicaConfig, _ *litestream.Replica) (_ *s
11091109
11101110 bucket , configPath := c .Bucket , c .Path
11111111 region , endpoint , skipVerify := c .Region , c .Endpoint , c .SkipVerify
1112- signPayload := false
1112+ signSetting := newBoolSetting ( false )
11131113 if v := c .SignPayload ; v != nil {
1114- signPayload = * v
1114+ signSetting . Set ( * v )
11151115 }
1116- requireContentMD5 := true
1116+ requireSetting := newBoolSetting ( true )
11171117 if v := c .RequireContentMD5 ; v != nil {
1118- requireContentMD5 = * v
1118+ requireSetting . Set ( * v )
11191119 }
11201120
11211121 // Use path style if an endpoint is explicitly set. This works because the
@@ -1126,21 +1126,28 @@ func NewS3ReplicaClientFromConfig(c *ReplicaConfig, _ *litestream.Replica) (_ *s
11261126 }
11271127
11281128 // Apply settings from URL, if specified.
1129+ var (
1130+ endpointWasSet bool
1131+ usignPayload bool
1132+ usignPayloadSet bool
1133+ urequireContentMD5 bool
1134+ urequireContentMD5Set bool
1135+ )
1136+ if endpoint != "" {
1137+ endpointWasSet = true
1138+ }
1139+
11291140 if c .URL != "" {
11301141 _ , host , upath , query , err := ParseReplicaURLWithQuery (c .URL )
11311142 if err != nil {
11321143 return nil , err
11331144 }
11341145
11351146 var (
1136- ubucket string
1137- uregion string
1138- uendpoint string
1139- uforcePathStyle bool
1140- usignPayload bool
1141- usignPayloadSet bool
1142- urequireContentMD5 bool
1143- urequireContentMD5Set bool
1147+ ubucket string
1148+ uregion string
1149+ uendpoint string
1150+ uforcePathStyle bool
11441151 )
11451152
11461153 if strings .HasPrefix (host , "arn:" ) {
@@ -1162,6 +1169,7 @@ func NewS3ReplicaClientFromConfig(c *ReplicaConfig, _ *litestream.Replica) (_ *s
11621169 if query .Get ("forcePathStyle" ) != "false" {
11631170 uforcePathStyle = true
11641171 }
1172+ endpointWasSet = true
11651173 }
11661174 if qRegion := query .Get ("region" ); qRegion != "" {
11671175 uregion = qRegion
@@ -1197,11 +1205,11 @@ func NewS3ReplicaClientFromConfig(c *ReplicaConfig, _ *litestream.Replica) (_ *s
11971205 if ! forcePathStyle {
11981206 forcePathStyle = uforcePathStyle
11991207 }
1200- if c . SignPayload == nil && usignPayloadSet {
1201- signPayload = usignPayload
1208+ if ! signSetting . set && usignPayloadSet {
1209+ signSetting . Set ( usignPayload )
12021210 }
1203- if c . RequireContentMD5 == nil && urequireContentMD5Set {
1204- requireContentMD5 = urequireContentMD5
1211+ if ! requireSetting . set && urequireContentMD5Set {
1212+ requireSetting . Set ( urequireContentMD5 )
12051213 }
12061214 }
12071215
@@ -1210,6 +1218,11 @@ func NewS3ReplicaClientFromConfig(c *ReplicaConfig, _ *litestream.Replica) (_ *s
12101218 return nil , fmt .Errorf ("bucket required for s3 replica" )
12111219 }
12121220
1221+ isTigris := isTigrisEndpoint (endpoint )
1222+ if ! isTigris && ! endpointWasSet && isTigrisEndpoint (c .Endpoint ) {
1223+ isTigris = true
1224+ }
1225+
12131226 // Build replica.
12141227 client := s3 .NewReplicaClient ()
12151228 client .AccessKeyID = c .AccessKeyID
@@ -1220,8 +1233,13 @@ func NewS3ReplicaClientFromConfig(c *ReplicaConfig, _ *litestream.Replica) (_ *s
12201233 client .Endpoint = endpoint
12211234 client .ForcePathStyle = forcePathStyle
12221235 client .SkipVerify = skipVerify
1223- client .SignPayload = signPayload
1224- client .RequireContentMD5 = requireContentMD5
1236+ if isTigris {
1237+ signSetting .ApplyDefault (true )
1238+ requireSetting .ApplyDefault (false )
1239+ }
1240+
1241+ client .SignPayload = signSetting .value
1242+ client .RequireContentMD5 = requireSetting .value
12251243
12261244 // Apply upload configuration if specified.
12271245 if c .PartSize != nil {
@@ -1625,6 +1643,39 @@ func boolQueryValue(query url.Values, keys ...string) (bool, bool) {
16251643 return false , false
16261644}
16271645
1646+ func isTigrisEndpoint (endpoint string ) bool {
1647+ endpoint = strings .TrimSpace (strings .ToLower (endpoint ))
1648+ if endpoint == "" {
1649+ return false
1650+ }
1651+ if strings .HasPrefix (endpoint , "http://" ) || strings .HasPrefix (endpoint , "https://" ) {
1652+ if u , err := url .Parse (endpoint ); err == nil && u .Host != "" {
1653+ endpoint = u .Host
1654+ }
1655+ }
1656+ return endpoint == "fly.storage.tigris.dev"
1657+ }
1658+
1659+ type boolSetting struct {
1660+ value bool
1661+ set bool
1662+ }
1663+
1664+ func newBoolSetting (defaultValue bool ) boolSetting {
1665+ return boolSetting {value : defaultValue }
1666+ }
1667+
1668+ func (s * boolSetting ) Set (value bool ) {
1669+ s .value = value
1670+ s .set = true
1671+ }
1672+
1673+ func (s * boolSetting ) ApplyDefault (value bool ) {
1674+ if ! s .set {
1675+ s .value = value
1676+ }
1677+ }
1678+
16281679func regionFromS3ARN (arn string ) string {
16291680 parts := strings .SplitN (arn , ":" , 6 )
16301681 if len (parts ) >= 4 {
0 commit comments