Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

net/http/httputil: export function singleJoiningSlash #44290

Open
FabianKramm opened this issue Feb 16, 2021 · 8 comments
Open

net/http/httputil: export function singleJoiningSlash #44290

FabianKramm opened this issue Feb 16, 2021 · 8 comments
Labels
NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@FabianKramm
Copy link

FabianKramm commented Feb 16, 2021

What version of Go are you using (go version)?

$ go version
go version go1.16beta1 darwin/amd64

Does this issue reproduce with the latest release?

Yes, the function singleJoiningSlash is not exported.

func singleJoiningSlash(a, b string) string {
aslash := strings.HasSuffix(a, "/")
bslash := strings.HasPrefix(b, "/")
switch {
case aslash && bslash:
return a + b[1:]
case !aslash && !bslash:
return a + "/" + b
}
return a + b
}

What did you expect to see?

The function should be exported, since several open source projects have to copy it instead of using it directly, which is suboptimal if you already use net/http/httputil as dependency:

Openshift:
https://github.com/openshift/console/blob/master/pkg/proxy/proxy.go#L69

AWS Proxy:
https://github.com/acquia/aws-proxy/blob/master/proxy/reverseproxy.go

Chronograf:
https://github.com/influxdata/influxdb/blob/master/chronograf/server/proxy.go

@toothrot toothrot added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Feb 17, 2021
@toothrot toothrot added this to the Backlog milestone Feb 17, 2021
@toothrot
Copy link
Contributor

/cc @bradfitz @neild

@neild
Copy link
Contributor

neild commented Feb 17, 2021

which is obviously bad

This is certainly not obvious to me. Copying a small, highly-specialized function seems much better than adding a dependency.

@FabianKramm
Copy link
Author

FabianKramm commented Feb 18, 2021

@neild thanks for your quick response! In all mentioned repositories (which I only copied the first 3 I found on google and there seem to be a lot more that copy this function and import that package), this package is already a dependency, so you would not introduce a new one. But I agree, I should have mentioned this condition.

Regardless of the question if code copying of small functions is bad, I don't really see a reason why making this function publicly accessible would cause any harm, when it has a clear benefit for people that do not want to copy code and maintain it themselves.

@neild
Copy link
Contributor

neild commented Feb 18, 2021

Every piece of exported API surface comes with a cost, both for the package exporting it and for every package importing it. The costs are less obvious on the users' side, but are real: Increased API surface makes a package more difficult to use, any change to a dependency has the potential to break the code depending on it, the existence of an exported function to do something may incorrectly imply that this is a thing that should be done.

It is tempting to see an internal function in a package, see a use for it, and then say that the function should be exported because it is demonstrably useful. Following this approach causes package APIs to grow organically, not by design.

Package APIs should grow because the design suggests it, not just because the code is there.

@FabianKramm
Copy link
Author

FabianKramm commented Feb 18, 2021

@neild I definitely agree that exporting functions always bears a risk and should correspond to a packages design, however I think in this case the design of the net/http/httputil package aligns very well with what the function is doing. According to the package itself the intend is to provide HTTP utility functions, complementing the more common ones in the net/http package.

The function singleJoiningSlash is clearly a utility function for me that fits into this and is also clearly needed by the community to be consumed. It is small, highly specialized and probably will never change, so I think the risk of exposing it is assessable. I don't think that the best practice in this case should be to just copy internal package code if you already consume this package.

@toothrot
Copy link
Contributor

I agree that the function has utility, but I disagree with "probably will never change". It's already been mentioned in several issues, and I can easily see how changing some corner cases (such as for empty strings, or similar) could be convenient for the package, but detrimental or impossible if it is exported.

See #25710 as an example of some hidden complexity.

@FabianKramm
Copy link
Author

@toothrot I see thanks, I was not aware of the discussions around it. I still feel somewhat unsatisfied with this, because I see very big open source projects copying and relying on this code (including me) without any knowledge about the corner cases and hidden complexity around it and they all have to find out about this themselves.

So while I agree that exporting the function as is might be suboptimal, is there not another way to provide a well documented publicly accessible function for that functionality in this package?

@networkimprov
Copy link

cc @ianlancetaylor as this seems like a Proposal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
Development

No branches or pull requests

4 participants