def try_read_until(fp, pattern, chunk_size=16384):
  """
  Perform buffered read of `fp` until pattern, and return all data accumulated and ends with pattern.
  If read failed (no pattern found), file position is backtracked.
  Else: file cursor will be at position right after pattern.

  :returns: None, if EOF reached, otherwise -- the data block (bytes) with data+pattern.
  """
  assert len(pattern) > 0 and isinstance(pattern, bytes)

  chunk_size = max(chunk_size, len(pattern))

  buff = []
  tail = b''
  data = b''

  old_pos = fp.tell()

  while True:
    chunk = fp.read(chunk_size)
    if not chunk:
      break

    split_pos = (tail + chunk).find(pattern)

    if split_pos >= 0:
      split_pos += len(pattern) # Include pattern into result
      buff.append( chunk[:split_pos-len(tail)] )
      data = b''.join(buff)
      break
    else:
      tail = chunk[-len(pattern):] # Memorize last len(pattern) positions to match at next iteration
      buff.append( chunk )

  fp.seek(old_pos + len(data))
  return data or None