Skip to content

escaped parameters fail to generate the correct url path when a base path is present. #1083

@james-lawrence

Description

@james-lawrence

If a path parameter needs to be escaped the runtime code generates an invalid path.

BasePath = "/foo"
Path = "/v1/{param}"

I suggest adding a transport to the HTTP client that dumps the request.

import openapiclient "github.com/go-openapi/runtime/client"
c := generatedClient.New(
		openapiclient.NewWithClient("localhost", "/foo", []string{"http"}, http.DefaultClient),
		nil,
)

// all of the following fail.
// passing in a raw string will invoke an invalid path: /foo/v1/param/value
c.ResourceMethod((&ResourceMethodParameters{}).WithParam("param/value"))
// passing in an escaped string will invoke an invalid path: /foo/v1/param/value
c.ResourceMethod((&ResourceMethodParameters{}).WithParam(url.PathEscape("param/value")))

These fail because of the runtime/client's interaction with URL.
The http client uses URL.EscapedPath() to determine what path to use.

func (u *URL) EscapedPath() string {
	if u.RawPath != "" && validEncodedPath(u.RawPath) {
		p, err := unescape(u.RawPath, encodePath)
		if err == nil && p == u.Path {
			return u.RawPath
		}
	}
	if u.Path == "*" {
		return "*" // don't escape (Issue 11202)
	}
	return escape(u.Path, encodePath)
}

client.Runtime.Submit has the following code:

func (r *Runtime) Submit(operation *runtime.ClientOperation) (interface{}, error) {
        // ...
	if req.URL.Path != "" && req.URL.Path != "/" && req.URL.Path[len(req.URL.Path)-1] == '/' {
		reinstateSlash = true
	}
	req.URL.Path = path.Join(r.BasePath, req.URL.Path)
	if reinstateSlash {
		req.URL.Path = req.URL.Path + "/"
	}
        // ...
}

setting req.URL.Path = path.Join(r.BasePath, req.URL.Path) causes url.EscapedPath() to return
req.URL.Path unescaped, since escape(u.Path, encodePath) only escapes the ?, it assumes the fragments have already been properly escaped.

the fix is to properly set both the RawPath and the Path in the submit function.

	req.URL.Path = path.Join(r.BasePath, req.URL.Path)
        req.URL.RawPath = path.Join(r.BasePath, req.URL.RawPath)

I also believe that func (r *request) BuildHTTP(...) (*http.Request, error) { should properly escape the path parameters, but that breaks current behaviour.

	// create http request
	path := r.pathPattern
	for k, v := range r.pathParams {
		log.Println("replacing", "{"+k+"}", "with", v, "in", path)
		path = strings.Replace(path, "{"+k+"}", url.PathEscape(v), -1)
	}

Environment

swagger version: master, d507f49
go version: 1.8.1
OS: linux

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions