requests does not remove the default port from response url

When requests is asked to open an url like `https://httpbin.org:443`, be it with a direct `requests.get` call or by a site redirection, the port part should be removed since it's the default one.

## Expected Result

`response.url` should not contain a port if it's the default one

## Actual Result

`response.url` contains the default port if the request url contained it

## Reproduction Steps

```python
import requests
print(requests.get('https://httpbin.org:443/get').url)
```

## System Information

    $ python -m requests.help

```
{
  "chardet": {
    "version": "3.0.4"
  }, 
  "cryptography": {
    "version": "1.8.1"
  }, 
  "idna": {
    "version": ""
  }, 
  "implementation": {
    "name": "CPython", 
    "version": "2.7.13"
  }, 
  "platform": {
    "release": "4.12.0-1-amd64", 
    "system": "Linux"
  }, 
  "pyOpenSSL": {
    "openssl_version": "100020bf", 
    "version": "16.2.0"
  }, 
  "requests": {
    "version": "2.18.4"
  }, 
  "system_ssl": {
    "version": "1010006f"
  }, 
  "urllib3": {
    "version": "1.21.1"
  }, 
  "using_pyopenssl": true
}
```

Why should we strip this from the URL? The URL is for the user only, it's not sent to the server, so what's the reason for stripping it?

- For end-users, real browsers already do strip it as soon as the user enters it in the URL bar.
- Developers don't need to handle 2 different format for the same thing (using the default port). If the port is present, IMHO it should safely be assumed that it's because the port is not the default one.
- The domain part is already normalized in some way, for example the hostname is lowercased, why not removing the default port as part of the normalization?

> For end-users, real browsers already do strip it as soon as the user enters it in the URL bar.

Ah, but Requests is not a browser. We do emulate the browser's behaviour in some cases, but not in all.

> Developers don't need to handle 2 different format for the same thing (using the default port). If the port is present, IMHO it should safely be assumed that it's because the port is not the default one.

Yes, they do. Ports may be present in any URL. If they're not handling ports, they're not doing their job.

> The domain part is already normalized in some way, for example the hostname is lowercased, why not removing the default port as part of the normalization?

I forget under which conditions it is necessary, but domain normalization isn't cosmetic, it's for a real technical reason. In this case, you're asking for a purely cosmetic change. That's no reason to break backwards compatibility or behaviour.

> Yes, they do. Ports may be present in any URL. If they're not handling ports, they're not doing their job.

requests is a helper library, its job is to minimize the burden of its users (developers) by avoiding to expose unnecessary low-level details. For example the library follows redirections by default to avoid its users having to handle the redirection by themselves (that's probably not the best example of a "low-level" thing but I didn't really look for another)

It's not about "not handling ports", users of requests probably don't even care about ports (I don't). It's about a library returning the URL it just made a request to, but that doesn't return consistent URLs, sometimes with ":443", sometimes without (this library is requests). When the port is not the default one, it's mandatory to return it, but when it is, it's better to be consistent, never return it or always (pick one).

> That's no reason to break backwards compatibility or behaviour.

Breaking compatibility is about making a new behavior that isn't _compatible_, not that isn't _identical_. In this case, the 2 URLs are compatible, just one of them is not pretty, and the result is not always consistent.

> Breaking compatibility is about making a new behavior that isn't compatible, not that isn't identical. In this case, the 2 URLs are compatible, just one of them is not pretty, and the result is not always consistent.

If we accept that dealing with the variation in URLs is an inconvenience then necessarily this is a compatibility break. Right now there is a clear logic: if we were given a URL with a port we return a URL with a port. If we change that logic then we will inevitably break people's code.

I'm fine with changing this in 3.0, but not before then. Along with that change we may want to consider moving to a better URL handling library (e.g. hyperlink) to help manage this complexity.

> For example the library follows redirections by default to avoid its users having to handle the redirection by themselves (that's probably not the best example of a "low-level" thing but I didn't really look for another)

Redirection isn't inherently low-level, especially since we give the user a way to tinker with redirection (by turning it off and handling it themselves).

I think a better low-level detail that we manage (but don't really since urllib3 handles this for us) is DNS lookup and trying different IP addresses until we find one that works. But then you have two wildly different ideas of low-level.

> users of requests probably don't even care about ports 

No. Some users of requests definitely do care about ports. Maybe something like 80% don't.

> but that doesn't return consistent URLs, sometimes with ":443", sometimes without (this library is requests)

It does consistently return URLs if the URLs used in the request are consistent.

> When the port is not the default one, it's mandatory to return it, but when it is, it's better to be consistent, never return it or always (pick one).

Again, we're consistent based on input.

> just one of them is not pretty

And I think this is the real heart of the problem. You're using the `.url` attribute of the Response object for some kind of display to some other user and you don't want to deal with that user's input first to get your consistent output. You feel as though Requests should handle everything for you and exonerate you from having to deal with user inputs. That is fundamentally not requests' job. Requests' job is to handle HTTP and the semantics thereof. Our job is not to prettify URLs for our users. Our users are fundamentally technical and should be able to handle exactly what you want to handle themselves. Users of Browsers are fundamentally different from Requests Users. The latter are a subset of the former, but they're the subset who know how and when to handle ports.

I really don't think this is something we need to change for 3.0 and I'm not convinced it's something we want to either.

> you don't want to deal with that user's input first

As I said in the very first message, it doesn't necessarily come from user input. I took an example URL since it was easier to demonstrate, but it can happen in a redirection, where user input isn't involved, and there it's even worse since the hostname isn't lowercased, so your "technical reason for lowercasing" doesn't really hold:

```
>>> requests.get('https://httpbin.org/redirect-to?url=https%3A%2F%2FHTtpbin.org:443%2Fget').url
'https://HTtpbin.org:443/get'
```

What is also ironic is that you already do this normalization is some part of the code, since the `Host` port **never contains the default port** even if put explicitly in the URL given as argument to `requests.get`...

```
% socat TCP-LISTEN:1234 STDIO | grep Host
Host: localhost:1234

Meanwhile:
requests.get('http://localhost:1234')

And:

# socat TCP-LISTEN:80 STDIO | grep Host
Host: localhost

Meanwhile:
requests.get('http://localhost:80')
```

That normalisation is required per the HTTP specification. None of us is saying we aren't technically capable of doing it, only that we don't currently do it and aren't convinced it's important for `response.url`.

I would like to weigh in on this issue with a use case I have that could help this discussion. 

We are performing a request to an url that is redirected by the server (302 or 301 redirect). For some business logic we need to determine the url and the port the request came from. 

In our use case this could be helpfull

    >>> import requests
    >>> r = requests.get('http://my-url.com')
    >>> r.url
    'https://another-url.com
    >>> r.port
    443

For me it made sense that since the response contains the url we were redirected to, I could also access a port attribute. In this (contrived) example it makes sense to parse the URL and figure out the port. But what if the server redirects to https on port 8080 for example and I needed to figure out port 8080? 

Suggestion: add a port attribute to the response attribute and normalize the response url. 

@MichaelAnckaert I think the problem in general is that `Response` instances shouldn't have ever had a URL. I understand it's convenient but semantically a bit wobbly. The *request* is made to a URL, and in fact `r.request.url` exists and is the same as `r.url`. If a port attribute were to make sense somewhere, then it would be on the `PreparedRequest` object, but even that's not exactly the best place to have it. In general, I really don't quite understand the issue with using `urlparse` or one of the several other libraries that work significantly better to retrieve the port. I really don't think this issue or your specific use case are appropriate changes for Requests.

intended behavior