# Copyright (C) The Arvados Authors. All rights reserved. # # SPDX-License-Identifier: Apache-2.0 import collections import socket import pycurl import math class PyCurlHelper: # Default Keep server connection timeout: 2 seconds # Default Keep server read timeout: 256 seconds # Default Keep server bandwidth minimum: 32768 bytes per second # Default Keep proxy connection timeout: 20 seconds # Default Keep proxy read timeout: 256 seconds # Default Keep proxy bandwidth minimum: 32768 bytes per second DEFAULT_TIMEOUT = (2, 256, 32768) DEFAULT_PROXY_TIMEOUT = (20, 256, 32768) def __init__(self, title_case_headers=False): self._socket = None self.title_case_headers = title_case_headers def _socket_open(self, *args, **kwargs): if len(args) + len(kwargs) == 2: return self._socket_open_pycurl_7_21_5(*args, **kwargs) else: return self._socket_open_pycurl_7_19_3(*args, **kwargs) def _socket_open_pycurl_7_19_3(self, family, socktype, protocol, address=None): return self._socket_open_pycurl_7_21_5( purpose=None, address=collections.namedtuple( 'Address', ['family', 'socktype', 'protocol', 'addr'], )(family, socktype, protocol, address)) def _socket_open_pycurl_7_21_5(self, purpose, address): """Because pycurl doesn't have CURLOPT_TCP_KEEPALIVE""" s = socket.socket(address.family, address.socktype, address.protocol) s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) # Will throw invalid protocol error on mac. This test prevents that. if hasattr(socket, 'TCP_KEEPIDLE'): s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 75) s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 75) self._socket = s return s def _setcurltimeouts(self, curl, timeouts, ignore_bandwidth=False): if not timeouts: return elif isinstance(timeouts, tuple): if len(timeouts) == 2: conn_t, xfer_t = timeouts bandwidth_bps = self.DEFAULT_TIMEOUT[2] else: conn_t, xfer_t, bandwidth_bps = timeouts else: conn_t, xfer_t = (timeouts, timeouts) bandwidth_bps = self.DEFAULT_TIMEOUT[2] curl.setopt(pycurl.CONNECTTIMEOUT_MS, int(conn_t*1000)) if not ignore_bandwidth: curl.setopt(pycurl.LOW_SPEED_TIME, int(math.ceil(xfer_t))) curl.setopt(pycurl.LOW_SPEED_LIMIT, int(math.ceil(bandwidth_bps))) def _headerfunction(self, header_line): if isinstance(header_line, bytes): header_line = header_line.decode('iso-8859-1') if ':' in header_line: name, value = header_line.split(':', 1) if self.title_case_headers: name = name.strip().title() else: name = name.strip().lower() value = value.strip() elif self._headers: name = self._lastheadername value = self._headers[name] + ' ' + header_line.strip() elif header_line.startswith('HTTP/'): name = 'x-status-line' value = header_line else: _logger.error("Unexpected header line: %s", header_line) return self._lastheadername = name self._headers[name] = value # Returning None implies all bytes were written