Skip to content

Commit bdc187a

Browse files
committed
cache status header, and bypass multipart requests
1 parent dcfdd76 commit bdc187a

File tree

10 files changed

+510
-74
lines changed

10 files changed

+510
-74
lines changed

api/handlers/cache_handlers.go

Lines changed: 90 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,13 @@ import (
1313
"time"
1414
)
1515

16+
const CACHE_STATUS_BYPASS = "BYPASS"
17+
const CACHE_STATUS_HIT = "HIT"
18+
const CACHE_STATUS_MISS = "MISS"
19+
1620
func GetCacheHandler(cache *graphcache.GraphCache, cfg *config.Config) http.HandlerFunc {
1721
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
22+
1823
w.Header().Add("Content-Type", "application/json")
1924

2025
// Create a new HTTP request with the same method, URL, and body as the original request
@@ -24,17 +29,34 @@ func GetCacheHandler(cache *graphcache.GraphCache, cfg *config.Config) http.Hand
2429
http.Error(w, "Error parsing target URL", http.StatusInternalServerError)
2530
}
2631

27-
proxyReq, err := http.NewRequest(r.Method, targetURL.String(), r.Body)
28-
if err != nil {
29-
fmt.Println(err)
30-
http.Error(w, "Error creating proxy request", http.StatusInternalServerError)
31-
}
32+
// only handle if the request is of content type application/json
33+
// for all other content types, pass the request to the origin server
34+
if r.Header.Get("Content-Type") != "application/json" {
35+
proxyReq, err := CopyRequest(r, cfg.Origin)
36+
if err != nil {
37+
fmt.Println(err)
38+
http.Error(w, "Error copying request", http.StatusInternalServerError)
39+
}
3240

33-
// Copy the headers from the original request to the proxy request
34-
for name, values := range r.Header {
35-
for _, value := range values {
36-
proxyReq.Header.Add(name, value)
41+
resp, err := SendRequest(proxyReq, w, map[string]interface{}{
42+
cfg.CacheHeaderName: CACHE_STATUS_BYPASS,
43+
})
44+
if err != nil {
45+
fmt.Println(err)
46+
http.Error(w, "error sending proxy request", http.StatusInternalServerError)
3747
}
48+
defer resp.Body.Close()
49+
50+
responseBody := new(bytes.Buffer)
51+
io.Copy(responseBody, resp.Body)
52+
w.Write(responseBody.Bytes())
53+
return
54+
}
55+
56+
proxyReq, err := CopyRequest(r, targetURL.String())
57+
if err != nil {
58+
fmt.Println(err)
59+
http.Error(w, "Error copying request", http.StatusInternalServerError)
3860
}
3961

4062
requestBody, err := io.ReadAll(proxyReq.Body)
@@ -78,27 +100,15 @@ func GetCacheHandler(cache *graphcache.GraphCache, cfg *config.Config) http.Hand
78100
proxyReq.Body = io.NopCloser(bytes.NewBuffer(transformedRequest.Bytes()))
79101
proxyReq.ContentLength = -1
80102

81-
client := http.Client{}
82-
// Send the proxy request using the custom transport
83-
resp, err := client.Do(proxyReq)
84-
if err != nil || resp == nil {
85-
http.Error(w, "Error sending proxy request", http.StatusInternalServerError)
86-
103+
resp, err := SendRequest(proxyReq, w, map[string]interface{}{
104+
cfg.CacheHeaderName: CACHE_STATUS_BYPASS,
105+
})
106+
if err != nil {
107+
fmt.Println(err)
108+
http.Error(w, "error sending proxy request", http.StatusInternalServerError)
87109
}
88110
defer resp.Body.Close()
89111

90-
// Copy the headers from the proxy response to the original response
91-
for name, values := range resp.Header {
92-
if name != "Content-Length" {
93-
for _, value := range values {
94-
w.Header().Add(name, value)
95-
}
96-
}
97-
}
98-
99-
// Set the status code of the original response to the status code of the proxy response
100-
w.WriteHeader(resp.StatusCode)
101-
102112
responseBody := new(bytes.Buffer)
103113
io.Copy(responseBody, resp.Body)
104114

@@ -116,6 +126,7 @@ func GetCacheHandler(cache *graphcache.GraphCache, cfg *config.Config) http.Hand
116126
if err != nil {
117127
http.Error(w, "error removing __typename", http.StatusInternalServerError)
118128
}
129+
119130
w.Write(res.Bytes())
120131
return
121132
}
@@ -133,32 +144,23 @@ func GetCacheHandler(cache *graphcache.GraphCache, cfg *config.Config) http.Hand
133144
if err != nil {
134145
http.Error(w, "error removing __typename", http.StatusInternalServerError)
135146
}
147+
w.Header().Add(cfg.CacheHeaderName, CACHE_STATUS_HIT)
136148
w.Write(res.Bytes())
137149
return
138150
}
139151

140152
proxyReq.Body = io.NopCloser(bytes.NewBuffer(transformedRequest.Bytes()))
141153
proxyReq.ContentLength = -1
142154

143-
client := http.Client{}
144-
145-
// Send the proxy request using the custom transport
146-
resp, err := client.Do(proxyReq)
155+
resp, err := SendRequest(proxyReq, w, map[string]interface{}{
156+
cfg.CacheHeaderName: CACHE_STATUS_MISS,
157+
})
147158
if err != nil {
148159
fmt.Println(err)
149160
http.Error(w, "error sending proxy request", http.StatusInternalServerError)
150161
}
151162
defer resp.Body.Close()
152163

153-
// copy the headers from the proxy response to the original response
154-
for name, values := range resp.Header {
155-
if name != "Content-Length" { // copy all headers except Content-Length
156-
for _, value := range values {
157-
w.Header().Add(name, value)
158-
}
159-
}
160-
}
161-
162164
responseBody := new(bytes.Buffer)
163165
io.Copy(responseBody, resp.Body)
164166

@@ -211,7 +213,54 @@ func GetCacheHandler(cache *graphcache.GraphCache, cfg *config.Config) http.Hand
211213
if err != nil {
212214
http.Error(w, "error removing __typename", http.StatusInternalServerError)
213215
}
216+
214217
w.Write(res.Bytes())
215-
w.Header().Add("graphql_cache", "miss")
216218
})
217219
}
220+
221+
func CopyRequest(r *http.Request, targetURL string) (*http.Request, error) {
222+
proxyReq, err := http.NewRequest(r.Method, targetURL, r.Body)
223+
if err != nil {
224+
fmt.Println(err)
225+
// http.Error(w, "Error creating proxy request", http.StatusInternalServerError)
226+
return nil, err
227+
}
228+
229+
// Copy the headers from the original request to the proxy request
230+
for name, values := range r.Header {
231+
for _, value := range values {
232+
proxyReq.Header.Add(name, value)
233+
}
234+
}
235+
236+
proxyReq.ContentLength = -1
237+
238+
return proxyReq, nil
239+
}
240+
241+
func SendRequest(proxyReq *http.Request, w http.ResponseWriter, headers map[string]interface{}) (*http.Response, error) {
242+
client := http.Client{}
243+
// Send the proxy request using the custom transport
244+
resp, err := client.Do(proxyReq)
245+
if err != nil || resp == nil {
246+
http.Error(w, "Error sending proxy request", http.StatusInternalServerError)
247+
return resp, err
248+
}
249+
250+
// Copy the headers from the proxy response to the original response
251+
for name, values := range resp.Header {
252+
if name != "Content-Length" {
253+
for _, value := range values {
254+
w.Header().Add(name, value)
255+
}
256+
}
257+
}
258+
259+
for key, value := range headers {
260+
w.Header().Add(key, fmt.Sprintf("%v", value))
261+
}
262+
263+
w.WriteHeader(resp.StatusCode)
264+
265+
return resp, nil
266+
}

