├── proxy_quic ├── quic_proxy_dispatcher.h ├── quic_curl_timer.h ├── sendmmsgtimer.h ├── quic_proxy_session.h ├── quic_proxy_packet_writer.h ├── sendmmsgtimer.cc ├── quic_proxy_stream.h ├── quic_curl_timer.cc ├── quic_proxy_dispatcher.cc ├── quic_proxy_stream.cc ├── quic_proxy_packet_writer.cc ├── quic_proxy_session.cc ├── quic_proxy_packet_reader.h ├── quic_proxy_backend.h ├── quic_proxy_curl.h ├── quic_proxy_server_bin.cc ├── quic_proxy_server.h ├── quic_proxy_packet_reader.cc ├── quic_proxy_backend.cc ├── quic_proxy_server.cc └── quic_proxy_curl.cc ├── logrun.pl ├── README.md └── README-en.md /proxy_quic/quic_proxy_dispatcher.h: -------------------------------------------------------------------------------- 1 | #ifndef QUICHE_QUIC_PROXYQUIC_QUIC_PROXY_DISPATCHER_H_ 2 | #define QUICHE_QUIC_PROXYQUIC_QUIC_PROXY_DISPATCHER_H_ 3 | 4 | #include "net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.h" 5 | 6 | namespace quic { 7 | 8 | class QuicProxyDispatcher : public QuicSimpleDispatcher { 9 | public: 10 | QuicProxyDispatcher( 11 | const QuicConfig* config, 12 | const QuicCryptoServerConfig* crypto_config, 13 | QuicVersionManager* version_manager, 14 | std::unique_ptr helper, 15 | std::unique_ptr session_helper, 16 | std::unique_ptr alarm_factory, 17 | QuicSimpleServerBackend* quic_simple_server_backend, 18 | uint8_t expected_connection_id_length); 19 | 20 | ~QuicProxyDispatcher() override; 21 | 22 | protected: 23 | QuicServerSessionBase* CreateQuicSession( 24 | QuicConnectionId connection_id, 25 | const QuicSocketAddress& client_address, 26 | QuicStringPiece alpn, 27 | const ParsedQuicVersion& version) override; 28 | 29 | }; 30 | 31 | } // namespace quic 32 | 33 | #endif // QUICHE_QUIC_PROXYQUIC_QUIC_PROXY_DISPATCHER_H_ 34 | -------------------------------------------------------------------------------- /proxy_quic/quic_curl_timer.h: -------------------------------------------------------------------------------- 1 | #ifndef QUICHE_QUIC_PROXYQUIC_QUIC_CURL_TIMER_H_ 2 | #define QUICHE_QUIC_PROXYQUIC_QUIC_CURL_TIMER_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h" 8 | 9 | 10 | namespace quic { 11 | 12 | class QuicProxyBackend; 13 | 14 | class QuicCurlTimer : public QuicEpollCallbackInterface { 15 | public: 16 | QuicCurlTimer(QuicProxyBackend* backend); 17 | QuicCurlTimer(const QuicCurlTimer&) = delete; 18 | QuicCurlTimer& operator=(const QuicCurlTimer&) = delete; 19 | ~QuicCurlTimer() override; 20 | 21 | int get_timerfd() { return timer_fd_; } 22 | void set_timerfd(int fd) { timer_fd_ = fd; } 23 | 24 | void InitializeCurlTimer(); 25 | 26 | std::string Name() const override { return "QuicCurlTimer"; } 27 | // From EpollCallbackInterface 28 | void OnRegistration(QuicEpollServer* eps, int fd, int event_mask) override {} 29 | void OnModification(int fd, int event_mask) override {} 30 | void OnEvent(int fd, QuicEpollEvent* event) override; 31 | void OnUnregistration(int fd, bool replaced) override {} 32 | 33 | void OnShutdown(QuicEpollServer* eps, int fd) override {} 34 | 35 | 36 | private: 37 | 38 | int timer_fd_; 39 | QuicProxyBackend* backend_; 40 | }; 41 | 42 | } // namespace quic 43 | 44 | #endif // QUICHE_QUIC_PROXYQUIC_QUIC_PROXY_HTTP_H_ 45 | -------------------------------------------------------------------------------- /logrun.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | use strict; 3 | use warnings; 4 | 5 | close STDIN; 6 | close STDOUT; 7 | close STDERR; 8 | 9 | die if ($#ARGV < 0); my $logdir = shift(@ARGV); 10 | die if ($#ARGV < 0); my $program = join(' ', @ARGV); 11 | my $stdout_execute = "/opt/scm/rotatelogs $logdir/stdout_%Y%m%d.log 86400 +480"; 12 | my $stderr_execute = "/opt/scm/rotatelogs $logdir/stderr_%Y%m%d.log 86400 +480"; 13 | 14 | pipe *PI1, *PO1 || die "Cannot open pipe, $!\n"; 15 | pipe *PI2, *PO2 || die "Cannot open pipe, $!\n"; 16 | my $oldfd = select(PO1); $|=1; select(PO2); $|=1; select($oldfd); 17 | 18 | my $pid1 = fork(); 19 | defined($pid1) || die "Cannot fork new process, $!\n"; 20 | if ($pid1 == 0) { 21 | close PO1; 22 | close PI2; 23 | close PO2; 24 | open(STDIN, "<&PI1") || die "Cannot redirect STDIN, $!\n"; 25 | exec($stdout_execute) || die "Cannot execute program, $!\n"; 26 | exit(0); 27 | } 28 | 29 | my $pid2 = fork(); 30 | defined($pid2) || die "Cannot fork new process, $!\n"; 31 | if ($pid2 == 0) { 32 | close PI1; 33 | close PO1; 34 | close PO2; 35 | open(STDIN, "<&PI2") || die "Cannot redirect STDIN, $!\n"; 36 | exec($stderr_execute) || die "Cannot execute program, $!\n"; 37 | exit(0); 38 | } 39 | 40 | close PI1; close PI2; 41 | 42 | open(STDOUT, ">&PO1") || die "Cannot redirect STDOUT, $!\n"; 43 | open(STDERR, ">&PO2") || die "Cannot redirect STDERR, $!\n"; 44 | exec($program) || die "Cannot execute program, $!\n"; 45 | -------------------------------------------------------------------------------- /proxy_quic/sendmmsgtimer.h: -------------------------------------------------------------------------------- 1 | #ifndef QUICHE_QUIC_PROXYQUIC_SENDMMSGTIMER_H_ 2 | #define QUICHE_QUIC_PROXYQUIC_SENDMMSGTIMER_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h" 8 | 9 | namespace quic { 10 | 11 | class QuicProxyPacketWriter; 12 | 13 | class SendMMsgTimer : public QuicEpollCallbackInterface { 14 | public: 15 | SendMMsgTimer(QuicProxyPacketWriter* writer); 16 | SendMMsgTimer(const SendMMsgTimer&) = delete; 17 | SendMMsgTimer& operator=(const SendMMsgTimer&) = delete; 18 | ~SendMMsgTimer() override; 19 | 20 | void InitializeTimer(QuicEpollServer* epoll_server, int interval); 21 | 22 | int get_timerfd() { return timer_fd_; } 23 | void set_timerfd(int fd) { timer_fd_ = fd; } 24 | 25 | std::string Name() const override { return "SendMMsgTimer"; } 26 | // From EpollCallbackInterface 27 | void OnRegistration(QuicEpollServer* eps, int fd, int event_mask) override {} 28 | void OnModification(int fd, int event_mask) override {} 29 | void OnEvent(int fd, QuicEpollEvent* event) override; 30 | void OnUnregistration(int fd, bool replaced) override {} 31 | 32 | void OnShutdown(QuicEpollServer* eps, int fd) override {} 33 | 34 | 35 | private: 36 | 37 | int timer_fd_; 38 | QuicProxyPacketWriter* writer_; 39 | }; 40 | 41 | } // namespace quic 42 | 43 | #endif // QUICHE_QUIC_PROXYQUIC_QUIC_PROXY_HTTP_H_ 44 | -------------------------------------------------------------------------------- /proxy_quic/quic_proxy_session.h: -------------------------------------------------------------------------------- 1 | #ifndef QUICHE_QUIC_PROXYQUIC_QUIC_PROXY_SESSION_H_ 2 | #define QUICHE_QUIC_PROXYQUIC_QUIC_PROXY_SESSION_H_ 3 | 4 | #include "net/third_party/quiche/src/quic/tools/quic_simple_server_session.h" 5 | 6 | namespace quic { 7 | 8 | 9 | class QuicProxySession : public QuicSimpleServerSession { 10 | public: 11 | QuicProxySession(const QuicConfig& config, 12 | const ParsedQuicVersionVector& supported_versions, 13 | QuicConnection* connection, 14 | QuicSession::Visitor* visitor, 15 | QuicCryptoServerStream::Helper* helper, 16 | const QuicCryptoServerConfig* crypto_config, 17 | QuicCompressedCertsCache* compressed_certs_cache, 18 | QuicSimpleServerBackend* quic_simple_server_backend); 19 | QuicProxySession(const QuicProxySession&) = delete; 20 | QuicProxySession& operator=(const QuicProxySession&) = delete; 21 | 22 | ~QuicProxySession() override; 23 | 24 | protected: 25 | // QuicSession methods: 26 | QuicSpdyStream* CreateIncomingStream(QuicStreamId id) override; 27 | QuicSpdyStream* CreateIncomingStream(PendingStream* pending) override; 28 | QuicSimpleServerStream* CreateOutgoingBidirectionalStream() override; 29 | QuicSimpleServerStream* CreateOutgoingUnidirectionalStream() override; 30 | 31 | }; 32 | 33 | } // namespace quic 34 | 35 | #endif // QUICHE_QUIC_PROXYQUIC_QUIC_PROXY_SESSION_H_ 36 | -------------------------------------------------------------------------------- /proxy_quic/quic_proxy_packet_writer.h: -------------------------------------------------------------------------------- 1 | #ifndef QUICHE_QUIC_CORE_QUIC_PROXY_PACKET_WRITER_H_ 2 | #define QUICHE_QUIC_CORE_QUIC_PROXY_PACKET_WRITER_H_ 3 | 4 | #include "net/third_party/quiche/src/quic/core/quic_default_packet_writer.h" 5 | #include "net/quic/platform/impl/quic_linux_socket_utils.h" 6 | 7 | namespace quic { 8 | 9 | const size_t max_cache_buffer_write_size = 32; 10 | 11 | // Proxy packet writer which wraps QuicLinuxSocketUtils WritePacket. 12 | class QUIC_EXPORT_PRIVATE QuicProxyPacketWriter : public QuicDefaultPacketWriter { 13 | public: 14 | explicit QuicProxyPacketWriter(int fd); 15 | QuicProxyPacketWriter(const QuicProxyPacketWriter&) = delete; 16 | QuicProxyPacketWriter& operator=(const QuicProxyPacketWriter&) = delete; 17 | ~QuicProxyPacketWriter() override; 18 | 19 | // QuicPacketWriter 20 | WriteResult WritePacket(const char* buffer, 21 | size_t buf_len, 22 | const QuicIpAddress& self_address, 23 | const QuicSocketAddress& peer_address, 24 | PerPacketOptions* options) override; 25 | WriteResult Flush() override; 26 | 27 | private: 28 | QuicDeque buffered_writes_; 29 | char writes_cache_[max_cache_buffer_write_size][kEthernetMTU]; 30 | size_t writes_cache_pos_; 31 | // QuicSyscallWrapper quic_sys_call_wrapper_; 32 | }; 33 | 34 | } // namespace quic 35 | 36 | #endif // QUICHE_QUIC_CORE_QUIC_PROXY_PACKET_WRITER_H_ 37 | -------------------------------------------------------------------------------- /proxy_quic/sendmmsgtimer.cc: -------------------------------------------------------------------------------- 1 | #include "net/third_party/quiche/src/quic/proxy_quic/sendmmsgtimer.h" 2 | #include "net/third_party/quiche/src/quic/proxy_quic/quic_proxy_packet_writer.h" 3 | #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" 4 | 5 | 6 | namespace quic { 7 | 8 | SendMMsgTimer::SendMMsgTimer(QuicProxyPacketWriter* writer) 9 | : timer_fd_(-1), writer_(writer) {} 10 | 11 | SendMMsgTimer::~SendMMsgTimer() { 12 | if (timer_fd_ != -1) { 13 | ::close(timer_fd_); 14 | timer_fd_ = -1; 15 | } 16 | } 17 | 18 | void SendMMsgTimer::InitializeTimer(QuicEpollServer* epoll_server, int interval) { 19 | if (timer_fd_ == -1) { 20 | timer_fd_ = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC); 21 | struct itimerspec its; 22 | memset(&its, 0, sizeof(struct itimerspec)); 23 | its.it_interval.tv_sec = 0; 24 | its.it_interval.tv_nsec = interval; //40ms 25 | its.it_value.tv_sec = 0; 26 | its.it_value.tv_nsec = interval; //40ms 27 | timerfd_settime(timer_fd_, TIMER_ABSTIME, &its, NULL); 28 | 29 | epoll_server->RegisterFD(timer_fd_, this, 30 | EPOLLIN | EPOLLOUT | EPOLLET); 31 | } 32 | } 33 | 34 | void SendMMsgTimer::OnEvent(int fd, QuicEpollEvent* event) { 35 | DCHECK_EQ(fd, timer_fd_); 36 | uint64_t count = 0; 37 | ssize_t err = 0; 38 | 39 | err = read(fd, &count, sizeof(uint64_t)); 40 | if (err == -1) { 41 | if (errno == EAGAIN) { 42 | return; 43 | } 44 | } 45 | 46 | if (err != sizeof(uint64_t)) { 47 | } 48 | 49 | writer_->Flush(); 50 | } 51 | 52 | 53 | 54 | } // namespace quic 55 | -------------------------------------------------------------------------------- /proxy_quic/quic_proxy_stream.h: -------------------------------------------------------------------------------- 1 | #ifndef QUICHE_QUIC_PROXYQUIC_QUIC_PROXY_STREAM_H_ 2 | #define QUICHE_QUIC_PROXYQUIC_QUIC_PROXY_STREAM_H_ 3 | 4 | #include "net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h" 5 | 6 | namespace quic { 7 | 8 | class QuicProxyCurl; 9 | 10 | // All this does right now is aggregate data, and on fin, send an HTTP 11 | // response. 12 | class QuicProxyStream : public QuicSimpleServerStream { 13 | public: 14 | QuicProxyStream(QuicStreamId id, 15 | QuicSpdySession* session, 16 | StreamType type, 17 | QuicSimpleServerBackend* quic_simple_server_backend); 18 | QuicProxyStream(PendingStream* pending, 19 | QuicSpdySession* session, 20 | StreamType type, 21 | QuicSimpleServerBackend* quic_simple_server_backend); 22 | QuicProxyStream(const QuicProxyStream&) = delete; 23 | QuicProxyStream& operator=(const QuicProxyStream&) = delete; 24 | ~QuicProxyStream() override; 25 | 26 | void set_proxy_curl(QuicProxyCurl* proxy) { quic_proxy_curl_ = proxy; } 27 | QuicProxyCurl* get_proxy_curl() { return quic_proxy_curl_; } 28 | std::string get_peer_ip(); 29 | 30 | 31 | bool OnStreamFrameAcked(QuicStreamOffset offset, 32 | QuicByteCount data_length, 33 | bool fin_acked, 34 | QuicTime::Delta ack_delay_time, 35 | QuicByteCount* newly_acked_length) override; 36 | 37 | private: 38 | QuicProxyCurl* quic_proxy_curl_; // Not owned. 39 | }; 40 | 41 | } // namespace quic 42 | 43 | #endif // QUICHE_QUIC_PROXYQUIC_QUIC_PROXY_STREAM_H_ 44 | -------------------------------------------------------------------------------- /proxy_quic/quic_curl_timer.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include "net/third_party/quiche/src/quic/proxy_quic/quic_curl_timer.h" 12 | #include "net/third_party/quiche/src/quic/proxy_quic/quic_proxy_server.h" 13 | #include "net/third_party/quiche/src/quic/proxy_quic/quic_proxy_backend.h" 14 | 15 | 16 | 17 | namespace quic { 18 | 19 | QuicCurlTimer::QuicCurlTimer(QuicProxyBackend* backend) 20 | : timer_fd_(-1), backend_(backend) {} 21 | 22 | QuicCurlTimer::~QuicCurlTimer() { 23 | if (timer_fd_ != -1) { 24 | ::close(timer_fd_); 25 | timer_fd_ = -1; 26 | } 27 | backend_ = nullptr; 28 | } 29 | 30 | void QuicCurlTimer::InitializeCurlTimer() { 31 | if (timer_fd_ == -1) { 32 | timer_fd_ = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); 33 | struct itimerspec its; 34 | memset(&its, 0, sizeof(struct itimerspec)); 35 | its.it_interval.tv_sec = 1; 36 | its.it_value.tv_sec = 1; 37 | timerfd_settime(timer_fd_, 0, &its, NULL); 38 | 39 | backend_->get_server()->epoll_server()->RegisterFD(timer_fd_, this, 40 | EPOLLIN | EPOLLOUT | EPOLLET); 41 | } 42 | } 43 | 44 | void QuicCurlTimer::OnEvent(int fd, QuicEpollEvent* event) { 45 | DCHECK_EQ(fd, timer_fd_); 46 | CURLMcode rc; 47 | uint64_t count = 0; 48 | ssize_t err = 0; 49 | 50 | err = read(fd, &count, sizeof(uint64_t)); 51 | if (err == -1) { 52 | if (errno == EAGAIN) { 53 | return; 54 | } 55 | } 56 | 57 | if (err != sizeof(uint64_t)) { 58 | } 59 | 60 | rc = curl_multi_socket_action(backend_->get_curlm(), 61 | CURL_SOCKET_TIMEOUT, 0, &backend_->still_running_); 62 | backend_->CheckCurlMultiInfo(); 63 | } 64 | 65 | 66 | 67 | } // namespace quic 68 | -------------------------------------------------------------------------------- /proxy_quic/quic_proxy_dispatcher.cc: -------------------------------------------------------------------------------- 1 | #include "net/third_party/quiche/src/quic/proxy_quic/quic_proxy_dispatcher.h" 2 | #include "net/third_party/quiche/src/quic/proxy_quic/quic_proxy_session.h" 3 | 4 | namespace quic { 5 | 6 | QuicProxyDispatcher::QuicProxyDispatcher( 7 | const QuicConfig* config, 8 | const QuicCryptoServerConfig* crypto_config, 9 | QuicVersionManager* version_manager, 10 | std::unique_ptr helper, 11 | std::unique_ptr session_helper, 12 | std::unique_ptr alarm_factory, 13 | QuicSimpleServerBackend* quic_simple_server_backend, 14 | uint8_t expected_connection_id_length) 15 | : QuicSimpleDispatcher(config, 16 | crypto_config, 17 | version_manager, 18 | std::move(helper), 19 | std::move(session_helper), 20 | std::move(alarm_factory), 21 | quic_simple_server_backend, 22 | expected_connection_id_length) { 23 | } 24 | 25 | QuicProxyDispatcher::~QuicProxyDispatcher() = default; 26 | 27 | QuicServerSessionBase* QuicProxyDispatcher::CreateQuicSession( 28 | QuicConnectionId connection_id, 29 | const QuicSocketAddress& client_address, 30 | QuicStringPiece /*alpn*/, 31 | const ParsedQuicVersion& version) { 32 | // The QuicServerSessionBase takes ownership of |connection| below. 33 | QuicConnection* connection = new QuicConnection( 34 | connection_id, client_address, helper(), alarm_factory(), writer(), 35 | /* owns_writer= */ false, Perspective::IS_SERVER, 36 | ParsedQuicVersionVector{version}); 37 | 38 | QuicServerSessionBase* session = new QuicProxySession( 39 | config(), GetSupportedVersions(), connection, this, session_helper(), 40 | crypto_config(), compressed_certs_cache(), server_backend()); 41 | session->Initialize(); 42 | return session; 43 | } 44 | 45 | } // namespace quic 46 | -------------------------------------------------------------------------------- /proxy_quic/quic_proxy_stream.cc: -------------------------------------------------------------------------------- 1 | #include "net/third_party/quiche/src/quic/proxy_quic/quic_proxy_stream.h" 2 | #include "net/third_party/quiche/src/quic/proxy_quic/quic_proxy_curl.h" 3 | #include "net/third_party/quiche/src/quic/core/quic_session.h" 4 | 5 | namespace quic { 6 | 7 | QuicProxyStream::QuicProxyStream( 8 | QuicStreamId id, 9 | QuicSpdySession* session, 10 | StreamType type, 11 | QuicSimpleServerBackend* quic_simple_server_backend) 12 | : QuicSimpleServerStream(id, session, type, quic_simple_server_backend), 13 | quic_proxy_curl_(nullptr) { 14 | } 15 | 16 | QuicProxyStream::QuicProxyStream( 17 | PendingStream* pending, 18 | QuicSpdySession* session, 19 | StreamType type, 20 | QuicSimpleServerBackend* quic_simple_server_backend) 21 | : QuicSimpleServerStream(pending, session, type, quic_simple_server_backend), 22 | quic_proxy_curl_(nullptr) { 23 | } 24 | 25 | QuicProxyStream::~QuicProxyStream() = default; 26 | 27 | std::string QuicProxyStream::get_peer_ip() { 28 | return session()->peer_address().host().Normalized().ToString(); 29 | } 30 | 31 | bool QuicProxyStream::OnStreamFrameAcked(QuicStreamOffset offset, 32 | QuicByteCount data_length, 33 | bool fin_acked, 34 | QuicTime::Delta ack_delay_time, 35 | QuicByteCount* newly_acked_length) { 36 | bool rs = QuicSimpleServerStream::OnStreamFrameAcked(offset, 37 | data_length, 38 | fin_acked, 39 | ack_delay_time, 40 | newly_acked_length); 41 | if (quic_proxy_curl_) { 42 | quic_proxy_curl_->ContinueDownload(BufferedDataBytes()); 43 | } else if (false == fin_sent() && 44 | BufferedDataBytes() == 0) { 45 | // quic_proxy_curl_ is nullptr and close this stream 46 | set_fin_sent(true); 47 | CloseWriteSide(); 48 | } 49 | return rs; 50 | } 51 | 52 | 53 | 54 | } // namespace quic 55 | -------------------------------------------------------------------------------- /proxy_quic/quic_proxy_packet_writer.cc: -------------------------------------------------------------------------------- 1 | #include "net/third_party/quiche/src/quic/proxy_quic/quic_proxy_packet_writer.h" 2 | 3 | namespace quic { 4 | 5 | QuicProxyPacketWriter::QuicProxyPacketWriter(int fd) 6 | : QuicDefaultPacketWriter(fd),writes_cache_pos_(0) {} 7 | 8 | QuicProxyPacketWriter::~QuicProxyPacketWriter() = default; 9 | 10 | WriteResult QuicProxyPacketWriter::WritePacket( 11 | const char* buffer, 12 | size_t buf_len, 13 | const QuicIpAddress& self_address, 14 | const QuicSocketAddress& peer_address, 15 | PerPacketOptions* options) { 16 | DCHECK(!IsWriteBlocked()); 17 | DCHECK(nullptr == options) 18 | << "QuicProxyPacketWriter does not accept any options."; 19 | 20 | if (buffered_writes_.size() >= max_cache_buffer_write_size) { 21 | Flush(); 22 | if (buffered_writes_.size() >= max_cache_buffer_write_size) { 23 | set_write_blocked(true); 24 | return WriteResult(WRITE_STATUS_BLOCKED, EAGAIN); 25 | } 26 | } 27 | 28 | char *cpy_buffer = writes_cache_[writes_cache_pos_%max_cache_buffer_write_size]; 29 | memcpy(cpy_buffer, buffer, buf_len); 30 | writes_cache_pos_++; 31 | buffered_writes_.emplace_back(cpy_buffer, buf_len, self_address, peer_address); 32 | if (buffered_writes_.size() >= max_cache_buffer_write_size) { 33 | // send 34 | WriteResult result = Flush(); 35 | if (IsWriteBlockedStatus(result.status)) { 36 | set_write_blocked(true); 37 | return result; 38 | } 39 | } 40 | 41 | return WriteResult(WRITE_STATUS_OK, buf_len); 42 | } 43 | 44 | WriteResult QuicProxyPacketWriter::Flush() { 45 | if (buffered_writes_.empty()) 46 | return WriteResult(WRITE_STATUS_OK, 0); 47 | 48 | QuicMMsgHdr mhdr( 49 | buffered_writes_.begin(), buffered_writes_.end(), kCmsgSpaceForIp, 50 | [](QuicMMsgHdr* mhdr, int i, const BufferedWrite& buffered_write) { 51 | mhdr->SetIpInNextCmsg(i, buffered_write.self_address); 52 | }); 53 | int num_packets_sent = 0; 54 | WriteResult result = QuicLinuxSocketUtils::WriteMultiplePackets( 55 | fd(), &mhdr, &num_packets_sent); 56 | if (num_packets_sent > 0) { 57 | buffered_writes_.erase(buffered_writes_.begin(), 58 | buffered_writes_.begin() + num_packets_sent); 59 | } 60 | return result; 61 | } 62 | 63 | 64 | } // namespace quic 65 | -------------------------------------------------------------------------------- /proxy_quic/quic_proxy_session.cc: -------------------------------------------------------------------------------- 1 | #include "net/third_party/quiche/src/quic/proxy_quic/quic_proxy_session.h" 2 | #include "net/third_party/quiche/src/quic/proxy_quic/quic_proxy_stream.h" 3 | 4 | 5 | namespace quic { 6 | 7 | QuicProxySession::QuicProxySession( 8 | const QuicConfig& config, 9 | const ParsedQuicVersionVector& supported_versions, 10 | QuicConnection* connection, 11 | QuicSession::Visitor* visitor, 12 | QuicCryptoServerStream::Helper* helper, 13 | const QuicCryptoServerConfig* crypto_config, 14 | QuicCompressedCertsCache* compressed_certs_cache, 15 | QuicSimpleServerBackend* quic_simple_server_backend) 16 | : QuicSimpleServerSession(config, 17 | supported_versions, 18 | connection, 19 | visitor, 20 | helper, 21 | crypto_config, 22 | compressed_certs_cache, 23 | quic_simple_server_backend) { 24 | } 25 | 26 | QuicProxySession::~QuicProxySession() = default; 27 | 28 | QuicSpdyStream* QuicProxySession::CreateIncomingStream(QuicStreamId id) { 29 | if (!ShouldCreateIncomingStream(id)) { 30 | return nullptr; 31 | } 32 | 33 | QuicSpdyStream* stream = new QuicProxyStream( 34 | id, this, BIDIRECTIONAL, server_backend()); 35 | ActivateStream(QuicWrapUnique(stream)); 36 | return stream; 37 | } 38 | 39 | QuicSpdyStream* QuicProxySession::CreateIncomingStream( 40 | PendingStream* pending) { 41 | QuicSpdyStream* stream = new QuicProxyStream( 42 | pending, this, BIDIRECTIONAL, server_backend()); 43 | ActivateStream(QuicWrapUnique(stream)); 44 | return stream; 45 | } 46 | 47 | QuicSimpleServerStream* 48 | QuicProxySession::CreateOutgoingBidirectionalStream() { 49 | DCHECK(false); 50 | return nullptr; 51 | } 52 | 53 | QuicSimpleServerStream* 54 | QuicProxySession::CreateOutgoingUnidirectionalStream() { 55 | if (!ShouldCreateOutgoingUnidirectionalStream()) { 56 | return nullptr; 57 | } 58 | 59 | QuicSimpleServerStream* stream = new QuicProxyStream( 60 | GetNextOutgoingUnidirectionalStreamId(), this, WRITE_UNIDIRECTIONAL, 61 | server_backend()); 62 | ActivateStream(QuicWrapUnique(stream)); 63 | return stream; 64 | } 65 | 66 | } // namespace quic 67 | -------------------------------------------------------------------------------- /proxy_quic/quic_proxy_packet_reader.h: -------------------------------------------------------------------------------- 1 | #ifndef QUICHE_QUIC_CORE_PROXYQUIC_PACKET_READER_H_ 2 | #define QUICHE_QUIC_CORE_PROXYQUIC_PACKET_READER_H_ 3 | 4 | #include "net/third_party/quiche/src/quic/core/quic_packet_reader.h" 5 | 6 | 7 | namespace quic { 8 | 9 | #ifndef MMSG_MORE 10 | #define MMSG_MORE 0 11 | #endif 12 | 13 | #ifndef MMSG_MORE_NO_ANDROID 14 | #define MMSG_MORE_NO_ANDROID 0 15 | #endif 16 | 17 | #if !MMSG_MORE 18 | // Read in larger batches to minimize recvmmsg overhead. 19 | const int kNumPacketsPerReadMmsgCall = 16; 20 | #endif 21 | 22 | class QuicProxyPacketReader : public QuicPacketReader { 23 | public: 24 | explicit QuicProxyPacketReader(); 25 | QuicProxyPacketReader(const QuicProxyPacketReader&) = delete; 26 | QuicProxyPacketReader& operator=(const QuicProxyPacketReader&) = delete; 27 | 28 | ~QuicProxyPacketReader() override; 29 | 30 | // Reads a number of packets from the given fd, and then passes them off to 31 | // the PacketProcessInterface. Returns true if there may be additional 32 | // packets available on the socket. 33 | // Populates |packets_dropped| if it is non-null and the socket is configured 34 | // to track dropped packets and some packets are read. 35 | // If the socket has timestamping enabled, the per packet timestamps will be 36 | // passed to the processor. Otherwise, |clock| will be used. 37 | bool ReadAndDispatchPackets(int fd, 38 | int port, 39 | const QuicClock& clock, 40 | ProcessPacketInterface* processor, 41 | QuicPacketCount* packets_dropped) override; 42 | 43 | private: 44 | #if !MMSG_MORE 45 | // Storage only used when recvmmsg is available. 46 | // TODO(danzh): change it to be a pointer to avoid the allocation on the stack 47 | // from exceeding maximum allowed frame size. 48 | // packets_ and mmsg_hdr_ are used to supply cbuf and buf to the recvmmsg 49 | // call. 50 | struct PacketData { 51 | iovec iov; 52 | // raw_address is used for address information provided by the recvmmsg 53 | // call on the packets. 54 | struct sockaddr_storage raw_address; 55 | // cbuf is used for ancillary data from the kernel on recvmmsg. 56 | char cbuf[kCmsgSpaceForReadPacket]; 57 | // buf is used for the data read from the kernel on recvmmsg. 58 | char buf[kMaxV4PacketSize]; 59 | }; 60 | PacketData packets_[kNumPacketsPerReadMmsgCall]; 61 | mmsghdr mmsg_hdr_[kNumPacketsPerReadMmsgCall]; 62 | #endif 63 | }; 64 | 65 | } // namespace quic 66 | 67 | #endif // QUICHE_QUIC_CORE_PROXYQUIC_PACKET_READER_H_ 68 | -------------------------------------------------------------------------------- /proxy_quic/quic_proxy_backend.h: -------------------------------------------------------------------------------- 1 | #ifndef QUICHE_QUIC_PROXYQUIC_QUIC_PROXY_BACKEND_H_ 2 | #define QUICHE_QUIC_PROXYQUIC_QUIC_PROXY_BACKEND_H_ 3 | 4 | 5 | #include 6 | 7 | #include "net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h" 8 | #include "net/third_party/quiche/src/quic/proxy_quic/quic_proxy_curl.h" 9 | #include "net/third_party/quiche/src/quic/proxy_quic/quic_curl_timer.h" 10 | #include "net/third_party/quiche/src/spdy/core/spdy_framer.h" 11 | 12 | namespace quic { 13 | 14 | class QuicProxyServer; 15 | class QuicProxyStream; 16 | 17 | class QuicProxyBackend : public QuicSimpleServerBackend { 18 | public: 19 | QuicProxyBackend(); 20 | QuicProxyBackend(const QuicProxyBackend&) = delete; 21 | QuicProxyBackend& operator=(const QuicProxyBackend&) = delete; 22 | ~QuicProxyBackend() override; 23 | 24 | QuicProxyServer *get_server() { return server_; } 25 | void set_server(QuicProxyServer *server) { server_ = server; } 26 | CURLM *get_curlm() { return multi_curl_; } 27 | QuicCurlTimer* get_curl_timer() { return &curl_timer_; } 28 | 29 | bool InitializeBackend(const std::string& backend_url) override; 30 | bool IsBackendInitialized() const override; 31 | void FetchResponseFromBackend( 32 | const spdy::SpdyHeaderBlock& request_headers, 33 | const std::string& request_body, 34 | QuicSimpleServerBackend::RequestHandler* quic_server_stream) override; 35 | void CloseBackendResponseStream( 36 | QuicSimpleServerBackend::RequestHandler* quic_server_stream) override; 37 | 38 | void InitializeCURLM(); 39 | void CheckCurlMultiInfo(); 40 | 41 | int still_running_; 42 | 43 | private: 44 | 45 | /* CURLMOPT_SOCKETFUNCTION */ 46 | static int CurlSockCB(CURL *e, curl_socket_t s, int what, 47 | void *cbp, void *sockp); 48 | void CurlRemoveSock(QuicProxyCurl* proxy); 49 | void CurlAddSock(curl_socket_t s, CURL *easy, int action); 50 | void CurlSetSock(QuicProxyCurl* proxy, curl_socket_t s, 51 | CURL *e, int act); 52 | 53 | static int CurlMultiTimerCB(CURLM *multi, long timeout_ms, 54 | QuicProxyBackend *backend); 55 | 56 | bool CreateProxyCurl(const std::string& request_url, 57 | QuicProxyStream* quic_stream, 58 | std::shared_ptr& proxy); 59 | 60 | bool cache_initialized_; 61 | 62 | // curl multiplexing IO 63 | CURLM *multi_curl_; 64 | 65 | QuicProxyServer *server_; 66 | std::unordered_map> proxy_http_hash_; 67 | std::list> proxy_http_dead_list_; 68 | 69 | QuicCurlTimer curl_timer_; 70 | GURL backend_url_; 71 | }; 72 | 73 | } // namespace quic 74 | 75 | #endif // QUICHE_QUIC_PROXYQUIC_QUIC_PROXY_BACKEND_H_ 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 介绍 2 | 3 | __推荐我的新项目__ https://github.com/evansun922/nginx-quic 4 | 5 | quic_proxy是一个quic代理服务,接收前端发来的quic协议请求,然后http代理到后端业务服务,例如nginx等。 6 | 7 | chromium 项目本身提供了一个测试用的quic服务,但是这个服务是单进程,且http回源需要整个文件下载完才能发给前端,大的视频文件这种方式是不能接受的。 8 | 在epoll_quic_server这个demo基础上,基于chromium架构,重新开发了一个支持高并发,可商用的quic to http代理服务,其特点: 9 | 10 | - 使用SO_REUSEADDR,提高udp服务使用多核的能力。 11 | - 使用recvmmsg,sendmmsg减少用户态与内核态切换,降低cpu,提高服务性能。 12 | - 使用chromium项目提供的quic协议,保证了总能使用最新版本的quic协议,避免使用其他quic开源项目协议更新慢,实现不完全的苦恼。 13 | - 本项目是c++项目,仅支持linux系统。 14 | - 由于chromium项目过于复杂难读,目前回源使用的是libcurl,未使用chromium项目自带的http代码。 15 | 16 | ## 编译步骤 17 | 18 | 1. 下载chromium,详见: [chromium下载](https://chromium.googlesource.com/chromium/src/+/master/docs/linux_build_instructions.md/) 19 | 2. 下载编译[libcurl](https://curl.haxx.se/download.html), 20 | 请使用boringssl编译libcurl,或者 --without-ssl,参考编译选项:./configure --enable-debug --disable-optimize --with-ssl=/path/boringssl --enable-shared=no --disable-pthreads 21 | 3. copy代码目录proxy_quic到net/third_party/quiche/src/quic/下。 22 | 4. 修改 chromium项目的 net/BUILD.gn 文件,找到 epoll_quic_server 服务配置,在下面追加以下内容: 23 | 24 | ```executable("quic_proxy_server") { 25 | sources = [ 26 | "third_party/quiche/src/quic/proxy_quic/quic_proxy_server_bin.cc", 27 | "third_party/quiche/src/quic/proxy_quic/quic_proxy_server.cc", 28 | "third_party/quiche/src/quic/proxy_quic/quic_proxy_backend.cc", 29 | "third_party/quiche/src/quic/proxy_quic/quic_proxy_curl.cc", 30 | "third_party/quiche/src/quic/proxy_quic/quic_curl_timer.cc", 31 | "third_party/quiche/src/quic/proxy_quic/quic_proxy_packet_writer.cc", 32 | "third_party/quiche/src/quic/proxy_quic/quic_proxy_packet_reader.cc", 33 | "third_party/quiche/src/quic/proxy_quic/sendmmsgtimer.cc", 34 | "third_party/quiche/src/quic/proxy_quic/quic_proxy_dispatcher.cc", 35 | "third_party/quiche/src/quic/proxy_quic/quic_proxy_session.cc", 36 | "third_party/quiche/src/quic/proxy_quic/quic_proxy_stream.cc", 37 | ] 38 | include_dirs = [ 39 | "/usr/local/include" 40 | ] 41 | deps = [ 42 | ":epoll_quic_tools", 43 | ":epoll_server", 44 | ":net", 45 | ":simple_quic_tools", 46 | "//base", 47 | "//third_party/boringssl", 48 | ] 49 | lib_dirs = [ 50 | "/usr/local/lib" 51 | ] 52 | libs = [ 53 | #"curl", 54 | "/usr/local/lib/libcurl.a", 55 | "/usr/lib/x86_64-linux-gnu/libz.a", 56 | ] 57 | } 58 | ``` 59 | 60 | 5. cd src; gn gen out/Debug 61 | 6. ninja -C out/Debug quic_proxy_server (5,6两步具体参数的使用,详见:[Build the QUIC client and server](https://www.chromium.org/quic/playing-with-quic)) 62 | 63 | ## 运行 64 | 前台运行: 65 | 66 | ``` 67 | out/Debug/quic_proxy_server --quic_proxy_backend_url=http://backend-host --certificate_file=/path/you.crt --key_file=/path/you.pkcs8 68 | ``` 69 | 70 | 后台运行: 71 | ``` 72 | ./logrun.pl /path/log-dir out/Debug/quic_proxy_server --port=443 --daemon=true --quic_proxy_backend_url=http://backend-host --certificate_file=/path/you.crt --key_file=/path/you.pkcs8 73 | ``` 74 | 75 | ## 联系 76 | 欢迎提出改进或者bug等问题,作者邮箱:sswin0922@163.com, QQ:15543852 77 | -------------------------------------------------------------------------------- /proxy_quic/quic_proxy_curl.h: -------------------------------------------------------------------------------- 1 | #ifndef QUICHE_QUIC_PROXYQUIC_QUIC_PROXY_HTTP_H_ 2 | #define QUICHE_QUIC_PROXYQUIC_QUIC_PROXY_HTTP_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h" 8 | #include "net/third_party/quiche/src/spdy/core/spdy_framer.h" 9 | 10 | namespace quic { 11 | 12 | class QuicProxyBackend; 13 | class QuicProxyStream; 14 | 15 | class QuicProxyCurl : public QuicEpollCallbackInterface { 16 | public: 17 | QuicProxyCurl(const std::string& request_url, 18 | QuicProxyBackend* backend, 19 | QuicProxyStream* quic_stream); 20 | QuicProxyCurl(const QuicProxyCurl&) = delete; 21 | QuicProxyCurl& operator=(const QuicProxyCurl&) = delete; 22 | ~QuicProxyCurl() override; 23 | 24 | CURL* get_curl() { return easy_; } 25 | int get_sockfd() { return sockfd_; } 26 | void set_sockfd(int fd) { sockfd_ = fd; } 27 | QuicProxyStream* get_stream() { return quic_stream_; } 28 | void set_stream(QuicProxyStream* stream) { quic_stream_ = stream; } 29 | uint32_t get_stream_id() { return stream_id_; } 30 | 31 | bool InitializeProxyCurl(); 32 | void StartHttp(const spdy::SpdyHeaderBlock& request_headers, 33 | const std::string& request_body); 34 | void ContinueDownload(uint64_t buffer_size); 35 | 36 | std::string Name() const override { return "QuicProxyCurl"; } 37 | // From EpollCallbackInterface 38 | void OnRegistration(QuicEpollServer* eps, int fd, int event_mask) override {} 39 | void OnModification(int fd, int event_mask) override {} 40 | void OnEvent(int fd, QuicEpollEvent* event) override; 41 | void OnUnregistration(int fd, bool replaced) override {} 42 | 43 | void OnShutdown(QuicEpollServer* eps, int fd) override {} 44 | 45 | 46 | private: 47 | static size_t WriteQuicCallback(void* contents, size_t size, 48 | size_t nmemb, void* userp); 49 | static size_t HttpHeaderCallback(void* contents, size_t size, 50 | size_t nmemb, void* userp); 51 | static size_t ReadQuicCallback(void* dest, 52 | size_t size, 53 | size_t nmemb, 54 | void* userp); 55 | static int ProgressQuicCallback(void* userp, 56 | double dltotal, 57 | double dlnow, 58 | double ultotal, 59 | double ulnow); 60 | 61 | int sockfd_; 62 | CURL* easy_; 63 | std::string request_url_; 64 | char error_[CURL_ERROR_SIZE]; 65 | QuicProxyBackend* backend_; 66 | QuicProxyStream* quic_stream_; 67 | uint32_t stream_id_; 68 | bool is_pause_; 69 | 70 | spdy::SpdyHeaderBlock spdy_headers_; 71 | size_t content_length_; 72 | size_t had_send_length_; 73 | 74 | struct curl_slist *request_header_list_; 75 | std::string request_body_; 76 | size_t request_body_pos_; 77 | 78 | }; 79 | 80 | } // namespace quic 81 | 82 | #endif // QUICHE_QUIC_PROXYQUIC_QUIC_PROXY_HTTP_H_ 83 | -------------------------------------------------------------------------------- /README-en.md: -------------------------------------------------------------------------------- 1 | ## QUIC Proxy 2 | 3 | __Recommend my new project__ https://github.com/evansun922/nginx-quic 4 | 5 | quic_proxy is a high-performance quic proxy service. it receive a request with quic from frontend and proxy to backend, e.g. nginx, etc. 6 | 7 | The chromium provide a demo service of quic, but this demo is a single process, and it download the full file from source station before it can be sent to the frontend, this is not acceptable for big file. 8 | 9 | We have rewritten epoll_quic_server based on the chromium, the new quic server support for high concurrency. its features: 10 | 11 | - Use SO_REUSEADDR to improve the ability of services to use multiple cores. 12 | - Use recvmmsg,sendmmsg to reduce user mode and kernel mode switching and improve service performance. 13 | - Use quic of chromium, we can always use the latest version of quic. 14 | - This project is a c++ project, support for linux systems only. 15 | - This project use libcurl to backend. 16 | 17 | ## Building 18 | 19 | 1. Download chromium, see [how to download chromium](https://chromium.googlesource.com/chromium/src/+/master/docs/linux_build_instructions.md/). 20 | 2. Download and build [libcurl](https://curl.haxx.se/download.html), you must build libcurl with boringssl, or use --without-ssl, refer to: ./configure --enable-debug --disable-optimize --with-ssl=/path/boringssl --enable-shared=no --disable-pthreads . 21 | 3. Copy dir "proxy_quic" to "net/third_party/quiche/src/quic/" . 22 | 4. Modify the "net/BUILD.gn" in chromium that find the configuration of epoll_quic_server and append the following: 23 | 24 | ```executable("quic_proxy_server") { 25 | sources = [ 26 | "third_party/quiche/src/quic/proxy_quic/quic_proxy_server_bin.cc", 27 | "third_party/quiche/src/quic/proxy_quic/quic_proxy_server.cc", 28 | "third_party/quiche/src/quic/proxy_quic/quic_proxy_backend.cc", 29 | "third_party/quiche/src/quic/proxy_quic/quic_proxy_curl.cc", 30 | "third_party/quiche/src/quic/proxy_quic/quic_curl_timer.cc", 31 | "third_party/quiche/src/quic/proxy_quic/quic_proxy_packet_writer.cc", 32 | "third_party/quiche/src/quic/proxy_quic/quic_proxy_packet_reader.cc", 33 | "third_party/quiche/src/quic/proxy_quic/sendmmsgtimer.cc", 34 | "third_party/quiche/src/quic/proxy_quic/quic_proxy_dispatcher.cc", 35 | "third_party/quiche/src/quic/proxy_quic/quic_proxy_session.cc", 36 | "third_party/quiche/src/quic/proxy_quic/quic_proxy_stream.cc", 37 | ] 38 | include_dirs = [ 39 | "/usr/local/include" 40 | ] 41 | deps = [ 42 | ":epoll_quic_tools", 43 | ":epoll_server", 44 | ":net", 45 | ":simple_quic_tools", 46 | "//base", 47 | "//third_party/boringssl", 48 | ] 49 | lib_dirs = [ 50 | "/usr/local/lib" 51 | ] 52 | libs = [ 53 | #"curl", 54 | "/usr/local/lib/libcurl.a", 55 | "/usr/lib/x86_64-linux-gnu/libz.a", 56 | ] 57 | } 58 | ``` 59 | 60 | 5. cd src; gn gen out/Debug 61 | 6. ninja -C out/Debug quic_proxy_server (step 5, 6 see [Build the QUIC client and server](https://www.chromium.org/quic/playing-with-quic)) 62 | 63 | ## Running 64 | ``` 65 | out/Debug/quic_proxy_server --quic_proxy_backend_url=http://backend-host --certificate_file=/path/you.crt --key_file=/path/you.pkcs8 66 | ``` 67 | for daemon: 68 | ``` 69 | ./logrun.pl /path/log-dir out/Debug/quic_proxy_server --port=443 --daemon=true --quic_proxy_backend_url=http://backend-host --certificate_file=/path/you.crt --key_file=/path/you.pkcs8 70 | ``` 71 | 72 | 73 | ## Contact 74 | my email: sswin0922@163.com 75 | -------------------------------------------------------------------------------- /proxy_quic/quic_proxy_server_bin.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // A binary wrapper for QuicServer. It listens forever on --port 6 | // (default 6121) until it's killed or ctrl-cd to death. 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "base/at_exit.h" 13 | #include "net/third_party/quiche/src/quic/platform/api/quic_default_proof_providers.h" 14 | #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" 15 | #include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" 16 | #include "net/third_party/quiche/src/quic/proxy_quic/quic_proxy_backend.h" 17 | #include "net/third_party/quiche/src/quic/proxy_quic/quic_proxy_server.h" 18 | 19 | DEFINE_QUIC_COMMAND_LINE_FLAG(int32_t, 20 | port, 21 | 6121, 22 | "The port the quic server will listen on."); 23 | 24 | DEFINE_QUIC_COMMAND_LINE_FLAG( 25 | std::string, 26 | quic_proxy_backend_url, 27 | "", 28 | "://:" 29 | " The URL for the single backend server hostname" 30 | " For example, \"http://xyz.com:80\""); 31 | 32 | DEFINE_QUIC_COMMAND_LINE_FLAG(bool, 33 | daemon, 34 | false, 35 | "The daemon runing."); 36 | 37 | DEFINE_QUIC_COMMAND_LINE_FLAG(bool, 38 | bbr, 39 | false, 40 | "Use bbr congestion control," 41 | " default cubic"); 42 | 43 | DEFINE_QUIC_COMMAND_LINE_FLAG(bool, 44 | test, 45 | false, 46 | "Runing single process for test"); 47 | 48 | DEFINE_QUIC_COMMAND_LINE_FLAG(int32_t, 49 | sendmmsgtimer_interval, 50 | 40000000, 51 | "Specify an interval to refresh the cache to send data" 52 | " in nanoseconds," 53 | " default 40000000"); 54 | 55 | DEFINE_QUIC_COMMAND_LINE_FLAG(int32_t, 56 | idle_network_timeout, 57 | -1, 58 | "Idle network timeout in seconds, " 59 | " default 600s"); 60 | 61 | 62 | static void worker(); 63 | 64 | int main(int argc, char* argv[]) { 65 | base::AtExitManager exit_manager; 66 | 67 | const char* usage = "Usage: quic_server [options]"; 68 | std::vector non_option_args = 69 | quic::QuicParseCommandLineFlags(usage, argc, argv); 70 | if (!non_option_args.empty()) { 71 | quic::QuicPrintCommandLineFlagHelp(usage); 72 | exit(0); 73 | } 74 | 75 | logging::LoggingSettings settings; 76 | settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; 77 | CHECK(logging::InitLogging(settings)); 78 | 79 | if (true == GetQuicFlag(FLAGS_bbr)) { 80 | SetQuicReloadableFlag(quic_default_to_bbr, true); 81 | } 82 | 83 | if (true == GetQuicFlag(FLAGS_test)) { 84 | // test 85 | worker(); 86 | return 0; 87 | } 88 | 89 | if (true == GetQuicFlag(FLAGS_daemon)) { 90 | if (0 !=daemon(1, 1) ) { 91 | exit(0); 92 | } 93 | } 94 | 95 | int cpu_count = ::get_nprocs(); 96 | for(int i = 0; i < cpu_count; i++) { 97 | pid_t pid = ::fork(); 98 | if (-1 == pid) { 99 | exit(0); 100 | } else if (0 == pid) { 101 | cpu_set_t mask; 102 | CPU_ZERO(&mask); 103 | CPU_SET(i,&mask); 104 | ::sched_setaffinity(0, sizeof(mask), &mask); 105 | worker(); 106 | exit(0); 107 | } 108 | } 109 | 110 | int wstatus; 111 | while (true) { 112 | pid_t pid = ::wait(&wstatus); 113 | if (-1 == pid) { 114 | continue; 115 | } 116 | pid = ::fork(); 117 | if (0 == pid) { 118 | worker(); 119 | } 120 | } 121 | 122 | return 0; 123 | } 124 | 125 | 126 | static void worker() { 127 | // init global curl 128 | curl_global_init(CURL_GLOBAL_ALL); 129 | 130 | quic::QuicProxyBackend proxy_backend; 131 | if (!GetQuicFlag(FLAGS_quic_proxy_backend_url).empty()) { 132 | proxy_backend.InitializeBackend( 133 | GetQuicFlag(FLAGS_quic_proxy_backend_url)); 134 | } 135 | 136 | quic::QuicProxyServer server(quic::CreateDefaultProofSource(), 137 | &proxy_backend, 138 | GetQuicFlag(FLAGS_sendmmsgtimer_interval), 139 | GetQuicFlag(FLAGS_idle_network_timeout)); 140 | 141 | if (!server.CreateUDPSocketAndListen(quic::QuicSocketAddress( 142 | quic::QuicIpAddress::Any6(), GetQuicFlag(FLAGS_port)))) { 143 | return; 144 | } 145 | 146 | proxy_backend.InitializeCURLM(); 147 | 148 | while (true) { 149 | server.WaitForEvents(); 150 | } 151 | 152 | curl_global_cleanup(); 153 | } 154 | -------------------------------------------------------------------------------- /proxy_quic/quic_proxy_server.h: -------------------------------------------------------------------------------- 1 | #ifndef QUICHE_QUIC_PROXYQUIC_QUIC_SERVER_H_ 2 | #define QUICHE_QUIC_PROXYQUIC_QUIC_SERVER_H_ 3 | 4 | #include 5 | 6 | #include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h" 7 | #include "net/third_party/quiche/src/quic/core/quic_config.h" 8 | #include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h" 9 | #include "net/third_party/quiche/src/quic/core/quic_framer.h" 10 | #include "net/third_party/quiche/src/quic/core/quic_packet_writer.h" 11 | #include "net/third_party/quiche/src/quic/core/quic_version_manager.h" 12 | #include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h" 13 | #include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" 14 | #include "net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h" 15 | 16 | namespace quic { 17 | 18 | namespace test { 19 | class QuicServerPeer; 20 | } // namespace test 21 | 22 | class QuicDispatcher; 23 | class QuicPacketReader; 24 | class SendMMsgTimer; 25 | 26 | class QuicProxyServer : public QuicEpollCallbackInterface { 27 | public: 28 | QuicProxyServer(std::unique_ptr proof_source, 29 | QuicSimpleServerBackend* quic_simple_server_backend, 30 | int interval, 31 | int idle_network_timeout); 32 | QuicProxyServer(std::unique_ptr proof_source, 33 | const QuicConfig& config, 34 | const QuicCryptoServerConfig::ConfigOptions& server_config_options, 35 | const ParsedQuicVersionVector& supported_versions, 36 | QuicSimpleServerBackend* quic_simple_server_backend, 37 | uint8_t expected_connection_id_length, 38 | int interval, 39 | int idle_network_timeout); 40 | QuicProxyServer(const QuicProxyServer&) = delete; 41 | QuicProxyServer& operator=(const QuicProxyServer&) = delete; 42 | 43 | ~QuicProxyServer() override; 44 | 45 | std::string Name() const override { return "QuicServer"; } 46 | 47 | // Start listening on the specified address. 48 | bool CreateUDPSocketAndListen(const QuicSocketAddress& address); 49 | 50 | // Wait up to 50ms, and handle any events which occur. 51 | void WaitForEvents(); 52 | 53 | // Server deletion is imminent. Start cleaning up the epoll server. 54 | virtual void Shutdown(); 55 | 56 | // From EpollCallbackInterface 57 | void OnRegistration(QuicEpollServer* eps, int fd, int event_mask) override {} 58 | void OnModification(int fd, int event_mask) override {} 59 | void OnEvent(int fd, QuicEpollEvent* event) override; 60 | void OnUnregistration(int fd, bool replaced) override {} 61 | 62 | void OnShutdown(QuicEpollServer* eps, int fd) override {} 63 | 64 | void SetChloMultiplier(size_t multiplier) { 65 | crypto_config_.set_chlo_multiplier(multiplier); 66 | } 67 | 68 | void SetPreSharedKey(QuicStringPiece key) { 69 | crypto_config_.set_pre_shared_key(key); 70 | } 71 | 72 | bool overflow_supported() { return overflow_supported_; } 73 | 74 | QuicPacketCount packets_dropped() { return packets_dropped_; } 75 | 76 | int port() { return port_; } 77 | QuicEpollServer* epoll_server() { return &epoll_server_; } 78 | 79 | QuicDispatcher* dispatcher() { return dispatcher_.get(); } 80 | 81 | protected: 82 | virtual QuicPacketWriter* CreateWriter(int fd); 83 | 84 | virtual QuicDispatcher* CreateQuicDispatcher(); 85 | 86 | const QuicConfig& config() const { return config_; } 87 | const QuicCryptoServerConfig& crypto_config() const { return crypto_config_; } 88 | 89 | QuicVersionManager* version_manager() { return &version_manager_; } 90 | 91 | QuicSimpleServerBackend* server_backend() { 92 | return quic_simple_server_backend_; 93 | } 94 | 95 | void set_silent_close(bool value) { silent_close_ = value; } 96 | 97 | uint8_t expected_connection_id_length() { 98 | return expected_connection_id_length_; 99 | } 100 | 101 | private: 102 | friend class quic::test::QuicServerPeer; 103 | 104 | // Initialize the internal state of the server. 105 | void Initialize(); 106 | 107 | // Accepts data from the framer and demuxes clients to sessions. 108 | std::unique_ptr dispatcher_; 109 | // Frames incoming packets and hands them to the dispatcher. 110 | QuicEpollServer epoll_server_; 111 | 112 | // The port the server is listening on. 113 | int port_; 114 | 115 | // Listening connection. Also used for outbound client communication. 116 | int fd_; 117 | 118 | // If overflow_supported_ is true this will be the number of packets dropped 119 | // during the lifetime of the server. This may overflow if enough packets 120 | // are dropped. 121 | QuicPacketCount packets_dropped_; 122 | 123 | // True if the kernel supports SO_RXQ_OVFL, the number of packets dropped 124 | // because the socket would otherwise overflow. 125 | bool overflow_supported_; 126 | 127 | // If true, do not call Shutdown on the dispatcher. Connections will close 128 | // without sending a final connection close. 129 | bool silent_close_; 130 | 131 | // config_ contains non-crypto parameters that are negotiated in the crypto 132 | // handshake. 133 | QuicConfig config_; 134 | // crypto_config_ contains crypto parameters for the handshake. 135 | QuicCryptoServerConfig crypto_config_; 136 | // crypto_config_options_ contains crypto parameters for the handshake. 137 | QuicCryptoServerConfig::ConfigOptions crypto_config_options_; 138 | 139 | // Used to generate current supported versions. 140 | QuicVersionManager version_manager_; 141 | 142 | // Point to a QuicPacketReader object on the heap. The reader allocates more 143 | // space than allowed on the stack. 144 | std::unique_ptr packet_reader_; 145 | 146 | QuicSimpleServerBackend* quic_simple_server_backend_; // unowned. 147 | 148 | // Connection ID length expected to be read on incoming IETF short headers. 149 | uint8_t expected_connection_id_length_; 150 | 151 | std::unique_ptr sendmmsg_timer_; 152 | int sendmmsgtimer_interval_; 153 | int idle_network_timeout_; 154 | }; 155 | 156 | } // namespace quic 157 | 158 | #endif // QUICHE_QUIC_PROXYQUIC_QUIC_SERVER_H_ 159 | -------------------------------------------------------------------------------- /proxy_quic/quic_proxy_packet_reader.cc: -------------------------------------------------------------------------------- 1 | #include "net/third_party/quiche/src/quic/proxy_quic/quic_proxy_packet_reader.h" 2 | 3 | #include 4 | #ifndef __APPLE__ 5 | // This is a GNU header that is not present on Apple platforms 6 | #include 7 | #endif 8 | #include 9 | #include 10 | 11 | #include "net/third_party/quiche/src/quic/core/quic_packets.h" 12 | #include "net/third_party/quiche/src/quic/core/quic_process_packet_interface.h" 13 | #include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" 14 | #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" 15 | #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" 16 | #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" 17 | #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" 18 | #include "net/third_party/quiche/src/quic/platform/api/quic_server_stats.h" 19 | #include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" 20 | #include "net/quic/platform/impl/quic_socket_utils.h" 21 | 22 | #ifndef SO_RXQ_OVFL 23 | #define SO_RXQ_OVFL 40 24 | #endif 25 | 26 | // #define QUIC_FLAG(type, flag, value) type flag = value; 27 | // QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_use_quic_time_for_received_timestamp, true) 28 | // #undef QUIC_FLAG 29 | 30 | namespace quic { 31 | 32 | QuicProxyPacketReader::QuicProxyPacketReader() { 33 | #if !MMSG_MORE 34 | // Zero initialize uninitialized memory. 35 | memset(mmsg_hdr_, 0, sizeof(mmsg_hdr_)); 36 | 37 | for (int i = 0; i < kNumPacketsPerReadMmsgCall; ++i) { 38 | packets_[i].iov.iov_base = packets_[i].buf; 39 | packets_[i].iov.iov_len = sizeof(packets_[i].buf); 40 | memset(&packets_[i].raw_address, 0, sizeof(packets_[i].raw_address)); 41 | memset(packets_[i].cbuf, 0, sizeof(packets_[i].cbuf)); 42 | memset(packets_[i].buf, 0, sizeof(packets_[i].buf)); 43 | 44 | msghdr* hdr = &mmsg_hdr_[i].msg_hdr; 45 | hdr->msg_name = &packets_[i].raw_address; 46 | hdr->msg_namelen = sizeof(sockaddr_storage); 47 | hdr->msg_iov = &packets_[i].iov; 48 | hdr->msg_iovlen = 1; 49 | 50 | hdr->msg_control = packets_[i].cbuf; 51 | hdr->msg_controllen = kCmsgSpaceForReadPacket; 52 | } 53 | #endif 54 | } 55 | 56 | QuicProxyPacketReader::~QuicProxyPacketReader() = default; 57 | 58 | bool QuicProxyPacketReader::ReadAndDispatchPackets( 59 | int fd, 60 | int port, 61 | const QuicClock& clock, 62 | ProcessPacketInterface* processor, 63 | QuicPacketCount* packets_dropped) { 64 | #if MMSG_MORE_NO_ANDROID 65 | return QuicPacketReader::ReadAndDispatchPackets( 66 | fd, port, clock, processor, packets_dropped); 67 | #else 68 | // Re-set the length fields in case recvmmsg has changed them. 69 | for (int i = 0; i < kNumPacketsPerReadMmsgCall; ++i) { 70 | // DCHECK_LE(kMaxPacketSize, packets_[i].iov.iov_len); 71 | msghdr* hdr = &mmsg_hdr_[i].msg_hdr; 72 | hdr->msg_namelen = sizeof(sockaddr_storage); 73 | DCHECK_EQ(1, static_cast(hdr->msg_iovlen)); 74 | hdr->msg_controllen = kCmsgSpaceForReadPacket; 75 | hdr->msg_flags = 0; 76 | } 77 | 78 | int packets_read = 79 | recvmmsg(fd, mmsg_hdr_, kNumPacketsPerReadMmsgCall, MSG_TRUNC, nullptr); 80 | 81 | if (packets_read <= 0) { 82 | return false; // recvmmsg failed. 83 | } 84 | 85 | // bool use_quic_time = 86 | // GetQuicReloadableFlag(quic_use_quic_time_for_received_timestamp); 87 | bool use_quic_time = true; 88 | QuicTime fallback_timestamp(QuicTime::Zero()); 89 | QuicWallTime fallback_walltimestamp = QuicWallTime::Zero(); 90 | for (int i = 0; i < packets_read; ++i) { 91 | if (mmsg_hdr_[i].msg_len == 0) { 92 | continue; 93 | } 94 | 95 | if (QUIC_PREDICT_FALSE(mmsg_hdr_[i].msg_hdr.msg_flags & MSG_CTRUNC)) { 96 | QUIC_BUG << "Incorrectly set control length: " 97 | << mmsg_hdr_[i].msg_hdr.msg_controllen << ", expected " 98 | << kCmsgSpaceForReadPacket; 99 | continue; 100 | } 101 | 102 | if (QUIC_PREDICT_FALSE(mmsg_hdr_[i].msg_hdr.msg_flags & MSG_TRUNC)) { 103 | QUIC_LOG_FIRST_N(WARNING, 100) 104 | << "Dropping truncated QUIC packet: buffer size:" 105 | << packets_[i].iov.iov_len << " packet size:" << mmsg_hdr_[i].msg_len; 106 | QUIC_SERVER_HISTOGRAM_COUNTS( 107 | "QuicProxyPacketReader.DroppedPacketSize", mmsg_hdr_[i].msg_len, 1, 10000, 108 | 20, "In QuicProxyPacketReader, the size of big packets that are dropped."); 109 | continue; 110 | } 111 | 112 | QuicSocketAddress peer_address(packets_[i].raw_address); 113 | QuicIpAddress self_ip; 114 | QuicWallTime packet_walltimestamp = QuicWallTime::Zero(); 115 | QuicSocketUtils::GetAddressAndTimestampFromMsghdr( 116 | &mmsg_hdr_[i].msg_hdr, &self_ip, &packet_walltimestamp); 117 | if (!self_ip.IsInitialized()) { 118 | QUIC_BUG << "Unable to get self IP address."; 119 | continue; 120 | } 121 | 122 | // This isn't particularly desirable, but not all platforms support socket 123 | // timestamping. 124 | QuicTime timestamp(QuicTime::Zero()); 125 | if (!use_quic_time) { 126 | if (packet_walltimestamp.IsZero()) { 127 | if (fallback_walltimestamp.IsZero()) { 128 | fallback_walltimestamp = clock.WallNow(); 129 | } 130 | packet_walltimestamp = fallback_walltimestamp; 131 | } 132 | timestamp = clock.ConvertWallTimeToQuicTime(packet_walltimestamp); 133 | 134 | } else { 135 | // QUIC_RELOADABLE_FLAG_COUNT(quic_use_quic_time_for_received_timestamp); 136 | if (packet_walltimestamp.IsZero()) { 137 | if (!fallback_timestamp.IsInitialized()) { 138 | fallback_timestamp = clock.Now(); 139 | } 140 | timestamp = fallback_timestamp; 141 | } else { 142 | timestamp = clock.ConvertWallTimeToQuicTime(packet_walltimestamp); 143 | } 144 | } 145 | int ttl = 0; 146 | bool has_ttl = 147 | QuicSocketUtils::GetTtlFromMsghdr(&mmsg_hdr_[i].msg_hdr, &ttl); 148 | // Because QuicSocketUtils::GetPacketHeadersFromMsghdr is not implemented 149 | // char* headers = nullptr; 150 | // size_t headers_length = 0; 151 | // QuicSocketUtils::GetPacketHeadersFromMsghdr(&mmsg_hdr_[i].msg_hdr, &headers, 152 | // &headers_length); 153 | // QuicReceivedPacket packet(reinterpret_cast(packets_[i].iov.iov_base), 154 | // mmsg_hdr_[i].msg_len, timestamp, false, ttl, 155 | // has_ttl, headers, headers_length, false); 156 | 157 | QuicReceivedPacket packet(reinterpret_cast(packets_[i].iov.iov_base), 158 | mmsg_hdr_[i].msg_len, timestamp, false, ttl, 159 | has_ttl, nullptr, 0, false); 160 | QuicSocketAddress self_address(self_ip, port); 161 | processor->ProcessPacket(self_address, peer_address, packet); 162 | } 163 | 164 | if (packets_dropped != nullptr) { 165 | QuicSocketUtils::GetOverflowFromMsghdr(&mmsg_hdr_[0].msg_hdr, 166 | packets_dropped); 167 | } 168 | 169 | // We may not have read all of the packets available on the socket. 170 | return packets_read == kNumPacketsPerReadMmsgCall; 171 | #endif // MMSG_MORE_NO_ANDROID 172 | } 173 | 174 | } // namespace quic 175 | -------------------------------------------------------------------------------- /proxy_quic/quic_proxy_backend.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "net/third_party/quiche/src/quic/proxy_quic/quic_proxy_backend.h" 5 | #include "net/third_party/quiche/src/quic/proxy_quic/quic_proxy_server.h" 6 | #include "net/third_party/quiche/src/quic/proxy_quic/quic_proxy_stream.h" 7 | 8 | using spdy::SpdyHeaderBlock; 9 | 10 | namespace quic { 11 | 12 | QuicProxyBackend::QuicProxyBackend() 13 | : still_running_(0), 14 | cache_initialized_(false), 15 | multi_curl_(curl_multi_init()), 16 | server_(nullptr), 17 | curl_timer_(this){} 18 | 19 | QuicProxyBackend::~QuicProxyBackend() { 20 | if (multi_curl_) { 21 | curl_multi_cleanup(multi_curl_); 22 | multi_curl_ = nullptr; 23 | } 24 | } 25 | 26 | bool QuicProxyBackend::InitializeBackend( 27 | const std::string& backend_url) { 28 | if (backend_url.empty()) { 29 | QUIC_BUG << "backend_url must not be empty."; 30 | return false; 31 | } 32 | 33 | backend_url_ = GURL(backend_url); 34 | // Only Http(s) backend supported 35 | if (!backend_url_.is_valid() || !backend_url_.SchemeIsHTTPOrHTTPS()) { 36 | LOG(ERROR) << "QUIC Proxy Backend URL '" << backend_url 37 | << "' is not valid !"; 38 | return false; 39 | } 40 | 41 | LOG(INFO) 42 | << "Successfully configured to run as a QUIC Proxy with Backend URL: " 43 | << backend_url_.spec(); 44 | 45 | cache_initialized_ = true; 46 | return true; 47 | } 48 | 49 | bool QuicProxyBackend::IsBackendInitialized() const { 50 | return cache_initialized_; 51 | } 52 | 53 | void QuicProxyBackend::FetchResponseFromBackend( 54 | const SpdyHeaderBlock& request_headers, 55 | const std::string& request_body, 56 | QuicSimpleServerBackend::RequestHandler* quic_stream) { 57 | 58 | for (auto it = request_headers.begin(); it != request_headers.end(); ++it) { 59 | LOG(INFO) << it->first.as_string() << ": " << it->second.as_string(); 60 | } 61 | 62 | std::shared_ptr proxy; 63 | auto path = request_headers.find(":path"); 64 | std::string url = backend_url_.spec() + 65 | std::string(path->second.substr(1, path->second.length())); 66 | QuicProxyStream* quic_proxy_stream = reinterpret_cast( 67 | static_cast(quic_stream)); 68 | if (false == CreateProxyCurl(url, 69 | quic_proxy_stream, 70 | proxy)) { 71 | LOG(WARNING) << "create proxy curl failed."; 72 | } 73 | 74 | quic_proxy_stream->set_proxy_curl(proxy.get()); 75 | proxy->StartHttp(request_headers, request_body); 76 | } 77 | 78 | // The memory cache does not have a per-stream handler 79 | void QuicProxyBackend::CloseBackendResponseStream( 80 | QuicSimpleServerBackend::RequestHandler* quic_stream) { 81 | QuicProxyStream* quic_proxy_stream = reinterpret_cast( 82 | static_cast(quic_stream)); 83 | LOG(INFO) << "CloseBackendResponseStream " << quic_proxy_stream->id(); 84 | QuicProxyCurl* proxy_curl = quic_proxy_stream->get_proxy_curl(); 85 | if (proxy_curl) { 86 | proxy_curl->set_stream(nullptr); 87 | quic_proxy_stream->set_proxy_curl(nullptr); 88 | } 89 | } 90 | 91 | void QuicProxyBackend::InitializeCURLM() { 92 | /* setup the generic multi interface options we want */ 93 | curl_multi_setopt(multi_curl_, CURLMOPT_SOCKETFUNCTION, 94 | QuicProxyBackend::CurlSockCB); 95 | curl_multi_setopt(multi_curl_, CURLMOPT_SOCKETDATA, this); 96 | curl_multi_setopt(multi_curl_, CURLMOPT_TIMERFUNCTION, 97 | QuicProxyBackend::CurlMultiTimerCB); 98 | curl_multi_setopt(multi_curl_, CURLMOPT_TIMERDATA, this); 99 | 100 | curl_timer_.InitializeCurlTimer(); 101 | } 102 | 103 | void QuicProxyBackend::CheckCurlMultiInfo() { 104 | char *eff_url; 105 | CURLMsg *msg; 106 | int msgs_left; 107 | QuicProxyCurl* proxy; 108 | CURL *easy; 109 | CURLcode res; 110 | bool is_clean_dead_proxy = true; 111 | 112 | while ((msg = curl_multi_info_read(multi_curl_, &msgs_left))) { 113 | if(msg->msg == CURLMSG_DONE) { 114 | easy = msg->easy_handle; 115 | res = msg->data.result; 116 | curl_easy_getinfo(easy, CURLINFO_PRIVATE, &proxy); 117 | curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &eff_url); 118 | // DVLOG(1) << 119 | LOG(INFO) << 120 | "DONE: " << eff_url << " => (" << res << ") stream id: " 121 | << proxy->get_stream_id(); 122 | 123 | curl_multi_remove_handle(multi_curl_, easy); 124 | auto proxy_ptr = proxy_http_hash_.find(easy); 125 | if (proxy_ptr != proxy_http_hash_.end()) { 126 | proxy_http_dead_list_.push_front(proxy_ptr->second); 127 | proxy_http_hash_.erase(proxy_ptr); 128 | } 129 | 130 | if (proxy->get_stream()) { 131 | proxy->get_stream()->set_proxy_curl(nullptr); 132 | proxy->set_stream(nullptr); 133 | } 134 | } 135 | is_clean_dead_proxy = false; 136 | } 137 | 138 | if (is_clean_dead_proxy) { 139 | proxy_http_dead_list_.clear(); 140 | } 141 | } 142 | 143 | 144 | 145 | int QuicProxyBackend::CurlSockCB(CURL *e, 146 | curl_socket_t s, 147 | int what, 148 | void *cbp, 149 | void *sockp) { 150 | QuicProxyBackend *backend = reinterpret_cast(cbp); 151 | QuicProxyCurl* proxy = reinterpret_cast(sockp); 152 | const char *what_str[] = { "none", "IN", "OUT", "INOUT", "REMOVE" }; 153 | 154 | DVLOG(1) 155 | // LOG(INFO) 156 | << "stream id: " << (proxy ? proxy->get_stream_id():0) 157 | << " socket callback: s=" << s << " e=" 158 | << e << " what=" << what_str[what]; 159 | 160 | if (what == CURL_POLL_REMOVE) { 161 | backend->CurlRemoveSock(proxy); 162 | } else { 163 | if (!proxy) { 164 | backend->CurlAddSock(s, e, what); 165 | } else { 166 | backend->CurlSetSock(proxy, s, e, what); 167 | } 168 | } 169 | return 0; 170 | } 171 | 172 | void QuicProxyBackend::CurlRemoveSock(QuicProxyCurl* proxy) { 173 | if (proxy) { 174 | if (proxy->get_sockfd() != -1) { 175 | server_->epoll_server()->UnregisterFD(proxy->get_sockfd()); 176 | } 177 | } 178 | } 179 | 180 | void QuicProxyBackend::CurlAddSock(curl_socket_t s, 181 | CURL *easy, 182 | int action) { 183 | auto proxy_iterator = proxy_http_hash_.find(easy); 184 | if (proxy_iterator == proxy_http_hash_.end()) { 185 | LOG(WARNING) << "Can not find proxy_iterator in proxy_http_hash_."; 186 | return; 187 | } 188 | 189 | QuicProxyCurl* proxy = proxy_iterator->second.get(); 190 | proxy->set_sockfd(s); 191 | server_->epoll_server()->RegisterFD(s, proxy, 192 | EPOLLIN | EPOLLOUT); 193 | CurlSetSock(proxy, s, easy, action); 194 | curl_multi_assign(multi_curl_, s, proxy); 195 | } 196 | 197 | void QuicProxyBackend::CurlSetSock(QuicProxyCurl* proxy, 198 | curl_socket_t s, 199 | CURL *e, int act) { 200 | if (act & CURL_POLL_IN) { 201 | server_->epoll_server()->StartRead(s); 202 | } else { 203 | server_->epoll_server()->StopRead(s); 204 | } 205 | 206 | if (act & CURL_POLL_OUT) { 207 | server_->epoll_server()->StartWrite(s); 208 | } else { 209 | server_->epoll_server()->StopWrite(s); 210 | } 211 | } 212 | 213 | int QuicProxyBackend::CurlMultiTimerCB(CURLM *multi, 214 | long timeout_ms, 215 | QuicProxyBackend *backend) { 216 | struct itimerspec its; 217 | 218 | if (timeout_ms > 0) { 219 | its.it_interval.tv_sec = 1; 220 | its.it_interval.tv_nsec = 0; 221 | its.it_value.tv_sec = timeout_ms / 1000; 222 | its.it_value.tv_nsec = (timeout_ms % 1000) * 1000 * 1000; 223 | } else if (timeout_ms == 0) { 224 | /* libcurl wants us to timeout now, however setting both fields of 225 | * new_value.it_value to zero disarms the timer. The closest we can 226 | * do is to schedule the timer to fire in 1 ns. */ 227 | its.it_interval.tv_sec = 1; 228 | its.it_interval.tv_nsec = 0; 229 | its.it_value.tv_sec = 0; 230 | its.it_value.tv_nsec = 1; 231 | } else { 232 | memset(&its, 0, sizeof(struct itimerspec)); 233 | } 234 | 235 | timerfd_settime(backend->curl_timer_.get_timerfd(), 0, &its, NULL); 236 | return 0; 237 | } 238 | 239 | bool QuicProxyBackend::CreateProxyCurl( 240 | const std::string& request_url, 241 | QuicProxyStream* quic_stream, 242 | std::shared_ptr& proxy) { 243 | auto proxy_ptr = std::make_shared(request_url, this, quic_stream); 244 | if (false == proxy_ptr->InitializeProxyCurl()) { 245 | return false; 246 | } 247 | 248 | proxy = proxy_ptr; 249 | proxy_http_hash_[proxy->get_curl()] = proxy; 250 | return true; 251 | } 252 | 253 | 254 | 255 | 256 | 257 | } // namespace quic 258 | -------------------------------------------------------------------------------- /proxy_quic/quic_proxy_server.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include "net/third_party/quiche/src/quic/proxy_quic/quic_proxy_server.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | #include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h" 18 | #include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" 19 | #include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h" 20 | #include "net/third_party/quiche/src/quic/core/quic_data_reader.h" 21 | #include "net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.h" 22 | #include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h" 23 | #include "net/third_party/quiche/src/quic/core/quic_packets.h" 24 | #include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h" 25 | #include "net/third_party/quiche/src/quic/platform/api/quic_clock.h" 26 | #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" 27 | #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" 28 | #include "net/quic/platform/impl/quic_epoll_clock.h" 29 | #include "net/quic/platform/impl/quic_socket_utils.h" 30 | #include "net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h" 31 | #include "net/third_party/quiche/src/quic/proxy_quic/quic_proxy_dispatcher.h" 32 | #include "net/third_party/quiche/src/quic/proxy_quic/quic_proxy_backend.h" 33 | #include "net/third_party/quiche/src/quic/proxy_quic/quic_proxy_packet_writer.h" 34 | #include "net/third_party/quiche/src/quic/proxy_quic/quic_proxy_packet_reader.h" 35 | #include "net/third_party/quiche/src/quic/proxy_quic/sendmmsgtimer.h" 36 | 37 | 38 | #ifndef SO_RXQ_OVFL 39 | #define SO_RXQ_OVFL 40 40 | #endif 41 | 42 | namespace quic { 43 | 44 | namespace { 45 | 46 | const int kEpollFlags = EPOLLIN | EPOLLOUT | EPOLLET; 47 | 48 | 49 | const char kSourceAddressTokenSecret[] = "secret"; 50 | 51 | } // namespace 52 | 53 | const size_t kNumSessionsToCreatePerSocketEvent = 16; 54 | 55 | 56 | QuicProxyServer::QuicProxyServer(std::unique_ptr proof_source, 57 | QuicSimpleServerBackend* quic_simple_server_backend, 58 | int interval, 59 | int idle_network_timeout) 60 | : QuicProxyServer(std::move(proof_source), 61 | QuicConfig(), 62 | QuicCryptoServerConfig::ConfigOptions(), 63 | AllSupportedVersions(), 64 | quic_simple_server_backend, 65 | kQuicDefaultConnectionIdLength, 66 | interval, 67 | idle_network_timeout){} 68 | 69 | QuicProxyServer::QuicProxyServer( 70 | std::unique_ptr proof_source, 71 | const QuicConfig& config, 72 | const QuicCryptoServerConfig::ConfigOptions& crypto_config_options, 73 | const ParsedQuicVersionVector& supported_versions, 74 | QuicSimpleServerBackend* quic_simple_server_backend, 75 | uint8_t expected_connection_id_length, 76 | int interval, 77 | int idle_network_timeout) 78 | : port_(0), 79 | fd_(-1), 80 | packets_dropped_(0), 81 | overflow_supported_(false), 82 | silent_close_(false), 83 | config_(config), 84 | crypto_config_(kSourceAddressTokenSecret, 85 | QuicRandom::GetInstance(), 86 | std::move(proof_source), 87 | KeyExchangeSource::Default()), 88 | crypto_config_options_(crypto_config_options), 89 | version_manager_(supported_versions), 90 | packet_reader_(new QuicProxyPacketReader()), 91 | // packet_reader_(new QuicPacketReader()), 92 | quic_simple_server_backend_(quic_simple_server_backend), 93 | expected_connection_id_length_(expected_connection_id_length), 94 | sendmmsgtimer_interval_(interval), 95 | idle_network_timeout_(idle_network_timeout){ 96 | if (-1 != idle_network_timeout_) { 97 | config_.SetIdleNetworkTimeout(QuicTime::Delta::FromSeconds(idle_network_timeout_), 98 | QuicTime::Delta::FromSeconds(idle_network_timeout_/2)); 99 | } 100 | Initialize(); 101 | } 102 | 103 | void QuicProxyServer::Initialize() { 104 | // If an initial flow control window has not explicitly been set, then use a 105 | // sensible value for a server: 1 MB for session, 64 KB for each stream. 106 | const uint32_t kInitialSessionFlowControlWindow = 1 * 1024 * 1024; // 1 MB 107 | const uint32_t kInitialStreamFlowControlWindow = 64 * 1024; // 64 KB 108 | if (config_.GetInitialStreamFlowControlWindowToSend() == 109 | kMinimumFlowControlSendWindow) { 110 | config_.SetInitialStreamFlowControlWindowToSend( 111 | kInitialStreamFlowControlWindow); 112 | } 113 | if (config_.GetInitialSessionFlowControlWindowToSend() == 114 | kMinimumFlowControlSendWindow) { 115 | config_.SetInitialSessionFlowControlWindowToSend( 116 | kInitialSessionFlowControlWindow); 117 | } 118 | 119 | QuicProxyBackend* proxy_backend = 120 | reinterpret_cast(quic_simple_server_backend_); 121 | proxy_backend->set_server(this); 122 | 123 | epoll_server_.set_timeout_in_us(50 * 1000); 124 | 125 | QuicEpollClock clock(&epoll_server_); 126 | 127 | std::unique_ptr scfg(crypto_config_.AddDefaultConfig( 128 | QuicRandom::GetInstance(), &clock, crypto_config_options_)); 129 | } 130 | 131 | QuicProxyServer::~QuicProxyServer() = default; 132 | 133 | bool QuicProxyServer::CreateUDPSocketAndListen(const QuicSocketAddress& address) { 134 | fd_ = QuicSocketUtils::CreateUDPSocket( 135 | address, 136 | /*receive_buffer_size =*/kDefaultSocketReceiveBuffer, 137 | /*send_buffer_size =*/kDefaultSocketReceiveBuffer, &overflow_supported_); 138 | if (fd_ < 0) { 139 | LOG(ERROR) << "CreateSocket() failed: " << strerror(errno); 140 | return false; 141 | } 142 | 143 | int boolean_value = 1; 144 | if (-1 == setsockopt(fd_, SOL_SOCKET, SO_REUSEADDR, 145 | reinterpret_cast(&boolean_value), 146 | sizeof(boolean_value))) { 147 | LOG(ERROR) << "SetReuseAddr() failed: " << strerror(errno); 148 | return false; 149 | } 150 | 151 | sockaddr_storage addr = address.generic_address(); 152 | int rc = bind(fd_, reinterpret_cast(&addr), sizeof(addr)); 153 | if (rc < 0) { 154 | QUIC_LOG(ERROR) << "Bind failed: " << strerror(errno); 155 | return false; 156 | } 157 | QUIC_LOG(INFO) << "Listening on " << address.ToString(); 158 | port_ = address.port(); 159 | if (port_ == 0) { 160 | QuicSocketAddress address; 161 | if (address.FromSocket(fd_) != 0) { 162 | LOG(ERROR) << "Unable to get self address. Error: " 163 | << strerror(errno); 164 | } 165 | port_ = address.port(); 166 | } 167 | 168 | epoll_server_.RegisterFD(fd_, this, kEpollFlags); 169 | dispatcher_.reset(CreateQuicDispatcher()); 170 | dispatcher_->InitializeWithWriter(CreateWriter(fd_)); 171 | 172 | return true; 173 | } 174 | 175 | QuicPacketWriter* QuicProxyServer::CreateWriter(int fd) { 176 | // return new QuicDefaultPacketWriter(fd); 177 | QuicProxyPacketWriter *writer = new QuicProxyPacketWriter(fd); 178 | sendmmsg_timer_.reset(new SendMMsgTimer(writer)); 179 | sendmmsg_timer_->InitializeTimer(&epoll_server_, 180 | sendmmsgtimer_interval_); 181 | return writer; 182 | } 183 | 184 | QuicDispatcher* QuicProxyServer::CreateQuicDispatcher() { 185 | QuicEpollAlarmFactory alarm_factory(&epoll_server_); 186 | return new QuicProxyDispatcher( 187 | &config_, &crypto_config_, &version_manager_, 188 | std::unique_ptr(new QuicEpollConnectionHelper( 189 | &epoll_server_, QuicAllocator::BUFFER_POOL)), 190 | std::unique_ptr( 191 | new QuicSimpleCryptoServerStreamHelper(QuicRandom::GetInstance())), 192 | std::unique_ptr( 193 | new QuicEpollAlarmFactory(&epoll_server_)), 194 | quic_simple_server_backend_, expected_connection_id_length_); 195 | } 196 | 197 | void QuicProxyServer::WaitForEvents() { 198 | epoll_server_.WaitForEventsAndExecuteCallbacks(); 199 | } 200 | 201 | void QuicProxyServer::Shutdown() { 202 | if (!silent_close_) { 203 | // Before we shut down the epoll server, give all active sessions a chance 204 | // to notify clients that they're closing. 205 | dispatcher_->Shutdown(); 206 | } 207 | 208 | epoll_server_.Shutdown(); 209 | 210 | close(fd_); 211 | fd_ = -1; 212 | } 213 | 214 | void QuicProxyServer::OnEvent(int fd, QuicEpollEvent* event) { 215 | DCHECK_EQ(fd, fd_); 216 | event->out_ready_mask = 0; 217 | 218 | if (event->in_events & EPOLLIN) { 219 | QUIC_DVLOG(1) << "EPOLLIN"; 220 | 221 | dispatcher_->ProcessBufferedChlos(kNumSessionsToCreatePerSocketEvent); 222 | 223 | bool more_to_read = true; 224 | while (more_to_read) { 225 | more_to_read = packet_reader_->ReadAndDispatchPackets( 226 | fd_, port_, QuicEpollClock(&epoll_server_), dispatcher_.get(), 227 | overflow_supported_ ? &packets_dropped_ : nullptr); 228 | } 229 | 230 | if (dispatcher_->HasChlosBuffered()) { 231 | // Register EPOLLIN event to consume buffered CHLO(s). 232 | event->out_ready_mask |= EPOLLIN; 233 | } 234 | } 235 | if (event->in_events & EPOLLOUT) { 236 | dispatcher_->OnCanWrite(); 237 | if (dispatcher_->HasPendingWrites()) { 238 | event->out_ready_mask |= EPOLLOUT; 239 | } 240 | } 241 | if (event->in_events & EPOLLERR) { 242 | } 243 | } 244 | 245 | } // namespace quic 246 | -------------------------------------------------------------------------------- /proxy_quic/quic_proxy_curl.cc: -------------------------------------------------------------------------------- 1 | #include "net/third_party/quiche/src/quic/proxy_quic/quic_proxy_curl.h" 2 | #include "net/third_party/quiche/src/quic/proxy_quic/quic_proxy_backend.h" 3 | #include "net/third_party/quiche/src/quic/proxy_quic/quic_proxy_stream.h" 4 | #include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" 5 | #include "net/http/http_util.h" 6 | 7 | 8 | namespace quic { 9 | 10 | QuicProxyCurl::QuicProxyCurl(const std::string& request_url, 11 | QuicProxyBackend* backend, 12 | QuicProxyStream* quic_stream) 13 | : sockfd_(-1), easy_(nullptr), request_url_(request_url), 14 | backend_(backend), quic_stream_(quic_stream), 15 | stream_id_(quic_stream->id()), 16 | is_pause_(false), 17 | content_length_(0), 18 | had_send_length_(0), 19 | request_header_list_(nullptr), 20 | request_body_(""), 21 | request_body_pos_(0) { 22 | error_[0] = '\0'; 23 | } 24 | 25 | 26 | QuicProxyCurl::~QuicProxyCurl() { 27 | if (easy_) { 28 | curl_easy_cleanup(easy_); 29 | easy_ = nullptr; 30 | sockfd_ = -1; 31 | quic_stream_ = nullptr; 32 | } 33 | 34 | if (request_header_list_) { 35 | curl_slist_free_all(request_header_list_); 36 | request_header_list_ = nullptr; 37 | } 38 | } 39 | 40 | bool QuicProxyCurl::InitializeProxyCurl() { 41 | easy_ = curl_easy_init(); 42 | if (!easy_) { 43 | LOG(WARNING) << "curl_easy_init() failed, exiting!"; 44 | return false; 45 | } 46 | 47 | return true; 48 | } 49 | 50 | void QuicProxyCurl::StartHttp(const spdy::SpdyHeaderBlock& request_headers, 51 | const std::string& request_body) { 52 | // header 53 | for (auto header = request_headers.begin(); 54 | header != request_headers.end(); 55 | ++header) { 56 | if (header->first[0] == ':') 57 | continue; 58 | 59 | std::string h = std::string(header->first) + 60 | ": " + std::string(header->second); 61 | request_header_list_ = 62 | curl_slist_append(request_header_list_, h.c_str()); 63 | } 64 | // X-Real-IP 65 | std::string x_real_ip = "x-real-ip: " + quic_stream_->get_peer_ip(); 66 | request_header_list_ = 67 | curl_slist_append(request_header_list_, x_real_ip.c_str()); 68 | 69 | std::string method = ""; 70 | auto it = request_headers.find(":method"); 71 | if (it != request_headers.end()) { 72 | method.append(it->second.as_string()); 73 | } 74 | 75 | if (!net::HttpUtil::IsValidHeaderName(method)) { 76 | LOG(DFATAL) << "Headers invalid or empty, ignoring: " 77 | << request_url_ 78 | << " quic stream: " << stream_id_; 79 | return; 80 | } 81 | 82 | DVLOG(1) << "http method is " << method 83 | << " quic stream id " << quic_stream_->id() 84 | << " url is " << request_url_; 85 | 86 | curl_easy_setopt(easy_, CURLOPT_URL, 87 | request_url_.c_str()); 88 | curl_easy_setopt(easy_, CURLOPT_CUSTOMREQUEST, 89 | method.c_str()); 90 | if (request_header_list_) { 91 | curl_easy_setopt(easy_, CURLOPT_HTTPHEADER, 92 | request_header_list_); 93 | } 94 | curl_easy_setopt(easy_, CURLOPT_WRITEFUNCTION, 95 | QuicProxyCurl::WriteQuicCallback); 96 | curl_easy_setopt(easy_, CURLOPT_WRITEDATA, this); 97 | curl_easy_setopt(easy_, CURLOPT_HEADERFUNCTION, 98 | QuicProxyCurl::HttpHeaderCallback); 99 | curl_easy_setopt(easy_, CURLOPT_HEADERDATA, this); 100 | curl_easy_setopt(easy_, CURLOPT_ERRORBUFFER, error_); 101 | curl_easy_setopt(easy_, CURLOPT_PRIVATE, this); 102 | // curl_easy_setopt(easy_, CURLOPT_PROGRESSFUNCTION, 103 | // QuicProxyCurl::ProgressQuicCallback); 104 | // curl_easy_setopt(easy_, CURLOPT_PROGRESSDATA, this); 105 | curl_easy_setopt(easy_, CURLOPT_FOLLOWLOCATION, 1L); 106 | // curl_easy_setopt(easy_, CURLOPT_LOW_SPEED_TIME, 3L); 107 | // curl_easy_setopt(easy_, CURLOPT_LOW_SPEED_LIMIT, 10L); 108 | 109 | // curl_easy_setopt(easy_, CURLOPT_NOPROGRESS, 0L); 110 | // curl_easy_setopt(easy_, CURLOPT_VERBOSE, 1L); 111 | curl_easy_setopt(easy_, CURLOPT_BUFFERSIZE, 1024*32L); 112 | // curl_easy_setopt(easy_, CURLOPT_MAX_RECV_SPEED_LARGE, (curl_off_t)(1024*1024*2)); 113 | 114 | if (method == "POST" || method == "PUT" || 115 | method == "PATCH") { 116 | // Upload content must be set 117 | if (!request_body.empty()) { 118 | LOG(INFO) << "request_body size is " << request_body.length(); 119 | request_body_ = request_body; 120 | curl_easy_setopt(easy_, CURLOPT_UPLOAD, 1L); 121 | curl_easy_setopt(easy_, CURLOPT_INFILESIZE, 122 | request_body_.length()); 123 | /* we want to use our own read function */ 124 | curl_easy_setopt(easy_, CURLOPT_READFUNCTION, 125 | QuicProxyCurl::ReadQuicCallback); 126 | /* pointer to pass to our read function */ 127 | curl_easy_setopt(easy_, CURLOPT_READDATA, this); 128 | } 129 | } 130 | 131 | DVLOG(1) 132 | << "Adding easy " << easy_ << " to " << request_url_; 133 | curl_multi_add_handle(backend_->get_curlm(), easy_); 134 | } 135 | 136 | void QuicProxyCurl::ContinueDownload(uint64_t buffer_size) { 137 | // const uint64_t continue_download_size = 1024*1024*20; 138 | // if (buffer_size <= continue_download_size && 139 | // is_pause_ == true) { 140 | // is_pause_ = false; 141 | // curl_easy_pause(easy_, CURLPAUSE_CONT); 142 | // } 143 | } 144 | 145 | void QuicProxyCurl::OnEvent(int fd, QuicEpollEvent* event) { 146 | DCHECK_EQ(fd, sockfd_); 147 | CURLMcode rc; 148 | struct itimerspec its; 149 | 150 | int action = (event->in_events & EPOLLIN ? CURL_CSELECT_IN : 0) | 151 | (event->in_events & EPOLLOUT ? CURL_CSELECT_OUT : 0); 152 | 153 | rc = curl_multi_socket_action(backend_->get_curlm(), 154 | fd, action, &backend_->still_running_); 155 | 156 | backend_->CheckCurlMultiInfo(); 157 | 158 | if (backend_->still_running_ <= 0) { 159 | memset(&its, 0, sizeof(struct itimerspec)); 160 | timerfd_settime(backend_->get_curl_timer()->get_timerfd(), 0, &its, NULL); 161 | } 162 | } 163 | 164 | size_t QuicProxyCurl::WriteQuicCallback(void* contents, 165 | size_t size, 166 | size_t nmemb, 167 | void* userp) { 168 | QuicProxyCurl* proxy = reinterpret_cast(userp); 169 | if (proxy->quic_stream_ == NULL) { 170 | LOG(INFO) << "Send data, but quic_stream is nullptr. " << proxy->stream_id_; 171 | return CURLE_WRITE_ERROR; 172 | } 173 | 174 | size_t real_size = size * nmemb; 175 | proxy->had_send_length_ += real_size; 176 | QuicStringPiece body(reinterpret_cast(contents), real_size); 177 | proxy->quic_stream_->WriteOrBufferBody(body, 178 | proxy->had_send_length_ == proxy->content_length_); 179 | 180 | // if (proxy->had_send_length_ == proxy->content_length_) { 181 | // LOG(INFO) << "WriteQuicCallback " << real_size 182 | // << " total recv size " << proxy->had_send_length_ 183 | // << " content_length " 184 | // << proxy->content_length_ 185 | // << " quic stream id " << proxy->quic_stream_->id(); 186 | // } 187 | 188 | return real_size; 189 | } 190 | 191 | size_t QuicProxyCurl::HttpHeaderCallback(void* contents, 192 | size_t size, 193 | size_t nmemb, 194 | void* userp) { 195 | QuicProxyCurl* proxy = reinterpret_cast(userp); 196 | if (proxy->quic_stream_ == nullptr) { 197 | LOG(INFO) << "Send data, but quic_stream is nullptr. " << proxy->stream_id_; 198 | return CURLE_WRITE_ERROR; 199 | } 200 | 201 | size_t real_size = size * nmemb; 202 | char *header_line = reinterpret_cast(contents); 203 | if (real_size == 2 && 204 | header_line[0] == '\r' && 205 | header_line[1] == '\n') { 206 | // read header over and send 207 | auto content_length = proxy->spdy_headers_.find("content-length"); 208 | if (content_length != proxy->spdy_headers_.end()) { 209 | QuicTextUtils::StringToSizeT(content_length->second, 210 | &proxy->content_length_); 211 | } 212 | std::string http_status(""); 213 | auto it_status = proxy->spdy_headers_.find(":status"); 214 | if (it_status != proxy->spdy_headers_.end()) { 215 | http_status = it_status->second.as_string(); 216 | } 217 | // std::string http_ver(""); 218 | // auto it_ver = proxy->spdy_headers_.find(":ver"); 219 | // if (it_ver != proxy->spdy_headers_.end()) { 220 | // http_ver = it_ver->second.as_string(); 221 | // proxy->spdy_headers_.erase(":ver"); 222 | // } 223 | std::string transfer_encoding(""); 224 | auto it_transfer_encoding = proxy->spdy_headers_.find("transfer-encoding"); 225 | if (it_transfer_encoding != proxy->spdy_headers_.end()) { 226 | transfer_encoding = it_transfer_encoding->second.as_string(); 227 | } 228 | 229 | bool fin = false; 230 | if (proxy->content_length_ == 0) { 231 | fin = true; 232 | if (http_status == "200" || 233 | http_status == "100" || 234 | transfer_encoding == "chunked") { 235 | fin = false; 236 | } 237 | } 238 | 239 | // LOG(INFO) << "send request header " << proxy->quic_stream_->id() 240 | // << " content-length " << proxy->content_length_ 241 | // << " fin " << fin; 242 | ((proxy->quic_stream_))->WriteHeaders(std::move(proxy->spdy_headers_), 243 | fin, nullptr); 244 | return real_size; 245 | } 246 | 247 | QuicStringPiece line(header_line, real_size - 2); 248 | // LOG(INFO) << "Respond header: " << line; 249 | 250 | if (line.substr(0, 4) == "HTTP") { 251 | size_t pos = line.find(" "); 252 | if (pos == std::string::npos) { 253 | LOG(DFATAL) << "Headers invalid or empty, ignoring: " 254 | << proxy->request_url_; 255 | return 0; 256 | } 257 | 258 | proxy->spdy_headers_[":status"] = line.substr(pos + 1, 3); 259 | // proxy->spdy_headers_[":ver"] = line.substr(5, 3); 260 | return real_size; 261 | } 262 | 263 | // Headers are "key: value". 264 | size_t pos = line.find(": "); 265 | if (pos == std::string::npos) { 266 | LOG(DFATAL) << "Headers invalid or empty, ignoring: " 267 | << proxy->request_url_; 268 | return real_size; 269 | } 270 | proxy->spdy_headers_.AppendValueOrAddHeader( 271 | QuicTextUtils::ToLower(line.substr(0, pos)), line.substr(pos + 2)); 272 | return real_size; 273 | } 274 | 275 | size_t QuicProxyCurl::ReadQuicCallback(void* dest, 276 | size_t size, 277 | size_t nmemb, 278 | void *userp) { 279 | QuicProxyCurl* proxy = reinterpret_cast(userp); 280 | size_t buffer_size = size * nmemb; 281 | size_t copy_this_much = proxy->request_body_.length() 282 | - proxy->request_body_pos_; 283 | if (copy_this_much > 0) { 284 | if (copy_this_much > buffer_size) 285 | copy_this_much = buffer_size; 286 | memcpy(dest, 287 | proxy->request_body_.c_str()+proxy->request_body_pos_, 288 | copy_this_much); 289 | proxy->request_body_pos_ += copy_this_much; 290 | return copy_this_much; 291 | } 292 | 293 | return 0; 294 | } 295 | 296 | int QuicProxyCurl::ProgressQuicCallback(void* userp, 297 | double dltotal, 298 | double dlnow, 299 | double ultotal, 300 | double ulnow) { 301 | QuicProxyCurl* proxy = reinterpret_cast(userp); 302 | if (proxy->quic_stream_ == nullptr) { 303 | LOG(INFO) << "Progress data, but quic_stream is nullptr. " << proxy->stream_id_; 304 | return CURLE_ABORTED_BY_CALLBACK; 305 | } 306 | 307 | const uint64_t pause_download_size = 1024*1024*40; 308 | 309 | if (proxy->quic_stream_->BufferedDataBytes() >= pause_download_size && 310 | proxy->is_pause_ == false) { 311 | proxy->is_pause_ = true; 312 | curl_easy_pause(proxy->easy_, CURLPAUSE_RECV); 313 | } 314 | return CURLE_OK; 315 | } 316 | 317 | } // namespace quic 318 | --------------------------------------------------------------------------------