Source code for oio.common.http_eventlet

# Copyright (C) 2017-2018 OpenIO SAS, as part of OpenIO SDS
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3.0 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library.

from urllib import quote

from oio.common.green import socket, HTTPConnection, HTTPResponse, _UNKNOWN
from oio.common.logger import get_logger

logger = get_logger({}, __name__)


[docs]class CustomHTTPResponse(HTTPResponse): def __init__(self, sock, debuglevel=0, strict=0, method=None): self.sock = sock self._actual_socket = sock.fd._sock self.fp = sock.makefile('rb') self.debuglevel = debuglevel self.strict = strict self._method = method self.msg = None self.version = _UNKNOWN self.status = _UNKNOWN self.reason = _UNKNOWN self.chunked = _UNKNOWN self.chunk_left = _UNKNOWN self.length = _UNKNOWN self.will_close = _UNKNOWN
[docs] def read(self, amount=None): try: return HTTPResponse.read(self, amount) except AttributeError as err: # We have seen that in production but could not reproduce. # This message will help us track the error further. if "no attribute 'recv'" in err.message: raise IOError('reading socket after close') else: raise
[docs] def force_close(self): if self._actual_socket: self._actual_socket.close() self._actual_socket = None self.close()
[docs] def close(self): HTTPResponse.close(self) if self.sock: try: # Prevent long CLOSE_WAIT state self.sock.shutdown(socket.SHUT_RDWR) except socket.error: pass self.sock = None self._actual_socket = None
[docs]class CustomHttpConnection(HTTPConnection): response_class = CustomHTTPResponse
[docs] def connect(self): conn = HTTPConnection.connect(self) self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) return conn
[docs] def set_cork(self, enabled=True): """ Enable or disable TCP_CORK on the underlying socket. """ self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_CORK, 1 if enabled else 0)
[docs] def putrequest(self, method, url, skip_host=0, skip_accept_encoding=0): self._method = method self._path = url return HTTPConnection.putrequest(self, method, url, skip_host, skip_accept_encoding)
[docs] def getresponse(self): response = HTTPConnection.getresponse(self) logger.debug('HTTP %s %s:%s %s', self._method, self.host, self.port, self._path) return response
[docs]def http_connect(host, method, path, headers=None, query_string=None): if isinstance(path, unicode): try: path = path.encode('utf-8') except UnicodeError as e: logger.exception('ERROR encoding to UTF-8: %s', str(e)) path = quote('/' + path) conn = CustomHttpConnection(host) if query_string: path += '?' + query_string conn.path = path conn.putrequest(method, path) if headers: for header, value in headers.items(): if isinstance(value, list): for k in value: conn.putheader(header, str(k)) else: conn.putheader(header, str(value)) conn.endheaders() return conn