16
16
package create
17
17
18
18
import (
19
- "bufio"
20
- "bytes"
21
- "fmt"
22
- "io"
23
- "io/ioutil"
24
- "mime"
25
19
"net/http"
26
- "net/http/httputil"
27
- "net/url"
28
20
"path"
29
21
"path/filepath"
30
22
"strings"
@@ -36,13 +28,8 @@ import (
36
28
37
29
"github.com/gohugoio/hugo/cache/filecache"
38
30
"github.com/gohugoio/hugo/common/hugio"
39
- "github.com/gohugoio/hugo/common/maps"
40
- "github.com/gohugoio/hugo/common/types"
41
- "github.com/gohugoio/hugo/helpers"
42
31
"github.com/gohugoio/hugo/resources"
43
32
"github.com/gohugoio/hugo/resources/resource"
44
-
45
- "github.com/pkg/errors"
46
33
)
47
34
48
35
// Client contains methods to create Resource objects.
@@ -150,203 +137,3 @@ func (c *Client) FromString(targetPath, content string) (resource.Resource, erro
150
137
})
151
138
})
152
139
}
153
-
154
- // FromRemote expects one or n-parts of a URL to a resource
155
- // If you provide multiple parts they will be joined together to the final URL.
156
- func (c * Client ) FromRemote (uri string , options map [string ]interface {}) (resource.Resource , error ) {
157
- if err := c .validateFromRemoteArgs (uri , options ); err != nil {
158
- return nil , err
159
- }
160
- rURL , err := url .Parse (uri )
161
- if err != nil {
162
- return nil , errors .Wrapf (err , "failed to parse URL for resource %s" , uri )
163
- }
164
-
165
- resourceID := helpers .HashString (uri , options )
166
-
167
- _ , httpResponse , err := c .cacheGetResource .GetOrCreate (resourceID , func () (io.ReadCloser , error ) {
168
- method , reqBody , err := getMethodAndBody (options )
169
- if err != nil {
170
- return nil , errors .Wrapf (err , "failed to get method or body for resource %s" , uri )
171
- }
172
-
173
- req , err := http .NewRequest (method , uri , reqBody )
174
- if err != nil {
175
- return nil , errors .Wrapf (err , "failed to create request for resource %s" , uri )
176
- }
177
- addDefaultHeaders (req )
178
-
179
- if _ , ok := options ["headers" ]; ok {
180
- headers , err := maps .ToStringMapE (options ["headers" ])
181
- if err != nil {
182
- return nil , errors .Wrapf (err , "failed to parse request headers for resource %s" , uri )
183
- }
184
- addUserProvidedHeaders (headers , req )
185
- }
186
-
187
- res , err := c .httpClient .Do (req )
188
- if err != nil {
189
- return nil , err
190
- }
191
-
192
- if res .StatusCode != http .StatusNotFound {
193
- if res .StatusCode < 200 || res .StatusCode > 299 {
194
- return nil , errors .Errorf ("failed to retrieve remote resource: %s" , http .StatusText (res .StatusCode ))
195
- }
196
- }
197
-
198
- httpResponse , err := httputil .DumpResponse (res , true )
199
- if err != nil {
200
- return nil , err
201
- }
202
-
203
- return hugio .ToReadCloser (bytes .NewReader (httpResponse )), nil
204
- })
205
- if err != nil {
206
- return nil , err
207
- }
208
- defer httpResponse .Close ()
209
-
210
- res , err := http .ReadResponse (bufio .NewReader (httpResponse ), nil )
211
- if err != nil {
212
- return nil , err
213
- }
214
-
215
- if res .StatusCode == http .StatusNotFound {
216
- // Not found. This matches how looksup for local resources work.
217
- return nil , nil
218
- }
219
-
220
- body , err := ioutil .ReadAll (res .Body )
221
- if err != nil {
222
- return nil , errors .Wrapf (err , "failed to read remote resource %s" , uri )
223
- }
224
-
225
- filename := path .Base (rURL .Path )
226
- if _ , params , _ := mime .ParseMediaType (res .Header .Get ("Content-Disposition" )); params != nil {
227
- if _ , ok := params ["filename" ]; ok {
228
- filename = params ["filename" ]
229
- }
230
- }
231
-
232
- var extension string
233
- if arr , _ := mime .ExtensionsByType (res .Header .Get ("Content-Type" )); len (arr ) == 1 {
234
- extension = arr [0 ]
235
- }
236
-
237
- // If extension was not determined by header, look for a file extention
238
- if extension == "" {
239
- if ext := path .Ext (filename ); ext != "" {
240
- extension = ext
241
- }
242
- }
243
-
244
- // If extension was not determined by header or file extention, try using content itself
245
- if extension == "" {
246
- if ct := http .DetectContentType (body ); ct != "application/octet-stream" {
247
- if ct == "image/jpeg" {
248
- extension = ".jpg"
249
- } else if arr , _ := mime .ExtensionsByType (ct ); arr != nil {
250
- extension = arr [0 ]
251
- }
252
- }
253
- }
254
-
255
- resourceID = filename [:len (filename )- len (path .Ext (filename ))] + "_" + resourceID + extension
256
-
257
- return c .rs .New (
258
- resources.ResourceSourceDescriptor {
259
- LazyPublish : true ,
260
- OpenReadSeekCloser : func () (hugio.ReadSeekCloser , error ) {
261
- return hugio .NewReadSeekerNoOpCloser (bytes .NewReader (body )), nil
262
- },
263
- RelTargetFilename : filepath .Clean (resourceID ),
264
- })
265
-
266
- }
267
-
268
- func (c * Client ) validateFromRemoteArgs (uri string , options map [string ]interface {}) error {
269
- if err := c .rs .ExecHelper .Sec ().CheckAllowedHTTPURL (uri ); err != nil {
270
- return err
271
- }
272
-
273
- if method , ok := options ["method" ].(string ); ok {
274
- if err := c .rs .ExecHelper .Sec ().CheckAllowedHTTPMethod (method ); err != nil {
275
- return err
276
- }
277
- }
278
- return nil
279
- }
280
-
281
- func addDefaultHeaders (req * http.Request , accepts ... string ) {
282
- for _ , accept := range accepts {
283
- if ! hasHeaderValue (req .Header , "Accept" , accept ) {
284
- req .Header .Add ("Accept" , accept )
285
- }
286
- }
287
- if ! hasHeaderKey (req .Header , "User-Agent" ) {
288
- req .Header .Add ("User-Agent" , "Hugo Static Site Generator" )
289
- }
290
- }
291
-
292
- func addUserProvidedHeaders (headers map [string ]interface {}, req * http.Request ) {
293
- if headers == nil {
294
- return
295
- }
296
- for key , val := range headers {
297
- vals := types .ToStringSlicePreserveString (val )
298
- for _ , s := range vals {
299
- req .Header .Add (key , s )
300
- }
301
- }
302
- }
303
-
304
- func hasHeaderValue (m http.Header , key , value string ) bool {
305
- var s []string
306
- var ok bool
307
-
308
- if s , ok = m [key ]; ! ok {
309
- return false
310
- }
311
-
312
- for _ , v := range s {
313
- if v == value {
314
- return true
315
- }
316
- }
317
- return false
318
- }
319
-
320
- func hasHeaderKey (m http.Header , key string ) bool {
321
- _ , ok := m [key ]
322
- return ok
323
- }
324
-
325
- func getMethodAndBody (options map [string ]interface {}) (string , io.Reader , error ) {
326
- if options == nil {
327
- return "GET" , nil , nil
328
- }
329
-
330
- if method , ok := options ["method" ].(string ); ok {
331
- method = strings .ToUpper (method )
332
- switch method {
333
- case "GET" , "DELETE" , "HEAD" , "OPTIONS" :
334
- return method , nil , nil
335
- case "POST" , "PUT" , "PATCH" :
336
- var body []byte
337
- if _ , ok := options ["body" ]; ok {
338
- switch b := options ["body" ].(type ) {
339
- case string :
340
- body = []byte (b )
341
- case []byte :
342
- body = b
343
- }
344
- }
345
- return method , bytes .NewBuffer (body ), nil
346
- }
347
-
348
- return "" , nil , fmt .Errorf ("invalid HTTP method %q" , method )
349
- }
350
-
351
- return "GET" , nil , nil
352
- }
0 commit comments