|
14 | 14 | try: |
15 | 15 | # Google App Engine blacklists parts of the socket module, this will prevent |
16 | 16 | # it from blowing up. |
17 | | - from socket import socket, AF_INET, SOCK_DGRAM, error as socket_error |
| 17 | + from socket import socket, AF_INET, AF_INET6, SOCK_DGRAM, has_ipv6, getaddrinfo, error as socket_error |
18 | 18 | has_socket = True |
19 | 19 | except: |
20 | 20 | has_socket = False |
@@ -114,15 +114,31 @@ def __init__(self, parsed_url): |
114 | 114 | self.check_scheme(parsed_url) |
115 | 115 | self._parsed_url = parsed_url |
116 | 116 |
|
| 117 | + def _get_addr_info(self, host, port): |
| 118 | + """ |
| 119 | + Selects the address to connect to, based on the supplied host/port |
| 120 | + information. This method prefers v4 addresses, and will only return |
| 121 | + a v6 address if it's the only option. |
| 122 | + """ |
| 123 | + addresses = getaddrinfo(host, port) |
| 124 | + if has_ipv6: |
| 125 | + v6_addresses = [info for info in addresses if info[0] == AF_INET6] |
| 126 | + v4_addresses = [info for info in addresses if info[0] == AF_INET] |
| 127 | + if v6_addresses and not v4_addresses: |
| 128 | + # The only time we return a v6 address is if it's the only option |
| 129 | + return v6_addresses[0] |
| 130 | + return addresses[0] |
| 131 | + |
117 | 132 | def send(self, data, headers): |
118 | 133 | auth_header = headers.get('X-Sentry-Auth') |
119 | 134 |
|
120 | 135 | if auth_header is None: |
121 | 136 | # silently ignore attempts to send messages without an auth header |
122 | 137 | return |
123 | 138 |
|
124 | | - host, port = self._parsed_url.netloc.split(':') |
125 | | - self._send_data(auth_header + '\n\n' + data, (host, int(port))) |
| 139 | + host, port = self._parsed_url.netloc.rsplit(':') |
| 140 | + addr_info = self._get_addr_info(host, int(port)) |
| 141 | + self._send_data(auth_header + '\n\n' + data, addr_info) |
126 | 142 |
|
127 | 143 | def compute_scope(self, url, scope): |
128 | 144 | path_bits = url.path.rsplit('/', 1) |
@@ -157,10 +173,12 @@ def __init__(self, parsed_url): |
157 | 173 | if not has_socket: |
158 | 174 | raise ImportError('UDPTransport requires the socket module') |
159 | 175 |
|
160 | | - def _send_data(self, data, addr): |
| 176 | + def _send_data(self, data, addr_info): |
161 | 177 | udp_socket = None |
| 178 | + af = addr_info[0] |
| 179 | + addr = addr_info[4] |
162 | 180 | try: |
163 | | - udp_socket = socket(AF_INET, SOCK_DGRAM) |
| 181 | + udp_socket = socket(af, SOCK_DGRAM) |
164 | 182 | udp_socket.setblocking(False) |
165 | 183 | udp_socket.sendto(data, addr) |
166 | 184 | except socket_error: |
|
0 commit comments