From 625723d7564b9f85f62d8e50bd5d3792cab02407 Mon Sep 17 00:00:00 2001
From: gjahn <gregor.jahn@bht-berlin.de>
Date: Wed, 20 Dec 2023 00:29:40 +0100
Subject: [PATCH] Add HEAD method to obtain MIME type & size w/o actually
 fetching the data

---
 README.md              |  7 +++++++
 routing/routes.go      | 17 +++++++++++++++++
 routing/routes_test.go | 12 +++++++++++-
 3 files changed, 35 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 74b1ca8..6195dd4 100644
--- a/README.md
+++ b/README.md
@@ -86,6 +86,13 @@ curl \
   http://localhost:8080/state/bar
 ```
 
+Find out MIME type and size of an entry:
+```bash
+curl \
+  -X HEAD \
+  http://localhost:8080/state/bar
+```
+
 Obtain an entry:
 ```bash
 curl \
diff --git a/routing/routes.go b/routing/routes.go
index 183609f..e69f7d5 100644
--- a/routing/routes.go
+++ b/routing/routes.go
@@ -194,6 +194,23 @@ func SetRoutes( router *f.App, config *configuration.Config, store state.Store,
     })
 
 
+    statePathGroup.Head( "/:name", func( c *f.Ctx ) error {
+        name := strings.Clone( c.Params( "name" ) )
+        existingItem, err := store.Fetch( name )
+        if err != nil {
+            return c.SendStatus( http.StatusInternalServerError )
+        }
+
+        if existingItem == nil {
+            return c.SendStatus( http.StatusNotFound )
+        }
+
+        c.Set( "Content-Type", existingItem.MimeType() )
+        c.Set( "Content-Length", fmt.Sprintf( "%d", len( existingItem.Data() ) ) )
+        return c.SendStatus( http.StatusOK )
+    })
+
+
     statePathGroup.Use( "*", func( c *f.Ctx ) error {
         if method := c.Method(); method == "OPTIONS" {
             c.Set( "Allow", "GET, PUT, DELETE, OPTIONS" )
diff --git a/routing/routes_test.go b/routing/routes_test.go
index 53e7f6a..8e88301 100644
--- a/routing/routes_test.go
+++ b/routing/routes_test.go
@@ -5,6 +5,7 @@ import (
     "fmt"
     "io"
     "os"
+    "strconv"
     "strings"
     "time"
     "math/rand"
@@ -167,7 +168,7 @@ func TestState( t *testing.T ){
 
     const statePath2 = "/state/another-test"
     const statePath2Mime = "application/octet-stream"
-    const statePath2BodySize = 64
+    const statePath2BodySize = 128
     statePath2Body := generateRandomBytes( statePath2BodySize )
 
     req := ht.NewRequest( "GET", statePath1, nil )
@@ -211,6 +212,15 @@ func TestState( t *testing.T ){
     res, _ = router.Test( req, -1 )
     assert.Equal( t, http.StatusCreated, res.StatusCode )
 
+    req = ht.NewRequest( "HEAD", statePath2, nil )
+    res, _ = router.Test( req, -1 )
+    contentLength, err := strconv.ParseInt( res.Header[ "Content-Length" ][0], 10, 64 )
+    assert.Nil( t, err )
+    assert.Equal( t, http.StatusOK, res.StatusCode )
+    assert.Equal( t, statePath2Mime, res.Header[ "Content-Type" ][0] )
+    assert.Equal( t, int64( statePath2BodySize ), contentLength )
+    assert.IsType( t, res.Body, http.NoBody )
+
     req = ht.NewRequest( "GET", statePath2, nil )
     res, _ = router.Test( req, -1 )
     bodyBytes, err := io.ReadAll( res.Body )
-- 
GitLab