diff --git a/get_http.go b/get_http.go index 36d38c469..24641353f 100644 --- a/get_http.go +++ b/get_http.go @@ -10,6 +10,7 @@ import ( "net/url" "os" "path/filepath" + "regexp" "strings" "time" @@ -293,50 +294,66 @@ func (g *HttpGetter) Get(dst string, u *url.URL) error { // If there is a subdir component, then we download the root separately // into a temporary directory, then copy over the proper subdir. source, subDir := SourceDirSubdir(source) - if subDir == "" { - var opts []ClientOption + if subDir != "" { + // We have a subdir, time to jump some hoops + return g.getSubdir(ctx, dst, source, subDir) + } + + var opts []ClientOption + + // Check if the protocol was switched to one which was not configured. + if g.client != nil && g.client.Getters != nil { + // We must first use the Detectors provided, because `X-Terraform-Get does + // not necessarily return a valid URL. We can replace the source string + // here, since the detectors would have been called immediately during the + // next Get anyway. + source, err = Detect(source, g.client.Pwd, g.client.Detectors) + if err != nil { + return err + } + + protocol := "" + // X-Terraform-Get allows paths relative to the previous request too, + // which won't have a protocol. + if !relativeGet(source) { + protocol = strings.Split(source, ":")[0] + } - // Check if the protocol was switched to one which was not configured. - // // Otherwise, all default getters are allowed. - if g.client != nil && g.client.Getters != nil { - protocol := strings.Split(source, ":")[0] + if protocol != "" { _, allowed := g.client.Getters[protocol] if !allowed { return fmt.Errorf("no getter available for X-Terraform-Get source protocol: %q", protocol) } } + } - // Add any getter client options. - if g.client != nil { - opts = g.client.Options - } - - // If the client is nil, we know we're using the HttpGetter directly. In this case, - // we don't know exactly which protocols are configued, but we can make a good guess. - // - // This prevents all default getters from being allowed when only using the - // HttpGetter directly. To enable protocol switching, a client "wrapper" must - // be used. - if g.client == nil { - opts = append(opts, WithGetters(map[string]Getter{ - "http": g, - "https": g, - })) - } - - // Ensure we pass along the context we constructed in this function. - // - // This is especially important to enforce a limit on X-Terraform-Get redirects - // which could be setup, if configured, at the top of this function. - opts = append(opts, WithContext(ctx)) + // Add any getter client options. + if g.client != nil { + opts = g.client.Options + } - // Note: this allows the protocol to be switched to another configured getters. - return Get(dst, source, opts...) + // If the client is nil, we know we're using the HttpGetter directly. In this case, + // we don't know exactly which protocols are configued, but we can make a good guess. + // + // This prevents all default getters from being allowed when only using the + // HttpGetter directly. To enable protocol switching, a client "wrapper" must + // be used. + if g.client == nil { + opts = append(opts, WithGetters(map[string]Getter{ + "http": g, + "https": g, + })) } - // We have a subdir, time to jump some hoops - return g.getSubdir(ctx, dst, source, subDir) + // Ensure we pass along the context we constructed in this function. + // + // This is especially important to enforce a limit on X-Terraform-Get redirects + // which could be setup, if configured, at the top of this function. + opts = append(opts, WithContext(ctx)) + + // Note: this allows the protocol to be switched to another configured getters. + return Get(dst, source, opts...) } // GetFile fetches the file from src and stores it at dst. @@ -566,6 +583,9 @@ func (g *HttpGetter) parseMeta(ctx context.Context, r io.Reader) (string, error) } } +// X-Terraform-Get allows paths relative to the previous request +var relativeGet = regexp.MustCompile(`^\.{0,2}/`).MatchString + // attrValue returns the attribute value for the case-insensitive key // `name', or the empty string if nothing is found. func attrValue(attrs []xml.Attr, name string) string {