1package httpd 2 3import ( 4 "bytes" 5 "crypto/tls" 6 "errors" 7 "fmt" 8 "regexp" 9 "strconv" 10 "time" 11 12 "github.com/influxdata/influxdb/monitor/diagnostics" 13 "github.com/influxdata/influxdb/toml" 14) 15 16const ( 17 // DefaultBindAddress is the default address to bind to. 18 DefaultBindAddress = ":8086" 19 20 // DefaultRealm is the default realm sent back when issuing a basic auth challenge. 21 DefaultRealm = "InfluxDB" 22 23 // DefaultBindSocket is the default unix socket to bind to. 24 DefaultBindSocket = "/var/run/influxdb.sock" 25 26 // DefaultMaxBodySize is the default maximum size of a client request body, in bytes. Specify 0 for no limit. 27 DefaultMaxBodySize = 25e6 28 29 // DefaultEnqueuedWriteTimeout is the maximum time a write request can wait to be processed. 30 DefaultEnqueuedWriteTimeout = 30 * time.Second 31) 32 33// Config represents a configuration for a HTTP service. 34type Config struct { 35 Enabled bool `toml:"enabled"` 36 BindAddress string `toml:"bind-address"` 37 AuthEnabled bool `toml:"auth-enabled"` 38 LogEnabled bool `toml:"log-enabled"` 39 SuppressWriteLog bool `toml:"suppress-write-log"` 40 WriteTracing bool `toml:"write-tracing"` 41 FluxEnabled bool `toml:"flux-enabled"` 42 FluxLogEnabled bool `toml:"flux-log-enabled"` 43 PprofEnabled bool `toml:"pprof-enabled"` 44 DebugPprofEnabled bool `toml:"debug-pprof-enabled"` 45 HTTPSEnabled bool `toml:"https-enabled"` 46 HTTPSCertificate string `toml:"https-certificate"` 47 HTTPSPrivateKey string `toml:"https-private-key"` 48 MaxRowLimit int `toml:"max-row-limit"` 49 MaxConnectionLimit int `toml:"max-connection-limit"` 50 SharedSecret string `toml:"shared-secret"` 51 Realm string `toml:"realm"` 52 UnixSocketEnabled bool `toml:"unix-socket-enabled"` 53 UnixSocketGroup *toml.Group `toml:"unix-socket-group"` 54 UnixSocketPermissions toml.FileMode `toml:"unix-socket-permissions"` 55 BindSocket string `toml:"bind-socket"` 56 MaxBodySize int `toml:"max-body-size"` 57 AccessLogPath string `toml:"access-log-path"` 58 AccessLogStatusFilters []StatusFilter `toml:"access-log-status-filters"` 59 MaxConcurrentWriteLimit int `toml:"max-concurrent-write-limit"` 60 MaxEnqueuedWriteLimit int `toml:"max-enqueued-write-limit"` 61 EnqueuedWriteTimeout time.Duration `toml:"enqueued-write-timeout"` 62 TLS *tls.Config `toml:"-"` 63} 64 65// NewConfig returns a new Config with default settings. 66func NewConfig() Config { 67 return Config{ 68 Enabled: true, 69 FluxEnabled: false, 70 FluxLogEnabled: false, 71 BindAddress: DefaultBindAddress, 72 LogEnabled: true, 73 PprofEnabled: true, 74 DebugPprofEnabled: false, 75 HTTPSEnabled: false, 76 HTTPSCertificate: "/etc/ssl/influxdb.pem", 77 MaxRowLimit: 0, 78 Realm: DefaultRealm, 79 UnixSocketEnabled: false, 80 UnixSocketPermissions: 0777, 81 BindSocket: DefaultBindSocket, 82 MaxBodySize: DefaultMaxBodySize, 83 EnqueuedWriteTimeout: DefaultEnqueuedWriteTimeout, 84 } 85} 86 87// Diagnostics returns a diagnostics representation of a subset of the Config. 88func (c Config) Diagnostics() (*diagnostics.Diagnostics, error) { 89 if !c.Enabled { 90 return diagnostics.RowFromMap(map[string]interface{}{ 91 "enabled": false, 92 }), nil 93 } 94 95 return diagnostics.RowFromMap(map[string]interface{}{ 96 "enabled": true, 97 "bind-address": c.BindAddress, 98 "https-enabled": c.HTTPSEnabled, 99 "max-row-limit": c.MaxRowLimit, 100 "max-connection-limit": c.MaxConnectionLimit, 101 "access-log-path": c.AccessLogPath, 102 }), nil 103} 104 105// StatusFilter will check if an http status code matches a certain pattern. 106type StatusFilter struct { 107 base int 108 divisor int 109} 110 111// reStatusFilter ensures that the format is digits optionally followed by X values. 112var reStatusFilter = regexp.MustCompile(`^([1-5]\d*)([xX]*)$`) 113 114// ParseStatusFilter will create a new status filter from the string. 115func ParseStatusFilter(s string) (StatusFilter, error) { 116 m := reStatusFilter.FindStringSubmatch(s) 117 if m == nil { 118 return StatusFilter{}, fmt.Errorf("status filter must be a digit that starts with 1-5 optionally followed by X characters") 119 } else if len(s) != 3 { 120 return StatusFilter{}, fmt.Errorf("status filter must be exactly 3 characters long") 121 } 122 123 // Compute the divisor and the expected value. If we have one X, we divide by 10 so we are only comparing 124 // the first two numbers. If we have two Xs, we divide by 100 so we only compare the first number. We 125 // then check if the result is equal to the remaining number. 126 base, err := strconv.Atoi(m[1]) 127 if err != nil { 128 return StatusFilter{}, err 129 } 130 131 divisor := 1 132 switch len(m[2]) { 133 case 1: 134 divisor = 10 135 case 2: 136 divisor = 100 137 } 138 return StatusFilter{ 139 base: base, 140 divisor: divisor, 141 }, nil 142} 143 144// Match will check if the status code matches this filter. 145func (sf StatusFilter) Match(statusCode int) bool { 146 if sf.divisor == 0 { 147 return false 148 } 149 return statusCode/sf.divisor == sf.base 150} 151 152// UnmarshalText parses a TOML value into a duration value. 153func (sf *StatusFilter) UnmarshalText(text []byte) error { 154 f, err := ParseStatusFilter(string(text)) 155 if err != nil { 156 return err 157 } 158 *sf = f 159 return nil 160} 161 162// MarshalText converts a duration to a string for decoding toml 163func (sf StatusFilter) MarshalText() (text []byte, err error) { 164 var buf bytes.Buffer 165 if sf.base != 0 { 166 buf.WriteString(strconv.Itoa(sf.base)) 167 } 168 169 switch sf.divisor { 170 case 1: 171 case 10: 172 buf.WriteString("X") 173 case 100: 174 buf.WriteString("XX") 175 default: 176 return nil, errors.New("invalid status filter") 177 } 178 return buf.Bytes(), nil 179} 180 181type StatusFilters []StatusFilter 182 183func (filters StatusFilters) Match(statusCode int) bool { 184 if len(filters) == 0 { 185 return true 186 } 187 188 for _, sf := range filters { 189 if sf.Match(statusCode) { 190 return true 191 } 192 } 193 return false 194} 195