Skip to content

Commit

Permalink
#741 uri cache mutex
Browse files Browse the repository at this point in the history
  • Loading branch information
grahamcrowell committed Jan 12, 2023
1 parent f0dcc53 commit 0f356df
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 0 deletions.
1 change: 1 addition & 0 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ jobs:
- run: go test -count=10 ./...
- run: go test -count=2 -covermode=atomic ./...
- run: go test -v -run TestRaceyPatternSchema -race ./...
- run: go test -v -run TestIssue741 -race ./...
env:
CGO_ENABLED: '1'
- run: git --no-pager diff --exit-code
Expand Down
43 changes: 43 additions & 0 deletions openapi3/issue741_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package openapi3

import (
"fmt"
"net/http"
"net/http/httptest"
"sync"
"testing"

"github.com/stretchr/testify/require"
)

func TestIssue741(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
body := `{"openapi":"3.0.0","info":{"title":"MyAPI","version":"0.1","description":"An API"},"paths":{},"components":{"schemas":{"Foo":{"type":"string"}}}}`
_, err := w.Write([]byte(body))
if err != nil {
panic(err)
}
}))
defer ts.Close()

rootSpec := []byte(fmt.Sprintf(
`{"openapi":"3.0.0","info":{"title":"MyAPI","version":"0.1","description":"An API"},"paths":{},"components":{"schemas":{"Bar1":{"$ref":"%s#/components/schemas/Foo"}}}}`,
ts.URL,
))

wg := &sync.WaitGroup{}
n := 10
for i := 0; i < n; i++ {
wg.Add(1)
go func() {
defer wg.Done()
loader := NewLoader()
loader.IsExternalRefsAllowed = true
doc, err := loader.LoadFromData(rootSpec)
require.NoError(t, err)
require.NotNil(t, doc)
}()
}
wg.Wait()
}
8 changes: 8 additions & 0 deletions openapi3/loader_uri_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ import (
"net/http"
"net/url"
"path/filepath"
"sync"
)

// ReadFromURIFunc defines a function which reads the contents of a resource
// located at a URI.
type ReadFromURIFunc func(loader *Loader, url *url.URL) ([]byte, error)

var uriMu = &sync.RWMutex{}

// ErrURINotSupported indicates the ReadFromURIFunc does not know how to handle a
// given URI.
var ErrURINotSupported = errors.New("unsupported URI")
Expand Down Expand Up @@ -92,12 +95,17 @@ func URIMapCache(reader ReadFromURIFunc) ReadFromURIFunc {
}
uri := location.String()
var ok bool
uriMu.RLock()
if buf, ok = cache[uri]; ok {
uriMu.RUnlock()
return
}
uriMu.RUnlock()
if buf, err = reader(loader, location); err != nil {
return
}
uriMu.Lock()
defer uriMu.Unlock()
cache[uri] = buf
return
}
Expand Down

0 comments on commit 0f356df

Please sign in to comment.