The main Bubble source repository. Contains the Bubble API server, the web UI, documentation and utilities. https://getbubblenow.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

93 lines
3.1 KiB

  1. #!/usr/bin/python3
  2. #
  3. # Copyright (c) 2020 Bubble, Inc. All rights reserved. For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/
  4. #
  5. # Replay a stream, as if mitmproxy were sending filter requests via the filter/apply API.
  6. #
  7. # This is an advanced tool for low-level debugging and diagnostics of the Bubble filtering system.
  8. #
  9. # Usage:
  10. #
  11. # breplay_stream.py path_prefix
  12. #
  13. # path_prefix : some file prefix
  14. #
  15. # This will list all files matching the prefix, sort them, and play them back.
  16. # These files should come in triplets: a .url file, a .headers.json file, and a .data file/
  17. #
  18. # To capture requests for later playback:
  19. # * Set debug_stream_fqdn and debug_stream_uri in mitmproxy/bubble_config.py and restart mitmproxy servers
  20. # * Request the debug fqdn and URI using a device whose traffic will be routed through the mitmproxy
  21. # * Capture files will be written to /tmp/bubble_stream_[request-id]_chunkXXXX.[url, headers.json, data]
  22. #
  23. # When capturing, the URL you request must match the debug_stream_fqdn and debug_stream_uri exactly
  24. # in order for capturing to be triggered. For example, if we have:
  25. #
  26. # debug_stream_fqdn = 'www.example.com'
  27. # debug_stream_uri = '/dir/test.html'
  28. #
  29. # Then the URL to use when requesting from the connected device is:
  30. #
  31. # https://www.example.com/dir/test.html
  32. #
  33. import glob
  34. import json
  35. import requests
  36. import sys
  37. HEADER_FILTER_PASSTHRU = 'X-Bubble-Passthru'
  38. def log (message):
  39. print(message, file=sys.stderr, flush=True)
  40. def replay_stream (prefix, out):
  41. url_files = glob.glob(prefix+'*.url')
  42. if url_files is None or len(url_files) == 0:
  43. log('No files found matching prefix: '+prefix)
  44. return
  45. url_files.sort()
  46. for u in url_files:
  47. chunk_file = replace_suffix(u, '.data')
  48. headers_file = replace_suffix(u, '.headers.json')
  49. with open(u, mode='r') as f:
  50. url = f.read()
  51. with open(headers_file, mode='r') as f:
  52. headers = json.load(f)
  53. with open(chunk_file, mode='rb') as f:
  54. chunk = f.read()
  55. log('sending '+str(len(chunk))+' bytes to '+url)
  56. try:
  57. response_data = replay_request(url, headers, chunk)
  58. except Exception as e:
  59. log('error sending filter request: '+repr(e))
  60. raise e
  61. log('received '+str(len(response_data))+' bytes')
  62. if len(response_data) > 0:
  63. out.write(response_data)
  64. def replace_suffix(f, suffix):
  65. return f[0:f.rfind('.')] + suffix
  66. def replay_request(url, headers, chunk):
  67. response = requests.post(url, data=chunk, headers=headers)
  68. if not response.ok:
  69. log('replay_request: Error fetching ' + url + ', HTTP status ' + str(response.status_code))
  70. return b''
  71. elif HEADER_FILTER_PASSTHRU in response.headers:
  72. log('replay_request: server returned X-Bubble-Passthru, not filtering subsequent requests')
  73. return chunk
  74. return response.content
  75. if __name__ == "__main__":
  76. with open('/tmp/replay_response', mode='wb') as out:
  77. replay_stream(sys.argv[1], out)
  78. out.close()