Skip to content

Instantly share code, notes, and snippets.

@hannesg
Last active June 23, 2016 13:16
Show Gist options
  • Save hannesg/541935ab2e4acc55ed8b85d65defe003 to your computer and use it in GitHub Desktop.
Save hannesg/541935ab2e4acc55ed8b85d65defe003 to your computer and use it in GitHub Desktop.
http request with chunked body

HTTP Request with Chunked Body

What does it do

  • server.go is a simple http server that reads the entire request body and returns it to the client.
  • client.go tries to send a http request with a chunked request body containing two bytes "ab". The request is send in two tcp frames.

Running the example

  • Install go ( https://golang.org ). We tested with 1.6 but older versions should work.

  • Start the server in one terminal ( 127.0.0.1:8080 is the address to bind on ):

      go run server.go 127.0.0.1:8080
    
  • Start the client in another terminal ( 127.0.0.1:8080 is the address to connect to ):

      go run client.go 127.0.0.1:8080
    

Expected output

client.go:

go run client.go 127.0.0.1:8080
Received code:200 body:"ab"
Received code:200 body:"ab"
Received code:200 body:"ab"
Received code:200 body:"ab"
...

server.go:

Received body:"ab"
Received body:"ab"
Received body:"ab"
...
package main
import (
"bufio"
"fmt"
"io/ioutil"
"net"
"net/http"
"os"
"time"
)
// Precomputed http request
var data []string = []string{
"PUT / HTTP/1.1\r\nHost: doesntmatter\r\nConnection: close\r\nTransfer-Encoding: chunked\r\n\r\n1\r\na\r\n",
"1\r\nb\r\n0\r\n\r\n",
}
func main() {
if len(os.Args) != 2 {
fmt.Printf("Usage: %s host:port\n\tExample: %s 127.0.0.1:80\n", os.Args[0], os.Args[0])
os.Exit(1)
}
for {
// Open a tcp socket to the target
soc, err := net.DialTimeout("tcp", os.Args[1], 50*time.Millisecond)
if err != nil {
if nerr, ok := err.(net.Error); ok {
if nerr.Timeout() {
fmt.Printf("Connection timed out to %s\n",os.Args[1])
time.Sleep(time.Second)
}
continue
}
panic(err)
}
// Send frames one-by-one
for _, frame := range data {
if n, err := soc.Write([]byte(frame)); err != nil {
panic(err)
} else if n != len(frame) {
panic("Short write")
}
}
// Drain the socket in the background and compare the result.
go func() {
defer soc.Close()
buf := bufio.NewReader(soc)
response, err := http.ReadResponse(buf, nil)
if err != nil {
// Ignore errors
return
}
content, err := ioutil.ReadAll(response.Body)
if err != nil {
return
}
fmt.Printf("Received code:%d body:%q\n", response.StatusCode, content)
}()
// Throtteling
time.Sleep(100 * time.Millisecond)
}
}
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
"strconv"
)
// A simple echo handler that replies the request body.
func echoHandler(w http.ResponseWriter, r *http.Request) {
buf := &bytes.Buffer{}
n, err := io.Copy(buf, r.Body)
if err != nil {
panic(err.Error())
}
fmt.Printf("Received body:%q\n", buf.Bytes())
w.Header().Add("Content-Length", strconv.FormatInt(n, 10))
w.WriteHeader(200)
io.Copy(w, buf)
}
func main() {
if len(os.Args) != 2 {
fmt.Printf("Usage: %s ip:port\n\tExample: %s 0.0.0.0:80\n", os.Args[0], os.Args[0])
os.Exit(1)
}
http.HandleFunc("/", echoHandler)
err := http.ListenAndServe(os.Args[1], nil)
if err != nil {
panic(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment