diff --git a/app/compression.go b/app/compression.go index 93634e5..1988028 100644 --- a/app/compression.go +++ b/app/compression.go @@ -5,7 +5,7 @@ import ( "compress/gzip" ) -func gzipCompress(data []byte) *bytes.Buffer { +func gzipCompress(data []byte) []byte { buf := &bytes.Buffer{} gz := gzip.NewWriter(buf) if _, err := gz.Write(data); err != nil { @@ -14,7 +14,5 @@ func gzipCompress(data []byte) *bytes.Buffer { if err := gz.Close(); err != nil { panic(err) } - - // fmt.Println("Hexadecimal Representation:", hex.EncodeToString(buf.Bytes())) - return buf + return buf.Bytes() } diff --git a/app/file.go b/app/file.go index facf823..09a7884 100644 --- a/app/file.go +++ b/app/file.go @@ -9,8 +9,8 @@ import ( var DIR string = "" func getFilepath(filename string) string { + // Set the DIR if not already set if DIR == "" { - if len(os.Args) != 3 { log.Fatal("Not enough args") } diff --git a/app/http.go b/app/http.go index 5adaca6..b806268 100644 --- a/app/http.go +++ b/app/http.go @@ -13,10 +13,6 @@ const ( HTTPDelimiter = "\r\n" ) -// type Header struct { -// Name string -// Value string -// } type Request struct { Method string Path string @@ -64,37 +60,44 @@ type Routes struct { } func Respond(conn net.Conn, req Request, res Response) { + // Create headers if not existent if res.Headers == nil { res.Headers = make(map[string]string) } - // isGzip := false - isGzip := strings.Contains(req.Headers["Accept-Encoding"], "gzip") - // if isGzip { - // res.Headers["Content-Encoding"] = "gzip" - // } - + // Base response line fmt.Fprintf(conn, "%s %d %s%s", res.Version, res.Code.Code, res.Code.Message, HTTPDelimiter) + var body []byte if res.Body != "" { body = []byte(res.Body) } else { body = res.BodyRaw } - if isGzip && len(body) > 0 { - res.Headers["Content-Encoding"] = "gzip" - body = gzipCompress(body).Bytes() - } bodySize := len(body) + + // Check if gzip is accepted and encode the body + isGzip := strings.Contains(req.Headers["Accept-Encoding"], "gzip") + if isGzip && bodySize > 0 { + body = gzipCompress(body) + bodySize = len(body) + res.Headers["Content-Encoding"] = "gzip" + } + + // Set the size of the content if bodySize > 0 { res.Headers["Content-Length"] = strconv.Itoa(bodySize) } + // Write headers for header, value := range res.Headers { fmt.Fprintf(conn, "%s: %s%s", header, value, HTTPDelimiter) } + // Delimiter for the body, always present fmt.Fprint(conn, HTTPDelimiter) + + // Write body if available if bodySize > 0 { conn.Write(body) } @@ -112,6 +115,7 @@ func parseRequest(conn net.Conn) (Request, bool) { request := Request{Headers: map[string]string{}} isBody := false for i, part := range parts { + // The first part is always the basic information if i == 0 { head := strings.Split(part, " ") if len(head) != 3 { @@ -123,6 +127,7 @@ func parseRequest(conn net.Conn) (Request, bool) { continue } + // Body if isBody { request.Body = part break @@ -130,6 +135,7 @@ func parseRequest(conn net.Conn) (Request, bool) { // Headers if part == "" { + // If the header is "empty" it means that we have arrived at the body, and we'll skip to it isBody = true continue } diff --git a/app/routes.go b/app/routes.go index 3d0514f..6d4b8af 100644 --- a/app/routes.go +++ b/app/routes.go @@ -43,6 +43,7 @@ var routes = Routes{ }, }, + // Read file { regex: regexp.MustCompile(`^/files/([A-Za-z0-9_\-.]+)`), method: "GET", @@ -60,6 +61,7 @@ var routes = Routes{ }, }, + // Write file { regex: regexp.MustCompile(`^/files/([A-Za-z0-9_\-.]+)`), method: "POST", diff --git a/app/server.go b/app/server.go index 65a576b..dbf6fc0 100644 --- a/app/server.go +++ b/app/server.go @@ -6,17 +6,6 @@ import ( "os" ) -// type Handler = func(req Request, res Response) -// type Middleware = func(next Handler) Handler - -// var m Middleware = func(next Handler) Handler { -// return func(req Request, res Response) { -// fmt.Println("Start") -// next(req, res) -// fmt.Println("End") -// } -// } - func handleConnection(conn net.Conn, routes Routes) { defer conn.Close() @@ -28,6 +17,7 @@ func handleConnection(conn net.Conn, routes Routes) { fmt.Println(req) + // Loop over the available routes. First string, then regexp for _, route := range routes.stringRoutes { if req.Path == route.path && req.Method == route.method { Respond(conn, req, route.handler(req)) @@ -45,12 +35,11 @@ func handleConnection(conn net.Conn, routes Routes) { } } + // Catch all 404 Respond(conn, req, Response{Version: req.Version, Code: NotFound}) } func main() { - fmt.Println("Logs from your program will appear here!") - l, err := net.Listen("tcp", "0.0.0.0:4221") if err != nil { fmt.Println("Failed to bind to port 4221") diff --git a/app/server_test.go b/app/server_test.go index 96ddb70..aacdc04 100644 --- a/app/server_test.go +++ b/app/server_test.go @@ -38,8 +38,11 @@ func checkResponse(t *testing.T, res *http.Response, expected Expected) { t.Errorf(`Expected body to be "%s" but got "%s"`, expected.body, body) } + log.Println("HEADERS", res.Header) for header, value := range expected.headers { - if actual := res.Header[header][0]; actual != value { + if res.Header[header] == nil { + t.Errorf(`Expected "%s" header to be present`, header) + } else if actual := res.Header[header][0]; actual != value { t.Errorf(`Expected "%s" header to be "%s" but got "%s"`, header, value, actual) } } @@ -64,23 +67,24 @@ func TestEcho(t *testing.T) { }}) } -// func TestEchoGzip(t *testing.T) { -// input := "abc" -// req, _ := http.NewRequest("GET", fmt.Sprintf("http://localhost:4221/echo/%s", input), nil) -// req.Header.Set("Accept-Encoding", "gzip") -// client := &http.Client{} -// res, _ := client.Do(req) +func TestEchoGzip(t *testing.T) { + input := "abc" + req, _ := http.NewRequest("GET", fmt.Sprintf("http://localhost:4221/echo/%s", input), nil) + req.Header.Set("Accept-Encoding", "gzip") + client := &http.Client{} + res, _ := client.Do(req) -// checkResponse(t, res, Expected{status: 200, body: input, headers: map[string]string{ -// "Content-Length": strconv.Itoa(len(input)), -// "Content-Type": "text/plain", -// "Content-Encoding": "gzip", -// }}) -// } + checkResponse(t, res, Expected{status: 200, body: input, headers: map[string]string{ + "Content-Length": "27", // Size of gzip "abc" + "Content-Type": "text/plain", + "Content-Encoding": "gzip", + }}) +} func TestUserAgent(t *testing.T) { input := "CodeCrafters/1.0" req, _ := http.NewRequest("GET", "http://localhost:4221/user-agent", nil) + req.Header.Del("") req.Header.Set("User-Agent", input) client := &http.Client{} res, _ := client.Do(req)