-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #633 from oasisprotocol/mitjat/e2e-cache-http
e2e regression tests: HTTP caching proxy, new cases
- Loading branch information
Showing
31 changed files
with
10,441 additions
and
157 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,5 @@ docker/node/etc/genesis.json | |
tests/e2e/testnet/net-runner | ||
|
||
# Local workdirs | ||
cache | ||
.* | ||
*.log |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,7 +14,6 @@ tests/e2e_regression/*/actual/* | |
|
||
# Log output. | ||
**/*.log | ||
/cache | ||
/logs | ||
|
||
# API files. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package httpproxy | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"net/url" | ||
"path/filepath" | ||
"regexp" | ||
"time" | ||
|
||
"github.com/oasisprotocol/nexus/cache/kvstore" | ||
cmdCommon "github.com/oasisprotocol/nexus/cmd/common" | ||
"github.com/oasisprotocol/nexus/config" | ||
) | ||
|
||
// cachingHttpProxy is a minimal HTTP handler that caches responses; it is inteded *for testing only* | ||
// and comes with several caveats: | ||
// - The cache is never invalidated. | ||
// - All requests are forwarded to the same target host. | ||
// - Requests are cached based on the full URL, NOT the headers or the method (GET vs POST) or the request body. | ||
// - Low-likelihood errors (e.g. malformed URLs) result in panics. | ||
type cachingHttpProxy struct { | ||
client *http.Client | ||
cache kvstore.KVStore | ||
targetHost string // Must include the protocol (e.g. "http://") | ||
} | ||
|
||
var _ http.Handler = (*cachingHttpProxy)(nil) | ||
|
||
type CacheableResponse struct { | ||
StatusCode int | ||
Header http.Header | ||
Body []byte | ||
} | ||
|
||
func (p cachingHttpProxy) ServeHTTP(w http.ResponseWriter, req *http.Request) { | ||
// Rewrite the request URL to new host | ||
targetURL, _ := url.JoinPath(p.targetHost, req.URL.RequestURI()) | ||
req, err := http.NewRequestWithContext(req.Context(), req.Method, targetURL, req.Body) | ||
if err != nil { | ||
panic(fmt.Sprintf("error rewriting request to %s %s: %v", req.Method, req.URL.String(), err)) | ||
} | ||
|
||
// Get the response to the modified request (via cache) | ||
cResp, err := kvstore.GetFromCacheOrCall(p.cache, false, kvstore.CacheKey(req.URL.RequestURI()), func() (*CacheableResponse, error) { | ||
resp, err2 := p.client.Do(req) | ||
if err2 != nil { | ||
return nil, err2 | ||
} | ||
defer resp.Body.Close() | ||
body, err2 := io.ReadAll(resp.Body) | ||
if err2 != nil { | ||
return nil, fmt.Errorf("error reading response body from %s: %v", resp.Request.URL.String(), err2) | ||
} | ||
return &CacheableResponse{ | ||
StatusCode: resp.StatusCode, | ||
Header: resp.Header, | ||
Body: body, | ||
}, nil | ||
}) | ||
if err != nil { | ||
cResp = &CacheableResponse{ | ||
StatusCode: http.StatusInternalServerError, | ||
Header: http.Header{}, | ||
Body: []byte(fmt.Sprintf("error proxying request: %v", err)), | ||
} | ||
} | ||
|
||
// Write out the response to `w`. | ||
for key, values := range cResp.Header { | ||
for _, value := range values { | ||
w.Header().Add(key, value) | ||
} | ||
} | ||
w.WriteHeader(cResp.StatusCode) // 200, 404, etc | ||
if _, err = io.Copy(w, bytes.NewReader(cResp.Body)); err != nil { | ||
panic(err) | ||
} | ||
} | ||
|
||
// Creates a http.Server that proxies all requests to the target URL. | ||
// The server caches all responses in a persisted key-value store, located in an | ||
// autogenerated subdirectory of the cache root dir. | ||
func NewHttpServer(cacheCfg config.CacheConfig, proxyCfg config.HttpCachingProxyConfig) (*http.Server, error) { | ||
// Derive the cache root dir from the target URL. | ||
cleanTargetUrl := regexp.MustCompile("[^a-zA-Z0-9]+").ReplaceAllString(proxyCfg.TargetURL, "_") | ||
cachePath := filepath.Join(cacheCfg.CacheDir, cleanTargetUrl) | ||
kvStore, err := kvstore.OpenKVStore(cmdCommon.RootLogger().WithModule("caching-http-proxy"), cachePath, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
handler := cachingHttpProxy{ | ||
client: &http.Client{}, | ||
cache: kvStore, | ||
targetHost: proxyCfg.TargetURL, | ||
} | ||
|
||
return &http.Server{ | ||
Addr: proxyCfg.HostAddr, | ||
Handler: handler, | ||
ReadHeaderTimeout: time.Second, | ||
}, nil | ||
} |
Oops, something went wrong.