httpproxytransport.cpp 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. /**
  2. * Copyright (c) 2020-2021 Paul-Louis Ageneau
  3. * Copyright (c) 2023 Eric Gressman
  4. *
  5. * This Source Code Form is subject to the terms of the Mozilla Public
  6. * License, v. 2.0. If a copy of the MPL was not distributed with this
  7. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
  8. */
  9. #include "httpproxytransport.hpp"
  10. #include "tcptransport.hpp"
  11. #include "http.hpp"
  12. #if RTC_ENABLE_WEBSOCKET
  13. namespace rtc::impl {
  14. using std::to_string;
  15. using std::chrono::system_clock;
  16. HttpProxyTransport::HttpProxyTransport(shared_ptr<TcpTransport> lower, std::string hostname,
  17. std::string service, state_callback stateCallback)
  18. : Transport(lower, std::move(stateCallback)), mHostname(std::move(hostname)),
  19. mService(std::move(service)) {
  20. PLOG_DEBUG << "Initializing HTTP proxy transport";
  21. if (!lower->isActive())
  22. throw std::logic_error("HTTP proxy transport expects the lower transport to be active");
  23. }
  24. HttpProxyTransport::~HttpProxyTransport() { unregisterIncoming(); }
  25. void HttpProxyTransport::start() {
  26. registerIncoming();
  27. changeState(State::Connecting);
  28. sendHttpRequest();
  29. }
  30. void HttpProxyTransport::stop() { unregisterIncoming(); }
  31. bool HttpProxyTransport::send(message_ptr message) {
  32. if (state() != State::Connected)
  33. throw std::runtime_error("HTTP proxy connection is not open");
  34. PLOG_VERBOSE << "Send size=" << message->size();
  35. return outgoing(message);
  36. }
  37. bool HttpProxyTransport::isActive() const { return true; }
  38. void HttpProxyTransport::incoming(message_ptr message) {
  39. auto s = state();
  40. if (s != State::Connecting && s != State::Connected)
  41. return; // Drop
  42. if (message) {
  43. PLOG_VERBOSE << "Incoming size=" << message->size();
  44. try {
  45. if (state() == State::Connecting) {
  46. mBuffer.insert(mBuffer.end(), message->begin(), message->end());
  47. if (size_t len = parseHttpResponse(mBuffer.data(), mBuffer.size())) {
  48. PLOG_INFO << "HTTP proxy connection open";
  49. changeState(State::Connected);
  50. mBuffer.erase(mBuffer.begin(), mBuffer.begin() + len);
  51. if (!mBuffer.empty()) {
  52. recv(make_message(mBuffer));
  53. mBuffer.clear();
  54. }
  55. }
  56. } else if (state() == State::Connected) {
  57. recv(std::move(message));
  58. }
  59. return;
  60. } catch (const std::exception &e) {
  61. PLOG_ERROR << e.what();
  62. }
  63. }
  64. if (state() == State::Connected) {
  65. PLOG_INFO << "HTTP proxy disconnected";
  66. changeState(State::Disconnected);
  67. recv(nullptr);
  68. } else {
  69. PLOG_ERROR << "HTTP proxy connection failed";
  70. changeState(State::Failed);
  71. }
  72. }
  73. bool HttpProxyTransport::sendHttpRequest() {
  74. PLOG_DEBUG << "Sending HTTP request to proxy";
  75. const string request = generateHttpRequest();
  76. auto data = reinterpret_cast<const byte *>(request.data());
  77. return outgoing(make_message(data, data + request.size()));
  78. }
  79. string HttpProxyTransport::generateHttpRequest() {
  80. return "CONNECT " + mHostname + ":" + mService + " HTTP/1.1\r\nHost: " + mHostname + "\r\n\r\n";
  81. }
  82. size_t HttpProxyTransport::parseHttpResponse(std::byte *buffer, size_t size) {
  83. std::list<string> lines;
  84. size_t length = parseHttpLines(buffer, size, lines);
  85. if (length == 0)
  86. return 0;
  87. if (lines.empty())
  88. throw std::runtime_error("Invalid response from HTTP proxy");
  89. std::istringstream status(std::move(lines.front()));
  90. lines.pop_front();
  91. string protocol;
  92. unsigned int code = 0;
  93. status >> protocol >> code;
  94. if (code != 200)
  95. throw std::runtime_error("Unexpected response code " + to_string(code) + " from HTTP proxy");
  96. return length;
  97. }
  98. } // namespace rtc::impl
  99. #endif