Hugo: "hugo server --port=80" gives "Error: Port 80 already in use" even when it is just "permission denied"

Created on 13 Jun 2016  路  11Comments  路  Source: gohugoio/hugo

As of Hugo v0.16, running hugo server --port=80 as an unprivileged user always returns an error message saying Error: Port 80 already in use regardless if the port is actually in use or not.

For comparison, also as an unpriviledged user, running netcat as nc -l 80 to listen on port 80 correctly returns the error nc: Permission denied.

The code in question resides in the server() function in https://github.com/spf13/hugo/blob/v0.16/commands/server.go#L123-L137:

    l, err := net.Listen("tcp", net.JoinHostPort(serverInterface, strconv.Itoa(serverPort)))
    if err == nil {
        l.Close()
    } else {
        if flagChanged(serverCmd.Flags(), "port") {
            // port set explicitly by user -- he/she probably meant it!
            return newSystemErrorF("Port %d already in use", serverPort)
        }
        jww.ERROR.Println("port", serverPort, "already in use, attempting to use an available port")
        sp, err := helpers.FindAvailablePort()
        if err != nil {
            return newSystemError("Unable to find alternative port to use:", err)
        }
        serverPort = sp.Port
    }

The fix should be simple: just need to add some checks what the error actually is instead of assuming "Can't open a port" === "The port is already in use".

Bug

All 11 comments

Fix will likely involve os.IsPermission.

listen tcp 127.0.0.1:80: bind: permission denied
Error: Port 80 already in use

Thank you for your effort, @g3wanghc!

However, as far as I know, "Port < 1024 can only be opened by root" is not a universal rule. For example, I think non-UNIX platforms like MS Windows has a different way of determining whether an unprivileged user can open a certain port or not.

Furthermore, even on Linux, there are ways to allow non-root users to listen on port < 1024, see, for example:

I just tested the setcap method, and I was happily running hugo server --port=80 as a non-root user!

So, instead of imposing artificial rules in the Hugo code, it is better to let the undelying OS tell us whether a port can be opened or not, and why. :wink:

I think the simplest and bestest (good language?) solution here is to trust the stdlib to provide us with nice error messages. So append the received error to a generic Hugo error and we should be fine. Doing magic IsPermission checks isn't very helpful when there is nothing we can do but inform.

@anthonyfok Thanks for the explanation! I forgot to take port binding rules of different OS into consideration. I will definitely look into those resources.

@g3wanghc:

bind: permission denied is not recognized by os.isPermission so its checking if the user is root instead.

I apologize for not having looked more closely into this, but I am sure there is another way to detect the reason why a port cannot be opened.

Or, simpler yet, maybe just print err instead of making up our own error message (with wrong assumption)? In that case, I guess we don't even need to look for use os.IsPermission or anything like that. ;-)

@anthonyfok Haha no problem, I shouldn't have done something so hacky.

The default messages of err are actually:

bind: permission denied vs
bind: address already in use

Like @bep suggested, we can just append the that err message to create a generic Hugo error.

Like @bep suggested, we can just append the that err message to create a generic Hugo error.

Yes! (I forgot to refresh the page so I missed @bep's message) I agree wholeheartedly with him. :-)

Is there a good way to distinguish between *net.OpError errors in goLang instead of using string comparisons?

switch err.(type) {
case *net.OpError:
    // ...
default:
    // ...
}

@anthonyfok code in c52bb4efbe40fe91e3dd7db81432702392550991

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bep picture bep  路  3Comments

tjamet picture tjamet  路  3Comments

arikroc picture arikroc  路  3Comments

vielmetti picture vielmetti  路  3Comments

moorereason picture moorereason  路  3Comments