I am new to Golang and am trying to build a Redis server in Go. The code was working before the parsing logic with concurrency. This parsing logic is sending the response fine. Now suddenly am not getting any response, When I hit a request from the client, both server and client terminals were struck. and when I hit control +c on the client side, I get all the print statements on the server terminal. but no response on the client side.
Is something wrong with the code?
package main
import (
"bufio"
"bytes"
"fmt"
"io"
"net"
"os"
"strconv"
"strings"
)
const seperator = "\r\n"
func main() {
fmt.Println("Logs from your program will appear here!")
l, err := net.Listen("tcp", "0.0.0.0:6379")
if err != nil {
fmt.Println("Failed to bind to port 6379", err.Error())
os.Exit(1)
}
defer l.Close()
for {
conn, err := l.Accept()
if err != nil {
fmt.Println("Unable to accept the connection", err.Error())
continue
}
go handleConnection(conn)
}
}
func handleConnection(c net.Conn) {
defer c.Close()
reader := bufio.NewReader(c)
requests := parser(reader)
if len(requests) < 1 {
fmt.Println("No requests or Something Wrong with Request Format")
}
for i := 0; i < len(requests); i++ {
subRequest := requests[i]
currentCommand := subRequest[0]
if strings.ToUpper(currentCommand) == "ECHO" {
response := echoHandler(subRequest)
if response == "" {
fmt.Println("bad response")
}
fmt.Println("response", response)
_, err := c.Write([]byte(response))
if err != nil {
fmt.Println("something went wrong while repsondiong", err.Error())
}
}
}
}
func echoHandler(subrequest []string) string {
if len(subrequest) != 2 {
fmt.Println("Invalid Request Format")
return ""
}
response := encoder(subrequest[1])
return response
}
func encoder(response string) string {
res := "$"
res += strconv.Itoa(len(response))
res += seperator
res += response
res += seperator
fmt.Println(res)
return res
}
func parser(buffer *bufio.Reader) [][]string {
requests := [][]string{}
// We are parsing the request into multiple Commands
// Each command starts with *<int>, <int> represents number of parameters, and there could be multiple requests.
var buf bytes.Buffer
_, err := io.Copy(&buf, buffer)
if err != nil {
panic(err)
}
request := buf.String()
println("Complete request:", request)
//Lets get all the subrequests that are there in this main request
subRequests := strings.Split(request, "*")
println("subrequests", subRequests)
for _, subreq := range subRequests {
if subreq == "" {
continue
}
// Each command is madeup of multiple lines, we are parsing the lines here
linesOfCommand := strings.Split(subreq, "\r\n")
println("subreq", subreq)
println("linesOfCommand", linesOfCommand)
if len(linesOfCommand) < 2 {
continue
}
//First line in each command represents numberofparameters
numberOfParamters, err := strconv.Atoi(linesOfCommand[0])
if err != nil {
fmt.Println("Invalid Command, unable to read number of parameters", err.Error())
continue
}
commands := []string{}
for i := 0; i < numberOfParamters; i++ {
lineIndex := i*2 + 2
if lineIndex >= len(linesOfCommand) {
fmt.Println("Command Length Mismatch")
continue
}
commands = append(commands, linesOfCommand[lineIndex])
}
requests = append(requests, commands)
}
return requests
}
Whats wrong in the code and is there a way to write the server input handling better
This line _, err := io.Copy(&buf, buffer)
in parser()
leads to the bug.
io.Copy
copies from src to dst until either EOF is reached on src or an error occurs. Your src (buffer) is a TCP connection of which EOF reaches on its closure. This is why you get a response from handler after shutting your client down.
Solving this is quite simple: read an exact number of bytes from the connection each time.