@@ -10,6 +10,7 @@ import (
1010 "net/url"
1111 "os"
1212 "path/filepath"
13+ "regexp"
1314 "strings"
1415 "time"
1516
@@ -293,50 +294,79 @@ func (g *HttpGetter) Get(dst string, u *url.URL) error {
293294 // If there is a subdir component, then we download the root separately
294295 // into a temporary directory, then copy over the proper subdir.
295296 source , subDir := SourceDirSubdir (source )
296- if subDir == "" {
297- var opts []ClientOption
298297
299- // Check if the protocol was switched to one which was not configured.
300- //
298+ var opts []ClientOption
299+
300+ // Check if the protocol was switched to one which was not configured.
301+ if g .client != nil && g .client .Getters != nil {
302+ // We must first use the Detectors provided, because `X-Terraform-Get does
303+ // not necessarily return a valid URL. We can replace the source string
304+ // here, since the detectors would have been called immediately during the
305+ // next Get anyway.
306+ source , err = Detect (source , g .client .Pwd , g .client .Detectors )
307+ if err != nil {
308+ return err
309+ }
310+
311+ protocol := ""
312+ // X-Terraform-Get allows paths relative to the previous request too,
313+ // which won't have a protocol.
314+ if ! relativeGet (source ) {
315+ protocol = strings .Split (source , ":" )[0 ]
316+ }
317+
301318 // Otherwise, all default getters are allowed.
302- if g .client != nil && g .client .Getters != nil {
303- protocol := strings .Split (source , ":" )[0 ]
319+ if protocol != "" {
304320 _ , allowed := g .client .Getters [protocol ]
305321 if ! allowed {
306322 return fmt .Errorf ("no getter available for X-Terraform-Get source protocol: %q" , protocol )
307323 }
308324 }
325+ }
309326
310- // Add any getter client options.
311- if g .client != nil {
312- opts = g .client .Options
313- }
327+ // Add any getter client options.
328+ if g .client != nil {
329+ opts = g .client .Options
330+ }
314331
315- // If the client is nil, we know we're using the HttpGetter directly. In this case,
316- // we don't know exactly which protocols are configued, but we can make a good guess.
317- //
318- // This prevents all default getters from being allowed when only using the
319- // HttpGetter directly. To enable protocol switching, a client "wrapper" must
320- // be used.
321- if g .client == nil {
332+ // If the client is nil, we know we're using the HttpGetter directly. In
333+ // this case, we don't know exactly which protocols are configured, but we
334+ // can make a good guess.
335+ //
336+ // This prevents all default getters from being allowed when only using the
337+ // HttpGetter directly. To enable protocol switching, a client "wrapper" must
338+ // be used.
339+ if g .client == nil {
340+ switch {
341+ case subDir != "" :
342+ // If there's a subdirectory, we will also need a file getter to
343+ // unpack it.
344+ opts = append (opts , WithGetters (map [string ]Getter {
345+ "file" : new (FileGetter ),
346+ "http" : g ,
347+ "https" : g ,
348+ }))
349+ default :
322350 opts = append (opts , WithGetters (map [string ]Getter {
323351 "http" : g ,
324352 "https" : g ,
325353 }))
326354 }
355+ }
327356
328- // Ensure we pass along the context we constructed in this function.
329- //
330- // This is especially important to enforce a limit on X-Terraform-Get redirects
331- // which could be setup, if configured, at the top of this function.
332- opts = append (opts , WithContext (ctx ))
357+ // Ensure we pass along the context we constructed in this function.
358+ //
359+ // This is especially important to enforce a limit on X-Terraform-Get redirects
360+ // which could be setup, if configured, at the top of this function.
361+ opts = append (opts , WithContext (ctx ))
333362
334- // Note: this allows the protocol to be switched to another configured getters.
335- return Get (dst , source , opts ... )
363+ if subDir != "" {
364+ // We have a subdir, time to jump some hoops
365+ return g .getSubdir (ctx , dst , source , subDir , opts ... )
336366 }
337367
338- // We have a subdir, time to jump some hoops
339- return g . getSubdir ( ctx , dst , source , subDir )
368+ // Note: this allows the protocol to be switched to another configured getters.
369+ return Get ( dst , source , opts ... )
340370}
341371
342372// GetFile fetches the file from src and stores it at dst.
@@ -478,7 +508,7 @@ func (g *HttpGetter) GetFile(dst string, src *url.URL) error {
478508
479509// getSubdir downloads the source into the destination, but with
480510// the proper subdir.
481- func (g * HttpGetter ) getSubdir (ctx context.Context , dst , source , subDir string ) error {
511+ func (g * HttpGetter ) getSubdir (ctx context.Context , dst , source , subDir string , opts ... ClientOption ) error {
482512 // Create a temporary directory to store the full source. This has to be
483513 // a non-existent directory.
484514 td , tdcloser , err := safetemp .Dir ("" , "getter" )
@@ -487,10 +517,6 @@ func (g *HttpGetter) getSubdir(ctx context.Context, dst, source, subDir string)
487517 }
488518 defer tdcloser .Close ()
489519
490- var opts []ClientOption
491- if g .client != nil {
492- opts = g .client .Options
493- }
494520 // Download that into the given directory
495521 if err := Get (td , source , opts ... ); err != nil {
496522 return err
@@ -566,6 +592,9 @@ func (g *HttpGetter) parseMeta(ctx context.Context, r io.Reader) (string, error)
566592 }
567593}
568594
595+ // X-Terraform-Get allows paths relative to the previous request
596+ var relativeGet = regexp .MustCompile (`^\.{0,2}/` ).MatchString
597+
569598// attrValue returns the attribute value for the case-insensitive key
570599// `name', or the empty string if nothing is found.
571600func attrValue (attrs []xml.Attr , name string ) string {
0 commit comments