[Need help] How to do chunked body http request with cronet-go?
diyism opened this issue · 1 comments
diyism commented
I can do chunked body http request with net/http.Client,
my server.go can process the received chunk one by one:
$ go run server.go
2024/05/16 17:06:50 Starting server on port 1081...
2024/05/16 17:06:53 received: aaaaaaaaaaaaaa
2024/05/16 17:06:56 received: bbbbbbbbbbbbbb
but if I enable cronet by uncommenting this line of "Transport: &cronet.RoundTripper{}" in my client.go, it will block there forever,
I can't figure out how to do chunked body request with cronet-go,
any hint?
my server.go:
package main
import (
"bufio"
"encoding/base64"
"fmt"
"io"
"log"
"net"
"net/http"
)
const (
udpIP = "127.0.0.1"
udpPort = 1082
)
func handlePost(w http.ResponseWriter, r *http.Request) {
var data []byte
reader := bufio.NewReader(r.Body)
for {
chunkSizeStr, err := reader.ReadString('\n')
if err != nil {
if err == io.EOF {
break
}
log.Printf("Error reading chunk size: %v", err)
http.Error(w, "Error reading chunk size", http.StatusInternalServerError)
return
}
var chunkSize int
if _, err := fmt.Sscanf(chunkSizeStr, "%x", &chunkSize); err != nil {
log.Printf("Invalid chunk size: %v", err)
http.Error(w, "Invalid chunk size", http.StatusBadRequest)
return
}
if chunkSize == 0 {
break
}
chunk := make([]byte, chunkSize)
if _, err := io.ReadFull(reader, chunk); err != nil {
log.Printf("Error reading chunk data: %v", err)
http.Error(w, "Error reading chunk data", http.StatusInternalServerError)
return
}
if _, err := reader.Discard(2); err != nil { // Discard the trailing "\r\n"
log.Printf("Error discarding CRLF: %v", err)
http.Error(w, "Error discarding CRLF", http.StatusInternalServerError)
return
}
decodedChunk, err := base64.StdEncoding.DecodeString(string(chunk))
if err != nil {
log.Printf("Base64 decoding error: %v", err)
continue
}
data = append(data, decodedChunk...)
log.Println("received: "+string(decodedChunk))
}
udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", udpIP, udpPort))
if err != nil {
log.Printf("Error resolving UDP address: %v", err)
return
}
conn, err := net.DialUDP("udp", nil, udpAddr)
if err != nil {
log.Printf("Error dialing UDP: %v", err)
return
}
defer conn.Close()
_, err = conn.Write(data)
if err != nil {
log.Printf("Error sending UDP data: %v", err)
return
}
w.Write([]byte("POST request processed"))
}
func main() {
http.HandleFunc("/", handlePost)
log.Println("Starting server on port 1081...")
err := http.ListenAndServe(":1081", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
my client.go:
package main
import (
"fmt"
"io"
"log"
"net/http"
"strconv"
"time"
//"github.com/SagerNet/cronet-go"
)
type ChunkedReader struct {
chunks [][]byte
delay time.Duration
index int
}
func (r *ChunkedReader) Read(p []byte) (n int, err error) {
log.Printf("Entering read function, current index: %d", r.index)
if r.index >= len(r.chunks) {
return 0, io.EOF
}
if r.index > 0 {
time.Sleep(r.delay)
}
chunk := r.chunks[r.index]
n = copy(p, chunk)
log.Printf("Copied chunk %d, length: %d", r.index, n)
r.index++
return n, nil
}
func formatChunk(data []byte) []byte {
size := strconv.FormatInt(int64(len(data)), 16)
return []byte(fmt.Sprintf("%s\r\n%s\r\n", size, data))
}
func main() {
client := &http.Client{
//Transport: &cronet.RoundTripper{},
}
data1 := []byte("YWFhYWFhYWFhYWFhYWE=")
data2 := []byte("YmJiYmJiYmJiYmJiYmI=")
chunk1 := formatChunk(data1)
chunk2 := formatChunk(data2)
finalChunk := []byte("0\r\n\r\n")
chunkedReader := &ChunkedReader{
chunks: [][]byte{chunk1, chunk2, finalChunk},
delay: 3 * time.Second,
}
headers := map[string]string{
"Host": "127.0.0.1",
"Content-Type": "application/octet-stream",
"Transfer-Encoding": "chunked",
}
req, err := http.NewRequest("POST", "http://127.0.0.1:1081", chunkedReader)
if err != nil {
log.Fatalf("Failed to create request: %v", err)
}
req.Proto = "HTTP/1.1"
req.ProtoMajor = 1
req.ProtoMinor = 1
for key, value := range headers {
req.Header.Set(key, value)
}
log.Print("Before client.Do")
resp, err := client.Do(req)
if err != nil {
log.Fatalf("Request failed: %v", err)
}
defer func() {
if cerr := resp.Body.Close(); cerr != nil {
log.Printf("Failed to close response body: %v", cerr)
}
}()
log.Print("Waiting for request to complete...")
fmt.Printf("Response status: %s\n", resp.Status)
}
diyism commented
log.Print("Setting up upload provider and request parameters")
uploadDataProvider := cronet.NewUploadDataProvider(uploadProvider)
requestParams := cronet.NewURLRequestParams()
requestParams.SetMethod("POST")
requestParams.SetUploadDataProvider(uploadDataProvider)
requestParams.SetUploadDataExecutor(executor)
callback := cronet.NewURLRequestCallback(nil)
urlRequest := cronet.NewURLRequest()
urlRequest.InitWithParams(rt.Engine, "http://127.0.0.1:1081", requestParams, callback, rt.Executor)
requestParams.Destroy()
log.Print("Executing Cronet URL request")
urlRequest.Start()