0

I have written a very simple TCP server, which reads a connections and responds back with HELLO WORLD.

import (
    "fmt"
    "log"
    "net"
)

func handleRequest(conn net.Conn) {

    buff := make([]byte, 10)

    _, err := conn.Read(buff)

    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(string(buff))

    conn.Write([]byte("HELLO WORLD"))
    conn.Close()
}

func main() {
    ln, err := net.Listen("tcp", ":8080")
    fmt.Println("Listening on Port 8080")
    if err != nil {
        log.Fatal(err)
    }
    for {
        conn, err := ln.Accept()
        if err != nil {
            log.Fatal(err)
        }
        go handleRequest(conn)
    }

}

What is the problem with this code? When I run curl http://localhost:8080, I get this output

curl: (56) Recv failure: Connection reset by peer
HELLO WORLD%
buff:=make([]byte,1024)

Now if I increase the buffer size, this code works fine and I do not get that error after running curl.

echo -n "Hello" | nc localhost 8080

If I run the above command it works without any issues.

I really don't understand the cause of this.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Shubhang b
  • 597
  • 1
  • 5
  • 12

1 Answers1

5

Curl sends a large-ish HTTP request. Try to print out (or log) what conn.Read got and you'll see it. Curl could be unhappy because it's not getting a proper HTTP response back.

conn.Read should not overflow the buffer anyway, it reads up to the buffer size.

On the other hand the pipe to nc just sends 5 bytes on the TCP socket.


Here's a sample TCP server that talks proper HTTP:

package main

import (
    "bufio"
    "fmt"
    "log"
    "net"
    "strings"
)

// handleConnection handles a single connected client.
func handleConnection(c net.Conn) {
    defer c.Close()

    scanner := bufio.NewScanner(c)
    // Scan first line for the request
    if !scanner.Scan() {
        log.Fatal(scanner.Err())
    }
    req := scanner.Text()
    for scanner.Scan() {
        // Scan until an empty line is seen
        if len(scanner.Text()) == 0 {
            break
        }
    }
    fmt.Println("req:", req)
    if strings.HasPrefix(req, "GET") {
        rt := fmt.Sprintf("HTTP/1.1 200 Success\r\n")
        rt += fmt.Sprintf("Connection: Close\r\n")
        rt += fmt.Sprintf("Content-Type: text/html\r\n\r\n")
        rt += fmt.Sprintf("<html><body>Nothing here</body></html>\r\n")
        c.Write([]byte(rt))
    } else {
        rt := fmt.Sprintf("HTTP/1.1 %v Error Occurred\r\n\r\n", 501)
        c.Write([]byte(rt))
    }
}

func main() {
    l, err := net.Listen("tcp", ":8080")
    if err != nil {
        log.Fatal(err)
    }
    defer l.Close()
    for {
        // Wait for a connection.
        conn, err := l.Accept()
        if err != nil {
            log.Fatal(err)
        }
        go handleConnection(conn)
    }
}
Eli Bendersky
  • 263,248
  • 89
  • 350
  • 412
  • Yes, curl actually prints some large header information, but if the buffer overflows shouldn't the read method return an error, it returns me the number of bytes processed. Also what if I don't know how big the incoming data is? How can I read that if I declare a fixed sized buffer. And curl Is getting back a response, I can see HELLO WORLD printed. – Shubhang b Jan 03 '20 at 04:35
  • @Shubhangb: the buffer doesn't overflow in Go. `Read` fills up the buffer and returns how many bytes were actually read. If the data is longer than the buffer and the buffer is of size N, it will read N bytes and return N. If you don't want to guess the maximal buffer size you could use `bytes.Buffer` with something like `io.Copy`. See https://stackoverflow.com/a/24818043/8206 for example – Eli Bendersky Jan 03 '20 at 04:40
  • I have understood the buffer part, regarding the error is it specific to curl? – Shubhang b Jan 03 '20 at 10:06
  • 2
    @Shubhangb: curl expects to talk HTTP. Your server doesn't talk HTTP, but just writes `"HELLO WORLD"` back. This is not a valid HTTP response in itself, so curl is unhappy. I'm updating my answer to show a sample of a socket server that returns proper HTTP responses. Try this one with curl - it will work fine. – Eli Bendersky Jan 03 '20 at 13:10