diff --git a/session/session.go b/session/session.go new file mode 100644 index 0000000..7bf6787 --- /dev/null +++ b/session/session.go @@ -0,0 +1,131 @@ +package session + +import ( + "encoding/json" + "errors" + "fmt" + "os" + "path/filepath" + "time" +) + +type Engine interface { + Read(string, any) error + Start() error + Stop() error + Store(string, any) error + Encode() error + Decode() error +} + +type FileEngine struct { + Dir string + Encoder + PurgeDuration time.Duration +} + +func NewFileEngine() *FileEngine { + dir := filepath.Join(os.TempDir(), "httpok", "sess") + return &FileEngine{ + Dir: dir, + Encoder: &JsonEncoder{}, + PurgeDuration: 2 * time.Minute, + } +} + +func (e *FileEngine) Read(sess string, v any) error { + sessFile := filepath.Join(e.Dir, fmt.Sprintf("%s.sess", sess)) + file, err := os.Open(sessFile) + if err != nil { + return err + } + + defer file.Close() + + buffer := make([]byte, 1024) + n, err := file.Read(buffer) + + if err != nil { + return err + } + err = e.Encoder.Decode(buffer[:n], v) + if err != nil { + return err + } + + return nil +} + +func (e *FileEngine) Start() error { + fileInfo, err := os.Stat(e.Dir) + if err != nil { + if os.IsNotExist(err) { + err := os.MkdirAll(e.Dir, 0o774) + if err != nil { + return errors.New( + fmt.Sprintf("error creating session dir %s: %v", e.Dir, + err), + ) + } + return nil + } + return errors.New( + fmt.Sprintf("error stating session dir %s: %v", e.Dir, err), + ) + } + + if fileInfo.Mode().IsRegular() { + return errors.New( + fmt.Sprintf("there is a file named as %s it is not possible to "+ + "create the sesssion dir", e.Dir), + ) + } + + return nil +} + +func (e *FileEngine) Stop() error { + return nil +} + +func (e *FileEngine) Store(sess string, v any) error { + sessFile := filepath.Join(e.Dir, fmt.Sprintf("%s.sess", sess)) + file, err := os.OpenFile(sessFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + return err + } + defer file.Close() + + data, err := e.Encoder.Encode(v) + + _, err = file.Write(data) + if err != nil { + return err + } + + return nil +} + +func (e *FileEngine) Encode() error { + return nil +} + +func (e *FileEngine) Decode() error { + return nil +} + +type Encoder interface { + Encode(any) ([]byte, error) + Decode([]byte, any) error +} + +type JsonEncoder struct { +} + +func (e *JsonEncoder) Encode(v any) ([]byte, error) { + return json.Marshal(v) +} + +func (e *JsonEncoder) Decode(data []byte, v any) error { + return json.Unmarshal(data, v) +} diff --git a/session/session_test.go b/session/session_test.go new file mode 100644 index 0000000..ef3d06b --- /dev/null +++ b/session/session_test.go @@ -0,0 +1,59 @@ +// Copyright 2023-2025 Flavio Garcia +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package session + +import ( + "fmt" + "os" + "path/filepath" + "testing" + + "github.com/candango/httpok/util" + "github.com/stretchr/testify/assert" +) + +// TODO: We need to keep building the session engine tests +func TestSessionServer(t *testing.T) { + engine := NewFileEngine() + err := engine.Start() + if err != nil { + t.Error(err) + } + + sess := util.RandomString(16) + + err = engine.Store(sess, map[string]interface{}{ + "key": "value", + }) + if err != nil { + t.Error(err) + } + var sessData map[string]interface{} + + err = engine.Read(sess, &sessData) + if err != nil { + t.Error(err) + } + + assert.Equal(t, "value", sessData["key"]) + defer func() { + sessFile := filepath.Join(engine.Dir, fmt.Sprintf("%s.sess", sess)) + err := os.Remove(sessFile) + if err != nil { + t.Error(err) + } + + }() +}