From 4c6164ef05d6f6ffe45a50527e24423776b49421 Mon Sep 17 00:00:00 2001
From: 6543 <6543@noreply.codeberg.org>
Date: Tue, 14 Jun 2022 18:23:34 +0200
Subject: [PATCH] Propagate ETag from gitea (#93)

close #15

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/93
---
 integration/get_test.go     | 18 ++++++++++--------
 server/gitea/client.go      |  1 +
 server/upstream/const.go    |  1 +
 server/upstream/upstream.go |  8 ++++++++
 4 files changed, 20 insertions(+), 8 deletions(-)

diff --git a/integration/get_test.go b/integration/get_test.go
index 973b049..9cb66f5 100644
--- a/integration/get_test.go
+++ b/integration/get_test.go
@@ -23,7 +23,7 @@ func TestGetRedirect(t *testing.T) {
 	if !assert.EqualValues(t, http.StatusTemporaryRedirect, resp.StatusCode) {
 		t.FailNow()
 	}
-	assert.EqualValues(t, "https://www.cabr2.de/", resp.Header["Location"][0])
+	assert.EqualValues(t, "https://www.cabr2.de/", resp.Header.Get("Location"))
 	assert.EqualValues(t, 0, getSize(resp.Body))
 }
 
@@ -35,9 +35,10 @@ func TestGetContent(t *testing.T) {
 	if !assert.EqualValues(t, http.StatusOK, resp.StatusCode) {
 		t.FailNow()
 	}
-	assert.EqualValues(t, "image/jpeg", resp.Header["Content-Type"][0])
-	assert.EqualValues(t, "124635", resp.Header["Content-Length"][0])
+	assert.EqualValues(t, "image/jpeg", resp.Header.Get("Content-Type"))
+	assert.EqualValues(t, "124635", resp.Header.Get("Content-Length"))
 	assert.EqualValues(t, 124635, getSize(resp.Body))
+	assert.Len(t, resp.Header.Get("ETag"), 42)
 
 	// specify branch
 	resp, err = getTestHTTPSClient().Get("https://momar.localhost.mock.directory:4430/pag/@master/")
@@ -45,8 +46,9 @@ func TestGetContent(t *testing.T) {
 	if !assert.EqualValues(t, http.StatusOK, resp.StatusCode) {
 		t.FailNow()
 	}
-	assert.EqualValues(t, "text/html; charset=utf-8", resp.Header["Content-Type"][0])
+	assert.EqualValues(t, "text/html; charset=utf-8", resp.Header.Get("Content-Type"))
 	assert.True(t, getSize(resp.Body) > 1000)
+	assert.Len(t, resp.Header.Get("ETag"), 42)
 }
 
 func TestCustomDomain(t *testing.T) {
@@ -56,8 +58,8 @@ func TestCustomDomain(t *testing.T) {
 	if !assert.EqualValues(t, http.StatusOK, resp.StatusCode) {
 		t.FailNow()
 	}
-	assert.EqualValues(t, "text/markdown; charset=utf-8", resp.Header["Content-Type"][0])
-	assert.EqualValues(t, "106", resp.Header["Content-Length"][0])
+	assert.EqualValues(t, "text/markdown; charset=utf-8", resp.Header.Get("Content-Type"))
+	assert.EqualValues(t, "106", resp.Header.Get("Content-Length"))
 	assert.EqualValues(t, 106, getSize(resp.Body))
 }
 
@@ -69,8 +71,8 @@ func TestGetNotFound(t *testing.T) {
 	if !assert.EqualValues(t, http.StatusNotFound, resp.StatusCode) {
 		t.FailNow()
 	}
-	assert.EqualValues(t, "text/html; charset=utf-8", resp.Header["Content-Type"][0])
-	assert.EqualValues(t, "37", resp.Header["Content-Length"][0])
+	assert.EqualValues(t, "text/html; charset=utf-8", resp.Header.Get("Content-Type"))
+	assert.EqualValues(t, "37", resp.Header.Get("Content-Length"))
 	assert.EqualValues(t, 37, getSize(resp.Body))
 }
 
diff --git a/server/gitea/client.go b/server/gitea/client.go
index 5410413..7b5d009 100644
--- a/server/gitea/client.go
+++ b/server/gitea/client.go
@@ -25,6 +25,7 @@ type Client struct {
 
 type FileResponse struct {
 	Exists   bool
+	ETag     []byte
 	MimeType string
 	Body     []byte
 }
diff --git a/server/upstream/const.go b/server/upstream/const.go
index ce76e21..247e1d1 100644
--- a/server/upstream/const.go
+++ b/server/upstream/const.go
@@ -12,6 +12,7 @@ var branchExistenceCacheTimeout = 5 * time.Minute
 
 // fileCacheTimeout specifies the timeout for the file content cache - you might want to make this quite long, depending
 // on your available memory.
+// TODO: move as option into cache interface
 var fileCacheTimeout = 5 * time.Minute
 
 // fileCacheSizeLimit limits the maximum file size that will be cached, and is set to 1 MB by default.
diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go
index da97021..7c9a035 100644
--- a/server/upstream/upstream.go
+++ b/server/upstream/upstream.go
@@ -161,6 +161,14 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaClient *gitea.Client,
 	mimeType := o.getMimeTypeByExtension()
 	ctx.Response.Header.SetContentType(mimeType)
 
+	// Set ETag
+	if cachedResponse.Exists {
+		ctx.Response.Header.SetBytesV(fasthttp.HeaderETag, cachedResponse.ETag)
+	} else if res != nil {
+		cachedResponse.ETag = res.Header.Peek(fasthttp.HeaderETag)
+		ctx.Response.Header.SetBytesV(fasthttp.HeaderETag, cachedResponse.ETag)
+	}
+
 	if ctx.Response.StatusCode() != fasthttp.StatusNotFound {
 		// Everything's okay so far
 		ctx.Response.SetStatusCode(fasthttp.StatusOK)