def replace(self, key, value): """ Replace existing header with new value. If header doesn't exist this method work like ``__setitem__``. Replacing leads to deletion of all existing headers with the same name. """ key = to_bytestring(key) indices = [] for (i, (k, v)) in enumerate(self._items): if _keys_equal(k, key): indices.append(i) # If the key isn't present, this is easy: just append and abort early. if not indices: self._items.append((key, value)) return # Delete all but the first. I swear, this is the correct slicing # syntax! base_index = indices[0] for i in indices[:0:-1]: self._items.pop(i) del self._items[base_index] self._items.insert(base_index, (key, value))
def __init__(self, ip, stream_id, host, task, send_cb, close_cb, header_encoder, header_decoder, receive_window_manager, remote_window_size, max_frame_size): self.ip = ip self.stream_id = stream_id self.host = host self.task = task self.state = STATE_IDLE # There are two flow control windows: one for data we're sending, # one for data being sent to us. self.receive_window_manager = receive_window_manager self.remote_window_size = remote_window_size self.max_frame_size = max_frame_size # This is the callback handed to the stream by its parent connection. # It is called when the stream wants to send data. It expects to # receive a list of frames that will be automatically serialized. self._send_cb = send_cb # This is the callback to be called when the stream is closed. self._close_cb = close_cb # A reference to the header encoder and decoder objects belonging to # the parent connection. self._encoder = header_encoder self._decoder = header_decoder self.request_headers = HTTPHeaderMap() # Convert the body to bytes if needed. self.request_body = to_bytestring(self.task.body) # request body not send blocked by send window # the left body will send when send window opened. self.request_body_left = len(self.request_body) # data list before decode self.response_header_datas = [] # Set to a key-value set of the response headers once their # HEADERS..CONTINUATION frame sequence finishes. self.response_headers = None # Unconsumed response data chunks self.response_body = [] self.response_body_len = 0 self.start_request()
def test_ping(self, frame_buffer): def data_callback(chunk, **kwargs): frame_buffer.add_data(chunk) c = HTTP20Connection('www.google.com') c._sock = DummySocket() c._send_cb = data_callback opaque = '00000000' c.ping(opaque) frames = list(frame_buffer) assert len(frames) == 1 f = frames[0] assert isinstance(f, PingFrame) assert f.opaque_data == to_bytestring(opaque)
def __delitem__(self, key): """ Sadly, __delitem__ is kind of stupid here, but the best we can do is delete all headers with a given key. To correctly achieve the 'KeyError on missing key' logic from dictionaries, we need to do this slowly. """ key = to_bytestring(key) indices = [] for (i, (k, v)) in enumerate(self._items): if _keys_equal(k, key): indices.append(i) if not indices: raise KeyError("Nonexistent header key: {}".format(key)) for i in indices[::-1]: self._items.pop(i)
def __getitem__(self, key): """ Unlike the dict __getitem__, this returns a list of items in the order they were added. These items are returned in 'canonical form', meaning that comma-separated values are split into multiple values. """ key = to_bytestring(key) values = [] for k, v in self._items: if _keys_equal(k, key): values.extend(x[1] for x in canonical_form(k, v)) if not values: raise KeyError("Nonexistent header key: {}".format(key)) return values
def request(self, method, url, body=None, headers=None): headers = headers or {} if const.HOST_HEADER in headers: headers[':authority'] = headers[const.HOST_HEADER] with self._write_lock: stream_id = self.putrequest(method, url) self._stream_id_context.stream_id = stream_id default_headers = (':method', ':scheme', ':authority', ':path') for name, value in headers.items(): is_default = util.to_native_string(name) in default_headers if isinstance(value, list): for item in value: self.putheader(name, item, stream_id, replace=is_default) else: self.putheader(name, value, stream_id, replace=is_default) final = True message_body = body if body is not None: if callable(body): final = False message_body = None else: if isinstance(body, (unicode, bytes)): message_body = util.to_bytestring(body) if len(message_body) == 0: message_body = None self.endheaders(message_body=message_body, final=final, stream_id=stream_id) if not final: body(self) return stream_id
def __contains__(self, key): """ If any header is present with this key, returns True. """ key = to_bytestring(key) return any(_keys_equal(key, k) for k, _ in self._items)