docs: def fetch(self, output_path, last_etag=None, lastmodified=None, progress_bar=False):
          """
          :param output_path:
          :param last_etag:
          :param lastmodified:
          :param progress_bar:
          :return:
          """
          req_headers = {}
          usr_agent = get_user_agent()
          if usr_agent is not None:
              req_headers['User-Agent'] = usr_agent
  
          if last_etag is not None:
              req_headers['If-None-Match'] = '"' + last_etag + '"'
          if lastmodified is not None:
              req_headers['If-Modified-Since'] = lastmodified
  
          response = requests.get(self.url, headers=req_headers, stream=progress_bar)
  
          if response.status_code == 304:
              # Conditions didn't match, no content returned
              return None
          if response.status_code != 200:
              raise requests.HTTPError
  
          with open(output_path, 'wb') as f:
              if progress_bar:
                  total_size = long(response.headers.get('content-length'))
                  for chunk in progress.bar(response.iter_content(chunk_size=self.chunk_size),
                                            expected_size=(total_size / self.chunk_size) + 1):
                      if chunk:
                          f.write(chunk)
                          f.flush()
              else:
                  f.write(response.content)
  
          headers = normalize_headers(response.headers)
  
          if 'etag' not in headers:
              headers['etag'] = fs_util.calc_file_md5(output_path)
  
          return headers