Skip to content

Commit

Permalink
add chunked http response
Browse files Browse the repository at this point in the history
  • Loading branch information
notsobad committed Jul 16, 2024
1 parent c739e3c commit 3ec6f8c
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 2 deletions.
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,14 @@ You can also redirect to a 'file://' protol address:

```bash
curl -v 'localhost:9527/redirect/301?url=file:///etc/passwd'
```
```
### :arrows_counterclockwise: Chunked http response

You can simulate chunked http response. Use the following format, `$COUNT` is the count of chunked message.

`http://localhost:9527/chunk/$COUNT`

Examples:

* http://localhost:9527/chunk/3
* http://localhost:9527/chunk/999
32 changes: 32 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"
"html/template"
"log"
"math/rand"
"mime"
"net/http"
"net/http/httputil"
Expand Down Expand Up @@ -178,6 +179,36 @@ func sizeHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, strings.Repeat("o", size))
}

func chunkHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)

count, _ := strconv.Atoi(vars["count"])

w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Transfer-Encoding", "chunked")

//time.Sleep(time.Duration(count) * time.Second)
for i := 0; i < count; i++ {
randomStr := randomString(10)
currentTime := time.Now().Format(time.RFC3339)
fmt.Fprintf(w, "Time: %s, Msg: %s\n", currentTime, randomStr)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
time.Sleep(1 * time.Second)
}
}

func randomString(n int) string {
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

s := make([]rune, n)
for i := range s {
s[i] = letters[rand.Intn(len(letters))]
}
return string(s)
}

func appRouter() http.Handler {
r := mux.NewRouter()
r.HandleFunc("/", indexHandler)
Expand All @@ -187,6 +218,7 @@ func appRouter() http.Handler {
r.HandleFunc("/slow/{time:[0-9]+}", slowHandler)
r.HandleFunc("/redirect/{method}", redirectHandler)
r.HandleFunc("/size/{size:[0-9]+}{measure:[k|m]?}{ext:.*}", sizeHandler)
r.HandleFunc("/chunk/{count:[0-9]+}", chunkHandler)
return r
}

Expand Down
57 changes: 56 additions & 1 deletion main_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package main

import (
"bufio"
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"

"github.com/gorilla/mux"
)

func TestIndexHandler(t *testing.T) {
Expand All @@ -21,7 +24,7 @@ func TestIndexHandler(t *testing.T) {
if w.Code != http.StatusOK {
t.Errorf("Got HTTP status code %d, expect 200", w.Code)
}
if !strings.Contains(w.Body.String(), "SERVER-ID") {
if !strings.Contains(w.Body.String(), "Server id:") {
t.Errorf("Got HTTP status code %d, expect 200", w.Code)
}
}
Expand Down Expand Up @@ -134,3 +137,55 @@ func TestSlowHandler(t *testing.T) {

}
}

// generate test for chunkHandler
func TestChunkHandler(t *testing.T) {
// 创建一个路由,模拟实际的路由行为
r := mux.NewRouter()
r.HandleFunc("/chunk/{count}", chunkHandler)

// 创建一个测试服务器
server := httptest.NewServer(r)
defer server.Close()

expectedCount := 5
url := fmt.Sprintf("%s/chunk/%d", server.URL, expectedCount)
// 发送请求到测试服务器
resp, err := http.Get(url)
if err != nil {
t.Fatalf("Failed to send request: %v", err)
}
defer resp.Body.Close()

// 检查Transfer-Encoding是否为chunked
if resp.TransferEncoding == nil || resp.TransferEncoding[0] != "chunked" {
t.Errorf("Expected chunked transfer encoding, got %v", resp.TransferEncoding)
}

// 读取响应体,确保它是分多次接收的
scanner := bufio.NewScanner(resp.Body)
lineCount := 0

last_time := time.Now()
for scanner.Scan() {
line := scanner.Text()
if strings.Contains(line, "Time:") && strings.Contains(line, "Msg:") {
lineCount++
// 检查每个分块之间的时间间隔是否大于1秒
if lineCount > 1 {
current_time := time.Now()
if current_time.Sub(last_time) < 1*time.Second {
t.Errorf("Chunk interval is less than 1 second")
}
}
}
}

if err := scanner.Err(); err != nil {
t.Fatalf("Error reading stream: %v", err)
}
// 确保我们接收到了预期数量的分块
if lineCount != expectedCount {
t.Errorf("Expected %d chunks, got %d", expectedCount, lineCount)
}
}

0 comments on commit 3ec6f8c

Please sign in to comment.