config/config.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ type Config struct {
2323
Host string `toml:"host"`
2424
Port int `toml:"port"`
2525
} `toml:"redis"`
26+
CacheHeaderName string `toml:"cache_header_name"`
2627
}
2728

2829
const CONFIG_FILE = "./config.toml"
@@ -74,11 +75,9 @@ func NewConfig() *Config {
7475
cfg.Handlers.HealthPath = "/health"
7576
}
7677

77-
return &Config{
78-
Origin: cfg.Origin,
79-
Port: cfg.Port,
80-
CacheBackend: cfg.CacheBackend,
81-
Handlers: cfg.Handlers,
82-
Redis: cfg.Redis,
78+
if cfg.CacheHeaderName == "" {
79+
cfg.CacheHeaderName = "x-orbit-cache"
8380
}
81+
82+
return &cfg
8483
}

graphcache/graphcache.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ func (gc *GraphCache) deleteTypename(data interface{}) interface{} {
103103

104104
func (gc *GraphCache) ParseASTBuildResponse(queryPrefix string, astQuery *ast.QueryDocument, requestBody GraphQLRequest) (interface{}, error) {
105105

106+
if len(astQuery.Operations) == 0 {
107+
return nil, errors.New("no operations found in query")
108+
}
109+
106110
queryDoc := astQuery.Operations[0]
107111

108112
reqVariables := requestBody.Variables

logo.svg

Lines changed: 5 additions & 0 deletions
Loading

main_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,3 +263,14 @@ func TestWithTodoOperations(t *testing.T) {
263263
assert.NotNil(t, todoResponse.User)
264264
assert.Equal(t, userID, todoResponse.User.ID.String())
265265
}
266+
267+
func TestMultipartRequestsBypass(t *testing.T) {
268+
client := test_client.NewGraphQLClient("http://localhost:9090/graphql", "http://localhost:9090")
269+
270+
resp, headers, _, err := client.UploadImage("./logo.svg")
271+
assert.Nil(t, err)
272+
assert.NotNil(t, resp)
273+
assert.NotNil(t, headers)
274+
275+
assert.Equal(t, "BYPASS", headers["X-Orbit-Cache"])
276+
}

0 commit comments

Comments
 (0)