def __init__(self, connection_settings): """ Initializes a socket object using low-level python socket objects. @param connection_settings: The connection settings for this socket @type connection_settings: ConnectionSettings @return: None @rtype : None """ self._request_throttle_sec = (float)(Settings().request_throttle_ms/1000.0)\ if Settings().request_throttle_ms else None self.connection_settings = connection_settings try: self._sock = None host = Settings().host target_ip = self.connection_settings.target_ip or host target_port = self.connection_settings.target_port if Settings().use_test_socket: self._sock = TestSocket(Settings().test_server) elif self.connection_settings.use_ssl: context = ssl.create_default_context() with socket.create_connection((target_ip, target_port or 443)) as sock: self._sock = context.wrap_socket(sock, server_hostname=host) else: self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._sock.connect((target_ip, target_port or 80)) except Exception as error: raise TransportLayerException( f"Exception Creating Socket: {error!s}")
def _closeSocket(self): """ Closes open socket object. @return: None @rtype : None """ try: self._sock.close() except Exception as error: raise TransportLayerException(f"Exception: {error!s}")
def _sendRequest(self, message): """ Sends message via current instance of socket object. @param message: Message to be sent. @type message : Str @return: None @rtype : None """ def _get_end_of_header(message): return message.index(DELIM) def _get_start_of_body(message): return _get_end_of_header(message) + len(DELIM) def _append_to_header(message, content): header = message[:_get_end_of_header(message )] + "\r\n" + content + DELIM return header + message[_get_start_of_body(message):] if "Content-Length: " not in message: contentlen = len(message[_get_start_of_body(message):]) message = _append_to_header(message, f"Content-Length: {contentlen}") if self.connection_settings.include_user_agent: message = _append_to_header( message, f"User-Agent: restler/{Settings().version}") # Attempt to throttle the request if necessary self._begin_throttle_request() try: RAW_LOGGING(f'Sending: {message!r}\n') self._sock.sendall(message.encode(UTF8)) except Exception as error: raise TransportLayerException(f"Exception Sending Data: {error!s}") finally: self._end_throttle_request()
def _recvResponse(self, req_timeout_sec): """ Reads data from socket object. @param req_timeout_sec: The time, in seconds, to wait for request to complete @type req_timeout_sec : Int @return: Data received on current socket. @rtype : Str """ global DELIM data = '' # get the data of header (and maybe more) bytes_received = 0 while DELIM not in data: try: self._sock.settimeout(req_timeout_sec) buf = self._sock.recv(2**20) bytes_received += len(buf) except Exception as error: raise TransportLayerException(f"Exception: {error!s}") if len(buf) == 0: return data data += buf.decode(UTF8) # Handle chunk encoding chuncked_encoding = False if 'Transfer-Encoding: chunked\r\n' in data or\ 'transfer-encoding: chunked\r\n' in data: chuncked_encoding = True if chuncked_encoding: while True: try: buf = self._sock.recv(2**20) bytes_received += len(buf) except Exception as error: raise TransportLayerException(f"Exception: {error!s}") if len(buf) == 0: return data data += buf.decode(UTF8) if data.endswith(DELIM): return data header_len = data.index(DELIM) + len(DELIM) if data[:12] in ["HTTP/1.1 204", "HTTP/1.1 304"]: content_len = 0 else: try: data_lower = data.lower() content_len = data_lower.split("content-length: ")[1] content_len = int(content_len.split('\r\n')[0]) except Exception as error: content_len = 2**20 bytes_remain = content_len - bytes_received + header_len # get rest of socket data while bytes_remain > 0: try: buf = self._sock.recv(2**20) except Exception as error: raise TransportLayerException(f"Exception: {error!s}") if len(buf) == 0: return data bytes_remain -= len(buf) data += buf.decode(UTF8) return data