mirror of
https://github.com/minio/minio.git
synced 2024-12-24 06:05:55 -05:00
Move config to v7 to fix previous v6 bug on null Notification entries. (#2650)
Thanks to @koolhead17 for reporting this.
This commit is contained in:
parent
11785529fc
commit
e3de237eb8
@ -36,6 +36,8 @@ func migrateConfig() {
|
||||
migrateV4ToV5()
|
||||
// Migrate version '5' to '6.
|
||||
migrateV5ToV6()
|
||||
// Migrate version '6' to '7'.
|
||||
migrateV6ToV7()
|
||||
}
|
||||
|
||||
// Version '1' is not supported anymore and deprecated, safe to delete.
|
||||
@ -151,6 +153,46 @@ func migrateV3ToV4() {
|
||||
console.Println("Migration from version ‘" + cv3.Version + "’ to ‘" + srvConfig.Version + "’ completed successfully.")
|
||||
}
|
||||
|
||||
// Version '4' to '5' migrates config, removes previous fields related
|
||||
// to backend types and server address. This change further simplifies
|
||||
// the config for future additions.
|
||||
func migrateV4ToV5() {
|
||||
cv4, err := loadConfigV4()
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
return
|
||||
}
|
||||
fatalIf(err, "Unable to load config version ‘4’.")
|
||||
if cv4.Version != "4" {
|
||||
return
|
||||
}
|
||||
|
||||
// Save only the new fields, ignore the rest.
|
||||
srvConfig := &configV5{}
|
||||
srvConfig.Version = "5"
|
||||
srvConfig.Credential = cv4.Credential
|
||||
srvConfig.Region = cv4.Region
|
||||
if srvConfig.Region == "" {
|
||||
// Region needs to be set for AWS Signature Version 4.
|
||||
srvConfig.Region = "us-east-1"
|
||||
}
|
||||
srvConfig.Logger.Console = cv4.Logger.Console
|
||||
srvConfig.Logger.File = cv4.Logger.File
|
||||
srvConfig.Logger.Syslog = cv4.Logger.Syslog
|
||||
srvConfig.Logger.AMQP.Enable = false
|
||||
srvConfig.Logger.ElasticSearch.Enable = false
|
||||
srvConfig.Logger.Redis.Enable = false
|
||||
|
||||
qc, err := quick.New(srvConfig)
|
||||
fatalIf(err, "Unable to initialize the quick config.")
|
||||
configFile, err := getConfigFile()
|
||||
fatalIf(err, "Unable to get config file.")
|
||||
|
||||
err = qc.Save(configFile)
|
||||
fatalIf(err, "Failed to migrate config from ‘"+cv4.Version+"’ to ‘"+srvConfig.Version+"’ failed.")
|
||||
|
||||
console.Println("Migration from version ‘" + cv4.Version + "’ to ‘" + srvConfig.Version + "’ completed successfully.")
|
||||
}
|
||||
|
||||
// Version '5' to '6' migrates config, removes previous fields related
|
||||
// to backend types and server address. This change further simplifies
|
||||
// the config for future additions.
|
||||
@ -165,8 +207,8 @@ func migrateV5ToV6() {
|
||||
}
|
||||
|
||||
// Save only the new fields, ignore the rest.
|
||||
srvConfig := &serverConfigV6{}
|
||||
srvConfig.Version = globalMinioConfigVersion
|
||||
srvConfig := &configV6{}
|
||||
srvConfig.Version = "6"
|
||||
srvConfig.Credential = cv5.Credential
|
||||
srvConfig.Region = cv5.Region
|
||||
if srvConfig.Region == "" {
|
||||
@ -176,6 +218,7 @@ func migrateV5ToV6() {
|
||||
srvConfig.Logger.Console = cv5.Logger.Console
|
||||
srvConfig.Logger.File = cv5.Logger.File
|
||||
srvConfig.Logger.Syslog = cv5.Logger.Syslog
|
||||
|
||||
srvConfig.Notify.AMQP = map[string]amqpNotify{
|
||||
"1": {
|
||||
Enable: cv5.Logger.AMQP.Enable,
|
||||
@ -217,34 +260,49 @@ func migrateV5ToV6() {
|
||||
console.Println("Migration from version ‘" + cv5.Version + "’ to ‘" + srvConfig.Version + "’ completed successfully.")
|
||||
}
|
||||
|
||||
// Version '4' to '5' migrates config, removes previous fields related
|
||||
// Version '6' to '7' migrates config, removes previous fields related
|
||||
// to backend types and server address. This change further simplifies
|
||||
// the config for future additions.
|
||||
func migrateV4ToV5() {
|
||||
cv4, err := loadConfigV4()
|
||||
func migrateV6ToV7() {
|
||||
cv6, err := loadConfigV6()
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
return
|
||||
}
|
||||
fatalIf(err, "Unable to load config version ‘4’.")
|
||||
if cv4.Version != "4" {
|
||||
fatalIf(err, "Unable to load config version ‘6’.")
|
||||
if cv6.Version != "6" {
|
||||
return
|
||||
}
|
||||
|
||||
// Save only the new fields, ignore the rest.
|
||||
srvConfig := &configV5{}
|
||||
srvConfig := &serverConfigV7{}
|
||||
srvConfig.Version = globalMinioConfigVersion
|
||||
srvConfig.Credential = cv4.Credential
|
||||
srvConfig.Region = cv4.Region
|
||||
srvConfig.Credential = cv6.Credential
|
||||
srvConfig.Region = cv6.Region
|
||||
if srvConfig.Region == "" {
|
||||
// Region needs to be set for AWS Signature Version 4.
|
||||
srvConfig.Region = "us-east-1"
|
||||
}
|
||||
srvConfig.Logger.Console = cv4.Logger.Console
|
||||
srvConfig.Logger.File = cv4.Logger.File
|
||||
srvConfig.Logger.Syslog = cv4.Logger.Syslog
|
||||
srvConfig.Logger.AMQP.Enable = false
|
||||
srvConfig.Logger.ElasticSearch.Enable = false
|
||||
srvConfig.Logger.Redis.Enable = false
|
||||
srvConfig.Logger.Console = cv6.Logger.Console
|
||||
srvConfig.Logger.File = cv6.Logger.File
|
||||
srvConfig.Logger.Syslog = cv6.Logger.Syslog
|
||||
srvConfig.Notify.AMQP = make(map[string]amqpNotify)
|
||||
srvConfig.Notify.ElasticSearch = make(map[string]elasticSearchNotify)
|
||||
srvConfig.Notify.Redis = make(map[string]redisNotify)
|
||||
if len(cv6.Notify.AMQP) == 0 {
|
||||
srvConfig.Notify.AMQP["1"] = amqpNotify{}
|
||||
} else {
|
||||
srvConfig.Notify.AMQP = cv6.Notify.AMQP
|
||||
}
|
||||
if len(cv6.Notify.ElasticSearch) == 0 {
|
||||
srvConfig.Notify.ElasticSearch["1"] = elasticSearchNotify{}
|
||||
} else {
|
||||
srvConfig.Notify.ElasticSearch = cv6.Notify.ElasticSearch
|
||||
}
|
||||
if len(cv6.Notify.Redis) == 0 {
|
||||
srvConfig.Notify.Redis["1"] = redisNotify{}
|
||||
} else {
|
||||
srvConfig.Notify.Redis = cv6.Notify.Redis
|
||||
}
|
||||
|
||||
qc, err := quick.New(srvConfig)
|
||||
fatalIf(err, "Unable to initialize the quick config.")
|
||||
@ -252,7 +310,7 @@ func migrateV4ToV5() {
|
||||
fatalIf(err, "Unable to get config file.")
|
||||
|
||||
err = qc.Save(configFile)
|
||||
fatalIf(err, "Failed to migrate config from ‘"+cv4.Version+"’ to ‘"+srvConfig.Version+"’ failed.")
|
||||
fatalIf(err, "Failed to migrate config from ‘"+cv6.Version+"’ to ‘"+srvConfig.Version+"’ failed.")
|
||||
|
||||
console.Println("Migration from version ‘" + cv4.Version + "’ to ‘" + srvConfig.Version + "’ completed successfully.")
|
||||
console.Println("Migration from version ‘" + cv6.Version + "’ to ‘" + srvConfig.Version + "’ completed successfully.")
|
||||
}
|
||||
|
@ -274,3 +274,39 @@ func loadConfigV5() (*configV5, error) {
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// configV6 server configuration version '6'.
|
||||
type configV6 struct {
|
||||
Version string `json:"version"`
|
||||
|
||||
// S3 API configuration.
|
||||
Credential credential `json:"credential"`
|
||||
Region string `json:"region"`
|
||||
|
||||
// Additional error logging configuration.
|
||||
Logger logger `json:"logger"`
|
||||
|
||||
// Notification queue configuration.
|
||||
Notify notifier `json:"notify"`
|
||||
}
|
||||
|
||||
// loadConfigV6 load config version '6'.
|
||||
func loadConfigV6() (*configV6, error) {
|
||||
configFile, err := getConfigFile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err = os.Stat(configFile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c := &configV6{}
|
||||
c.Version = "6"
|
||||
qc, err := quick.New(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := qc.Load(configFile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
@ -23,8 +23,8 @@ import (
|
||||
"github.com/minio/minio/pkg/quick"
|
||||
)
|
||||
|
||||
// serverConfigV6 server configuration version '5'.
|
||||
type serverConfigV6 struct {
|
||||
// serverConfigV7 server configuration version '7'.
|
||||
type serverConfigV7 struct {
|
||||
Version string `json:"version"`
|
||||
|
||||
// S3 API configuration.
|
||||
@ -45,7 +45,7 @@ type serverConfigV6 struct {
|
||||
func initConfig() error {
|
||||
if !isConfigFileExists() {
|
||||
// Initialize server config.
|
||||
srvCfg := &serverConfigV6{}
|
||||
srvCfg := &serverConfigV7{}
|
||||
srvCfg.Version = globalMinioConfigVersion
|
||||
srvCfg.Region = "us-east-1"
|
||||
srvCfg.Credential = mustGenAccessKeys()
|
||||
@ -84,7 +84,7 @@ func initConfig() error {
|
||||
if _, err = os.Stat(configFile); err != nil {
|
||||
return err
|
||||
}
|
||||
srvCfg := &serverConfigV6{}
|
||||
srvCfg := &serverConfigV7{}
|
||||
srvCfg.Version = globalMinioConfigVersion
|
||||
srvCfg.rwMutex = &sync.RWMutex{}
|
||||
qc, err := quick.New(srvCfg)
|
||||
@ -103,10 +103,10 @@ func initConfig() error {
|
||||
}
|
||||
|
||||
// serverConfig server config.
|
||||
var serverConfig *serverConfigV6
|
||||
var serverConfig *serverConfigV7
|
||||
|
||||
// GetVersion get current config version.
|
||||
func (s serverConfigV6) GetVersion() string {
|
||||
func (s serverConfigV7) GetVersion() string {
|
||||
s.rwMutex.RLock()
|
||||
defer s.rwMutex.RUnlock()
|
||||
return s.Version
|
||||
@ -114,135 +114,135 @@ func (s serverConfigV6) GetVersion() string {
|
||||
|
||||
/// Logger related.
|
||||
|
||||
func (s *serverConfigV6) SetAMQPNotifyByID(accountID string, amqpn amqpNotify) {
|
||||
func (s *serverConfigV7) SetAMQPNotifyByID(accountID string, amqpn amqpNotify) {
|
||||
s.rwMutex.Lock()
|
||||
defer s.rwMutex.Unlock()
|
||||
s.Notify.AMQP[accountID] = amqpn
|
||||
}
|
||||
|
||||
func (s serverConfigV6) GetAMQP() map[string]amqpNotify {
|
||||
func (s serverConfigV7) GetAMQP() map[string]amqpNotify {
|
||||
s.rwMutex.RLock()
|
||||
defer s.rwMutex.RUnlock()
|
||||
return s.Notify.AMQP
|
||||
}
|
||||
|
||||
// GetAMQPNotify get current AMQP logger.
|
||||
func (s serverConfigV6) GetAMQPNotifyByID(accountID string) amqpNotify {
|
||||
func (s serverConfigV7) GetAMQPNotifyByID(accountID string) amqpNotify {
|
||||
s.rwMutex.RLock()
|
||||
defer s.rwMutex.RUnlock()
|
||||
return s.Notify.AMQP[accountID]
|
||||
}
|
||||
|
||||
func (s *serverConfigV6) SetElasticSearchNotifyByID(accountID string, esNotify elasticSearchNotify) {
|
||||
func (s *serverConfigV7) SetElasticSearchNotifyByID(accountID string, esNotify elasticSearchNotify) {
|
||||
s.rwMutex.Lock()
|
||||
defer s.rwMutex.Unlock()
|
||||
s.Notify.ElasticSearch[accountID] = esNotify
|
||||
}
|
||||
|
||||
func (s serverConfigV6) GetElasticSearch() map[string]elasticSearchNotify {
|
||||
func (s serverConfigV7) GetElasticSearch() map[string]elasticSearchNotify {
|
||||
s.rwMutex.RLock()
|
||||
defer s.rwMutex.RUnlock()
|
||||
return s.Notify.ElasticSearch
|
||||
}
|
||||
|
||||
// GetElasticSearchNotify get current ElasicSearch logger.
|
||||
func (s serverConfigV6) GetElasticSearchNotifyByID(accountID string) elasticSearchNotify {
|
||||
func (s serverConfigV7) GetElasticSearchNotifyByID(accountID string) elasticSearchNotify {
|
||||
s.rwMutex.RLock()
|
||||
defer s.rwMutex.RUnlock()
|
||||
return s.Notify.ElasticSearch[accountID]
|
||||
}
|
||||
|
||||
func (s *serverConfigV6) SetRedisNotifyByID(accountID string, rNotify redisNotify) {
|
||||
func (s *serverConfigV7) SetRedisNotifyByID(accountID string, rNotify redisNotify) {
|
||||
s.rwMutex.Lock()
|
||||
defer s.rwMutex.Unlock()
|
||||
s.Notify.Redis[accountID] = rNotify
|
||||
}
|
||||
|
||||
func (s serverConfigV6) GetRedis() map[string]redisNotify {
|
||||
func (s serverConfigV7) GetRedis() map[string]redisNotify {
|
||||
s.rwMutex.RLock()
|
||||
defer s.rwMutex.RUnlock()
|
||||
return s.Notify.Redis
|
||||
}
|
||||
|
||||
// GetRedisNotify get current Redis logger.
|
||||
func (s serverConfigV6) GetRedisNotifyByID(accountID string) redisNotify {
|
||||
func (s serverConfigV7) GetRedisNotifyByID(accountID string) redisNotify {
|
||||
s.rwMutex.RLock()
|
||||
defer s.rwMutex.RUnlock()
|
||||
return s.Notify.Redis[accountID]
|
||||
}
|
||||
|
||||
// SetFileLogger set new file logger.
|
||||
func (s *serverConfigV6) SetFileLogger(flogger fileLogger) {
|
||||
func (s *serverConfigV7) SetFileLogger(flogger fileLogger) {
|
||||
s.rwMutex.Lock()
|
||||
defer s.rwMutex.Unlock()
|
||||
s.Logger.File = flogger
|
||||
}
|
||||
|
||||
// GetFileLogger get current file logger.
|
||||
func (s serverConfigV6) GetFileLogger() fileLogger {
|
||||
func (s serverConfigV7) GetFileLogger() fileLogger {
|
||||
s.rwMutex.RLock()
|
||||
defer s.rwMutex.RUnlock()
|
||||
return s.Logger.File
|
||||
}
|
||||
|
||||
// SetConsoleLogger set new console logger.
|
||||
func (s *serverConfigV6) SetConsoleLogger(clogger consoleLogger) {
|
||||
func (s *serverConfigV7) SetConsoleLogger(clogger consoleLogger) {
|
||||
s.rwMutex.Lock()
|
||||
defer s.rwMutex.Unlock()
|
||||
s.Logger.Console = clogger
|
||||
}
|
||||
|
||||
// GetConsoleLogger get current console logger.
|
||||
func (s serverConfigV6) GetConsoleLogger() consoleLogger {
|
||||
func (s serverConfigV7) GetConsoleLogger() consoleLogger {
|
||||
s.rwMutex.RLock()
|
||||
defer s.rwMutex.RUnlock()
|
||||
return s.Logger.Console
|
||||
}
|
||||
|
||||
// SetSyslogLogger set new syslog logger.
|
||||
func (s *serverConfigV6) SetSyslogLogger(slogger syslogLogger) {
|
||||
func (s *serverConfigV7) SetSyslogLogger(slogger syslogLogger) {
|
||||
s.rwMutex.Lock()
|
||||
defer s.rwMutex.Unlock()
|
||||
s.Logger.Syslog = slogger
|
||||
}
|
||||
|
||||
// GetSyslogLogger get current syslog logger.
|
||||
func (s *serverConfigV6) GetSyslogLogger() syslogLogger {
|
||||
func (s *serverConfigV7) GetSyslogLogger() syslogLogger {
|
||||
s.rwMutex.RLock()
|
||||
defer s.rwMutex.RUnlock()
|
||||
return s.Logger.Syslog
|
||||
}
|
||||
|
||||
// SetRegion set new region.
|
||||
func (s *serverConfigV6) SetRegion(region string) {
|
||||
func (s *serverConfigV7) SetRegion(region string) {
|
||||
s.rwMutex.Lock()
|
||||
defer s.rwMutex.Unlock()
|
||||
s.Region = region
|
||||
}
|
||||
|
||||
// GetRegion get current region.
|
||||
func (s serverConfigV6) GetRegion() string {
|
||||
func (s serverConfigV7) GetRegion() string {
|
||||
s.rwMutex.RLock()
|
||||
defer s.rwMutex.RUnlock()
|
||||
return s.Region
|
||||
}
|
||||
|
||||
// SetCredentials set new credentials.
|
||||
func (s *serverConfigV6) SetCredential(creds credential) {
|
||||
func (s *serverConfigV7) SetCredential(creds credential) {
|
||||
s.rwMutex.Lock()
|
||||
defer s.rwMutex.Unlock()
|
||||
s.Credential = creds
|
||||
}
|
||||
|
||||
// GetCredentials get current credentials.
|
||||
func (s serverConfigV6) GetCredential() credential {
|
||||
func (s serverConfigV7) GetCredential() credential {
|
||||
s.rwMutex.RLock()
|
||||
defer s.rwMutex.RUnlock()
|
||||
return s.Credential
|
||||
}
|
||||
|
||||
// Save config.
|
||||
func (s serverConfigV6) Save() error {
|
||||
func (s serverConfigV7) Save() error {
|
||||
s.rwMutex.RLock()
|
||||
defer s.rwMutex.RUnlock()
|
||||
|
91
cmd/config-v7_test.go
Normal file
91
cmd/config-v7_test.go
Normal file
@ -0,0 +1,91 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestServerConfig(t *testing.T) {
|
||||
rootPath, err := newTestConfig("us-east-1")
|
||||
if err != nil {
|
||||
t.Fatalf("Init Test config failed")
|
||||
}
|
||||
// remove the root folder after the test ends.
|
||||
defer removeAll(rootPath)
|
||||
|
||||
if serverConfig.GetRegion() != "us-east-1" {
|
||||
t.Errorf("Expecting region `us-east-1` found %s", serverConfig.GetRegion())
|
||||
}
|
||||
|
||||
// Set new region and verify.
|
||||
serverConfig.SetRegion("us-west-1")
|
||||
if serverConfig.GetRegion() != "us-west-1" {
|
||||
t.Errorf("Expecting region `us-west-1` found %s", serverConfig.GetRegion())
|
||||
}
|
||||
|
||||
// Set new amqp notification id.
|
||||
serverConfig.SetAMQPNotifyByID("2", amqpNotify{})
|
||||
savedNotifyCfg1 := serverConfig.GetAMQPNotifyByID("2")
|
||||
if !reflect.DeepEqual(savedNotifyCfg1, amqpNotify{}) {
|
||||
t.Errorf("Expecting AMQP config %#v found %#v", amqpNotify{}, savedNotifyCfg1)
|
||||
}
|
||||
|
||||
// Set new elastic search notification id.
|
||||
serverConfig.SetElasticSearchNotifyByID("2", elasticSearchNotify{})
|
||||
savedNotifyCfg2 := serverConfig.GetElasticSearchNotifyByID("2")
|
||||
if !reflect.DeepEqual(savedNotifyCfg2, elasticSearchNotify{}) {
|
||||
t.Errorf("Expecting Elasticsearch config %#v found %#v", elasticSearchNotify{}, savedNotifyCfg2)
|
||||
}
|
||||
|
||||
// Set new redis notification id.
|
||||
serverConfig.SetRedisNotifyByID("2", redisNotify{})
|
||||
savedNotifyCfg3 := serverConfig.GetRedisNotifyByID("2")
|
||||
if !reflect.DeepEqual(savedNotifyCfg3, redisNotify{}) {
|
||||
t.Errorf("Expecting Redis config %#v found %#v", redisNotify{}, savedNotifyCfg3)
|
||||
}
|
||||
|
||||
// Set new console logger.
|
||||
serverConfig.SetConsoleLogger(consoleLogger{
|
||||
Enable: true,
|
||||
})
|
||||
consoleCfg := serverConfig.GetConsoleLogger()
|
||||
if !reflect.DeepEqual(consoleCfg, consoleLogger{Enable: true}) {
|
||||
t.Errorf("Expecting console logger config %#v found %#v", consoleLogger{Enable: true}, consoleCfg)
|
||||
}
|
||||
|
||||
// Set new file logger.
|
||||
serverConfig.SetFileLogger(fileLogger{
|
||||
Enable: true,
|
||||
})
|
||||
fileCfg := serverConfig.GetFileLogger()
|
||||
if !reflect.DeepEqual(fileCfg, fileLogger{Enable: true}) {
|
||||
t.Errorf("Expecting file logger config %#v found %#v", fileLogger{Enable: true}, consoleCfg)
|
||||
}
|
||||
|
||||
// Set new syslog logger.
|
||||
serverConfig.SetSyslogLogger(syslogLogger{
|
||||
Enable: true,
|
||||
})
|
||||
sysLogCfg := serverConfig.GetSyslogLogger()
|
||||
if !reflect.DeepEqual(sysLogCfg, syslogLogger{Enable: true}) {
|
||||
t.Errorf("Expecting syslog logger config %#v found %#v", syslogLogger{Enable: true}, sysLogCfg)
|
||||
}
|
||||
|
||||
// Match version.
|
||||
if serverConfig.GetVersion() != globalMinioConfigVersion {
|
||||
t.Errorf("Expecting version %s found %s", serverConfig.GetVersion(), globalMinioConfigVersion)
|
||||
}
|
||||
|
||||
// Attempt to save.
|
||||
if err := serverConfig.Save(); err != nil {
|
||||
t.Fatalf("Unable to save updated config file %s", err)
|
||||
}
|
||||
|
||||
// Do this only once here.
|
||||
setGlobalConfigPath(rootPath)
|
||||
|
||||
// Initialize server config.
|
||||
if err := initConfig(); err != nil {
|
||||
t.Fatalf("Unable to initialize from updated config file %s", err)
|
||||
}
|
||||
}
|
@ -30,7 +30,7 @@ const (
|
||||
|
||||
// minio configuration related constants.
|
||||
const (
|
||||
globalMinioConfigVersion = "6"
|
||||
globalMinioConfigVersion = "7"
|
||||
globalMinioConfigDir = ".minio"
|
||||
globalMinioCertsDir = "certs"
|
||||
globalMinioCertFile = "public.crt"
|
||||
|
@ -25,14 +25,14 @@ import (
|
||||
// Tests http.Header clone.
|
||||
func TestCloneHeader(t *testing.T) {
|
||||
headers := []http.Header{
|
||||
http.Header{
|
||||
{
|
||||
"Content-Type": {"text/html; charset=UTF-8"},
|
||||
"Content-Length": {"0"},
|
||||
},
|
||||
http.Header{
|
||||
{
|
||||
"Content-Length": {"0", "1", "2"},
|
||||
},
|
||||
http.Header{
|
||||
{
|
||||
"Expires": {"-1"},
|
||||
"Content-Length": {"0"},
|
||||
"Content-Encoding": {"gzip"},
|
||||
|
Loading…
Reference in New Issue
Block a user