Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
2d6ebedb0f | |||
12d9acfdb3 |
6
.github/workflows/go-cross.yml
vendored
6
.github/workflows/go-cross.yml
vendored
@ -1,5 +1,9 @@
|
|||||||
name: Go Matrix
|
name: Go Matrix
|
||||||
on: [push, pull_request]
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
|
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
GO_VERSION: 1.17
|
GO_VERSION: 1.17
|
||||||
GOLANGCI_LINT_VERSION: v1.44.0
|
GOLANGCI_LINT_VERSION: v1.46.2
|
||||||
CGO_ENABLED: 0
|
CGO_ENABLED: 0
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
@ -39,6 +39,7 @@ linters:
|
|||||||
- varnamelen
|
- varnamelen
|
||||||
- nilnil
|
- nilnil
|
||||||
- ifshort
|
- ifshort
|
||||||
|
- exhaustruct
|
||||||
|
|
||||||
issues:
|
issues:
|
||||||
exclude-use-default: false
|
exclude-use-default: false
|
||||||
|
19
app.go
19
app.go
@ -1,7 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@ -181,7 +180,7 @@ func dataHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
attachment = false
|
attachment = false
|
||||||
}
|
}
|
||||||
|
|
||||||
content := fillContent(size)
|
content := &contentReader{size: size}
|
||||||
|
|
||||||
if attachment {
|
if attachment {
|
||||||
w.Header().Set("Content-Disposition", "Attachment")
|
w.Header().Set("Content-Disposition", "Attachment")
|
||||||
@ -311,22 +310,6 @@ func healthHandler(w http.ResponseWriter, req *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fillContent(length int64) io.ReadSeeker {
|
|
||||||
charset := "-ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
||||||
b := make([]byte, length)
|
|
||||||
|
|
||||||
for i := range b {
|
|
||||||
b[i] = charset[i%len(charset)]
|
|
||||||
}
|
|
||||||
|
|
||||||
if length > 0 {
|
|
||||||
b[0] = '|'
|
|
||||||
b[length-1] = '|'
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytes.NewReader(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getEnv(key, fallback string) string {
|
func getEnv(key, fallback string) string {
|
||||||
value := os.Getenv(key)
|
value := os.Getenv(key)
|
||||||
if len(value) == 0 {
|
if len(value) == 0 {
|
||||||
|
66
content.go
Normal file
66
content.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
const contentCharset = "-ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
|
||||||
|
type contentReader struct {
|
||||||
|
size int64
|
||||||
|
cur int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read implements the io.Read interface.
|
||||||
|
func (r *contentReader) Read(p []byte) (int, error) {
|
||||||
|
length := r.size - 1
|
||||||
|
|
||||||
|
if r.cur >= length {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
if len(p) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var n int
|
||||||
|
if r.cur == 0 {
|
||||||
|
p[n] = '|'
|
||||||
|
r.cur++
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
|
||||||
|
for n < len(p) && r.cur <= length {
|
||||||
|
p[n] = contentCharset[int(r.cur)%len(contentCharset)]
|
||||||
|
r.cur++
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.cur >= length {
|
||||||
|
p[n-1] = '|'
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seek implements the io.Seek interface.
|
||||||
|
func (r *contentReader) Seek(offset int64, whence int) (int64, error) {
|
||||||
|
switch whence {
|
||||||
|
default:
|
||||||
|
return 0, errors.New("seek: invalid whence")
|
||||||
|
case io.SeekStart:
|
||||||
|
offset += 0
|
||||||
|
case io.SeekCurrent:
|
||||||
|
offset += r.cur
|
||||||
|
case io.SeekEnd:
|
||||||
|
offset += r.size - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if offset < 0 {
|
||||||
|
return 0, errors.New("seek: invalid offset")
|
||||||
|
}
|
||||||
|
|
||||||
|
r.cur = offset
|
||||||
|
|
||||||
|
return offset, nil
|
||||||
|
}
|
82
content_test.go
Normal file
82
content_test.go
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_contentReader_Read(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
size int64
|
||||||
|
content string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "simple",
|
||||||
|
size: 40,
|
||||||
|
content: "|ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJK|",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
test := test
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
d := &contentReader{size: test.size}
|
||||||
|
|
||||||
|
b, err := io.ReadAll(d)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("contentReader.Read() error = %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(b) != test.content {
|
||||||
|
t.Errorf("return content does not match expected value: got %s want %s", b, test.content)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_contentReader_ReadSeek(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
offset int64
|
||||||
|
seekWhence int
|
||||||
|
size int64
|
||||||
|
content string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "simple",
|
||||||
|
offset: 10,
|
||||||
|
seekWhence: io.SeekCurrent,
|
||||||
|
size: 40,
|
||||||
|
content: "JKLMNOPQRSTUVWXYZ-ABCDEFGHIJK|",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
test := test
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
d := &contentReader{size: test.size}
|
||||||
|
|
||||||
|
_, err := d.Seek(test.offset, test.seekWhence)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("contentReader.Seek() error = %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := io.ReadAll(d)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("contentReader.Read() error = %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(b) != test.content {
|
||||||
|
t.Errorf("return content does not match expected value: got %s want %s", b, test.content)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user