reader.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. // Copyright 2017 Google Inc. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package compiler
  15. import (
  16. "errors"
  17. "fmt"
  18. "gopkg.in/yaml.v2"
  19. "io/ioutil"
  20. "log"
  21. "net/http"
  22. "net/url"
  23. "path/filepath"
  24. "strings"
  25. )
  26. var fileCache map[string][]byte
  27. var infoCache map[string]interface{}
  28. var count int64
  29. var verboseReader = false
  30. func initializeFileCache() {
  31. if fileCache == nil {
  32. fileCache = make(map[string][]byte, 0)
  33. }
  34. }
  35. func initializeInfoCache() {
  36. if infoCache == nil {
  37. infoCache = make(map[string]interface{}, 0)
  38. }
  39. }
  40. // FetchFile gets a specified file from the local filesystem or a remote location.
  41. func FetchFile(fileurl string) ([]byte, error) {
  42. initializeFileCache()
  43. bytes, ok := fileCache[fileurl]
  44. if ok {
  45. if verboseReader {
  46. log.Printf("Cache hit %s", fileurl)
  47. }
  48. return bytes, nil
  49. }
  50. if verboseReader {
  51. log.Printf("Fetching %s", fileurl)
  52. }
  53. response, err := http.Get(fileurl)
  54. if err != nil {
  55. return nil, err
  56. }
  57. if response.StatusCode != 200 {
  58. return nil, errors.New(fmt.Sprintf("Error downloading %s: %s", fileurl, response.Status))
  59. }
  60. defer response.Body.Close()
  61. bytes, err = ioutil.ReadAll(response.Body)
  62. if err == nil {
  63. fileCache[fileurl] = bytes
  64. }
  65. return bytes, err
  66. }
  67. // ReadBytesForFile reads the bytes of a file.
  68. func ReadBytesForFile(filename string) ([]byte, error) {
  69. // is the filename a url?
  70. fileurl, _ := url.Parse(filename)
  71. if fileurl.Scheme != "" {
  72. // yes, fetch it
  73. bytes, err := FetchFile(filename)
  74. if err != nil {
  75. return nil, err
  76. }
  77. return bytes, nil
  78. }
  79. // no, it's a local filename
  80. bytes, err := ioutil.ReadFile(filename)
  81. if err != nil {
  82. return nil, err
  83. }
  84. return bytes, nil
  85. }
  86. // ReadInfoFromBytes unmarshals a file as a yaml.MapSlice.
  87. func ReadInfoFromBytes(filename string, bytes []byte) (interface{}, error) {
  88. initializeInfoCache()
  89. cachedInfo, ok := infoCache[filename]
  90. if ok {
  91. if verboseReader {
  92. log.Printf("Cache hit info for file %s", filename)
  93. }
  94. return cachedInfo, nil
  95. }
  96. if verboseReader {
  97. log.Printf("Reading info for file %s", filename)
  98. }
  99. var info yaml.MapSlice
  100. err := yaml.Unmarshal(bytes, &info)
  101. if err != nil {
  102. return nil, err
  103. }
  104. infoCache[filename] = info
  105. return info, nil
  106. }
  107. // ReadInfoForRef reads a file and return the fragment needed to resolve a $ref.
  108. func ReadInfoForRef(basefile string, ref string) (interface{}, error) {
  109. initializeInfoCache()
  110. {
  111. info, ok := infoCache[ref]
  112. if ok {
  113. if verboseReader {
  114. log.Printf("Cache hit for ref %s#%s", basefile, ref)
  115. }
  116. return info, nil
  117. }
  118. }
  119. if verboseReader {
  120. log.Printf("Reading info for ref %s#%s", basefile, ref)
  121. }
  122. count = count + 1
  123. basedir, _ := filepath.Split(basefile)
  124. parts := strings.Split(ref, "#")
  125. var filename string
  126. if parts[0] != "" {
  127. filename = basedir + parts[0]
  128. } else {
  129. filename = basefile
  130. }
  131. bytes, err := ReadBytesForFile(filename)
  132. if err != nil {
  133. return nil, err
  134. }
  135. info, err := ReadInfoFromBytes(filename, bytes)
  136. if err != nil {
  137. log.Printf("File error: %v\n", err)
  138. } else {
  139. if len(parts) > 1 {
  140. path := strings.Split(parts[1], "/")
  141. for i, key := range path {
  142. if i > 0 {
  143. m, ok := info.(yaml.MapSlice)
  144. if ok {
  145. found := false
  146. for _, section := range m {
  147. if section.Key == key {
  148. info = section.Value
  149. found = true
  150. }
  151. }
  152. if !found {
  153. infoCache[ref] = nil
  154. return nil, NewError(nil, fmt.Sprintf("could not resolve %s", ref))
  155. }
  156. }
  157. }
  158. }
  159. }
  160. }
  161. infoCache[ref] = info
  162. return info, nil
  163. }