Skip to content

Commit 9191a58

Browse files
authored
Merge pull request #647 from appital/tls-bytes
Support TLS configuration as raw bytes
2 parents 6e564c1 + d3b4f98 commit 9191a58

File tree

6 files changed

+324
-59
lines changed

6 files changed

+324
-59
lines changed

config/configuration.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,47 @@ const (
739739
// - A filepath to a file with read access.
740740
SocketCAFile string = "SocketCAFile"
741741

742+
// SocketPrivateKeyBytes is an optional value containing raw bytes of a PEM
743+
// encoded private key to use for secure TLS communications.
744+
// Must be used with SocketCertificateBytes.
745+
// Must contain PEM encoded data.
746+
//
747+
// Required: No
748+
//
749+
// Default: N/A
750+
//
751+
// Valid Values:
752+
// - Raw bytes containing a valid PEM encoded private key.
753+
SocketPrivateKeyBytes string = "SocketPrivateKeyBytes"
754+
755+
// SocketCertificateBytes is an optional value containing raw bytes of a PEM
756+
// encoded certificate to use for secure TLS communications.
757+
// Must be used with SocketPrivateKeyBytes.
758+
// Must contain PEM encoded data.
759+
//
760+
// Required: No
761+
//
762+
// Default: N/A
763+
//
764+
// Valid Values:
765+
// - Raw bytes containing a valid PEM encoded certificate.
766+
SocketCertificateBytes string = "SocketCertificateBytes"
767+
768+
// SocketCABytes is an optional value containing raw bytes of a PEM encoded
769+
// root CA to use for secure TLS communications. For acceptors, client
770+
// certificates will be verified against this CA. For initiators, clients
771+
// will use the CA to verify the server certificate. If not configured,
772+
// initiators will verify the server certificates using the host's root CA
773+
// set.
774+
//
775+
// Required: No
776+
//
777+
// Default: N/A
778+
//
779+
// Valid Values:
780+
// - Raw bytes containing a valid PEM encoded CA.
781+
SocketCABytes string = "SocketCABytes"
782+
742783
// SocketInsecureSkipVerify controls whether a client verifies the server's certificate chain and host name.
743784
// If SocketInsecureSkipVerify is set to Y, crypto/tls accepts any certificate presented by the server and any host name in that certificate.
744785
// In this mode, TLS is susceptible to machine-in-the-middle attacks unless custom verification is used.

session_factory.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ func (f sessionFactory) newSession(
284284
for _, dayStr := range dayStrs {
285285
day, ok := dayLookup[dayStr]
286286
if !ok {
287-
err = IncorrectFormatForSetting{Setting: config.Weekdays, Value: weekdaysStr}
287+
err = IncorrectFormatForSetting{Setting: config.Weekdays, Value: []byte(weekdaysStr)}
288288
return
289289
}
290290
weekdays = append(weekdays, day)
@@ -315,7 +315,7 @@ func (f sessionFactory) newSession(
315315
parseDay := func(setting, dayStr string) (day time.Weekday, err error) {
316316
day, ok := dayLookup[dayStr]
317317
if !ok {
318-
return day, IncorrectFormatForSetting{Setting: setting, Value: dayStr}
318+
return day, IncorrectFormatForSetting{Setting: setting, Value: []byte(dayStr)}
319319
}
320320
return
321321
}
@@ -355,7 +355,7 @@ func (f sessionFactory) newSession(
355355
s.timestampPrecision = Nanos
356356

357357
default:
358-
err = IncorrectFormatForSetting{Setting: config.TimeStampPrecision, Value: precisionStr}
358+
err = IncorrectFormatForSetting{Setting: config.TimeStampPrecision, Value: []byte(precisionStr)}
359359
return
360360
}
361361
}

session_settings.go

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import (
2323

2424
// SessionSettings maps session settings to values with typed accessors.
2525
type SessionSettings struct {
26-
settings map[string]string
26+
settings map[string][]byte
2727
}
2828

2929
// ConditionallyRequiredSetting indicates a missing setting.
@@ -37,8 +37,9 @@ func (e ConditionallyRequiredSetting) Error() string {
3737

3838
// IncorrectFormatForSetting indicates a setting that is incorrectly formatted.
3939
type IncorrectFormatForSetting struct {
40-
Setting, Value string
41-
Err error
40+
Setting string
41+
Value []byte
42+
Err error
4243
}
4344

4445
func (e IncorrectFormatForSetting) Error() string {
@@ -47,7 +48,7 @@ func (e IncorrectFormatForSetting) Error() string {
4748

4849
// Init initializes or resets SessionSettings.
4950
func (s *SessionSettings) Init() {
50-
s.settings = make(map[string]string)
51+
s.settings = make(map[string][]byte)
5152
}
5253

5354
// NewSessionSettings returns a newly initialized SessionSettings instance.
@@ -58,8 +59,8 @@ func NewSessionSettings() *SessionSettings {
5859
return s
5960
}
6061

61-
// Set assigns a value to a setting on SessionSettings.
62-
func (s *SessionSettings) Set(setting string, val string) {
62+
// SetRaw assigns a value to a setting on SessionSettings.
63+
func (s *SessionSettings) SetRaw(setting string, val []byte) {
6364
// Lazy init.
6465
if s.settings == nil {
6566
s.Init()
@@ -68,69 +69,87 @@ func (s *SessionSettings) Set(setting string, val string) {
6869
s.settings[setting] = val
6970
}
7071

72+
// Set assigns a string value to a setting on SessionSettings.
73+
func (s *SessionSettings) Set(setting string, val string) {
74+
// Lazy init
75+
if s.settings == nil {
76+
s.Init()
77+
}
78+
79+
s.settings[setting] = []byte(val)
80+
}
81+
7182
// HasSetting returns true if a setting is set, false if not.
7283
func (s *SessionSettings) HasSetting(setting string) bool {
7384
_, ok := s.settings[setting]
7485
return ok
7586
}
7687

77-
// Setting is a settings string accessor. Returns an error if the setting is missing.
78-
func (s *SessionSettings) Setting(setting string) (string, error) {
88+
// RawSetting is a settings accessor that returns the raw byte slice value of
89+
// the setting. Returns an error if the setting is missing.
90+
func (s *SessionSettings) RawSetting(setting string) ([]byte, error) {
7991
val, ok := s.settings[setting]
8092
if !ok {
81-
return val, ConditionallyRequiredSetting{setting}
93+
return nil, ConditionallyRequiredSetting{Setting: setting}
8294
}
8395

8496
return val, nil
8597
}
8698

87-
// IntSetting returns the requested setting parsed as an int. Returns an errror if the setting is not set or cannot be parsed as an int.
88-
func (s *SessionSettings) IntSetting(setting string) (val int, err error) {
89-
stringVal, err := s.Setting(setting)
99+
// Setting is a settings string accessor. Returns an error if the setting is missing.
100+
func (s *SessionSettings) Setting(setting string) (string, error) {
101+
val, err := s.RawSetting(setting)
102+
if err != nil {
103+
return "", err
104+
}
90105

106+
return string(val), nil
107+
}
108+
109+
// IntSetting returns the requested setting parsed as an int. Returns an errror if the setting is not set or cannot be parsed as an int.
110+
func (s *SessionSettings) IntSetting(setting string) (int, error) {
111+
rawVal, err := s.RawSetting(setting)
91112
if err != nil {
92-
return
113+
return 0, err
93114
}
94115

95-
if val, err = strconv.Atoi(stringVal); err != nil {
96-
return val, IncorrectFormatForSetting{Setting: setting, Value: stringVal, Err: err}
116+
if val, err := strconv.Atoi(string(rawVal)); err == nil {
117+
return val, nil
97118
}
98119

99-
return
120+
return 0, IncorrectFormatForSetting{Setting: setting, Value: rawVal, Err: err}
100121
}
101122

102123
// DurationSetting returns the requested setting parsed as a time.Duration.
103124
// Returns an error if the setting is not set or cannot be parsed as a time.Duration.
104-
func (s *SessionSettings) DurationSetting(setting string) (val time.Duration, err error) {
105-
stringVal, err := s.Setting(setting)
106-
125+
func (s *SessionSettings) DurationSetting(setting string) (time.Duration, error) {
126+
rawVal, err := s.RawSetting(setting)
107127
if err != nil {
108-
return
128+
return 0, err
109129
}
110130

111-
if val, err = time.ParseDuration(stringVal); err != nil {
112-
return val, IncorrectFormatForSetting{Setting: setting, Value: stringVal, Err: err}
131+
if val, err := time.ParseDuration(string(rawVal)); err == nil {
132+
return val, nil
113133
}
114134

115-
return
135+
return 0, IncorrectFormatForSetting{Setting: setting, Value: rawVal, Err: err}
116136
}
117137

118138
// BoolSetting returns the requested setting parsed as a boolean. Returns an error if the setting is not set or cannot be parsed as a bool.
119139
func (s SessionSettings) BoolSetting(setting string) (bool, error) {
120-
stringVal, err := s.Setting(setting)
121-
140+
rawVal, err := s.RawSetting(setting)
122141
if err != nil {
123142
return false, err
124143
}
125144

126-
switch stringVal {
145+
switch string(rawVal) {
127146
case "Y", "y":
128147
return true, nil
129148
case "N", "n":
130149
return false, nil
131150
}
132151

133-
return false, IncorrectFormatForSetting{Setting: setting, Value: stringVal}
152+
return false, IncorrectFormatForSetting{Setting: setting, Value: rawVal}
134153
}
135154

136155
func (s *SessionSettings) overlay(overlay *SessionSettings) {

session_settings_test.go

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
package quickfix
1717

1818
import (
19+
"bytes"
1920
"testing"
21+
"time"
2022

2123
"github.com/quickfixgo/quickfix/config"
2224
)
@@ -55,10 +57,15 @@ func TestSessionSettings_IntSettings(t *testing.T) {
5557
}
5658

5759
s.Set(config.SocketAcceptPort, "notanint")
58-
if _, err := s.IntSetting(config.SocketAcceptPort); err == nil {
60+
_, err := s.IntSetting(config.SocketAcceptPort)
61+
if err == nil {
5962
t.Error("Expected error for unparsable value")
6063
}
6164

65+
if err.Error() != `"notanint" is invalid for SocketAcceptPort` {
66+
t.Errorf("Expected %s, got %s", `"notanint" is invalid for SocketAcceptPort`, err)
67+
}
68+
6269
s.Set(config.SocketAcceptPort, "1005")
6370
val, err := s.IntSetting(config.SocketAcceptPort)
6471
if err != nil {
@@ -77,10 +84,15 @@ func TestSessionSettings_BoolSettings(t *testing.T) {
7784
}
7885

7986
s.Set(config.ResetOnLogon, "notabool")
80-
if _, err := s.BoolSetting(config.ResetOnLogon); err == nil {
87+
_, err := s.BoolSetting(config.ResetOnLogon)
88+
if err == nil {
8189
t.Error("Expected error for unparsable value")
8290
}
8391

92+
if err.Error() != `"notabool" is invalid for ResetOnLogon` {
93+
t.Errorf("Expected %s, got %s", `"notabool" is invalid for ResetOnLogon`, err)
94+
}
95+
8496
var boolTests = []struct {
8597
input string
8698
expected bool
@@ -105,6 +117,55 @@ func TestSessionSettings_BoolSettings(t *testing.T) {
105117
}
106118
}
107119

120+
func TestSessionSettings_DurationSettings(t *testing.T) {
121+
s := NewSessionSettings()
122+
if _, err := s.BoolSetting(config.ReconnectInterval); err == nil {
123+
t.Error("Expected error for unknown setting")
124+
}
125+
126+
s.Set(config.ReconnectInterval, "not duration")
127+
128+
_, err := s.DurationSetting(config.ReconnectInterval)
129+
if err == nil {
130+
t.Error("Expected error for unparsable value")
131+
}
132+
133+
if err.Error() != `"not duration" is invalid for ReconnectInterval` {
134+
t.Errorf("Expected %s, got %s", `"not duration" is invalid for ReconnectInterval`, err)
135+
}
136+
137+
s.Set(config.ReconnectInterval, "10s")
138+
139+
got, err := s.DurationSetting(config.ReconnectInterval)
140+
if err != nil {
141+
t.Error("Unexpected err", err)
142+
}
143+
144+
expected, _ := time.ParseDuration("10s")
145+
146+
if got != expected {
147+
t.Errorf("Expected %v, got %v", expected, got)
148+
}
149+
}
150+
151+
func TestSessionSettings_ByteSettings(t *testing.T) {
152+
s := NewSessionSettings()
153+
if _, err := s.RawSetting(config.SocketPrivateKeyBytes); err == nil {
154+
t.Error("Expected error for unknown setting")
155+
}
156+
157+
s.SetRaw(config.SocketPrivateKeyBytes, []byte("pembytes"))
158+
159+
got, err := s.RawSetting(config.SocketPrivateKeyBytes)
160+
if err != nil {
161+
t.Error("Unexpected err", err)
162+
}
163+
164+
if !bytes.Equal([]byte("pembytes"), got) {
165+
t.Errorf("Expected %v, got %v", []byte("pembytes"), got)
166+
}
167+
}
168+
108169
func TestSessionSettings_Clone(t *testing.T) {
109170
s := NewSessionSettings()
110171

0 commit comments

Comments
 (0)