@@ -431,30 +431,49 @@ def update_headers(self, headers: Optional[LooseHeaders]) -> None:
431
431
"""Update request headers."""
432
432
self .headers : CIMultiDict [str ] = CIMultiDict ()
433
433
434
- # add host
434
+ # Build the host header
435
435
if _YARL_SUPPORTS_HOST_SUBCOMPONENT :
436
- netloc = self .url .host_subcomponent
437
- assert netloc is not None
436
+ host = self .url .host_subcomponent
437
+ # host_subcomponent is None when the URL is a relative URL.
438
+ # but we know we do not have a relative URL here.
439
+ assert host is not None
438
440
else :
439
- netloc = cast (str , self .url .raw_host )
440
- if helpers .is_ipv6_address (netloc ):
441
- netloc = f"[{ netloc } ]"
442
- # See https://github.com/aio-libs/aiohttp/issues/3636.
443
- netloc = netloc .rstrip ("." )
444
- if self .url .port is not None and not self .url .is_default_port ():
445
- netloc += ":" + str (self .url .port )
446
- self .headers [hdrs .HOST ] = netloc
447
-
448
- if headers :
449
- if isinstance (headers , (dict , MultiDictProxy , MultiDict )):
450
- headers = headers .items ()
451
-
452
- for key , value in headers : # type: ignore[misc]
453
- # A special case for Host header
454
- if key .lower () == "host" :
455
- self .headers [key ] = value
456
- else :
457
- self .headers .add (key , value )
441
+ host = cast (str , self .url .raw_host )
442
+ if helpers .is_ipv6_address (host ):
443
+ host = f"[{ host } ]"
444
+
445
+ if host [- 1 ] == "." :
446
+ # Remove all trailing dots from the netloc as while
447
+ # they are valid FQDNs in DNS, TLS validation fails.
448
+ # See https://github.com/aio-libs/aiohttp/issues/3636.
449
+ # To avoid string manipulation we only call rstrip if
450
+ # the last character is a dot.
451
+ host = host .rstrip ("." )
452
+
453
+ # If explicit port is not None, it means that the port was
454
+ # explicitly specified in the URL. In this case we check
455
+ # if its not the default port for the scheme and add it to
456
+ # the host header. We check explicit_port first because
457
+ # yarl caches explicit_port and its likely to already be
458
+ # in the cache and non-default port URLs are far less common.
459
+ explicit_port = self .url .explicit_port
460
+ if explicit_port is not None and not self .url .is_default_port ():
461
+ host = f"{ host } :{ explicit_port } "
462
+
463
+ self .headers [hdrs .HOST ] = host
464
+
465
+ if not headers :
466
+ return
467
+
468
+ if isinstance (headers , (dict , MultiDictProxy , MultiDict )):
469
+ headers = headers .items ()
470
+
471
+ for key , value in headers : # type: ignore[misc]
472
+ # A special case for Host header
473
+ if key .lower () == "host" :
474
+ self .headers [key ] = value
475
+ else :
476
+ self .headers .add (key , value )
458
477
459
478
def update_auto_headers (self , skip_auto_headers : Optional [Iterable [str ]]) -> None :
460
479
if skip_auto_headers is not None :
0 commit comments