We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
scope["server"] can be None
From https://asgi.readthedocs.io/en/latest/specs/www.html#connection-scope

> server: A two-item iterable of [host, port], where host is the listening address for this server as a unicode string, and port is the integer listening port. Optional, defaults to None.

https://github.com/encode/starlette/blob/master/starlette/datastructures.py#L11 doesn't handle that option, it assumes scope["server"] is always a two-pair



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `starlette/datastructures.py`
Content:
```
1 import typing
2 from starlette.types import Scope
3 from urllib.parse import parse_qsl, unquote, urlparse, ParseResult
4 
5 
6 class URL:
7     def __init__(self, url: str = "", scope: Scope = None) -> None:
8         if scope is not None:
9             assert not url, 'Cannot set both "url" and "scope".'
10             scheme = scope["scheme"]
11             host, port = scope["server"]
12             path = scope.get("root_path", "") + scope["path"]
13             query_string = scope["query_string"]
14 
15             default_port = {"http": 80, "https": 443, "ws": 80, "wss": 443}[scheme]
16             if port == default_port:
17                 url = "%s://%s%s" % (scheme, host, path)
18             else:
19                 url = "%s://%s:%s%s" % (scheme, host, port, path)
20 
21             if query_string:
22                 url += "?" + unquote(query_string.decode())
23         self._url = url
24 
25     @property
26     def components(self) -> ParseResult:
27         if not hasattr(self, "_components"):
28             self._components = urlparse(self._url)
29         return self._components
30 
31     @property
32     def scheme(self) -> str:
33         return self.components.scheme
34 
35     @property
36     def netloc(self) -> str:
37         return self.components.netloc
38 
39     @property
40     def path(self) -> str:
41         return self.components.path
42 
43     @property
44     def params(self) -> str:
45         return self.components.params
46 
47     @property
48     def query(self) -> str:
49         return self.components.query
50 
51     @property
52     def fragment(self) -> str:
53         return self.components.fragment
54 
55     @property
56     def username(self) -> typing.Union[None, str]:
57         return self.components.username
58 
59     @property
60     def password(self) -> typing.Union[None, str]:
61         return self.components.password
62 
63     @property
64     def hostname(self) -> typing.Union[None, str]:
65         return self.components.hostname
66 
67     @property
68     def port(self) -> typing.Optional[int]:
69         return self.components.port
70 
71     def replace(self, **kwargs: typing.Any) -> "URL":
72         if "hostname" in kwargs or "port" in kwargs:
73             hostname = kwargs.pop("hostname", self.hostname)
74             port = kwargs.pop("port", self.port)
75             if port is None:
76                 kwargs["netloc"] = hostname
77             else:
78                 kwargs["netloc"] = "%s:%d" % (hostname, port)
79         components = self.components._replace(**kwargs)
80         return URL(components.geturl())
81 
82     def __eq__(self, other):
83         return str(self) == str(other)
84 
85     def __str__(self):
86         return self._url
87 
88 
89 # Type annotations for valid `__init__` values to QueryParams and Headers.
90 StrPairs = typing.Sequence[typing.Tuple[str, str]]
91 BytesPairs = typing.List[typing.Tuple[bytes, bytes]]
92 StrDict = typing.Mapping[str, str]
93 
94 
95 class QueryParams(StrDict):
96     """
97     An immutable multidict.
98     """
99 
100     def __init__(
101         self, value: typing.Union[str, typing.Union[StrDict, StrPairs]] = None
102     ) -> None:
103         if value is None:
104             value = []
105         elif isinstance(value, str):
106             value = parse_qsl(value)
107 
108         if hasattr(value, "items"):
109             items = list(typing.cast(StrDict, value).items())
110         else:
111             items = list(typing.cast(StrPairs, value))
112         self._dict = {k: v for k, v in reversed(items)}
113         self._list = items
114 
115     def getlist(self, key: typing.Any) -> typing.List[str]:
116         return [item_value for item_key, item_value in self._list if item_key == key]
117 
118     def keys(self) -> typing.List[str]:  # type: ignore
119         return [key for key, value in self._list]
120 
121     def values(self) -> typing.List[str]:  # type: ignore
122         return [value for key, value in self._list]
123 
124     def items(self) -> StrPairs:  # type: ignore
125         return list(self._list)
126 
127     def get(self, key: typing.Any, default: typing.Any = None) -> typing.Any:
128         if key in self._dict:
129             return self._dict[key]
130         else:
131             return default
132 
133     def __getitem__(self, key: typing.Any) -> str:
134         return self._dict[key]
135 
136     def __contains__(self, key: typing.Any) -> bool:
137         return key in self._dict
138 
139     def __iter__(self) -> typing.Iterator[typing.Any]:
140         return iter(self._list)
141 
142     def __len__(self) -> int:
143         return len(self._list)
144 
145     def __eq__(self, other: typing.Any) -> bool:
146         if not isinstance(other, QueryParams):
147             other = QueryParams(other)
148         return sorted(self._list) == sorted(other._list)
149 
150     def __repr__(self) -> str:
151         return "QueryParams(%s)" % repr(self._list)
152 
153 
154 class Headers(typing.Mapping[str, str]):
155     """
156     An immutable, case-insensitive multidict.
157     """
158 
159     def __init__(self, raw_headers: typing.Optional[BytesPairs] = None) -> None:
160         if raw_headers is None:
161             self._list = []  # type: BytesPairs
162         else:
163             for header_key, header_value in raw_headers:
164                 assert isinstance(header_key, bytes)
165                 assert isinstance(header_value, bytes)
166                 assert header_key == header_key.lower()
167             self._list = raw_headers
168 
169     def keys(self) -> typing.List[str]:  # type: ignore
170         return [key.decode("latin-1") for key, value in self._list]
171 
172     def values(self) -> typing.List[str]:  # type: ignore
173         return [value.decode("latin-1") for key, value in self._list]
174 
175     def items(self) -> StrPairs:  # type: ignore
176         return [
177             (key.decode("latin-1"), value.decode("latin-1"))
178             for key, value in self._list
179         ]
180 
181     def get(self, key: str, default: typing.Any = None) -> typing.Any:
182         try:
183             return self[key]
184         except KeyError:
185             return default
186 
187     def getlist(self, key: str) -> typing.List[str]:
188         get_header_key = key.lower().encode("latin-1")
189         return [
190             item_value.decode("latin-1")
191             for item_key, item_value in self._list
192             if item_key == get_header_key
193         ]
194 
195     def mutablecopy(self) -> "MutableHeaders":
196         return MutableHeaders(self._list[:])
197 
198     def __getitem__(self, key: str) -> str:
199         get_header_key = key.lower().encode("latin-1")
200         for header_key, header_value in self._list:
201             if header_key == get_header_key:
202                 return header_value.decode("latin-1")
203         raise KeyError(key)
204 
205     def __contains__(self, key: typing.Any) -> bool:
206         get_header_key = key.lower().encode("latin-1")
207         for header_key, header_value in self._list:
208             if header_key == get_header_key:
209                 return True
210         return False
211 
212     def __iter__(self) -> typing.Iterator[typing.Any]:
213         return iter(self.items())
214 
215     def __len__(self) -> int:
216         return len(self._list)
217 
218     def __eq__(self, other: typing.Any) -> bool:
219         if not isinstance(other, Headers):
220             return False
221         return sorted(self._list) == sorted(other._list)
222 
223     def __repr__(self) -> str:
224         return "%s(%s)" % (self.__class__.__name__, repr(self.items()))
225 
226 
227 class MutableHeaders(Headers):
228     def __setitem__(self, key: str, value: str) -> None:
229         """
230         Set the header `key` to `value`, removing any duplicate entries.
231         Retains insertion order.
232         """
233         set_key = key.lower().encode("latin-1")
234         set_value = value.encode("latin-1")
235 
236         found_indexes = []
237         for idx, (item_key, item_value) in enumerate(self._list):
238             if item_key == set_key:
239                 found_indexes.append(idx)
240 
241         for idx in reversed(found_indexes[1:]):
242             del self._list[idx]
243 
244         if found_indexes:
245             idx = found_indexes[0]
246             self._list[idx] = (set_key, set_value)
247         else:
248             self._list.append((set_key, set_value))
249 
250     def __delitem__(self, key: str) -> None:
251         """
252         Remove the header `key`.
253         """
254         del_key = key.lower().encode("latin-1")
255 
256         pop_indexes = []
257         for idx, (item_key, item_value) in enumerate(self._list):
258             if item_key == del_key:
259                 pop_indexes.append(idx)
260 
261         for idx in reversed(pop_indexes):
262             del (self._list[idx])
263 
264     def setdefault(self, key: str, value: str) -> str:
265         """
266         If the header `key` does not exist, then set it to `value`.
267         Returns the header value.
268         """
269         set_key = key.lower().encode("latin-1")
270         set_value = value.encode("latin-1")
271 
272         for idx, (item_key, item_value) in enumerate(self._list):
273             if item_key == set_key:
274                 return item_value.decode("latin-1")
275         self._list.append((set_key, set_value))
276         return value
277 
278     def update(self, other: dict):
279         for key, val in other.items():
280             self[key] = val
281
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/starlette/datastructures.py b/starlette/datastructures.py
--- a/starlette/datastructures.py
+++ b/starlette/datastructures.py
@@ -7,16 +7,20 @@
     def __init__(self, url: str = "", scope: Scope = None) -> None:
         if scope is not None:
             assert not url, 'Cannot set both "url" and "scope".'
-            scheme = scope["scheme"]
-            host, port = scope["server"]
+            scheme = scope.get("scheme", "http")
+            server = scope.get("server", None)
             path = scope.get("root_path", "") + scope["path"]
             query_string = scope["query_string"]
 
-            default_port = {"http": 80, "https": 443, "ws": 80, "wss": 443}[scheme]
-            if port == default_port:
-                url = "%s://%s%s" % (scheme, host, path)
+            if server is None:
+                url = path
             else:
-                url = "%s://%s:%s%s" % (scheme, host, port, path)
+                host, port = server
+                default_port = {"http": 80, "https": 443, "ws": 80, "wss": 443}[scheme]
+                if port == default_port:
+                    url = "%s://%s%s" % (scheme, host, path)
+                else:
+                    url = "%s://%s:%s%s" % (scheme, host, port, path)
 
             if query_string:
                 url += "?" + unquote(query_string.decode())
@@ -85,6 +89,9 @@
     def __str__(self):
         return self._url
 
+    def __repr__(self):
+        return "%s(%s)" % (self.__class__.__name__, repr(self._url))
+
 
 # Type annotations for valid `__init__` values to QueryParams and Headers.
 StrPairs = typing.Sequence[typing.Tuple[str, str]]

{"golden_diff": "diff --git a/starlette/datastructures.py b/starlette/datastructures.py\n--- a/starlette/datastructures.py\n+++ b/starlette/datastructures.py\n@@ -7,16 +7,20 @@\n     def __init__(self, url: str = \"\", scope: Scope = None) -> None:\n         if scope is not None:\n             assert not url, 'Cannot set both \"url\" and \"scope\".'\n-            scheme = scope[\"scheme\"]\n-            host, port = scope[\"server\"]\n+            scheme = scope.get(\"scheme\", \"http\")\n+            server = scope.get(\"server\", None)\n             path = scope.get(\"root_path\", \"\") + scope[\"path\"]\n             query_string = scope[\"query_string\"]\n \n-            default_port = {\"http\": 80, \"https\": 443, \"ws\": 80, \"wss\": 443}[scheme]\n-            if port == default_port:\n-                url = \"%s://%s%s\" % (scheme, host, path)\n+            if server is None:\n+                url = path\n             else:\n-                url = \"%s://%s:%s%s\" % (scheme, host, port, path)\n+                host, port = server\n+                default_port = {\"http\": 80, \"https\": 443, \"ws\": 80, \"wss\": 443}[scheme]\n+                if port == default_port:\n+                    url = \"%s://%s%s\" % (scheme, host, path)\n+                else:\n+                    url = \"%s://%s:%s%s\" % (scheme, host, port, path)\n \n             if query_string:\n                 url += \"?\" + unquote(query_string.decode())\n@@ -85,6 +89,9 @@\n     def __str__(self):\n         return self._url\n \n+    def __repr__(self):\n+        return \"%s(%s)\" % (self.__class__.__name__, repr(self._url))\n+\n \n # Type annotations for valid `__init__` values to QueryParams and Headers.\n StrPairs = typing.Sequence[typing.Tuple[str, str]]\n", "issue": "scope[\"server\"] can be None\nFrom https://asgi.readthedocs.io/en/latest/specs/www.html#connection-scope\r\n\r\n> server: A two-item iterable of [host, port], where host is the listening address for this server as a unicode string, and port is the integer listening port. Optional, defaults to None.\r\n\r\nhttps://github.com/encode/starlette/blob/master/starlette/datastructures.py#L11 doesn't handle that option, it assumes scope[\"server\"] is always a two-pair\r\n\r\n\n", "before_files": [{"content": "import typing\nfrom starlette.types import Scope\nfrom urllib.parse import parse_qsl, unquote, urlparse, ParseResult\n\n\nclass URL:\n    def __init__(self, url: str = \"\", scope: Scope = None) -> None:\n        if scope is not None:\n            assert not url, 'Cannot set both \"url\" and \"scope\".'\n            scheme = scope[\"scheme\"]\n            host, port = scope[\"server\"]\n            path = scope.get(\"root_path\", \"\") + scope[\"path\"]\n            query_string = scope[\"query_string\"]\n\n            default_port = {\"http\": 80, \"https\": 443, \"ws\": 80, \"wss\": 443}[scheme]\n            if port == default_port:\n                url = \"%s://%s%s\" % (scheme, host, path)\n            else:\n                url = \"%s://%s:%s%s\" % (scheme, host, port, path)\n\n            if query_string:\n                url += \"?\" + unquote(query_string.decode())\n        self._url = url\n\n    @property\n    def components(self) -> ParseResult:\n        if not hasattr(self, \"_components\"):\n            self._components = urlparse(self._url)\n        return self._components\n\n    @property\n    def scheme(self) -> str:\n        return self.components.scheme\n\n    @property\n    def netloc(self) -> str:\n        return self.components.netloc\n\n    @property\n    def path(self) -> str:\n        return self.components.path\n\n    @property\n    def params(self) -> str:\n        return self.components.params\n\n    @property\n    def query(self) -> str:\n        return self.components.query\n\n    @property\n    def fragment(self) -> str:\n        return self.components.fragment\n\n    @property\n    def username(self) -> typing.Union[None, str]:\n        return self.components.username\n\n    @property\n    def password(self) -> typing.Union[None, str]:\n        return self.components.password\n\n    @property\n    def hostname(self) -> typing.Union[None, str]:\n        return self.components.hostname\n\n    @property\n    def port(self) -> typing.Optional[int]:\n        return self.components.port\n\n    def replace(self, **kwargs: typing.Any) -> \"URL\":\n        if \"hostname\" in kwargs or \"port\" in kwargs:\n            hostname = kwargs.pop(\"hostname\", self.hostname)\n            port = kwargs.pop(\"port\", self.port)\n            if port is None:\n                kwargs[\"netloc\"] = hostname\n            else:\n                kwargs[\"netloc\"] = \"%s:%d\" % (hostname, port)\n        components = self.components._replace(**kwargs)\n        return URL(components.geturl())\n\n    def __eq__(self, other):\n        return str(self) == str(other)\n\n    def __str__(self):\n        return self._url\n\n\n# Type annotations for valid `__init__` values to QueryParams and Headers.\nStrPairs = typing.Sequence[typing.Tuple[str, str]]\nBytesPairs = typing.List[typing.Tuple[bytes, bytes]]\nStrDict = typing.Mapping[str, str]\n\n\nclass QueryParams(StrDict):\n    \"\"\"\n    An immutable multidict.\n    \"\"\"\n\n    def __init__(\n        self, value: typing.Union[str, typing.Union[StrDict, StrPairs]] = None\n    ) -> None:\n        if value is None:\n            value = []\n        elif isinstance(value, str):\n            value = parse_qsl(value)\n\n        if hasattr(value, \"items\"):\n            items = list(typing.cast(StrDict, value).items())\n        else:\n            items = list(typing.cast(StrPairs, value))\n        self._dict = {k: v for k, v in reversed(items)}\n        self._list = items\n\n    def getlist(self, key: typing.Any) -> typing.List[str]:\n        return [item_value for item_key, item_value in self._list if item_key == key]\n\n    def keys(self) -> typing.List[str]:  # type: ignore\n        return [key for key, value in self._list]\n\n    def values(self) -> typing.List[str]:  # type: ignore\n        return [value for key, value in self._list]\n\n    def items(self) -> StrPairs:  # type: ignore\n        return list(self._list)\n\n    def get(self, key: typing.Any, default: typing.Any = None) -> typing.Any:\n        if key in self._dict:\n            return self._dict[key]\n        else:\n            return default\n\n    def __getitem__(self, key: typing.Any) -> str:\n        return self._dict[key]\n\n    def __contains__(self, key: typing.Any) -> bool:\n        return key in self._dict\n\n    def __iter__(self) -> typing.Iterator[typing.Any]:\n        return iter(self._list)\n\n    def __len__(self) -> int:\n        return len(self._list)\n\n    def __eq__(self, other: typing.Any) -> bool:\n        if not isinstance(other, QueryParams):\n            other = QueryParams(other)\n        return sorted(self._list) == sorted(other._list)\n\n    def __repr__(self) -> str:\n        return \"QueryParams(%s)\" % repr(self._list)\n\n\nclass Headers(typing.Mapping[str, str]):\n    \"\"\"\n    An immutable, case-insensitive multidict.\n    \"\"\"\n\n    def __init__(self, raw_headers: typing.Optional[BytesPairs] = None) -> None:\n        if raw_headers is None:\n            self._list = []  # type: BytesPairs\n        else:\n            for header_key, header_value in raw_headers:\n                assert isinstance(header_key, bytes)\n                assert isinstance(header_value, bytes)\n                assert header_key == header_key.lower()\n            self._list = raw_headers\n\n    def keys(self) -> typing.List[str]:  # type: ignore\n        return [key.decode(\"latin-1\") for key, value in self._list]\n\n    def values(self) -> typing.List[str]:  # type: ignore\n        return [value.decode(\"latin-1\") for key, value in self._list]\n\n    def items(self) -> StrPairs:  # type: ignore\n        return [\n            (key.decode(\"latin-1\"), value.decode(\"latin-1\"))\n            for key, value in self._list\n        ]\n\n    def get(self, key: str, default: typing.Any = None) -> typing.Any:\n        try:\n            return self[key]\n        except KeyError:\n            return default\n\n    def getlist(self, key: str) -> typing.List[str]:\n        get_header_key = key.lower().encode(\"latin-1\")\n        return [\n            item_value.decode(\"latin-1\")\n            for item_key, item_value in self._list\n            if item_key == get_header_key\n        ]\n\n    def mutablecopy(self) -> \"MutableHeaders\":\n        return MutableHeaders(self._list[:])\n\n    def __getitem__(self, key: str) -> str:\n        get_header_key = key.lower().encode(\"latin-1\")\n        for header_key, header_value in self._list:\n            if header_key == get_header_key:\n                return header_value.decode(\"latin-1\")\n        raise KeyError(key)\n\n    def __contains__(self, key: typing.Any) -> bool:\n        get_header_key = key.lower().encode(\"latin-1\")\n        for header_key, header_value in self._list:\n            if header_key == get_header_key:\n                return True\n        return False\n\n    def __iter__(self) -> typing.Iterator[typing.Any]:\n        return iter(self.items())\n\n    def __len__(self) -> int:\n        return len(self._list)\n\n    def __eq__(self, other: typing.Any) -> bool:\n        if not isinstance(other, Headers):\n            return False\n        return sorted(self._list) == sorted(other._list)\n\n    def __repr__(self) -> str:\n        return \"%s(%s)\" % (self.__class__.__name__, repr(self.items()))\n\n\nclass MutableHeaders(Headers):\n    def __setitem__(self, key: str, value: str) -> None:\n        \"\"\"\n        Set the header `key` to `value`, removing any duplicate entries.\n        Retains insertion order.\n        \"\"\"\n        set_key = key.lower().encode(\"latin-1\")\n        set_value = value.encode(\"latin-1\")\n\n        found_indexes = []\n        for idx, (item_key, item_value) in enumerate(self._list):\n            if item_key == set_key:\n                found_indexes.append(idx)\n\n        for idx in reversed(found_indexes[1:]):\n            del self._list[idx]\n\n        if found_indexes:\n            idx = found_indexes[0]\n            self._list[idx] = (set_key, set_value)\n        else:\n            self._list.append((set_key, set_value))\n\n    def __delitem__(self, key: str) -> None:\n        \"\"\"\n        Remove the header `key`.\n        \"\"\"\n        del_key = key.lower().encode(\"latin-1\")\n\n        pop_indexes = []\n        for idx, (item_key, item_value) in enumerate(self._list):\n            if item_key == del_key:\n                pop_indexes.append(idx)\n\n        for idx in reversed(pop_indexes):\n            del (self._list[idx])\n\n    def setdefault(self, key: str, value: str) -> str:\n        \"\"\"\n        If the header `key` does not exist, then set it to `value`.\n        Returns the header value.\n        \"\"\"\n        set_key = key.lower().encode(\"latin-1\")\n        set_value = value.encode(\"latin-1\")\n\n        for idx, (item_key, item_value) in enumerate(self._list):\n            if item_key == set_key:\n                return item_value.decode(\"latin-1\")\n        self._list.append((set_key, set_value))\n        return value\n\n    def update(self, other: dict):\n        for key, val in other.items():\n            self[key] = val\n", "path": "starlette/datastructures.py"}], "after_files": [{"content": "import typing\nfrom starlette.types import Scope\nfrom urllib.parse import parse_qsl, unquote, urlparse, ParseResult\n\n\nclass URL:\n    def __init__(self, url: str = \"\", scope: Scope = None) -> None:\n        if scope is not None:\n            assert not url, 'Cannot set both \"url\" and \"scope\".'\n            scheme = scope.get(\"scheme\", \"http\")\n            server = scope.get(\"server\", None)\n            path = scope.get(\"root_path\", \"\") + scope[\"path\"]\n            query_string = scope[\"query_string\"]\n\n            if server is None:\n                url = path\n            else:\n                host, port = server\n                default_port = {\"http\": 80, \"https\": 443, \"ws\": 80, \"wss\": 443}[scheme]\n                if port == default_port:\n                    url = \"%s://%s%s\" % (scheme, host, path)\n                else:\n                    url = \"%s://%s:%s%s\" % (scheme, host, port, path)\n\n            if query_string:\n                url += \"?\" + unquote(query_string.decode())\n        self._url = url\n\n    @property\n    def components(self) -> ParseResult:\n        if not hasattr(self, \"_components\"):\n            self._components = urlparse(self._url)\n        return self._components\n\n    @property\n    def scheme(self) -> str:\n        return self.components.scheme\n\n    @property\n    def netloc(self) -> str:\n        return self.components.netloc\n\n    @property\n    def path(self) -> str:\n        return self.components.path\n\n    @property\n    def params(self) -> str:\n        return self.components.params\n\n    @property\n    def query(self) -> str:\n        return self.components.query\n\n    @property\n    def fragment(self) -> str:\n        return self.components.fragment\n\n    @property\n    def username(self) -> typing.Union[None, str]:\n        return self.components.username\n\n    @property\n    def password(self) -> typing.Union[None, str]:\n        return self.components.password\n\n    @property\n    def hostname(self) -> typing.Union[None, str]:\n        return self.components.hostname\n\n    @property\n    def port(self) -> typing.Optional[int]:\n        return self.components.port\n\n    def replace(self, **kwargs: typing.Any) -> \"URL\":\n        if \"hostname\" in kwargs or \"port\" in kwargs:\n            hostname = kwargs.pop(\"hostname\", self.hostname)\n            port = kwargs.pop(\"port\", self.port)\n            if port is None:\n                kwargs[\"netloc\"] = hostname\n            else:\n                kwargs[\"netloc\"] = \"%s:%d\" % (hostname, port)\n        components = self.components._replace(**kwargs)\n        return URL(components.geturl())\n\n    def __eq__(self, other):\n        return str(self) == str(other)\n\n    def __str__(self):\n        return self._url\n\n    def __repr__(self):\n        return \"%s(%s)\" % (self.__class__.__name__, repr(self._url))\n\n\n# Type annotations for valid `__init__` values to QueryParams and Headers.\nStrPairs = typing.Sequence[typing.Tuple[str, str]]\nBytesPairs = typing.List[typing.Tuple[bytes, bytes]]\nStrDict = typing.Mapping[str, str]\n\n\nclass QueryParams(StrDict):\n    \"\"\"\n    An immutable multidict.\n    \"\"\"\n\n    def __init__(\n        self, value: typing.Union[str, typing.Union[StrDict, StrPairs]] = None\n    ) -> None:\n        if value is None:\n            value = []\n        elif isinstance(value, str):\n            value = parse_qsl(value)\n\n        if hasattr(value, \"items\"):\n            items = list(typing.cast(StrDict, value).items())\n        else:\n            items = list(typing.cast(StrPairs, value))\n        self._dict = {k: v for k, v in reversed(items)}\n        self._list = items\n\n    def getlist(self, key: typing.Any) -> typing.List[str]:\n        return [item_value for item_key, item_value in self._list if item_key == key]\n\n    def keys(self) -> typing.List[str]:  # type: ignore\n        return [key for key, value in self._list]\n\n    def values(self) -> typing.List[str]:  # type: ignore\n        return [value for key, value in self._list]\n\n    def items(self) -> StrPairs:  # type: ignore\n        return list(self._list)\n\n    def get(self, key: typing.Any, default: typing.Any = None) -> typing.Any:\n        if key in self._dict:\n            return self._dict[key]\n        else:\n            return default\n\n    def __getitem__(self, key: typing.Any) -> str:\n        return self._dict[key]\n\n    def __contains__(self, key: typing.Any) -> bool:\n        return key in self._dict\n\n    def __iter__(self) -> typing.Iterator[typing.Any]:\n        return iter(self._list)\n\n    def __len__(self) -> int:\n        return len(self._list)\n\n    def __eq__(self, other: typing.Any) -> bool:\n        if not isinstance(other, QueryParams):\n            other = QueryParams(other)\n        return sorted(self._list) == sorted(other._list)\n\n    def __repr__(self) -> str:\n        return \"QueryParams(%s)\" % repr(self._list)\n\n\nclass Headers(typing.Mapping[str, str]):\n    \"\"\"\n    An immutable, case-insensitive multidict.\n    \"\"\"\n\n    def __init__(self, raw_headers: typing.Optional[BytesPairs] = None) -> None:\n        if raw_headers is None:\n            self._list = []  # type: BytesPairs\n        else:\n            for header_key, header_value in raw_headers:\n                assert isinstance(header_key, bytes)\n                assert isinstance(header_value, bytes)\n                assert header_key == header_key.lower()\n            self._list = raw_headers\n\n    def keys(self) -> typing.List[str]:  # type: ignore\n        return [key.decode(\"latin-1\") for key, value in self._list]\n\n    def values(self) -> typing.List[str]:  # type: ignore\n        return [value.decode(\"latin-1\") for key, value in self._list]\n\n    def items(self) -> StrPairs:  # type: ignore\n        return [\n            (key.decode(\"latin-1\"), value.decode(\"latin-1\"))\n            for key, value in self._list\n        ]\n\n    def get(self, key: str, default: typing.Any = None) -> typing.Any:\n        try:\n            return self[key]\n        except KeyError:\n            return default\n\n    def getlist(self, key: str) -> typing.List[str]:\n        get_header_key = key.lower().encode(\"latin-1\")\n        return [\n            item_value.decode(\"latin-1\")\n            for item_key, item_value in self._list\n            if item_key == get_header_key\n        ]\n\n    def mutablecopy(self) -> \"MutableHeaders\":\n        return MutableHeaders(self._list[:])\n\n    def __getitem__(self, key: str) -> str:\n        get_header_key = key.lower().encode(\"latin-1\")\n        for header_key, header_value in self._list:\n            if header_key == get_header_key:\n                return header_value.decode(\"latin-1\")\n        raise KeyError(key)\n\n    def __contains__(self, key: typing.Any) -> bool:\n        get_header_key = key.lower().encode(\"latin-1\")\n        for header_key, header_value in self._list:\n            if header_key == get_header_key:\n                return True\n        return False\n\n    def __iter__(self) -> typing.Iterator[typing.Any]:\n        return iter(self.items())\n\n    def __len__(self) -> int:\n        return len(self._list)\n\n    def __eq__(self, other: typing.Any) -> bool:\n        if not isinstance(other, Headers):\n            return False\n        return sorted(self._list) == sorted(other._list)\n\n    def __repr__(self) -> str:\n        return \"%s(%s)\" % (self.__class__.__name__, repr(self.items()))\n\n\nclass MutableHeaders(Headers):\n    def __setitem__(self, key: str, value: str) -> None:\n        \"\"\"\n        Set the header `key` to `value`, removing any duplicate entries.\n        Retains insertion order.\n        \"\"\"\n        set_key = key.lower().encode(\"latin-1\")\n        set_value = value.encode(\"latin-1\")\n\n        found_indexes = []\n        for idx, (item_key, item_value) in enumerate(self._list):\n            if item_key == set_key:\n                found_indexes.append(idx)\n\n        for idx in reversed(found_indexes[1:]):\n            del self._list[idx]\n\n        if found_indexes:\n            idx = found_indexes[0]\n            self._list[idx] = (set_key, set_value)\n        else:\n            self._list.append((set_key, set_value))\n\n    def __delitem__(self, key: str) -> None:\n        \"\"\"\n        Remove the header `key`.\n        \"\"\"\n        del_key = key.lower().encode(\"latin-1\")\n\n        pop_indexes = []\n        for idx, (item_key, item_value) in enumerate(self._list):\n            if item_key == del_key:\n                pop_indexes.append(idx)\n\n        for idx in reversed(pop_indexes):\n            del (self._list[idx])\n\n    def setdefault(self, key: str, value: str) -> str:\n        \"\"\"\n        If the header `key` does not exist, then set it to `value`.\n        Returns the header value.\n        \"\"\"\n        set_key = key.lower().encode(\"latin-1\")\n        set_value = value.encode(\"latin-1\")\n\n        for idx, (item_key, item_value) in enumerate(self._list):\n            if item_key == set_key:\n                return item_value.decode(\"latin-1\")\n        self._list.append((set_key, set_value))\n        return value\n\n    def update(self, other: dict):\n        for key, val in other.items():\n            self[key] = val\n", "path": "starlette/datastructures.py"}]}