chore: improve linting and error handling across the codebase
- Update golangci-lint-action to version 7 in GitHub workflow - Specify version 2.0 for golangci-lint-action in GitHub workflow - Set golangci-lint configuration version to "2" - Enable specific linters and disable default linters in golangci configuration - Add exclusions and formatters configurations in golangci configuration - Replace deprecated `ioutil.ReadAll` with `io.ReadAll` in sse-decoder.go - Use grouped variable declaration for `contentType` and `noCache` in sse-encoder.go - Add error handling for `WriteString` operations in sse-encoder.go - Add `nolint:exhaustive` comment for `kindOfData` switch statement in sse-encoder.go - Adjust test assertions for better readability in sse_test.go - Add error handling for `Encode` function calls in tests and benchmarks Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
This commit is contained in:
@@ -21,8 +21,9 @@ jobs:
|
|||||||
go-version: "^1"
|
go-version: "^1"
|
||||||
|
|
||||||
- name: Setup golangci-lint
|
- name: Setup golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v6
|
uses: golangci/golangci-lint-action@v7
|
||||||
with:
|
with:
|
||||||
|
version: v2.0
|
||||||
args: --verbose
|
args: --verbose
|
||||||
|
|
||||||
- name: Bearer
|
- name: Bearer
|
||||||
|
|||||||
+48
-1
@@ -1,3 +1,50 @@
|
|||||||
|
version: "2"
|
||||||
linters:
|
linters:
|
||||||
disable:
|
default: none
|
||||||
|
enable:
|
||||||
|
- bodyclose
|
||||||
|
- dogsled
|
||||||
|
- dupl
|
||||||
- errcheck
|
- errcheck
|
||||||
|
- exhaustive
|
||||||
|
- gochecknoinits
|
||||||
|
- goconst
|
||||||
|
- gocritic
|
||||||
|
- gocyclo
|
||||||
|
- goprintffuncname
|
||||||
|
- gosec
|
||||||
|
- govet
|
||||||
|
- ineffassign
|
||||||
|
- lll
|
||||||
|
- misspell
|
||||||
|
- nakedret
|
||||||
|
- noctx
|
||||||
|
- nolintlint
|
||||||
|
- rowserrcheck
|
||||||
|
- staticcheck
|
||||||
|
- unconvert
|
||||||
|
- unparam
|
||||||
|
- unused
|
||||||
|
- whitespace
|
||||||
|
exclusions:
|
||||||
|
generated: lax
|
||||||
|
presets:
|
||||||
|
- comments
|
||||||
|
- common-false-positives
|
||||||
|
- legacy
|
||||||
|
- std-error-handling
|
||||||
|
paths:
|
||||||
|
- third_party$
|
||||||
|
- builtin$
|
||||||
|
- examples$
|
||||||
|
formatters:
|
||||||
|
enable:
|
||||||
|
- gofmt
|
||||||
|
- gofumpt
|
||||||
|
- goimports
|
||||||
|
exclusions:
|
||||||
|
generated: lax
|
||||||
|
paths:
|
||||||
|
- third_party$
|
||||||
|
- builtin$
|
||||||
|
- examples$
|
||||||
|
|||||||
+6
-5
@@ -7,7 +7,6 @@ package sse
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type decoder struct {
|
type decoder struct {
|
||||||
@@ -22,7 +21,8 @@ func Decode(r io.Reader) ([]Event, error) {
|
|||||||
func (d *decoder) dispatchEvent(event Event, data string) {
|
func (d *decoder) dispatchEvent(event Event, data string) {
|
||||||
dataLength := len(data)
|
dataLength := len(data)
|
||||||
if dataLength > 0 {
|
if dataLength > 0 {
|
||||||
//If the data buffer's last character is a U+000A LINE FEED (LF) character, then remove the last character from the data buffer.
|
// If the data buffer's last character is a U+000A LINE FEED (LF) character,
|
||||||
|
// then remove the last character from the data buffer.
|
||||||
data = data[:dataLength-1]
|
data = data[:dataLength-1]
|
||||||
dataLength--
|
dataLength--
|
||||||
}
|
}
|
||||||
@@ -37,7 +37,7 @@ func (d *decoder) dispatchEvent(event Event, data string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) decode(r io.Reader) ([]Event, error) {
|
func (d *decoder) decode(r io.Reader) ([]Event, error) {
|
||||||
buf, err := ioutil.ReadAll(r)
|
buf, err := io.ReadAll(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -96,7 +96,8 @@ func (d *decoder) decode(r io.Reader) ([]Event, error) {
|
|||||||
currentEvent.Id = string(value)
|
currentEvent.Id = string(value)
|
||||||
case "retry":
|
case "retry":
|
||||||
// If the field value consists of only characters in the range U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9),
|
// If the field value consists of only characters in the range U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9),
|
||||||
// then interpret the field value as an integer in base ten, and set the event stream's reconnection time to that integer.
|
// then interpret the field value as an integer in base ten, and set the event stream's
|
||||||
|
// reconnection time to that integer.
|
||||||
// Otherwise, ignore the field.
|
// Otherwise, ignore the field.
|
||||||
currentEvent.Id = string(value)
|
currentEvent.Id = string(value)
|
||||||
case "data":
|
case "data":
|
||||||
@@ -105,7 +106,7 @@ func (d *decoder) decode(r io.Reader) ([]Event, error) {
|
|||||||
// then append a single U+000A LINE FEED (LF) character to the data buffer.
|
// then append a single U+000A LINE FEED (LF) character to the data buffer.
|
||||||
dataBuffer.WriteString("\n")
|
dataBuffer.WriteString("\n")
|
||||||
default:
|
default:
|
||||||
//Otherwise. The field is ignored.
|
// Otherwise. The field is ignored.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-18
@@ -20,8 +20,10 @@ import (
|
|||||||
|
|
||||||
const ContentType = "text/event-stream;charset=utf-8"
|
const ContentType = "text/event-stream;charset=utf-8"
|
||||||
|
|
||||||
var contentType = []string{ContentType}
|
var (
|
||||||
var noCache = []string{"no-cache"}
|
contentType = []string{ContentType}
|
||||||
|
noCache = []string{"no-cache"}
|
||||||
|
)
|
||||||
|
|
||||||
var fieldReplacer = strings.NewReplacer(
|
var fieldReplacer = strings.NewReplacer(
|
||||||
"\n", "\\n",
|
"\n", "\\n",
|
||||||
@@ -48,48 +50,48 @@ func Encode(writer io.Writer, event Event) error {
|
|||||||
|
|
||||||
func writeId(w stringWriter, id string) {
|
func writeId(w stringWriter, id string) {
|
||||||
if len(id) > 0 {
|
if len(id) > 0 {
|
||||||
w.WriteString("id:")
|
_, _ = w.WriteString("id:")
|
||||||
fieldReplacer.WriteString(w, id)
|
_, _ = fieldReplacer.WriteString(w, id)
|
||||||
w.WriteString("\n")
|
_, _ = w.WriteString("\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeEvent(w stringWriter, event string) {
|
func writeEvent(w stringWriter, event string) {
|
||||||
if len(event) > 0 {
|
if len(event) > 0 {
|
||||||
w.WriteString("event:")
|
_, _ = w.WriteString("event:")
|
||||||
fieldReplacer.WriteString(w, event)
|
_, _ = fieldReplacer.WriteString(w, event)
|
||||||
w.WriteString("\n")
|
_, _ = w.WriteString("\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeRetry(w stringWriter, retry uint) {
|
func writeRetry(w stringWriter, retry uint) {
|
||||||
if retry > 0 {
|
if retry > 0 {
|
||||||
w.WriteString("retry:")
|
_, _ = w.WriteString("retry:")
|
||||||
w.WriteString(strconv.FormatUint(uint64(retry), 10))
|
_, _ = w.WriteString(strconv.FormatUint(uint64(retry), 10))
|
||||||
w.WriteString("\n")
|
_, _ = w.WriteString("\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeData(w stringWriter, data interface{}) error {
|
func writeData(w stringWriter, data interface{}) error {
|
||||||
w.WriteString("data:")
|
_, _ = w.WriteString("data:")
|
||||||
|
|
||||||
bData, ok := data.([]byte)
|
bData, ok := data.([]byte)
|
||||||
if ok {
|
if ok {
|
||||||
dataReplacer.WriteString(w, string(bData))
|
_, _ = dataReplacer.WriteString(w, string(bData))
|
||||||
w.WriteString("\n\n")
|
_, _ = w.WriteString("\n\n")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch kindOfData(data) {
|
switch kindOfData(data) { //nolint:exhaustive
|
||||||
case reflect.Struct, reflect.Slice, reflect.Map:
|
case reflect.Struct, reflect.Slice, reflect.Map:
|
||||||
err := json.NewEncoder(w).Encode(data)
|
err := json.NewEncoder(w).Encode(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
w.WriteString("\n")
|
_, _ = w.WriteString("\n")
|
||||||
default:
|
default:
|
||||||
dataReplacer.WriteString(w, fmt.Sprint(data))
|
_, _ = dataReplacer.WriteString(w, fmt.Sprint(data))
|
||||||
w.WriteString("\n\n")
|
_, _ = w.WriteString("\n\n")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-8
@@ -170,22 +170,25 @@ func TestEncodeFloat(t *testing.T) {
|
|||||||
func TestEncodeStream(t *testing.T) {
|
func TestEncodeStream(t *testing.T) {
|
||||||
w := new(bytes.Buffer)
|
w := new(bytes.Buffer)
|
||||||
|
|
||||||
Encode(w, Event{
|
_ = Encode(w, Event{
|
||||||
Event: "float",
|
Event: "float",
|
||||||
Data: 1.5,
|
Data: 1.5,
|
||||||
})
|
})
|
||||||
|
|
||||||
Encode(w, Event{
|
_ = Encode(w, Event{
|
||||||
Id: "123",
|
Id: "123",
|
||||||
Data: map[string]interface{}{"foo": "bar", "bar": "foo"},
|
Data: map[string]interface{}{"foo": "bar", "bar": "foo"},
|
||||||
})
|
})
|
||||||
|
|
||||||
Encode(w, Event{
|
_ = Encode(w, Event{
|
||||||
Id: "124",
|
Id: "124",
|
||||||
Event: "chat",
|
Event: "chat",
|
||||||
Data: "hi! dude",
|
Data: "hi! dude",
|
||||||
})
|
})
|
||||||
assert.Equal(t, w.String(), "event:float\ndata:1.5\n\nid:123\ndata:{\"bar\":\"foo\",\"foo\":\"bar\"}\n\nid:124\nevent:chat\ndata:hi! dude\n\n")
|
assert.Equal(t, w.String(),
|
||||||
|
"event:float\ndata:1.5\n\n"+
|
||||||
|
"id:123\ndata:{\"bar\":\"foo\",\"foo\":\"bar\"}\n\n"+
|
||||||
|
"id:124\nevent:chat\ndata:hi! dude\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRenderSSE(t *testing.T) {
|
func TestRenderSSE(t *testing.T) {
|
||||||
@@ -207,7 +210,7 @@ func BenchmarkResponseWriter(b *testing.B) {
|
|||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
(Event{
|
_ = (Event{
|
||||||
Event: "new_message",
|
Event: "new_message",
|
||||||
Data: "hi! how are you? I am fine. this is a long stupid message!!!",
|
Data: "hi! how are you? I am fine. this is a long stupid message!!!",
|
||||||
}).Render(w)
|
}).Render(w)
|
||||||
@@ -219,7 +222,7 @@ func BenchmarkFullSSE(b *testing.B) {
|
|||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
Encode(buf, Event{
|
_ = Encode(buf, Event{
|
||||||
Event: "new_message",
|
Event: "new_message",
|
||||||
Id: "13435",
|
Id: "13435",
|
||||||
Retry: 10,
|
Retry: 10,
|
||||||
@@ -234,7 +237,7 @@ func BenchmarkNoRetrySSE(b *testing.B) {
|
|||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
Encode(buf, Event{
|
_ = Encode(buf, Event{
|
||||||
Event: "new_message",
|
Event: "new_message",
|
||||||
Id: "13435",
|
Id: "13435",
|
||||||
Data: "hi! how are you? I am fine. this is a long stupid message!!!",
|
Data: "hi! how are you? I am fine. this is a long stupid message!!!",
|
||||||
@@ -248,7 +251,7 @@ func BenchmarkSimpleSSE(b *testing.B) {
|
|||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
Encode(buf, Event{
|
_ = Encode(buf, Event{
|
||||||
Event: "new_message",
|
Event: "new_message",
|
||||||
Data: "hi! how are you? I am fine. this is a long stupid message!!!",
|
Data: "hi! how are you? I am fine. this is a long stupid message!!!",
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user