Skip to content

Commit 470f2dd

Browse files
committed
Decompress Brotli files on disk
Fallback Brotli 404s to uncompressed data paths Clear If-Modified-Since on requests to force cache update
1 parent 5198823 commit 470f2dd

File tree

4 files changed

+126
-37
lines changed

4 files changed

+126
-37
lines changed

go.mod

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
module github.com/FlashpointProject/FlashpointGameServer
22

3-
go 1.18
3+
go 1.20
44

55
require (
66
github.com/FlashpointProject/zipfs v0.0.0-20231103125305-60ba85d03bce
77
github.com/elazarl/goproxy v0.0.0-20231031074852-3ec07828be7a
88
)
99

10-
require golang.org/x/text v0.13.0 // indirect
10+
require (
11+
github.com/andybalholm/brotli v1.1.1
12+
golang.org/x/text v0.13.0 // indirect
13+
)

go.sum

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
github.com/FlashpointProject/zipfs v0.0.0-20231103125305-60ba85d03bce h1:4RwhE31KQcg9LbHZNuPZQedi19mKfYlKv6gCdWAx9rM=
22
github.com/FlashpointProject/zipfs v0.0.0-20231103125305-60ba85d03bce/go.mod h1:ksRykBq1p588rPM2QwZXLvuB/HFiBqlHe3sJhTXhNIo=
3+
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
4+
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
5+
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
6+
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
37
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
48
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
59
github.com/elazarl/goproxy v0.0.0-20231031074852-3ec07828be7a h1:r72lWG/xCv9MLpRTss5BQVHDURXaaD6OwS2HkI5/+Ls=
@@ -12,6 +16,7 @@ github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYe
1216
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
1317
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
1418
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
19+
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
1520
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
1621
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
1722
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=

go.work.sum

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
2+
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
3+
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=

main.go

Lines changed: 113 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"time"
2121

2222
"github.com/FlashpointProject/zipfs"
23+
"github.com/andybalholm/brotli"
2324
"github.com/elazarl/goproxy"
2425
)
2526

@@ -47,7 +48,6 @@ type ServerSettings struct {
4748
ExtScriptTypes []string `json:"extScriptTypes"`
4849
ExtIndexTypes []string `json:"extIndexTypes"`
4950
ExtGzippeddTypes []string `json:"extGzippedTypes"`
50-
ExtBrotliTypes []string `json:"extBrotliTypes"`
5151
ExtMimeTypes map[string]string `json:"extMimeTypes"`
5252
}
5353

@@ -162,8 +162,6 @@ func setContentType(r *http.Request, resp *http.Response) {
162162
return
163163
}
164164

165-
resp.Header.Del("Content-Type")
166-
167165
fnameHeader := resp.Header.Get("ZIPSVR_FILENAME")
168166
fname := strings.TrimSpace(r.URL.Path)
169167
rext := strings.ToLower(filepath.Ext(fnameHeader))
@@ -184,18 +182,15 @@ func setContentType(r *http.Request, resp *http.Response) {
184182
}
185183

