diff --git a/.cspell.json b/.cspell.json new file mode 100644 index 0000000..d866e52 --- /dev/null +++ b/.cspell.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json", + "useGitignore": true, + "ignorePaths": [".vscode", ".gitignore"], + "dictionaries": [ + "en_US", + "companies", + "misc", + "softwareTerms", + "bash", + "filetypes", + "powershell", + "lorem-ipsum" + ], + "words": [] +} \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..4eb2283 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +text eol=lf \ No newline at end of file diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml index b18eef9..cfb7acd 100644 --- a/.github/workflows/pullrequest.yml +++ b/.github/workflows/pullrequest.yml @@ -16,20 +16,21 @@ jobs: steps: - name: Clone Repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup GO - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: - go-version: "1.18.x" + go-version-file: './go.work' + - name: Running Lint (go vet) - run: go vet -json ./core > govetoutput.json + run: go vet -json ./src/core > govetoutput.json - name: Running Test - run: go test ./core -coverprofile=coverage.out + run: go test ./src/core -coverprofile=coverage.out - name: SonarCloud Scan uses: SonarSource/sonarcloud-github-action@master diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9252f86 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Open Source BR + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/core/protocol_test.go b/core/protocol_test.go deleted file mode 100644 index 4a63290..0000000 --- a/core/protocol_test.go +++ /dev/null @@ -1,126 +0,0 @@ -package core - -import ( - "bytes" - "fmt" - "net" - "testing" - "time" -) - -// StreamConn is a fake to net.Conn for test purpose only -type StreamConn struct { - buffer bytes.Buffer -} - -func (stream *StreamConn) LocalAddr() net.Addr { - return nil -} - -func (stream *StreamConn) RemoteAddr() net.Addr { - return nil -} - -func (stream *StreamConn) Close() error { - return fmt.Errorf("stream conn fake: not implemented, for test purpose only)") -} - -func (stream *StreamConn) SetDeadline(t time.Time) error { - return fmt.Errorf("stream conn fake: not implemented, for test purpose only)") -} - -func (stream *StreamConn) SetReadDeadline(t time.Time) error { - return fmt.Errorf("stream conn fake: not implemented, for test purpose only)") -} - -func (stream *StreamConn) SetWriteDeadline(t time.Time) error { - return fmt.Errorf("stream conn fake: not implemented, for test purpose only)") -} - -func NewStreamConn() *StreamConn { - return &StreamConn{buffer: bytes.Buffer{}} -} - -func NewStreamConnWithData(defaultMessage string) *StreamConn { - conn := &StreamConn{buffer: bytes.Buffer{}} - WriteMessage(defaultMessage, conn) - - return conn -} - -func (stream *StreamConn) Read(b []byte) (n int, err error) { - var buffer []byte - currentBufferSize := len(b) - - // Will simulate limit size to read buffer. For test purpose only - if currentBufferSize > 20 { - buffer = make([]byte, 20) - - } else { - buffer = make([]byte, currentBufferSize) - } - - sizes, err := stream.buffer.Read(buffer) - - copy(b, buffer) - - return sizes, err - -} - -func (stream *StreamConn) Write(b []byte) (n int, err error) { - var buffer []byte - currentMessageSize := len(b) - - // Will simulate limit size to write buffer. For test purpose only - if currentMessageSize > 20 { - buffer = make([]byte, 20) - - } else { - buffer = make([]byte, currentMessageSize) - } - - copy(buffer, b) - - return stream.buffer.Write(buffer) -} - -func TestWriteShortMessage(t *testing.T) { - stream := NewStreamConn() - - bytesWritter, _ := WriteMessage("hellou!", stream) - - if bytesWritter != 7 { - t.FailNow() - } -} - -func TestWriteLargeMessage(t *testing.T) { - stream := NewStreamConn() - - bytesWritter, _ := WriteMessage("message to write buffer", stream) - - if bytesWritter != 23 { - t.FailNow() - } -} - -func TestReadShortMessage(t *testing.T) { - stream := NewStreamConnWithData("Hello!") - - message, _ := ReadMessage(stream) - - if message != "Hello!" { - t.FailNow() - } -} - -func TestReadLargeMessage(t *testing.T) { - stream := NewStreamConnWithData("message to read buffer") - - message, _ := ReadMessage(stream) - - if message != "message to read buffer" { - t.FailNow() - } -} diff --git a/go.mod b/go.mod deleted file mode 100644 index 6f1793c..0000000 --- a/go.mod +++ /dev/null @@ -1,4 +0,0 @@ -module github.com/open-source-br/epp - -go 1.18 - diff --git a/go.work b/go.work index 6e9786f..4bc6af1 100644 --- a/go.work +++ b/go.work @@ -1,3 +1,3 @@ -go 1.18 +go 1.21.6 -use ./core +use ./src/core diff --git a/sonar-project.properties b/sonar-project.properties index 2e9b91f..093defc 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -6,6 +6,7 @@ sonar.projectName=EPP sonar.go.coverage.reportPaths=coverage.out sonar.go.govet.reportPaths=govetoutput.json + sonar.sources=. sonar.exclusions=**/*_test.go diff --git a/core/go.mod b/src/core/go.mod similarity index 81% rename from core/go.mod rename to src/core/go.mod index 995703f..105f082 100644 --- a/core/go.mod +++ b/src/core/go.mod @@ -1,3 +1,3 @@ module github.com/open-source-br/epp/core -go 1.18 +go 1.21.6 diff --git a/core/protocol.go b/src/core/protocol.go similarity index 80% rename from core/protocol.go rename to src/core/protocol.go index 6408f6a..9e7d6e9 100644 --- a/core/protocol.go +++ b/src/core/protocol.go @@ -3,8 +3,8 @@ package core import ( "encoding/binary" "fmt" + "io" "log" - "net" ) /* @@ -39,10 +39,10 @@ import ( const TotalLength int32 = 4 -func writeBufferSize(size int, conn net.Conn) (n int, err error) { +func writeBufferSize(size int, stream io.Writer) (n int, err error) { buffer := make([]byte, TotalLength) binary.BigEndian.PutUint32(buffer, uint32(size)) - sizeWritten, err := conn.Write(buffer) + sizeWritten, err := stream.Write(buffer) if err != nil { err := fmt.Errorf("error on write size buffer: %s", err) @@ -52,10 +52,10 @@ func writeBufferSize(size int, conn net.Conn) (n int, err error) { return sizeWritten, nil } -func readBufferSize(conn net.Conn) (size int, err error) { +func readBufferSize(stream io.Reader) (size int, err error) { bufferSize := make([]byte, TotalLength) - if _, err := conn.Read(bufferSize); err != nil { + if _, err := stream.Read(bufferSize); err != nil { err := fmt.Errorf("error on read size buffer: %s", err) return -1, err } @@ -63,14 +63,14 @@ func readBufferSize(conn net.Conn) (size int, err error) { return int(binary.BigEndian.Uint32(bufferSize)), nil } -func writePendingBytes(messageSize int, messageSizeWritten int, messageBuffer []byte, conn net.Conn) (n int, err error) { +func writePendingBytes(messageSize int, messageSizeWritten int, messageBuffer []byte, stream io.Writer) (n int, err error) { var messageSizeCurrent = messageSizeWritten for { if messageSize != messageSizeCurrent { buffer := messageBuffer[messageSizeWritten:messageSize] - currentWriteBytesSize, err := conn.Write(buffer) + currentWriteBytesSize, err := stream.Write(buffer) if err != nil { err := fmt.Errorf("error on write message buffer: %s", err) @@ -86,14 +86,14 @@ func writePendingBytes(messageSize int, messageSizeWritten int, messageBuffer [] } -func readPendingBytes(messageSize int, messageSizeRead int, messageBuffer []byte, conn net.Conn) (buffer []byte, err error) { +func readPendingBytes(messageSize int, messageSizeRead int, messageBuffer []byte, stream io.Reader) (buffer []byte, err error) { var messageSizeCurrent = messageSizeRead for { if messageSize != messageSizeCurrent { buffer := make([]byte, messageSize-messageSizeCurrent) - currentReadBytesSize, err := conn.Read(buffer) + currentReadBytesSize, err := stream.Read(buffer) messageSizeCurrent += currentReadBytesSize @@ -112,11 +112,11 @@ func readPendingBytes(messageSize int, messageSizeRead int, messageBuffer []byte } } -func WriteMessage(message string, conn net.Conn) (n int, err error) { +func WriteMessage(message string, stream io.Writer) (n int, err error) { messageBuffer := []byte(message) messageSize := len(messageBuffer) - writeBufferSize(messageSize, conn) - messageSizeWritten, err := conn.Write(messageBuffer) + writeBufferSize(messageSize, stream) + messageSizeWritten, err := stream.Write(messageBuffer) if err != nil { err := fmt.Errorf("error on write message buffer: %s", err) @@ -126,7 +126,7 @@ func WriteMessage(message string, conn net.Conn) (n int, err error) { if messageSize != messageSizeWritten { log.Println("not all bytes were written in first tentative, trying to write pending ") - byteWritten, err := writePendingBytes(messageSize, messageSizeWritten, messageBuffer, conn) + byteWritten, err := writePendingBytes(messageSize, messageSizeWritten, messageBuffer, stream) if err != nil { err := fmt.Errorf("error on write pending bytes to server: %s", err) @@ -135,14 +135,19 @@ func WriteMessage(message string, conn net.Conn) (n int, err error) { return byteWritten, nil } - return messageSizeWritten, nil } -func ReadMessage(conn net.Conn) (message string, err error) { - messageSize, _ := readBufferSize(conn) +func ReadMessage(stream io.Reader) (message string, err error) { + messageSize, _ := readBufferSize(stream) + + if messageSize <= 0 { + err := fmt.Errorf("error on read empty buffer: %s", err) + return "", err + } + messageBuffer := make([]byte, messageSize) - messageSizeRead, err := conn.Read(messageBuffer) + messageSizeRead, err := stream.Read(messageBuffer) if err != nil { err := fmt.Errorf("error on read message buffer: %s", err) @@ -152,7 +157,7 @@ func ReadMessage(conn net.Conn) (message string, err error) { if messageSize != messageSizeRead { log.Println("not all bytes were read in first tentative, trying to read pending") - buffer, err := readPendingBytes(messageSize, messageSizeRead, messageBuffer, conn) + buffer, err := readPendingBytes(messageSize, messageSizeRead, messageBuffer, stream) if err != nil { err := fmt.Errorf("error on read pending bytes from server: %s", err) diff --git a/src/core/protocol_buffer_mock.go b/src/core/protocol_buffer_mock.go new file mode 100644 index 0000000..695a6a0 --- /dev/null +++ b/src/core/protocol_buffer_mock.go @@ -0,0 +1,49 @@ +package core + +import ( + "bytes" +) + +// Stream is a mock for test purpose only +type MockStream struct { + buffer bytes.Buffer + limitReader int + limitWriter int +} + +func (stream *MockStream) Read(b []byte) (n int, err error) { + var buffer []byte + currentBufferSize := len(b) + + // Will simulate limit size to read buffer. For test purpose only + if currentBufferSize > stream.limitReader { + buffer = make([]byte, stream.limitReader) + + } else { + buffer = make([]byte, currentBufferSize) + } + + sizes, err := stream.buffer.Read(buffer) + + copy(b, buffer) + + return sizes, err + +} + +func (stream *MockStream) Write(b []byte) (n int, err error) { + var buffer []byte + currentMessageSize := len(b) + + // Will simulate limit size to write buffer. For test purpose only + if currentMessageSize > stream.limitWriter { + buffer = make([]byte, stream.limitWriter) + + } else { + buffer = make([]byte, currentMessageSize) + } + + copy(buffer, b) + + return stream.buffer.Write(buffer) +} diff --git a/src/core/protocol_test.go b/src/core/protocol_test.go new file mode 100644 index 0000000..d7f221e --- /dev/null +++ b/src/core/protocol_test.go @@ -0,0 +1,69 @@ +package core + +import ( + "bytes" + "testing" +) + +func TestWriteShortMessage(t *testing.T) { + stream := &MockStream{buffer: bytes.Buffer{}, limitReader: 20, limitWriter: 20} + + bytesWriter, _ := WriteMessage("Hello!", stream) + + if bytesWriter != 6 { + t.Log("Expected 6 bytes written, got ', bytesWriter") + t.FailNow() + } +} + +func TestWriteLargeMessage(t *testing.T) { + stream := &MockStream{buffer: bytes.Buffer{}, limitReader: 20, limitWriter: 20} + + bytesWriter, _ := WriteMessage("message to write buffer", stream) + + if bytesWriter != 23 { + t.FailNow() + } +} +func TestReadShortMessage(t *testing.T) { + stream := &MockStream{buffer: bytes.Buffer{}, limitReader: 20, limitWriter: 20} + WriteMessage("Hello!", stream) // echo + + message, _ := ReadMessage(stream) + + if message != "Hello!" { + t.FailNow() + } +} + +func TestReadLargeMessage(t *testing.T) { + stream := &MockStream{buffer: bytes.Buffer{}, limitReader: 20, limitWriter: 20} + WriteMessage("message to read buffer", stream) // echo + + message, _ := ReadMessage(stream) + + if message != "message to read buffer" { + t.FailNow() + } +} + +func TestReadFromEmptyStream(t *testing.T) { + stream := &MockStream{buffer: bytes.Buffer{}, limitReader: 20, limitWriter: 20} + + _, err := ReadMessage(stream) + + if err == nil { + t.Fatalf("Expected error when reading from empty stream, got nil") + } +} + +func TestReadShortMessageWithShortLimitReaderAndWriter(t *testing.T) { + stream := &MockStream{buffer: bytes.Buffer{}, limitReader: 4, limitWriter: 4} + WriteMessage("Hello!", stream) // echo + + message, _ := ReadMessage(stream) + + if message != "Hello!" { + t.FailNow() + } +}