Skip to content

Commit

Permalink
fix(https): use multireader and add quickcheck tests
Browse files Browse the repository at this point in the history
- Combine bytes.NewReader and zeroReader via io.MultiReader.
- Add test for `len(p) == 0`.
- Add quickcheck test for `NewRequestReader`.
  • Loading branch information
mxinden committed Jun 22, 2023
1 parent 900de9a commit 2296d8c
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 23 deletions.
55 changes: 32 additions & 23 deletions perf/impl/https/v0.1/main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
Expand Down Expand Up @@ -60,35 +61,43 @@ func handleRequest(w http.ResponseWriter, r *http.Request) {
}
}

var zeroSlice = make([]byte, BlockSize) // Pre-made zero-filled slice
func newRequestReader(downloadBytes uint64, uploadBytes uint64) io.Reader {
p := make([]byte, 8)
binary.BigEndian.PutUint64(p, downloadBytes)

type customReader struct {
downloadBytes uint64
uploadBytes uint64
position uint64
return io.MultiReader(bytes.NewReader(p), &zeroReader{n: int(uploadBytes)})
}

func (c *customReader) Read(p []byte) (int, error) {
if c.position < 8 {
binary.BigEndian.PutUint64(p, c.downloadBytes)
c.position += 8
return 8, nil
} else if c.position-8 < c.uploadBytes {
bytesToWrite := min(min(len(p), int(c.uploadBytes-(c.position-8))), len(zeroSlice))
copy(p[:bytesToWrite], zeroSlice[:bytesToWrite]) // zero the slice
c.position += uint64(bytesToWrite)
return bytesToWrite, nil
}
var zeroSlice = make([]byte, BlockSize)

return 0, io.EOF
type zeroReader struct {
n int
}

// Helper function to get minimum of two integers
func min(a, b int) int {
if a < b {
return a
func (z *zeroReader) Read(p []byte) (n int, err error) {
if z.n <= 0 {
return 0, io.EOF
}

// calculate the number of zeros to write
n = len(p)
if n > z.n {
n = int(z.n)
}
return b
if n > len(zeroSlice) {
n = len(zeroSlice)
}

// set the necessary bytes to zero
copy(p, zeroSlice[:n])

z.n -= n

if z.n == 0 {
err = io.EOF
}

return n, err
}

func runClient(serverAddr string, uploadBytes, downloadBytes uint64) (time.Duration, time.Duration, error) {
Expand All @@ -100,7 +109,7 @@ func runClient(serverAddr string, uploadBytes, downloadBytes uint64) (time.Durat
},
}

reqBody := &customReader{downloadBytes: downloadBytes, uploadBytes: uploadBytes}
reqBody := newRequestReader(downloadBytes, uploadBytes)

req, err := http.NewRequest("POST", fmt.Sprintf("https://%s/", serverAddr), reqBody)
if err != nil {
Expand Down
57 changes: 57 additions & 0 deletions perf/impl/https/v0.1/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package main

import (
"encoding/binary"
"io/ioutil"
"testing"
"testing/quick"
)

func TestNewRequestReaderSmallP(t *testing.T) {
reader := newRequestReader(0, 0)
reader.Read([]byte{})
}

func TestNewRequestReader(t *testing.T) {
f := func(downloadBytes uint64, uploadBytes uint64) bool {
const MaxUploadBytes = 64 * 1024 * 1024 // 64 megabytes

// Skip if uploadBytes is too large
if uploadBytes > MaxUploadBytes {
return true
}

// Create a new reader
r := newRequestReader(downloadBytes, uploadBytes)

// Read everything from the reader
data, err := ioutil.ReadAll(r)
if err != nil {
t.Fatalf("Failed to read from the reader: %v", err)
}

// The length of data should be 8 (for downloadBytes) + uploadBytes
if len(data) != int(8+uploadBytes) {
return false
}

// The first 8 bytes should represent downloadBytes
readDownloadBytes := binary.BigEndian.Uint64(data[:8])
if readDownloadBytes != downloadBytes {
return false
}

// The rest of the bytes should all be zero
for i := 8; i < len(data); i++ {
if data[i] != 0 {
return false
}
}

return true
}

if err := quick.Check(f, nil); err != nil {
t.Error(err)
}
}

0 comments on commit 2296d8c

Please sign in to comment.