186184
// Check if brotli compressed
187-
for _, element := range serverSettings.ExtBrotliTypes {
188-
if element == e {
189-
resp.Header.Set("Content-Encoding", "br")
190-
// Try and use the previous ending for content type, if exists
191-
prevExt := filepath.Ext(strings.TrimSuffix(strings.ToLower(fname), "."+e))
192-
if prevExt != "" {
193-
prevMime := serverSettings.ExtMimeTypes[prevExt[1:]]
194-
if prevMime != "" {
195-
mime = prevMime
196-
}
185+
if e == "br" {
186+
resp.Header.Set("Content-Encoding", "br")
187+
// Try and use the previous ending for content type, if exists
188+
prevExt := filepath.Ext(strings.TrimSuffix(strings.ToLower(fname), "."+e))
189+
if prevExt != "" {
190+
prevMime := serverSettings.ExtMimeTypes[prevExt[1:]]
191+
if prevMime != "" {
192+
mime = prevMime
197193
}
198-
break
199194
}
200195
}
201196
}
@@ -214,34 +209,27 @@ func setContentType(r *http.Request, resp *http.Response) {
214209
}
215210

216211
// Check if brotli compressed
217-
for _, element := range serverSettings.ExtBrotliTypes {
218-
if element == e {
219-
resp.Header.Set("Content-Encoding", "br")
220-
// Try and use the previous ending for content type, if exists
221-
prevExt := filepath.Ext(strings.TrimSuffix(strings.ToLower(fnameHeader), "."+e))
222-
if prevExt != "" {
223-
prevMime := serverSettings.ExtMimeTypes[prevExt[1:]]
224-
if prevMime != "" {
225-
mime = prevMime
226-
}
212+
if e == "br" {
213+
resp.Header.Set("Content-Encoding", "br")
214+
// Try and use the previous ending for content type, if exists
215+
prevExt := filepath.Ext(strings.TrimSuffix(strings.ToLower(fnameHeader), "."+e))
216+
if prevExt != "" {
217+
prevMime := serverSettings.ExtMimeTypes[prevExt[1:]]
218+
if prevMime != "" {
219+
mime = prevMime
227220
}
228-
break
229221
}
230222
}
231223
}
232224

233225
// Set content type header
234226
if mime != "" {
227+
resp.Header.Del("Content-Type")
235228
resp.Header.Set("Content-Type", mime)
236229
}
237230
}
238231

239-
func handleRequest(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
240-
// Remove port from host if exists (old apps don't clean it before sending requests?)
241-
r.URL.Host = strings.Split(r.URL.Host, ":")[0]
242-
// Clone the body into both requests by reading and making 2 new readers
243-
contents, _ := io.ReadAll(r.Body)
244-
232+
func getProxyResp(r *http.Request, contents []byte) (*http.Response, error) {
245233
// Copy the original request
246234
gamezipRequest := &http.Request{
247235
Method: r.Method,
@@ -266,10 +254,10 @@ func handleRequest(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http
266254

267255
proxyResp, err := client.Do(proxyReq)
268256
if err != nil {
269-
fmt.Printf("UNHANDLED GAMEZIP SERVER ERROR: %s\n", err)
257+
return nil, err
270258
}
271259
if proxyResp.StatusCode >= 500 {
272-
fmt.Println("Gamezip Server Error: ", proxyResp.StatusCode)
260+
return proxyResp, nil
273261
}
274262

275263
// Check Legacy
@@ -318,12 +306,102 @@ func handleRequest(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http
318306
proxyResp = legacyResp
319307
} else {
320308
fmt.Printf("UNHANDLED EXTERNAL LEGACY ERROR: %s\n", err)
309+
return nil, err
310+
}
311+
}
312+
}
313+
314+
return proxyResp, nil
315+
}
316+
317+
func handleRequest(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
318+
// Remove port from host if exists (old apps don't clean it before sending requests?)
319+
r.URL.Host = strings.Split(r.URL.Host, ":")[0]
320+
r.Header.Del("If-Modified-Since")
321+
// Clone the body into both requests by reading and making 2 new readers
322+
contents, _ := io.ReadAll(r.Body)
323+
324+
proxyResp, err := getProxyResp(r, contents)
325+
if err != nil {
326+
fmt.Printf("UNHANDLED GAMEZIP SERVER ERROR: %s\n", err)
327+
return r, nil
328+
}
329+
if proxyResp.StatusCode >= 500 {
330+
fmt.Println("Gamezip Server Error: ", proxyResp.StatusCode)
331+
return r, proxyResp
332+
}
333+
334+
// Not found
335+
if proxyResp.StatusCode == 404 {
336+
// If it's a Brotli file request, check for an uncompressed file instead
337+
if strings.HasSuffix(r.URL.Path, ".br") {
338+
// Create a modified request with the path without .br extension
339+
unbrotliRequest := *r // Create a copy of the original request
340+
unbrotliRequest.URL = &url.URL{} // Create a new URL to avoid modifying the original
341+
*unbrotliRequest.URL = *r.URL // Copy all URL fields
342+
343+
// Remove the .br extension from the path
344+
unbrotliRequest.URL.Path = strings.TrimSuffix(r.URL.Path, ".br")
345+
346+
// Use the existing getProxyResp function to make the request
347+
unbrotliResp, err := getProxyResp(&unbrotliRequest, contents)
348+
if err != nil {
349+
fmt.Printf("Error requesting uncompressed file: %s\n", err)
350+
return r, proxyResp
321351
}
352+
353+
// If we found the uncompressed version, use it
354+
if unbrotliResp.StatusCode == 200 {
355+
fmt.Printf("Found uncompressed version of %s\n", r.URL.Path)
356+
return r, unbrotliResp
357+
}
358+
359+
// Close the response if we're not using it
360+
unbrotliResp.Body.Close()
322361
}
323362
}
324363

325-
// Update the content type based upon ext for now.
326-
setContentType(r, proxyResp)
364+
if proxyResp.StatusCode < 400 {
365+
setContentType(r, proxyResp)
366+
}
367+
368+
fmt.Printf("Response: Status=%d, URL=%s\n",
369+
proxyResp.StatusCode,
370+
r.URL.String())
371+
372+
// Check if content is brotli-encoded and decode it so we can respond to HTTP requests
373+
if proxyResp.StatusCode < 400 && strings.ToLower(proxyResp.Header.Get("Content-Encoding")) == "br" {
374+
// Read the brotli-encoded body
375+
brBody, err := io.ReadAll(proxyResp.Body)
376+
if err != nil {
377+
fmt.Printf("Error reading brotli-encoded body: %s\n", err)
378+
} else {
379+
// Close the original body
380+
proxyResp.Body.Close()
381+
382+
// Decode the brotli content
383+
reader := brotli.NewReader(bytes.NewReader(brBody))
384+
decodedBody, err := io.ReadAll(reader)
385+
if err != nil {
386+
fmt.Printf("Error decoding brotli content: %s\n", err)
387+
// Restore original body if decoding fails
388+
proxyResp.Body = io.NopCloser(bytes.NewReader(brBody))
389+
} else {
390+
// Replace the body with decoded content
391+
proxyResp.Body = io.NopCloser(bytes.NewReader(decodedBody))
392+
// Remove the Content-Encoding header
393+
proxyResp.Header.Del("Content-Encoding")
394+
395+
// Update Content-Length if it exists
396+
if proxyResp.ContentLength > 0 {
397+
proxyResp.ContentLength = int64(len(decodedBody))
398+
proxyResp.Header.Set("Content-Length", fmt.Sprintf("%d", len(decodedBody)))
399+
}
400+
401+
fmt.Println("Decoded brotli content for HTTP request")
402+
}
403+
}
404+
}
327405

328406
// Add extra headers
329407
proxyResp.Header.Set("Access-Control-Allow-Headers", "*")

0 commit comments

Comments
 (0)