From d2c6f597d26fbd6d72ab9cc1fda35a620cf37968 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 20 Apr 2022 13:50:43 +0200 Subject: [PATCH 01/13] Bonjour - resolve hostname to IP using mdns. Octoprint - on Windows use Bonjour resolving to get IP address before the http connection. Only use Windows address resolving if Bonjour couldn't resolve. --- src/slic3r/Utils/Bonjour.cpp | 417 ++++++++++++++++++++++++++++++++- src/slic3r/Utils/Bonjour.hpp | 12 + src/slic3r/Utils/OctoPrint.cpp | 161 ++++++++++++- src/slic3r/Utils/OctoPrint.hpp | 5 + 4 files changed, 577 insertions(+), 18 deletions(-) diff --git a/src/slic3r/Utils/Bonjour.cpp b/src/slic3r/Utils/Bonjour.cpp index f121e6e87..1980cc8bb 100644 --- a/src/slic3r/Utils/Bonjour.cpp +++ b/src/slic3r/Utils/Bonjour.cpp @@ -13,6 +13,8 @@ #include #include #include +#include +#include using boost::optional; using boost::system::error_code; @@ -238,6 +240,7 @@ struct DnsRR_A enum { TAG = 0x1 }; asio::ip::address_v4 ip; + std::string name; static void decode(optional &result, const DnsResource &rr) { @@ -255,6 +258,7 @@ struct DnsRR_AAAA enum { TAG = 0x1c }; asio::ip::address_v6 ip; + std::string name; static void decode(optional &result, const DnsResource &rr) { @@ -438,8 +442,14 @@ private: void parse_rr(const std::vector &buffer, DnsResource &&rr, size_t dataoffset, const Bonjour::TxtKeys &txt_keys) { switch (rr.type) { - case DnsRR_A::TAG: DnsRR_A::decode(this->rr_a, rr); break; - case DnsRR_AAAA::TAG: DnsRR_AAAA::decode(this->rr_aaaa, rr); break; + case DnsRR_A::TAG: + DnsRR_A::decode(this->rr_a, rr); + this->rr_a->name = rr.name; + break; + case DnsRR_AAAA::TAG: + DnsRR_AAAA::decode(this->rr_aaaa, rr); + this->rr_aaaa->name = rr.name; + break; case DnsRR_SRV::TAG: { auto srv = DnsRR_SRV::decode(buffer, rr, dataoffset); if (srv) { this->sdmap.insert_srv(std::move(rr.name), std::move(*srv)); } @@ -484,20 +494,23 @@ std::ostream& operator<<(std::ostream &os, const DnsMessage &msg) struct BonjourRequest { static const asio::ip::address_v4 MCAST_IP4; + static const asio::ip::address_v6 MCAST_IP6; static const uint16_t MCAST_PORT; std::vector data; - static optional make(const std::string &service, const std::string &protocol); - + static optional make_PTR(const std::string &service, const std::string &protocol); + static optional make_A(const std::string& hostname); + static optional make_AAAA(const std::string& hostname); private: BonjourRequest(std::vector &&data) : data(std::move(data)) {} }; -const asio::ip::address_v4 BonjourRequest::MCAST_IP4{0xe00000fb}; +const asio::ip::address_v4 BonjourRequest::MCAST_IP4{ 0xe00000fb }; +const asio::ip::address_v6 BonjourRequest::MCAST_IP6 = asio::ip::make_address_v6("ff02::fb"); const uint16_t BonjourRequest::MCAST_PORT = 5353; -optional BonjourRequest::make(const std::string &service, const std::string &protocol) +optional BonjourRequest::make_PTR(const std::string &service, const std::string &protocol) { if (service.size() > 15 || protocol.size() > 15) { return boost::none; @@ -535,6 +548,75 @@ optional BonjourRequest::make(const std::string &service, const return BonjourRequest(std::move(data)); } +optional BonjourRequest::make_A(const std::string& hostname) +{ + // todo: why is this and what is real max + if (hostname.size() > 30) { + return boost::none; + } + + std::vector data; + data.reserve(hostname.size() + 18); + + // Add metadata + static const unsigned char rq_meta[] = { + 0x00, 0x00, // Query ID (zero for mDNS) + 0x00, 0x00, // Flags + 0x00, 0x01, // One query + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Zero Answer, Authority, and Additional RRs + }; + std::copy(rq_meta, rq_meta + sizeof(rq_meta), std::back_inserter(data)); + + // Add hostname without .local + data.push_back(hostname.size()); + data.insert(data.end(), hostname.begin(), hostname.end()); + + // Add the rest of A record + static const unsigned char ptr_tail[] = { + 0x05, // length of "local" + 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00,// "local" string and terminator + 0x00, 0x01, // Type A + 0x00, 0xff, // Class - 01 is internet 0xff is any + }; + std::copy(ptr_tail, ptr_tail + sizeof(ptr_tail), std::back_inserter(data)); + + return BonjourRequest(std::move(data)); +} + +optional BonjourRequest::make_AAAA(const std::string& hostname) +{ + // todo: why is this and what is real max + if (hostname.size() > 30) { + return boost::none; + } + + std::vector data; + data.reserve(hostname.size() + 18); + + // Add metadata + static const unsigned char rq_meta[] = { + 0x00, 0x00, // Query ID (zero for mDNS) + 0x00, 0x00, // Flags + 0x00, 0x01, // One query + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Zero Answer, Authority, and Additional RRs + }; + std::copy(rq_meta, rq_meta + sizeof(rq_meta), std::back_inserter(data)); + + // Add hostname without .local + data.push_back(hostname.size()); + data.insert(data.end(), hostname.begin(), hostname.end()); + + // Add the rest of A record + static const unsigned char ptr_tail[] = { + 0x05, // length of "local" + 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00, // "local" string and terminator + 0x00, 0x1c, // Type AAAA + 0x00, 0xff, // Class - 01 is internet 0xff is any + }; + std::copy(ptr_tail, ptr_tail + sizeof(ptr_tail), std::back_inserter(data)); + + return BonjourRequest(std::move(data)); +} // API - private part @@ -546,17 +628,22 @@ struct Bonjour::priv TxtKeys txt_keys; unsigned timeout; unsigned retries; + std::string hostname; + + std::vector replies; std::vector buffer; std::thread io_thread; Bonjour::ReplyFn replyfn; Bonjour::CompleteFn completefn; + Bonjour::ResolveFn resolvefn; priv(std::string &&service); std::string strip_service_dn(const std::string &service_name) const; - void udp_receive(udp::endpoint from, size_t bytes); + void udp_receive_lookup(udp::endpoint from, size_t bytes); void lookup_perform(); + void resolve_perform(); }; Bonjour::priv::priv(std::string &&service) @@ -582,7 +669,7 @@ std::string Bonjour::priv::strip_service_dn(const std::string &service_name) con } } -void Bonjour::priv::udp_receive(udp::endpoint from, size_t bytes) +void Bonjour::priv::udp_receive_lookup(udp::endpoint from, size_t bytes) { if (bytes == 0 || !replyfn) { return; @@ -621,7 +708,7 @@ void Bonjour::priv::lookup_perform() { service_dn = (boost::format("_%1%._%2%.local") % service % protocol).str(); - const auto brq = BonjourRequest::make(service, protocol); + const auto brq = BonjourRequest::make_PTR(service, protocol); if (!brq) { return; } @@ -659,7 +746,7 @@ void Bonjour::priv::lookup_perform() udp::endpoint recv_from; const auto recv_handler = [&](const error_code &error, size_t bytes) { - if (!error) { self->udp_receive(recv_from, bytes); } + if (!error) { self->udp_receive_lookup(recv_from, bytes); } }; socket.async_receive_from(asio::buffer(buffer, buffer.size()), recv_from, recv_handler); @@ -678,6 +765,282 @@ void Bonjour::priv::lookup_perform() } } +class ResolveSocket; +// One session for socket::async_receive_from(). +// Keeps buffer of data so it is not overwritten when another async_receive_from starts. +struct UdpSession : boost::enable_shared_from_this{ + + UdpSession(ResolveSocket* sckt, Bonjour::ReplyFn rfn) : socket(sckt), replyfn(rfn) + { + buffer.resize(DnsMessage::MAX_SIZE); + } + + void handle_receive(const error_code& error, size_t bytes); + + udp::endpoint remote_endpoint; + std::vector buffer; + ResolveSocket* socket; + Bonjour::ReplyFn replyfn; +}; + +// Udp socket for Bonjour::resolve_perform(). +// Starts receiving answers after first send() call until io_service is stopped. +class ResolveSocket +{ +private: + typedef boost::shared_ptr SharedSession; + Bonjour::ReplyFn replyfn; // this doesnt call same fn as replyfn of Bonjour class + asio::ip::address multicast_address; + udp::socket socket; + udp::endpoint mcast_endpoint; + boost::shared_ptr< boost::asio::io_service > io_service; + std::string hostname; + boost::optional request_A; + boost::optional request_AAAA; + asio::ip::multicast::outbound_interface outbound_interface; +public: + ResolveSocket(const std::string& hostname, Bonjour::ReplyFn replyfn, const asio::ip::address& multicast_address, const asio::ip::address& interface_address, boost::shared_ptr< boost::asio::io_service > io_service); + + void send(); + void async_receive(); + void cancel() { socket.cancel(); } + std::string get_hostname() { return hostname; } +private: + void receive_handler(SharedSession session, const error_code& error, size_t bytes); +}; + +ResolveSocket::ResolveSocket(const std::string& hostname, Bonjour::ReplyFn replyfn, const asio::ip::address& multicast_address, const asio::ip::address& interface_address, boost::shared_ptr< boost::asio::io_service > io_service) + : hostname(hostname) + , replyfn(replyfn) + , multicast_address(multicast_address) + , socket(*io_service) + , io_service(io_service) +{ + assert(!hostname.empty() && replyfn); + try { + // open socket + boost::asio::ip::udp::endpoint listen_endpoint(multicast_address.is_v4() ? udp::v4() : udp::v6(), BonjourRequest::MCAST_PORT); + socket.open(listen_endpoint.protocol()); + // set socket to listen + socket.set_option(udp::socket::reuse_address(true)); + socket.bind(listen_endpoint); + if (interface_address.is_v4()) { + // listen for multicast on given interface + socket.set_option(boost::asio::ip::multicast::join_group(multicast_address.to_v4(), interface_address.to_v4())); + // send to interface + socket.set_option(asio::ip::multicast::outbound_interface(interface_address.to_v4())); + } else { + // listen for multicast on given interface + socket.set_option(boost::asio::ip::multicast::join_group(multicast_address.to_v6(), interface_address.to_v6().scope_id())); + // send to interface + socket.set_option(asio::ip::multicast::outbound_interface(interface_address.to_v6().scope_id())); + } + mcast_endpoint = udp::endpoint(multicast_address, BonjourRequest::MCAST_PORT); + + BOOST_LOG_TRIVIAL(info) << "Socket created. Multicast: " << multicast_address << ". Interface: " << interface_address; + } + catch (std::exception& e) { + BOOST_LOG_TRIVIAL(error) << e.what(); + } +} + +void ResolveSocket::send() +{ + try { + if (!request_A) { + // BonjourRequest::make_A / AAAA is now implemented to add .local correctly after the hostname. + // If that is unsufficient, we need to change make_A / AAAA and pass full hostname. + std::string trimmed_hostname = hostname; + if (size_t dot_pos = trimmed_hostname.find_first_of('.'); dot_pos != std::string::npos) + trimmed_hostname = trimmed_hostname.substr(0, dot_pos); + request_A = BonjourRequest::make_A(trimmed_hostname); + } + if (!request_AAAA) { + std::string trimmed_hostname = hostname; + if (size_t dot_pos = trimmed_hostname.find_first_of('.'); dot_pos != std::string::npos) + trimmed_hostname = trimmed_hostname.substr(0, dot_pos); + request_AAAA = BonjourRequest::make_AAAA(trimmed_hostname); + } + // multicast both queries + socket.send_to(asio::buffer(request_A->data), mcast_endpoint); + socket.send_to(asio::buffer(request_AAAA->data), mcast_endpoint); + + + // Should we care if this is called while already receiving? (async_receive call from receive_handler) + async_receive(); + } + catch (std::exception& e) { + BOOST_LOG_TRIVIAL(error) << e.what(); + } +} + +void ResolveSocket::async_receive() +{ + try { + // our session to hold the buffer + endpoint + auto session = boost::make_shared(this, replyfn); + socket.async_receive_from(asio::buffer(session->buffer, session->buffer.size()) + , session->remote_endpoint + , boost::bind(&ResolveSocket::receive_handler, this, session, asio::placeholders::error, asio::placeholders::bytes_transferred)); + } + catch (std::exception& e) { + BOOST_LOG_TRIVIAL(error) << e.what(); + } +} + +void ResolveSocket::receive_handler(SharedSession session, const error_code& error, size_t bytes) +{ + // let io_service to handle the datagram on session + io_service->post(bind(&UdpSession::handle_receive, session, error, bytes)); + // immediately accept new datagrams + async_receive(); +} + +void UdpSession::handle_receive(const error_code& error, size_t bytes) +{ + if (error) { + // todo: what level? do we even log? There might be callbacks when timer runs out + BOOST_LOG_TRIVIAL(info) << error.message(); + return; + } + if (bytes == 0 || !replyfn) { + // todo: log something? + return; + } + + buffer.resize(bytes); +#if 0 + // this is log of buffer, be careful with logging here - called from async_receive + std::string str; + char const hex_chars[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + for (size_t i = 0; i < buffer.size(); i++) { + const char ch = buffer[i]; + str += hex_chars[(ch & 0xF0) >> 4]; + str += hex_chars[(ch & 0x0F) >> 0]; + } + BOOST_LOG_TRIVIAL(debug) << remote_endpoint.address()<< " " << str; +#endif + // decode buffer, txt keys are not needed for A / AAAA answer + auto dns_msg = DnsMessage::decode(buffer, Bonjour::TxtKeys()); + if (dns_msg) { + asio::ip::address ip; + std::string answer_name; + if (dns_msg->rr_a) { + ip = dns_msg->rr_a->ip; + answer_name = dns_msg->rr_a->name; + } + else if (dns_msg->rr_aaaa) { + ip = dns_msg->rr_aaaa->ip; + answer_name = dns_msg->rr_aaaa->name; + } + else + return; // not matching query type with answer type + + if (!answer_name.empty()) { + // transform both strings to lower. Should we really do it? + std::string name_tolower = answer_name; + std::transform(name_tolower.begin(), name_tolower.end(), name_tolower.begin(), + [](unsigned char c) { return std::tolower(c); }); + std::string hostname_tolower = socket->get_hostname(); + std::transform(hostname_tolower.begin(), hostname_tolower.end(), hostname_tolower.begin(), + [](unsigned char c) { return std::tolower(c); }); + if (name_tolower == hostname_tolower) { + BonjourReply reply(ip, 0, std::string(), answer_name, BonjourReply::TxtData()); + replyfn(std::move(reply)); + } + } + } +} + +void Bonjour::priv::resolve_perform() +{ + // reply callback is shared to every UDPSession which is called asyn + boost::mutex replies_guard; + std::vector replies; + // examples would probably store [self] to the lambda (and the timer one), is it ok not to do it? (Should be c++03) + const auto reply_callback = [&rpls = replies, &guard = replies_guard](BonjourReply&& reply) + { + guard.lock(); + if (std::find(rpls.begin(), rpls.end(), reply) == rpls.end()) + rpls.push_back(reply); + guard.unlock(); + }; + + boost::shared_ptr< boost::asio::io_service > io_service( + new boost::asio::io_service + ); + + std::vector sockets; + + // resolve intefaces - from PR#6646 + std::vector interfaces; + asio::ip::udp::resolver resolver(*io_service); + // ipv4 interfaces + auto results = resolver.resolve(udp::v4(), asio::ip::host_name(), ""); + for (auto const& r : results) { + auto const addr = r.endpoint().address(); + if (addr.is_loopback()) continue; + interfaces.emplace_back(addr); + } + // create ipv4 socket for each interface + // each will send to querry to for both ipv4 and ipv6 + for (const auto intrfc : interfaces) + sockets.emplace_back(new ResolveSocket(hostname, reply_callback, BonjourRequest::MCAST_IP4, intrfc, io_service)); + // ipv6 interfaces + interfaces.clear(); + results = resolver.resolve(udp::v6(), asio::ip::host_name(), ""); + for (auto const& r : results) { + auto const addr = r.endpoint().address(); + if (addr.is_loopback()) continue; + interfaces.emplace_back(addr); + } + // create ipv6 socket for each interface + // each will send to querry to for both ipv4 and ipv6 + for (const auto intrfc : interfaces) + sockets.emplace_back(new ResolveSocket(hostname, reply_callback, BonjourRequest::MCAST_IP6, intrfc, io_service)); + + try { + // send first queries + for each (auto * socket in sockets) + socket->send(); + + // timer settings + asio::deadline_timer timer(*io_service); + retries--; + std::function timer_handler = [&](const error_code& error) { + replies_guard.lock(); + int replies_count = replies.size(); + replies_guard.unlock(); + // end + if (retries == 0 || error || replies_count > 0) { + // is this correct ending? + io_service->stop(); + replies_guard.lock(); + if (replies_count > 0 && resolvefn) { + resolvefn(replies); + } + replies_guard.unlock(); + // restart timer + } else { + retries--; + timer.expires_from_now(boost::posix_time::seconds(timeout)); + timer.async_wait(timer_handler); + // trigger another round of queries + for each (auto * socket in sockets) + socket->send(); + } + }; + // start timer + timer.expires_from_now(boost::posix_time::seconds(timeout)); + timer.async_wait(timer_handler); + // start io_service, it will run until it has something to do - so in this case until stop is called in timer + io_service->run(); + } + catch (std::exception& e) { + BOOST_LOG_TRIVIAL(error) << e.what(); + } +} + // API - public part @@ -770,6 +1133,12 @@ Bonjour& Bonjour::set_timeout(unsigned timeout) return *this; } +Bonjour& Bonjour::set_hostname(const std::string& hostname) +{ + if (p) { p->hostname = hostname; } + return *this; +} + Bonjour& Bonjour::set_retries(unsigned retries) { if (p && retries > 0) { p->retries = retries; } @@ -788,6 +1157,12 @@ Bonjour& Bonjour::on_complete(CompleteFn fn) return *this; } +Bonjour& Bonjour::on_resolve(ResolveFn fn) +{ + if (p) { p->resolvefn = std::move(fn); } + return *this; +} + Bonjour::Ptr Bonjour::lookup() { auto self = std::make_shared(std::move(*this)); @@ -803,4 +1178,26 @@ Bonjour::Ptr Bonjour::lookup() } +Bonjour::Ptr Bonjour::resolve() +{ + auto self = std::make_shared(std::move(*this)); + + if (self->p) { + auto io_thread = std::thread([self]() { + self->p->resolve_perform(); + }); + self->p->io_thread = std::move(io_thread); + } + + return self; } + +void Bonjour::resolve_sync() +{ + if (p) + p->resolve_perform(); +} + + +} + diff --git a/src/slic3r/Utils/Bonjour.hpp b/src/slic3r/Utils/Bonjour.hpp index e61cd1833..156100941 100644 --- a/src/slic3r/Utils/Bonjour.hpp +++ b/src/slic3r/Utils/Bonjour.hpp @@ -49,6 +49,7 @@ public: typedef std::shared_ptr Ptr; typedef std::function ReplyFn; typedef std::function CompleteFn; + typedef std::function&)> ResolveFn; typedef std::set TxtKeys; Bonjour(std::string service); @@ -65,11 +66,22 @@ public: // ^ Note: By default there is 1 retry (meaning 1 broadcast is sent). // Timeout is per one retry, ie. total time spent listening = retries * timeout. // If retries > 1, then care needs to be taken as more than one reply from the same service may be received. + + // sets hostname queried by resolve() + Bonjour& set_hostname(const std::string& hostname); Bonjour& on_reply(ReplyFn fn); Bonjour& on_complete(CompleteFn fn); + Bonjour& on_resolve(ResolveFn fn); + // lookup all devices by given TxtKeys + // each correct reply is passed back in ReplyFn, finishes with CompleteFn Ptr lookup(); + // performs resolving of hostname into vector of ip adresses passed back by ResolveFn + // needs set_hostname and on_resolve to be called before. + Ptr resolve(); + // resolve on the current thread + void resolve_sync(); private: std::unique_ptr p; }; diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index 250b16b4a..f726bcccf 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -18,7 +19,7 @@ #include "slic3r/GUI/GUI.hpp" #include "Http.hpp" #include "libslic3r/AppConfig.hpp" - +#include "Bonjour.hpp" namespace fs = boost::filesystem; namespace pt = boost::property_tree; @@ -103,11 +104,60 @@ OctoPrint::OctoPrint(DynamicPrintConfig *config) : const char* OctoPrint::get_name() const { return "OctoPrint"; } -bool OctoPrint::test(wxString &msg) const +bool OctoPrint::test_with_resolved_ip(wxString &msg) const +{ + // Since the request is performed synchronously here, + // it is ok to refer to `msg` from within the closure + const char* name = get_name(); + bool res = true; + // Msg contains ip string. + auto url = substitute_host(make_url("api/version"), GUI::into_u8(msg)); + msg.Clear(); + + BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url; + + auto http = Http::get(std::move(url)); + set_auth(http); + http + .on_error([&](std::string body, std::string error, unsigned status) { + BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting version: %2%, HTTP %3%, body: `%4%`") % name % error % status % body; + res = false; + msg = format_error(body, error, status); + }) + .on_complete([&, this](std::string body, unsigned) { + BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got version: %2%") % name % body; + + try { + std::stringstream ss(body); + pt::ptree ptree; + pt::read_json(ss, ptree); + + if (!ptree.get_optional("api")) { + res = false; + return; + } + + const auto text = ptree.get_optional("text"); + res = validate_version_text(text); + if (!res) { + msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "OctoPrint")).str()); + } + } + catch (const std::exception&) { + res = false; + msg = "Could not parse server response."; + } + }) + .ssl_revoke_best_effort(m_ssl_revoke_best_effort) + .perform_sync(); + + return res; +} + +bool OctoPrint::test(wxString& msg) const { // Since the request is performed synchronously here, // it is ok to refer to `msg` from within the closure - const char *name = get_name(); bool res = true; @@ -147,8 +197,8 @@ bool OctoPrint::test(wxString &msg) const } }) #ifdef WIN32 - .ssl_revoke_best_effort(m_ssl_revoke_best_effort) - .on_ip_resolve([&](std::string address) { + .ssl_revoke_best_effort(m_ssl_revoke_best_effort) + .on_ip_resolve([&](std::string address) { // Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail. // Remember resolved address to be reused at successive REST API call. msg = GUI::from_u8(address); @@ -159,6 +209,7 @@ bool OctoPrint::test(wxString &msg) const return res; } + wxString OctoPrint::get_test_ok_msg () const { return _(L("Connection to OctoPrint works correctly.")); @@ -174,7 +225,101 @@ wxString OctoPrint::get_test_failed_msg (wxString &msg) const bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const { - const char *name = get_name(); +#ifndef WIN32 + return upload_inner_with_host(upload_data, prorgess_fn, error_fn); +#endif // !WIN32 + + // decide what to do based on m_host - resolve hostname or upload to ip + std::vector resolved_addr; + boost::system::error_code ec; + boost::asio::ip::address host_ip = boost::asio::ip::make_address(m_host, ec); + if (!ec) { + resolved_addr.push_back(host_ip); + } else if ( GUI::get_app_config()->get("allow_ip_resolve") == "1"){ + Bonjour("octoprint") + .set_hostname(m_host) + .set_retries(10) // number of rounds of queries send + .set_timeout(1) // after each timeout, if there is any answer, the resolving will stop + .on_resolve([&ra = resolved_addr](const std::vector& replies) { + std::vector resolved_addr; + for each (const auto & rpl in replies) { + boost::asio::ip::address ip(rpl.ip); + ra.emplace_back(ip); + BOOST_LOG_TRIVIAL(info) << "Resolved IP address: " << rpl.ip; + } + }) + .resolve_sync(); + } + if (resolved_addr.empty()) { + BOOST_LOG_TRIVIAL(error) << "PrusaSlicer failed to resolve hostname " << m_host << " into the IP address. Starting upload with system resolving."; + return false;//upload_inner_with_host(upload_data, prorgess_fn, error_fn); + } + return upload_inner(upload_data, prorgess_fn, error_fn, resolved_addr); +} +bool OctoPrint::upload_inner(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, const std::vector& resolved_addr) const +{ + wxString error_message; + for each (const auto& ip in resolved_addr) { + // If test fails, test_msg_or_host_ip contains the error message. + // Otherwise on Windows it contains the resolved IP address of the host. + // Test_msg already contains resolved ip and will be cleared on start of test(). + wxString test_msg_or_host_ip = GUI::from_u8(ip.to_string()); + if (!test_with_resolved_ip(test_msg_or_host_ip)) { + error_message = test_msg_or_host_ip; + BOOST_LOG_TRIVIAL(info) << test_msg_or_host_ip; + continue; + } + + const char* name = get_name(); + const auto upload_filename = upload_data.upload_path.filename(); + const auto upload_parent_path = upload_data.upload_path.parent_path(); + std::string url = substitute_host(make_url("api/files/local"), ip.to_string()); + bool result = true; + + BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Uploading file %2% at %3%, filename: %4%, path: %5%, print: %6%") + % name + % upload_data.source_path + % url + % upload_filename.string() + % upload_parent_path.string() + % (upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false"); + + auto http = Http::post(std::move(url)); + set_auth(http); + http.form_add("print", upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false") + .form_add("path", upload_parent_path.string()) // XXX: slashes on windows ??? + .form_add_file("file", upload_data.source_path.string(), upload_filename.string()) + .on_complete([&](std::string body, unsigned status) { + BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%: %3%") % name % status % body; + }) + .on_error([&](std::string body, std::string error, unsigned status) { + BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading file: %2%, HTTP %3%, body: `%4%`") % name % error % status % body; + error_fn(format_error(body, error, status)); + result = false; + }) + .on_progress([&](Http::Progress progress, bool& cancel) { + prorgess_fn(std::move(progress), cancel); + if (cancel) { + // Upload was canceled + BOOST_LOG_TRIVIAL(info) << "Octoprint: Upload canceled"; + result = false; + } + }) +#ifdef WIN32 + .ssl_revoke_best_effort(m_ssl_revoke_best_effort) +#endif + .perform_sync(); + if (result) + return true; + } + // todo: failed. Should we try again with host? + error_fn(std::move(error_message)); + return false; +} + +bool OctoPrint::upload_inner_with_host(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const +{ + const char* name = get_name(); const auto upload_filename = upload_data.upload_path.filename(); const auto upload_parent_path = upload_data.upload_path.parent_path(); @@ -182,7 +327,7 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro // If test fails, test_msg_or_host_ip contains the error message. // Otherwise on Windows it contains the resolved IP address of the host. wxString test_msg_or_host_ip; - if (! test(test_msg_or_host_ip)) { + if (!test(test_msg_or_host_ip)) { error_fn(std::move(test_msg_or_host_ip)); return false; } @@ -233,7 +378,7 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro error_fn(format_error(body, error, status)); res = false; }) - .on_progress([&](Http::Progress progress, bool &cancel) { + .on_progress([&](Http::Progress progress, bool& cancel) { prorgess_fn(std::move(progress), cancel); if (cancel) { // Upload was canceled diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp index 262efe9ff..ee8e5baf7 100644 --- a/src/slic3r/Utils/OctoPrint.hpp +++ b/src/slic3r/Utils/OctoPrint.hpp @@ -8,6 +8,7 @@ #include "PrintHost.hpp" #include "libslic3r/PrintConfig.hpp" +class boost::asio::ip::address; namespace Slic3r { @@ -35,6 +36,10 @@ public: protected: virtual bool validate_version_text(const boost::optional &version_text) const; + virtual bool upload_inner(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, const std::vector& resolved_addr) const; + virtual bool test_with_resolved_ip(wxString& curl_msg) const; + virtual bool upload_inner_with_host(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const; + private: std::string m_host; From 457a5d4a6543c2ec0f92f3a0d671a675bd967110 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 21 Apr 2022 14:48:55 +0200 Subject: [PATCH 02/13] system ifdefs --- src/slic3r/Utils/OctoPrint.cpp | 14 ++++++++------ src/slic3r/Utils/OctoPrint.hpp | 4 +++- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index f726bcccf..842e0cfd7 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -104,6 +104,7 @@ OctoPrint::OctoPrint(DynamicPrintConfig *config) : const char* OctoPrint::get_name() const { return "OctoPrint"; } +#ifdef WIN32 bool OctoPrint::test_with_resolved_ip(wxString &msg) const { // Since the request is performed synchronously here, @@ -153,6 +154,7 @@ bool OctoPrint::test_with_resolved_ip(wxString &msg) const return res; } +#endif //WIN32 bool OctoPrint::test(wxString& msg) const { @@ -227,8 +229,7 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro { #ifndef WIN32 return upload_inner_with_host(upload_data, prorgess_fn, error_fn); -#endif // !WIN32 - +#else // decide what to do based on m_host - resolve hostname or upload to ip std::vector resolved_addr; boost::system::error_code ec; @@ -254,9 +255,11 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro BOOST_LOG_TRIVIAL(error) << "PrusaSlicer failed to resolve hostname " << m_host << " into the IP address. Starting upload with system resolving."; return false;//upload_inner_with_host(upload_data, prorgess_fn, error_fn); } - return upload_inner(upload_data, prorgess_fn, error_fn, resolved_addr); + return upload_inner_with_resolved_ip(upload_data, prorgess_fn, error_fn, resolved_addr); +#endif // WIN32 } -bool OctoPrint::upload_inner(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, const std::vector& resolved_addr) const +#ifdef WIN32 +bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, const std::vector& resolved_addr) const { wxString error_message; for each (const auto& ip in resolved_addr) { @@ -305,9 +308,7 @@ bool OctoPrint::upload_inner(PrintHostUpload upload_data, ProgressFn prorgess_fn result = false; } }) -#ifdef WIN32 .ssl_revoke_best_effort(m_ssl_revoke_best_effort) -#endif .perform_sync(); if (result) return true; @@ -316,6 +317,7 @@ bool OctoPrint::upload_inner(PrintHostUpload upload_data, ProgressFn prorgess_fn error_fn(std::move(error_message)); return false; } +#endif //WIN32 bool OctoPrint::upload_inner_with_host(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const { diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp index ee8e5baf7..f2db4bc1a 100644 --- a/src/slic3r/Utils/OctoPrint.hpp +++ b/src/slic3r/Utils/OctoPrint.hpp @@ -36,8 +36,10 @@ public: protected: virtual bool validate_version_text(const boost::optional &version_text) const; - virtual bool upload_inner(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, const std::vector& resolved_addr) const; +#ifdef WIN32 + virtual bool upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, const std::vector& resolved_addr) const; virtual bool test_with_resolved_ip(wxString& curl_msg) const; +#endif virtual bool upload_inner_with_host(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const; From 36f473c9840ae56854591974e49afe41ce9937a8 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Fri, 22 Apr 2022 17:52:17 +0200 Subject: [PATCH 03/13] Bonjour lookup function in same style as resolve function. Includes refactoring of resolve function. refactor --- src/slic3r/Utils/Bonjour.cpp | 587 +++++++++++++++++---------------- src/slic3r/Utils/Bonjour.hpp | 199 ++++++++++- src/slic3r/Utils/OctoPrint.cpp | 4 +- src/slic3r/Utils/OctoPrint.hpp | 2 +- 4 files changed, 494 insertions(+), 298 deletions(-) diff --git a/src/slic3r/Utils/Bonjour.cpp b/src/slic3r/Utils/Bonjour.cpp index 1980cc8bb..87847bee5 100644 --- a/src/slic3r/Utils/Bonjour.cpp +++ b/src/slic3r/Utils/Bonjour.cpp @@ -7,14 +7,11 @@ #include #include #include -#include -#include #include -#include #include #include -#include -#include +#include +#include using boost::optional; using boost::system::error_code; @@ -406,7 +403,7 @@ struct DnsMessage DnsSDMap sdmap; - static optional decode(const std::vector &buffer, const Bonjour::TxtKeys &txt_keys) + static optional decode(const std::vector& buffer, const Bonjour::TxtKeys& txt_keys) { const auto size = buffer.size(); if (size < DnsHeader::SIZE + DnsQuestion::MIN_SIZE || size > MAX_SIZE) { @@ -430,36 +427,36 @@ struct DnsMessage auto rr = DnsResource::decode(buffer, offset, dataoffset); if (!rr) { return boost::none; - } else { + } + else { res.parse_rr(buffer, std::move(*rr), dataoffset, txt_keys); } } return std::move(res); } - private: - void parse_rr(const std::vector &buffer, DnsResource &&rr, size_t dataoffset, const Bonjour::TxtKeys &txt_keys) + void parse_rr(const std::vector& buffer, DnsResource&& rr, size_t dataoffset, const Bonjour::TxtKeys& txt_keys) { switch (rr.type) { - case DnsRR_A::TAG: - DnsRR_A::decode(this->rr_a, rr); - this->rr_a->name = rr.name; - break; - case DnsRR_AAAA::TAG: - DnsRR_AAAA::decode(this->rr_aaaa, rr); - this->rr_aaaa->name = rr.name; - break; - case DnsRR_SRV::TAG: { - auto srv = DnsRR_SRV::decode(buffer, rr, dataoffset); - if (srv) { this->sdmap.insert_srv(std::move(rr.name), std::move(*srv)); } - break; - } - case DnsRR_TXT::TAG: { - auto txt = DnsRR_TXT::decode(rr, txt_keys); - if (txt) { this->sdmap.insert_txt(std::move(rr.name), std::move(*txt)); } - break; - } + case DnsRR_A::TAG: + DnsRR_A::decode(this->rr_a, rr); + this->rr_a->name = rr.name; + break; + case DnsRR_AAAA::TAG: + DnsRR_AAAA::decode(this->rr_aaaa, rr); + this->rr_aaaa->name = rr.name; + break; + case DnsRR_SRV::TAG: { + auto srv = DnsRR_SRV::decode(buffer, rr, dataoffset); + if (srv) { this->sdmap.insert_srv(std::move(rr.name), std::move(*srv)); } + break; + } + case DnsRR_TXT::TAG: { + auto txt = DnsRR_TXT::decode(rr, txt_keys); + if (txt) { this->sdmap.insert_txt(std::move(rr.name), std::move(*txt)); } + break; + } } } }; @@ -490,22 +487,6 @@ std::ostream& operator<<(std::ostream &os, const DnsMessage &msg) return os << "])"; } - -struct BonjourRequest -{ - static const asio::ip::address_v4 MCAST_IP4; - static const asio::ip::address_v6 MCAST_IP6; - static const uint16_t MCAST_PORT; - - std::vector data; - - static optional make_PTR(const std::string &service, const std::string &protocol); - static optional make_A(const std::string& hostname); - static optional make_AAAA(const std::string& hostname); -private: - BonjourRequest(std::vector &&data) : data(std::move(data)) {} -}; - const asio::ip::address_v4 BonjourRequest::MCAST_IP4{ 0xe00000fb }; const asio::ip::address_v6 BonjourRequest::MCAST_IP6 = asio::ip::make_address_v6("ff02::fb"); const uint16_t BonjourRequest::MCAST_PORT = 5353; @@ -618,44 +599,8 @@ optional BonjourRequest::make_AAAA(const std::string& hostname) return BonjourRequest(std::move(data)); } -// API - private part - -struct Bonjour::priv -{ - const std::string service; - std::string protocol; - std::string service_dn; - TxtKeys txt_keys; - unsigned timeout; - unsigned retries; - std::string hostname; - - std::vector replies; - - std::vector buffer; - std::thread io_thread; - Bonjour::ReplyFn replyfn; - Bonjour::CompleteFn completefn; - Bonjour::ResolveFn resolvefn; - - priv(std::string &&service); - - std::string strip_service_dn(const std::string &service_name) const; - void udp_receive_lookup(udp::endpoint from, size_t bytes); - void lookup_perform(); - void resolve_perform(); -}; - -Bonjour::priv::priv(std::string &&service) - : service(std::move(service)) - , protocol("tcp") - , timeout(10) - , retries(1) -{ - buffer.resize(DnsMessage::MAX_SIZE); -} - -std::string Bonjour::priv::strip_service_dn(const std::string &service_name) const +namespace { +std::string strip_service_dn(const std::string& service_name, const std::string& service_dn) { if (service_name.size() <= service_dn.size()) { return service_name; @@ -668,155 +613,19 @@ std::string Bonjour::priv::strip_service_dn(const std::string &service_name) con return service_name; } } +} // namespace -void Bonjour::priv::udp_receive_lookup(udp::endpoint from, size_t bytes) +UdpSession::UdpSession(Bonjour::ReplyFn rfn) : replyfn(rfn) { - if (bytes == 0 || !replyfn) { - return; - } - - buffer.resize(bytes); - auto dns_msg = DnsMessage::decode(buffer, txt_keys); - if (dns_msg) { - asio::ip::address ip = from.address(); - if (dns_msg->rr_a) { ip = dns_msg->rr_a->ip; } - else if (dns_msg->rr_aaaa) { ip = dns_msg->rr_aaaa->ip; } - - for (auto &sdpair : dns_msg->sdmap) { - if (! sdpair.second.srv) { - continue; - } - - const auto &srv = *sdpair.second.srv; - auto service_name = strip_service_dn(sdpair.first); - - std::string path; - std::string version; - - BonjourReply::TxtData txt_data; - if (sdpair.second.txt) { - txt_data = std::move(sdpair.second.txt->data); - } - - BonjourReply reply(ip, srv.port, std::move(service_name), srv.hostname, std::move(txt_data)); - replyfn(std::move(reply)); - } - } + buffer.resize(DnsMessage::MAX_SIZE); } -void Bonjour::priv::lookup_perform() -{ - service_dn = (boost::format("_%1%._%2%.local") % service % protocol).str(); - - const auto brq = BonjourRequest::make_PTR(service, protocol); - if (!brq) { - return; - } - - auto self = this; - - try { - boost::asio::io_service io_service; - udp::socket socket(io_service); - socket.open(udp::v4()); - socket.set_option(udp::socket::reuse_address(true)); - udp::endpoint mcast(BonjourRequest::MCAST_IP4, BonjourRequest::MCAST_PORT); - socket.send_to(asio::buffer(brq->data), mcast); - - bool expired = false; - bool retry = false; - asio::deadline_timer timer(io_service); - retries--; - std::function timer_handler = [&](const error_code &error) { - if (retries == 0 || error) { - expired = true; - if (self->completefn) { - self->completefn(); - } - } else { - retry = true; - retries--; - timer.expires_from_now(boost::posix_time::seconds(timeout)); - timer.async_wait(timer_handler); - } - }; - - timer.expires_from_now(boost::posix_time::seconds(timeout)); - timer.async_wait(timer_handler); - - udp::endpoint recv_from; - const auto recv_handler = [&](const error_code &error, size_t bytes) { - if (!error) { self->udp_receive_lookup(recv_from, bytes); } - }; - socket.async_receive_from(asio::buffer(buffer, buffer.size()), recv_from, recv_handler); - - while (io_service.run_one()) { - if (expired) { - socket.cancel(); - } else if (retry) { - retry = false; - socket.send_to(asio::buffer(brq->data), mcast); - } else { - buffer.resize(DnsMessage::MAX_SIZE); - socket.async_receive_from(asio::buffer(buffer, buffer.size()), recv_from, recv_handler); - } - } - } catch (std::exception& /* e */) { - } -} - -class ResolveSocket; -// One session for socket::async_receive_from(). -// Keeps buffer of data so it is not overwritten when another async_receive_from starts. -struct UdpSession : boost::enable_shared_from_this{ - - UdpSession(ResolveSocket* sckt, Bonjour::ReplyFn rfn) : socket(sckt), replyfn(rfn) - { - buffer.resize(DnsMessage::MAX_SIZE); - } - - void handle_receive(const error_code& error, size_t bytes); - - udp::endpoint remote_endpoint; - std::vector buffer; - ResolveSocket* socket; - Bonjour::ReplyFn replyfn; -}; - -// Udp socket for Bonjour::resolve_perform(). -// Starts receiving answers after first send() call until io_service is stopped. -class ResolveSocket -{ -private: - typedef boost::shared_ptr SharedSession; - Bonjour::ReplyFn replyfn; // this doesnt call same fn as replyfn of Bonjour class - asio::ip::address multicast_address; - udp::socket socket; - udp::endpoint mcast_endpoint; - boost::shared_ptr< boost::asio::io_service > io_service; - std::string hostname; - boost::optional request_A; - boost::optional request_AAAA; - asio::ip::multicast::outbound_interface outbound_interface; -public: - ResolveSocket(const std::string& hostname, Bonjour::ReplyFn replyfn, const asio::ip::address& multicast_address, const asio::ip::address& interface_address, boost::shared_ptr< boost::asio::io_service > io_service); - - void send(); - void async_receive(); - void cancel() { socket.cancel(); } - std::string get_hostname() { return hostname; } -private: - void receive_handler(SharedSession session, const error_code& error, size_t bytes); -}; - -ResolveSocket::ResolveSocket(const std::string& hostname, Bonjour::ReplyFn replyfn, const asio::ip::address& multicast_address, const asio::ip::address& interface_address, boost::shared_ptr< boost::asio::io_service > io_service) - : hostname(hostname) - , replyfn(replyfn) +UdpSocket::UdpSocket( Bonjour::ReplyFn replyfn, const asio::ip::address& multicast_address, const asio::ip::address& interface_address, std::shared_ptr< boost::asio::io_service > io_service) + : replyfn(replyfn) , multicast_address(multicast_address) , socket(*io_service) , io_service(io_service) { - assert(!hostname.empty() && replyfn); try { // open socket boost::asio::ip::udp::endpoint listen_endpoint(multicast_address.is_v4() ? udp::v4() : udp::v6(), BonjourRequest::MCAST_PORT); @@ -844,27 +653,35 @@ ResolveSocket::ResolveSocket(const std::string& hostname, Bonjour::ReplyFn reply } } -void ResolveSocket::send() + +UdpSocket::UdpSocket( Bonjour::ReplyFn replyfn, const asio::ip::address& multicast_address, std::shared_ptr< boost::asio::io_service > io_service) + : replyfn(replyfn) + , multicast_address(multicast_address) + , socket(*io_service) + , io_service(io_service) { try { - if (!request_A) { - // BonjourRequest::make_A / AAAA is now implemented to add .local correctly after the hostname. - // If that is unsufficient, we need to change make_A / AAAA and pass full hostname. - std::string trimmed_hostname = hostname; - if (size_t dot_pos = trimmed_hostname.find_first_of('.'); dot_pos != std::string::npos) - trimmed_hostname = trimmed_hostname.substr(0, dot_pos); - request_A = BonjourRequest::make_A(trimmed_hostname); - } - if (!request_AAAA) { - std::string trimmed_hostname = hostname; - if (size_t dot_pos = trimmed_hostname.find_first_of('.'); dot_pos != std::string::npos) - trimmed_hostname = trimmed_hostname.substr(0, dot_pos); - request_AAAA = BonjourRequest::make_AAAA(trimmed_hostname); - } - // multicast both queries - socket.send_to(asio::buffer(request_A->data), mcast_endpoint); - socket.send_to(asio::buffer(request_AAAA->data), mcast_endpoint); + // open socket + boost::asio::ip::udp::endpoint listen_endpoint(multicast_address.is_v4() ? udp::v4() : udp::v6(), BonjourRequest::MCAST_PORT); + socket.open(listen_endpoint.protocol()); + // set socket to listen + socket.set_option(udp::socket::reuse_address(true)); + socket.bind(listen_endpoint); + socket.set_option(boost::asio::ip::multicast::join_group(multicast_address)); + mcast_endpoint = udp::endpoint(multicast_address, BonjourRequest::MCAST_PORT); + BOOST_LOG_TRIVIAL(info) << "Socket created. Multicast: " << multicast_address; + } + catch (std::exception& e) { + BOOST_LOG_TRIVIAL(error) << e.what(); + } +} + +void UdpSocket::send() +{ + try { + for (const auto& request : requests) + socket.send_to(asio::buffer(request.m_data), mcast_endpoint); // Should we care if this is called while already receiving? (async_receive call from receive_handler) async_receive(); @@ -874,30 +691,86 @@ void ResolveSocket::send() } } -void ResolveSocket::async_receive() +void UdpSocket::async_receive() { try { // our session to hold the buffer + endpoint - auto session = boost::make_shared(this, replyfn); + auto session = create_session(); socket.async_receive_from(asio::buffer(session->buffer, session->buffer.size()) , session->remote_endpoint - , boost::bind(&ResolveSocket::receive_handler, this, session, asio::placeholders::error, asio::placeholders::bytes_transferred)); + , boost::bind(&UdpSocket::receive_handler, this, session, asio::placeholders::error, asio::placeholders::bytes_transferred)); } catch (std::exception& e) { BOOST_LOG_TRIVIAL(error) << e.what(); } } -void ResolveSocket::receive_handler(SharedSession session, const error_code& error, size_t bytes) +void UdpSocket::receive_handler(SharedSession session, const boost::system::error_code& error, size_t bytes) { // let io_service to handle the datagram on session - io_service->post(bind(&UdpSession::handle_receive, session, error, bytes)); + // from boost documentation io_service::post: + // The io_service guarantees that the handler will only be called in a thread in which the run(), run_one(), poll() or poll_one() member functions is currently being invoked. + io_service->post(boost::bind(&UdpSession::handle_receive, session, error, bytes)); // immediately accept new datagrams async_receive(); } -void UdpSession::handle_receive(const error_code& error, size_t bytes) +SharedSession LookupSocket::create_session() const +{ + return std::shared_ptr< LookupSession >(new LookupSession(this, replyfn)); +} + + +void LookupSession::handle_receive(const error_code& error, size_t bytes) { + assert(socket); + + if (error) { + BOOST_LOG_TRIVIAL(error) << error.message(); + return; + } + if (bytes == 0 || !replyfn) { + return; + } + + buffer.resize(bytes); + auto dns_msg = DnsMessage::decode(buffer, socket->get_txt_keys()); + if (dns_msg) { + asio::ip::address ip = remote_endpoint.address(); + if (dns_msg->rr_a) { ip = dns_msg->rr_a->ip; } + else if (dns_msg->rr_aaaa) { ip = dns_msg->rr_aaaa->ip; } + + for (auto& sdpair : dns_msg->sdmap) { + if (!sdpair.second.srv) { + continue; + } + + const auto& srv = *sdpair.second.srv; + auto service_name = strip_service_dn(sdpair.first, socket->get_service_dn()); + + std::string path; + std::string version; + + BonjourReply::TxtData txt_data; + if (sdpair.second.txt) { + txt_data = std::move(sdpair.second.txt->data); + } + + BonjourReply reply(ip, srv.port, std::move(service_name), srv.hostname, std::move(txt_data)); + replyfn(std::move(reply)); + } + } +} + +SharedSession ResolveSocket::create_session() const +{ + return std::shared_ptr< ResolveSession > (new ResolveSession(this, replyfn)); +} + + +void ResolveSession::handle_receive(const error_code& error, size_t bytes) +{ + assert(socket); if (error) { // todo: what level? do we even log? There might be callbacks when timer runs out BOOST_LOG_TRIVIAL(info) << error.message(); @@ -910,7 +783,6 @@ void UdpSession::handle_receive(const error_code& error, size_t bytes) buffer.resize(bytes); #if 0 - // this is log of buffer, be careful with logging here - called from async_receive std::string str; char const hex_chars[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; for (size_t i = 0; i < buffer.size(); i++) { @@ -952,81 +824,210 @@ void UdpSession::handle_receive(const error_code& error, size_t bytes) } } -void Bonjour::priv::resolve_perform() +// API - private part + +struct Bonjour::priv { - // reply callback is shared to every UDPSession which is called asyn - boost::mutex replies_guard; - std::vector replies; - // examples would probably store [self] to the lambda (and the timer one), is it ok not to do it? (Should be c++03) - const auto reply_callback = [&rpls = replies, &guard = replies_guard](BonjourReply&& reply) - { - guard.lock(); - if (std::find(rpls.begin(), rpls.end(), reply) == rpls.end()) - rpls.push_back(reply); - guard.unlock(); - }; + const std::string service; + std::string protocol; + std::string service_dn; + TxtKeys txt_keys; + unsigned timeout; + unsigned retries; + std::string hostname; - boost::shared_ptr< boost::asio::io_service > io_service( - new boost::asio::io_service - ); +// std::vector replies; + + std::vector buffer; + std::thread io_thread; + Bonjour::ReplyFn replyfn; + Bonjour::CompleteFn completefn; + Bonjour::ResolveFn resolvefn; + + priv(std::string&& service); + + // void udp_receive_lookup(udp::endpoint from, size_t bytes); + void lookup_perform(); + void resolve_perform(); +}; + +Bonjour::priv::priv(std::string&& service) + : service(std::move(service)) + , protocol("tcp") + , timeout(10) + , retries(1) +{ + buffer.resize(DnsMessage::MAX_SIZE); +} + +void Bonjour::priv::lookup_perform() +{ + service_dn = (boost::format("_%1%._%2%.local") % service % protocol).str(); + + std::shared_ptr< boost::asio::io_service > io_service(new boost::asio::io_service); + + std::vector sockets; - std::vector sockets; - // resolve intefaces - from PR#6646 std::vector interfaces; asio::ip::udp::resolver resolver(*io_service); + boost::system::error_code ec; // ipv4 interfaces - auto results = resolver.resolve(udp::v4(), asio::ip::host_name(), ""); - for (auto const& r : results) { - auto const addr = r.endpoint().address(); - if (addr.is_loopback()) continue; - interfaces.emplace_back(addr); + auto results = resolver.resolve(udp::v4(), asio::ip::host_name(), "", ec); + if (!ec) { + for (const auto & r : results) { + const auto addr = r.endpoint().address(); + if (addr.is_loopback()) continue; + interfaces.emplace_back(std::move(addr)); + } + // create ipv4 socket for each interface + // each will send to querry to for both ipv4 and ipv6 + for (const auto& intrfc : interfaces) + sockets.emplace_back(new LookupSocket(txt_keys, service, service_dn, protocol, replyfn, BonjourRequest::MCAST_IP4, intrfc, io_service)); + } else { + BOOST_LOG_TRIVIAL(info) << "Failed to resolve ipv4 interfaces: " << ec.message(); } - // create ipv4 socket for each interface - // each will send to querry to for both ipv4 and ipv6 - for (const auto intrfc : interfaces) - sockets.emplace_back(new ResolveSocket(hostname, reply_callback, BonjourRequest::MCAST_IP4, intrfc, io_service)); + if (sockets.empty()) + sockets.emplace_back(new LookupSocket(txt_keys, service, service_dn, protocol, replyfn, BonjourRequest::MCAST_IP4, io_service)); // ipv6 interfaces interfaces.clear(); - results = resolver.resolve(udp::v6(), asio::ip::host_name(), ""); - for (auto const& r : results) { - auto const addr = r.endpoint().address(); - if (addr.is_loopback()) continue; - interfaces.emplace_back(addr); + //udp::resolver::query query(host, PORT, boost::asio::ip::resolver_query_base::numeric_service); + results = resolver.resolve(udp::v6(), asio::ip::host_name(), "", ec); + if (!ec) + { + for (const auto& r : results) { + const auto addr = r.endpoint().address(); + if (addr.is_loopback()) continue; + interfaces.emplace_back(std::move(addr)); + } + // create ipv6 socket for each interface + // each will send to querry to for both ipv4 and ipv6 + for (const auto& intrfc : interfaces) + sockets.emplace_back(new LookupSocket(txt_keys, service, service_dn, protocol, replyfn, BonjourRequest::MCAST_IP6, intrfc, io_service)); + if (interfaces.empty()) + sockets.emplace_back(new LookupSocket(txt_keys, service, service_dn, protocol, replyfn, BonjourRequest::MCAST_IP6, io_service)); + } else { + BOOST_LOG_TRIVIAL(info)<< "Failed to resolve ipv6 interfaces: " << ec.message(); } - // create ipv6 socket for each interface - // each will send to querry to for both ipv4 and ipv6 - for (const auto intrfc : interfaces) - sockets.emplace_back(new ResolveSocket(hostname, reply_callback, BonjourRequest::MCAST_IP6, intrfc, io_service)); - + try { // send first queries - for each (auto * socket in sockets) + for (auto * socket : sockets) socket->send(); // timer settings asio::deadline_timer timer(*io_service); retries--; std::function timer_handler = [&](const error_code& error) { - replies_guard.lock(); - int replies_count = replies.size(); - replies_guard.unlock(); // end - if (retries == 0 || error || replies_count > 0) { + if (retries == 0 || error) { // is this correct ending? io_service->stop(); - replies_guard.lock(); - if (replies_count > 0 && resolvefn) { - resolvefn(replies); + if (completefn) { + completefn(); } - replies_guard.unlock(); // restart timer } else { retries--; timer.expires_from_now(boost::posix_time::seconds(timeout)); timer.async_wait(timer_handler); // trigger another round of queries - for each (auto * socket in sockets) + for (auto * socket : sockets) + socket->send(); + } + }; + // start timer + timer.expires_from_now(boost::posix_time::seconds(timeout)); + timer.async_wait(timer_handler); + // start io_service, it will run until it has something to do - so in this case until stop is called in timer + io_service->run(); + } + catch (std::exception& e) { + BOOST_LOG_TRIVIAL(error) << e.what(); + } +} + +void Bonjour::priv::resolve_perform() +{ + // reply callback is shared to every UDPSession which is called on same thread as io_service->run(); + // thus no need to mutex replies in reply_callback, same should go with the timer + std::vector replies; + // examples would store [self] to the lambda (and the timer one), is it ok not to do it? (Should be c++03) + const auto reply_callback = [&rpls = replies](BonjourReply&& reply) + { + if (std::find(rpls.begin(), rpls.end(), reply) == rpls.end()) + rpls.push_back(reply); + }; + + std::shared_ptr< boost::asio::io_service > io_service(new boost::asio::io_service); + + std::vector sockets; + + // resolve intefaces - from PR#6646 + std::vector interfaces; + asio::ip::udp::resolver resolver(*io_service); + boost::system::error_code ec; + // ipv4 interfaces + auto results = resolver.resolve(udp::v4(), asio::ip::host_name(), "", ec); + if (!ec) { + for (auto const& r : results) { + auto const addr = r.endpoint().address(); + if (addr.is_loopback()) continue; + interfaces.emplace_back(addr); + } + // create ipv4 socket for each interface + // each will send to querry to for both ipv4 and ipv6 + for (const auto& intrfc : interfaces) + sockets.emplace_back(new ResolveSocket(hostname, reply_callback, BonjourRequest::MCAST_IP4, intrfc, io_service)); + } else { + BOOST_LOG_TRIVIAL(info) << "Failed to resolve ipv4 interfaces: " << ec.message(); + } + if (sockets.empty()) + sockets.emplace_back(new ResolveSocket(hostname, reply_callback, BonjourRequest::MCAST_IP4, io_service)); + + // ipv6 interfaces + interfaces.clear(); + results = resolver.resolve(udp::v6(), asio::ip::host_name(), "", ec); + if (!ec) { + for (auto const& r : results) { + auto const addr = r.endpoint().address(); + if (addr.is_loopback()) continue; + interfaces.emplace_back(addr); + } + // create ipv6 socket for each interface + // each will send to querry to for both ipv4 and ipv6 + for (const auto& intrfc : interfaces) + sockets.emplace_back(new ResolveSocket(hostname, reply_callback, BonjourRequest::MCAST_IP6, intrfc, io_service)); + if (interfaces.empty()) + sockets.emplace_back(new ResolveSocket(hostname, reply_callback, BonjourRequest::MCAST_IP6, io_service)); + } else { + BOOST_LOG_TRIVIAL(info) << "Failed to resolve ipv6 interfaces: " << ec.message(); + } + + try { + // send first queries + for (auto * socket : sockets) + socket->send(); + + // timer settings + asio::deadline_timer timer(*io_service); + retries--; + std::function timer_handler = [&](const error_code& error) { + int replies_count = replies.size(); + // end + if (retries == 0 || error || replies_count > 0) { + // is this correct ending? + io_service->stop(); + if (replies_count > 0 && resolvefn) { + resolvefn(replies); + } + // restart timer + } else { + retries--; + timer.expires_from_now(boost::posix_time::seconds(timeout)); + timer.async_wait(timer_handler); + // trigger another round of queries + for (auto * socket : sockets) socket->send(); } }; diff --git a/src/slic3r/Utils/Bonjour.hpp b/src/slic3r/Utils/Bonjour.hpp index 156100941..50b71791f 100644 --- a/src/slic3r/Utils/Bonjour.hpp +++ b/src/slic3r/Utils/Bonjour.hpp @@ -7,12 +7,17 @@ #include #include #include -#include +#include +#include +#include +#include +#include namespace Slic3r { + struct BonjourReply { typedef std::unordered_map TxtData; @@ -40,7 +45,6 @@ struct BonjourReply std::ostream& operator<<(std::ostream &, const BonjourReply &); - /// Bonjour lookup performer class Bonjour : public std::enable_shared_from_this { private: @@ -86,6 +90,197 @@ private: std::unique_ptr p; }; +struct BonjourRequest +{ + static const boost::asio::ip::address_v4 MCAST_IP4; + static const boost::asio::ip::address_v6 MCAST_IP6; + static const uint16_t MCAST_PORT; + + std::vector m_data; + + static boost::optional make_PTR(const std::string& service, const std::string& protocol); + static boost::optional make_A(const std::string& hostname); + static boost::optional make_AAAA(const std::string& hostname); +private: + BonjourRequest(std::vector&& data) : m_data(std::move(data)) {} +}; + + +class LookupSocket; +class ResolveSocket; + +// Session is created for each async_receive of socket. On receive, its handle_receive method is called (Thru io_service->post). +// ReplyFn is called if correct datagram was received. +class UdpSession +{ +public: + UdpSession(Bonjour::ReplyFn rfn); + virtual void handle_receive(const boost::system::error_code& error, size_t bytes) = 0; + std::vector buffer; + boost::asio::ip::udp::endpoint remote_endpoint; +protected: + Bonjour::ReplyFn replyfn; +}; +typedef std::shared_ptr SharedSession; +// Session for LookupSocket +class LookupSession : public UdpSession +{ +public: + LookupSession(const LookupSocket* sckt, Bonjour::ReplyFn rfn) : UdpSession(rfn), socket(sckt) {} + void handle_receive(const boost::system::error_code& error, size_t bytes) override; +protected: + // const pointer to socket to get needed data as txt_keys etc. + const LookupSocket* socket; +}; +// Session for ResolveSocket +class ResolveSession : public UdpSession +{ +public: + ResolveSession(const ResolveSocket* sckt, Bonjour::ReplyFn rfn) : UdpSession(rfn), socket(sckt) {} + void handle_receive(const boost::system::error_code& error, size_t bytes) override; +protected: + // const pointer to seocket to get hostname during handle_receive + const ResolveSocket* socket; +}; + +// Udp socket, starts receiving answers after first send() call until io_service is stopped. +class UdpSocket +{ +public: + // Two constructors: 1st is with interface which must be resolved before calling this + UdpSocket(Bonjour::ReplyFn replyfn + , const boost::asio::ip::address& multicast_address + , const boost::asio::ip::address& interface_address + , std::shared_ptr< boost::asio::io_service > io_service); + + UdpSocket(Bonjour::ReplyFn replyfn + , const boost::asio::ip::address& multicast_address + , std::shared_ptr< boost::asio::io_service > io_service); + + void send(); + void async_receive(); + void cancel() { socket.cancel(); } +protected: + void receive_handler(SharedSession session, const boost::system::error_code& error, size_t bytes); + virtual SharedSession create_session() const = 0; + + Bonjour::ReplyFn replyfn; + boost::asio::ip::address multicast_address; + boost::asio::ip::udp::socket socket; + boost::asio::ip::udp::endpoint mcast_endpoint; + std::shared_ptr< boost::asio::io_service > io_service; + std::vector requests; +}; + +class LookupSocket : public UdpSocket +{ +public: + LookupSocket(Bonjour::TxtKeys txt_keys + , std::string service + , std::string service_dn + , std::string protocol + , Bonjour::ReplyFn replyfn + , const boost::asio::ip::address& multicast_address + , const boost::asio::ip::address& interface_address + , std::shared_ptr< boost::asio::io_service > io_service) + : UdpSocket(replyfn, multicast_address, interface_address, io_service) + , txt_keys(txt_keys) + , service(service) + , service_dn(service_dn) + , protocol(protocol) + { + assert(!service.empty() && replyfn); + create_request(); + } + + LookupSocket(Bonjour::TxtKeys txt_keys + , std::string service + , std::string service_dn + , std::string protocol + , Bonjour::ReplyFn replyfn + , const boost::asio::ip::address& multicast_address + , std::shared_ptr< boost::asio::io_service > io_service) + : UdpSocket(replyfn, multicast_address, io_service) + , txt_keys(txt_keys) + , service(service) + , service_dn(service_dn) + , protocol(protocol) + { + assert(!service.empty() && replyfn); + create_request(); + } + + const Bonjour::TxtKeys get_txt_keys() const { return txt_keys; } + const std::string get_service() const { return service; } + const std::string get_service_dn() const { return service_dn; } + +protected: + SharedSession create_session() const override; + void create_request() + { + requests.clear(); + // create PTR request + if (auto rqst = BonjourRequest::make_PTR(service, protocol); rqst) + requests.push_back(std::move(rqst.get())); + } + boost::optional request; + Bonjour::TxtKeys txt_keys; + std::string service; + std::string service_dn; + std::string protocol; +}; + +class ResolveSocket : public UdpSocket +{ +public: + ResolveSocket(const std::string& hostname + , Bonjour::ReplyFn replyfn + , const boost::asio::ip::address& multicast_address + , const boost::asio::ip::address& interface_address + , std::shared_ptr< boost::asio::io_service > io_service) + : UdpSocket(replyfn, multicast_address, interface_address, io_service) + , hostname(hostname) + + { + assert(!hostname.empty() && replyfn); + create_requests(); + } + + ResolveSocket(const std::string& hostname + , Bonjour::ReplyFn replyfn + , const boost::asio::ip::address& multicast_address + , std::shared_ptr< boost::asio::io_service > io_service) + : UdpSocket(replyfn, multicast_address, io_service) + , hostname(hostname) + + { + assert(!hostname.empty() && replyfn); + create_requests(); + } + + std::string get_hostname() const { return hostname; } +protected: + SharedSession create_session() const override; + void create_requests() + { + requests.clear(); + // BonjourRequest::make_A / AAAA is now implemented to add .local correctly after the hostname. + // If that is unsufficient, we need to change make_A / AAAA and pass full hostname. + std::string trimmed_hostname = hostname; + if (size_t dot_pos = trimmed_hostname.find_first_of('.'); dot_pos != std::string::npos) + trimmed_hostname = trimmed_hostname.substr(0, dot_pos); + if (auto rqst = BonjourRequest::make_A(trimmed_hostname); rqst) + requests.push_back(std::move(rqst.get())); + + trimmed_hostname = hostname; + if (size_t dot_pos = trimmed_hostname.find_first_of('.'); dot_pos != std::string::npos) + trimmed_hostname = trimmed_hostname.substr(0, dot_pos); + if (auto rqst = BonjourRequest::make_AAAA(trimmed_hostname); rqst) + requests.push_back(std::move(rqst.get())); + } + + std::string hostname; +}; } diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index 842e0cfd7..4bbef8d38 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -243,7 +243,7 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro .set_timeout(1) // after each timeout, if there is any answer, the resolving will stop .on_resolve([&ra = resolved_addr](const std::vector& replies) { std::vector resolved_addr; - for each (const auto & rpl in replies) { + for (const auto & rpl : replies) { boost::asio::ip::address ip(rpl.ip); ra.emplace_back(ip); BOOST_LOG_TRIVIAL(info) << "Resolved IP address: " << rpl.ip; @@ -262,7 +262,7 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, const std::vector& resolved_addr) const { wxString error_message; - for each (const auto& ip in resolved_addr) { + for (const auto& ip : resolved_addr) { // If test fails, test_msg_or_host_ip contains the error message. // Otherwise on Windows it contains the resolved IP address of the host. // Test_msg already contains resolved ip and will be cleared on start of test(). diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp index f2db4bc1a..96a21d7c0 100644 --- a/src/slic3r/Utils/OctoPrint.hpp +++ b/src/slic3r/Utils/OctoPrint.hpp @@ -4,11 +4,11 @@ #include #include #include +#include #include "PrintHost.hpp" #include "libslic3r/PrintConfig.hpp" -class boost::asio::ip::address; namespace Slic3r { From 9fd3108726c04aa842000c6222361289ec723571 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Fri, 6 May 2022 14:47:20 +0200 Subject: [PATCH 04/13] Resolved IP dialog before uploading - needs better design and to not show if only 1x v4 and v6 is resolved. --- src/slic3r/GUI/BonjourDialog.cpp | 42 ++++++++++++++++++++++++++++++++ src/slic3r/GUI/BonjourDialog.hpp | 19 +++++++++++++-- src/slic3r/Utils/OctoPrint.cpp | 31 +++++++++++++++-------- src/slic3r/Utils/OctoPrint.hpp | 3 +-- 4 files changed, 81 insertions(+), 14 deletions(-) diff --git a/src/slic3r/GUI/BonjourDialog.cpp b/src/slic3r/GUI/BonjourDialog.cpp index 516b1ab4a..69ab23896 100644 --- a/src/slic3r/GUI/BonjourDialog.cpp +++ b/src/slic3r/GUI/BonjourDialog.cpp @@ -233,7 +233,49 @@ void BonjourDialog::on_timer_process() } } +IPListDialog::IPListDialog(wxWindow* parent, const wxString& hostname, const std::vector& ips, size_t& selected_index) + : wxDialog(parent, wxID_ANY, _(L("Multiple resolved IP addresses")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) + , m_list(new wxListView(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxSIMPLE_BORDER)) + , m_selected_index (selected_index) +{ + const int em = GUI::wxGetApp().em_unit(); + m_list->SetMinSize(wxSize(20 * em, 30 * em)); + wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL); + auto* label = new wxStaticText(this, wxID_ANY, GUI::format_wxstr(_L("There are several IP addresses resolving to hostname %1%.\n Please select one that should be used."), hostname)); + vsizer->Add(label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, em); + + m_list->SetSingleStyle(wxLC_SINGLE_SEL); + m_list->AppendColumn(_(L("Address")), wxLIST_FORMAT_LEFT, 20 * em); + + for (size_t i = 0; i < ips.size(); i++) + auto item = m_list->InsertItem(i, boost::nowide::widen(ips[i].to_string())); + + m_list->Select(0); + + vsizer->Add(m_list, 1, wxEXPAND | wxALL, em); + + wxBoxSizer* button_sizer = new wxBoxSizer(wxHORIZONTAL); + button_sizer->Add(new wxButton(this, wxID_OK, "OK"), 0, wxALL, em); + button_sizer->Add(new wxButton(this, wxID_CANCEL, "Cancel"), 0, wxALL, em); + + vsizer->Add(button_sizer, 0, wxALIGN_CENTER); + SetSizerAndFit(vsizer); + + GUI::wxGetApp().UpdateDlgDarkUI(this); +} + +IPListDialog::~IPListDialog() +{ +} + +void IPListDialog::EndModal(int retCode) +{ + if (retCode == wxID_OK) { + m_selected_index = (size_t)m_list->GetFirstSelected(); + } + wxDialog::EndModal(retCode); +} } diff --git a/src/slic3r/GUI/BonjourDialog.hpp b/src/slic3r/GUI/BonjourDialog.hpp index def0838d7..6d1af491b 100644 --- a/src/slic3r/GUI/BonjourDialog.hpp +++ b/src/slic3r/GUI/BonjourDialog.hpp @@ -4,6 +4,7 @@ #include #include +#include #include "libslic3r/PrintConfig.hpp" @@ -11,7 +12,7 @@ class wxListView; class wxStaticText; class wxTimer; class wxTimerEvent; - +class boost::asio::ip::address; namespace Slic3r { @@ -41,12 +42,26 @@ private: unsigned timer_state; Slic3r::PrinterTechnology tech; - void on_reply(BonjourReplyEvent &); + virtual void on_reply(BonjourReplyEvent &); void on_timer(wxTimerEvent &); void on_timer_process(); }; +class IPListDialog : public wxDialog +{ +public: + IPListDialog(wxWindow* parent, const wxString& hostname, const std::vector& ips, size_t& selected_index); + IPListDialog(IPListDialog&&) = delete; + IPListDialog(const IPListDialog&) = delete; + IPListDialog& operator=(IPListDialog&&) = delete; + IPListDialog& operator=(const IPListDialog&) = delete; + ~IPListDialog(); + virtual void EndModal(int retCode) wxOVERRIDE; +private: + wxListView* m_list; + size_t& m_selected_index; +}; } diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index 4bbef8d38..3b18b1960 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -20,6 +20,7 @@ #include "Http.hpp" #include "libslic3r/AppConfig.hpp" #include "Bonjour.hpp" +#include "slic3r/GUI/BonjourDialog.hpp" namespace fs = boost::filesystem; namespace pt = boost::property_tree; @@ -115,7 +116,7 @@ bool OctoPrint::test_with_resolved_ip(wxString &msg) const auto url = substitute_host(make_url("api/version"), GUI::into_u8(msg)); msg.Clear(); - BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url; + BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Get version at: %2%") % name % url; auto http = Http::get(std::move(url)); set_auth(http); @@ -126,7 +127,7 @@ bool OctoPrint::test_with_resolved_ip(wxString &msg) const msg = format_error(body, error, status); }) .on_complete([&, this](std::string body, unsigned) { - BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got version: %2%") % name % body; + BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Got version: %2%") % name % body; try { std::stringstream ss(body); @@ -253,30 +254,40 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro } if (resolved_addr.empty()) { BOOST_LOG_TRIVIAL(error) << "PrusaSlicer failed to resolve hostname " << m_host << " into the IP address. Starting upload with system resolving."; - return false;//upload_inner_with_host(upload_data, prorgess_fn, error_fn); + return upload_inner_with_host(upload_data, prorgess_fn, error_fn); } - return upload_inner_with_resolved_ip(upload_data, prorgess_fn, error_fn, resolved_addr); + if (resolved_addr.size() > 1) { + size_t selected_index = resolved_addr.size(); + IPListDialog dialog(nullptr, boost::nowide::widen(m_host), resolved_addr, selected_index); + if (dialog.ShowModal() == wxID_OK && selected_index < resolved_addr.size()) { + return upload_inner_with_resolved_ip(upload_data, prorgess_fn, error_fn, resolved_addr[selected_index]); + } + } + return false; #endif // WIN32 } #ifdef WIN32 -bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, const std::vector& resolved_addr) const +bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, const boost::asio::ip::address& resolved_addr) const { wxString error_message; - for (const auto& ip : resolved_addr) { + //for (const auto& ip : resolved_addr) + { // If test fails, test_msg_or_host_ip contains the error message. // Otherwise on Windows it contains the resolved IP address of the host. // Test_msg already contains resolved ip and will be cleared on start of test(). - wxString test_msg_or_host_ip = GUI::from_u8(ip.to_string()); + wxString test_msg_or_host_ip = GUI::from_u8(resolved_addr.to_string()); if (!test_with_resolved_ip(test_msg_or_host_ip)) { error_message = test_msg_or_host_ip; - BOOST_LOG_TRIVIAL(info) << test_msg_or_host_ip; - continue; + //BOOST_LOG_TRIVIAL(info) << test_msg_or_host_ip; + error_fn(std::move(error_message)); + //continue; + return false; } const char* name = get_name(); const auto upload_filename = upload_data.upload_path.filename(); const auto upload_parent_path = upload_data.upload_path.parent_path(); - std::string url = substitute_host(make_url("api/files/local"), ip.to_string()); + std::string url = substitute_host(make_url("api/files/local"), resolved_addr.to_string()); bool result = true; BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Uploading file %2% at %3%, filename: %4%, path: %5%, print: %6%") diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp index 96a21d7c0..deaeec888 100644 --- a/src/slic3r/Utils/OctoPrint.hpp +++ b/src/slic3r/Utils/OctoPrint.hpp @@ -37,12 +37,11 @@ public: protected: virtual bool validate_version_text(const boost::optional &version_text) const; #ifdef WIN32 - virtual bool upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, const std::vector& resolved_addr) const; + virtual bool upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, const boost::asio::ip::address& resolved_addr) const; virtual bool test_with_resolved_ip(wxString& curl_msg) const; #endif virtual bool upload_inner_with_host(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const; - private: std::string m_host; std::string m_apikey; From bc167ce90bc23ff5375bbe5b782fbb8df036446d Mon Sep 17 00:00:00 2001 From: David Kocik Date: Fri, 6 May 2022 17:10:02 +0200 Subject: [PATCH 05/13] Decision tree for different types and numbers of resolved IPs. --- src/slic3r/Utils/OctoPrint.cpp | 123 +++++++++++++++++---------------- src/slic3r/Utils/OctoPrint.hpp | 2 +- 2 files changed, 66 insertions(+), 59 deletions(-) diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index 3b18b1960..d180562b8 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -116,7 +116,7 @@ bool OctoPrint::test_with_resolved_ip(wxString &msg) const auto url = substitute_host(make_url("api/version"), GUI::into_u8(msg)); msg.Clear(); - BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Get version at: %2%") % name % url; + BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url; auto http = Http::get(std::move(url)); set_auth(http); @@ -127,7 +127,7 @@ bool OctoPrint::test_with_resolved_ip(wxString &msg) const msg = format_error(body, error, status); }) .on_complete([&, this](std::string body, unsigned) { - BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Got version: %2%") % name % body; + BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Got version: %2%") % name % body; try { std::stringstream ss(body); @@ -253,10 +253,20 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro .resolve_sync(); } if (resolved_addr.empty()) { + // no resolved addresses - try system resolving BOOST_LOG_TRIVIAL(error) << "PrusaSlicer failed to resolve hostname " << m_host << " into the IP address. Starting upload with system resolving."; return upload_inner_with_host(upload_data, prorgess_fn, error_fn); - } - if (resolved_addr.size() > 1) { + } else if (resolved_addr.size() == 1) { + // one address resolved - upload there + return upload_inner_with_resolved_ip(upload_data, prorgess_fn, error_fn, resolved_addr.front()); + } else if (resolved_addr.size() == 2 && resolved_addr[0].is_v4() != resolved_addr[1].is_v4()) { + // there are just 2 addresses and 1 is ip_v4 and other is ip_v6 + // try sending to both. (First without verbose, so user doesnt get error msg twice) + if (!upload_inner_with_resolved_ip(upload_data, prorgess_fn, error_fn, resolved_addr.front(), false)) + return upload_inner_with_resolved_ip(upload_data, prorgess_fn, error_fn, resolved_addr.back()); + return true; + } else { + // There are multiple addresses - user needs to choose which to use. size_t selected_index = resolved_addr.size(); IPListDialog dialog(nullptr, boost::nowide::widen(m_host), resolved_addr, selected_index); if (dialog.ShowModal() == wxID_OK && selected_index < resolved_addr.size()) { @@ -267,66 +277,63 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro #endif // WIN32 } #ifdef WIN32 -bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, const boost::asio::ip::address& resolved_addr) const +bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, const boost::asio::ip::address& resolved_addr, bool verbose /*= true*/) const { - wxString error_message; - //for (const auto& ip : resolved_addr) - { - // If test fails, test_msg_or_host_ip contains the error message. - // Otherwise on Windows it contains the resolved IP address of the host. - // Test_msg already contains resolved ip and will be cleared on start of test(). - wxString test_msg_or_host_ip = GUI::from_u8(resolved_addr.to_string()); - if (!test_with_resolved_ip(test_msg_or_host_ip)) { - error_message = test_msg_or_host_ip; - //BOOST_LOG_TRIVIAL(info) << test_msg_or_host_ip; - error_fn(std::move(error_message)); - //continue; - return false; - } - - const char* name = get_name(); - const auto upload_filename = upload_data.upload_path.filename(); - const auto upload_parent_path = upload_data.upload_path.parent_path(); - std::string url = substitute_host(make_url("api/files/local"), resolved_addr.to_string()); - bool result = true; + // If test fails, test_msg_or_host_ip contains the error message. + // Otherwise on Windows it contains the resolved IP address of the host. + // Test_msg already contains resolved ip and will be cleared on start of test(). + wxString test_msg_or_host_ip = GUI::from_u8(resolved_addr.to_string()); + if (!test_with_resolved_ip(test_msg_or_host_ip)) { + if (verbose) + error_fn(std::move(test_msg_or_host_ip)); + else BOOST_LOG_TRIVIAL(info) << test_msg_or_host_ip; + //continue; + return false; + } - BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Uploading file %2% at %3%, filename: %4%, path: %5%, print: %6%") - % name - % upload_data.source_path - % url - % upload_filename.string() - % upload_parent_path.string() - % (upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false"); + const char* name = get_name(); + const auto upload_filename = upload_data.upload_path.filename(); + const auto upload_parent_path = upload_data.upload_path.parent_path(); + std::string url = substitute_host(make_url("api/files/local"), resolved_addr.to_string()); + bool result = true; - auto http = Http::post(std::move(url)); - set_auth(http); - http.form_add("print", upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false") - .form_add("path", upload_parent_path.string()) // XXX: slashes on windows ??? - .form_add_file("file", upload_data.source_path.string(), upload_filename.string()) - .on_complete([&](std::string body, unsigned status) { - BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%: %3%") % name % status % body; - }) - .on_error([&](std::string body, std::string error, unsigned status) { + BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Uploading file %2% at %3%, filename: %4%, path: %5%, print: %6%") + % name + % upload_data.source_path + % url + % upload_filename.string() + % upload_parent_path.string() + % (upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false"); + + auto http = Http::post(std::move(url)); + set_auth(http); + http.form_add("print", upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false") + .form_add("path", upload_parent_path.string()) // XXX: slashes on windows ??? + .form_add_file("file", upload_data.source_path.string(), upload_filename.string()) + .on_complete([&](std::string body, unsigned status) { + BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%: %3%") % name % status % body; + }) + .on_error([&](std::string body, std::string error, unsigned status) { + if (verbose) { BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading file: %2%, HTTP %3%, body: `%4%`") % name % error % status % body; error_fn(format_error(body, error, status)); + } + else + BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Error uploading file: %2%, HTTP %3%, body: `%4%`") % name % error % status % body; + result = false; + }) + .on_progress([&](Http::Progress progress, bool& cancel) { + prorgess_fn(std::move(progress), cancel); + if (cancel) { + // Upload was canceled + BOOST_LOG_TRIVIAL(info) << "Octoprint: Upload canceled"; result = false; - }) - .on_progress([&](Http::Progress progress, bool& cancel) { - prorgess_fn(std::move(progress), cancel); - if (cancel) { - // Upload was canceled - BOOST_LOG_TRIVIAL(info) << "Octoprint: Upload canceled"; - result = false; - } - }) - .ssl_revoke_best_effort(m_ssl_revoke_best_effort) - .perform_sync(); - if (result) - return true; - } - // todo: failed. Should we try again with host? - error_fn(std::move(error_message)); - return false; + } + }) + .ssl_revoke_best_effort(m_ssl_revoke_best_effort) + .perform_sync(); + + return result; } #endif //WIN32 diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp index deaeec888..d65bf7340 100644 --- a/src/slic3r/Utils/OctoPrint.hpp +++ b/src/slic3r/Utils/OctoPrint.hpp @@ -37,7 +37,7 @@ public: protected: virtual bool validate_version_text(const boost::optional &version_text) const; #ifdef WIN32 - virtual bool upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, const boost::asio::ip::address& resolved_addr) const; + virtual bool upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, const boost::asio::ip::address& resolved_addr, bool verbose = true) const; virtual bool test_with_resolved_ip(wxString& curl_msg) const; #endif virtual bool upload_inner_with_host(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const; From 89e288dcf85b6022ea320a9fc41d188bee60c588 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 9 May 2022 12:56:24 +0200 Subject: [PATCH 06/13] Propagate resolved ip address to the UI layer (both upload dialog and notification) --- src/slic3r/GUI/NotificationManager.cpp | 21 ++++++++++++++++++++- src/slic3r/GUI/NotificationManager.hpp | 9 ++++++++- src/slic3r/GUI/PrintHostDialogs.cpp | 13 +++++++++++++ src/slic3r/GUI/PrintHostDialogs.hpp | 4 +++- src/slic3r/Utils/AstroBox.cpp | 2 +- src/slic3r/Utils/AstroBox.hpp | 2 +- src/slic3r/Utils/Duet.cpp | 2 +- src/slic3r/Utils/Duet.hpp | 2 +- src/slic3r/Utils/FlashAir.cpp | 2 +- src/slic3r/Utils/FlashAir.hpp | 2 +- src/slic3r/Utils/MKS.cpp | 2 +- src/slic3r/Utils/MKS.hpp | 2 +- src/slic3r/Utils/OctoPrint.cpp | 25 ++++++++++++++----------- src/slic3r/Utils/OctoPrint.hpp | 6 +++--- src/slic3r/Utils/PrintHost.cpp | 11 +++++++++++ src/slic3r/Utils/PrintHost.hpp | 3 ++- src/slic3r/Utils/Repetier.cpp | 2 +- src/slic3r/Utils/Repetier.hpp | 2 +- 18 files changed, 84 insertions(+), 28 deletions(-) diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index fc20e43f5..737b8aa6c 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -1823,7 +1823,7 @@ void NotificationManager::push_upload_job_notification(int id, float filesize, } std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host); NotificationData data{ NotificationType::PrintHostUpload, NotificationLevel::ProgressBarNotificationLevel, 10, text }; - push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, 0, id, filesize), 0); + push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, 0, id, filesize, filename, host), 0); } void NotificationManager::set_upload_job_notification_percentage(int id, const std::string& filename, const std::string& host, float percentage) { @@ -1832,6 +1832,21 @@ void NotificationManager::set_upload_job_notification_percentage(int id, const s PrintHostUploadNotification* phun = dynamic_cast(notification.get()); if (phun->compare_job_id(id)) { phun->set_percentage(percentage); + if (phun->get_host() != host) + phun->set_host(host); + wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); + break; + } + } + } +} +void NotificationManager::set_upload_job_notification_host(int id, const std::string& host) +{ + for (std::unique_ptr& notification : m_pop_notifications) { + if (notification->get_type() == NotificationType::PrintHostUpload) { + PrintHostUploadNotification* phun = dynamic_cast(notification.get()); + if (phun->compare_job_id(id)) { + phun->set_host(host); wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); break; } @@ -1845,6 +1860,8 @@ void NotificationManager::upload_job_notification_show_canceled(int id, const st PrintHostUploadNotification* phun = dynamic_cast(notification.get()); if (phun->compare_job_id(id)) { phun->cancel(); + if (phun->get_host() != host) + phun->set_host(host); wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); break; } @@ -1858,6 +1875,8 @@ void NotificationManager::upload_job_notification_show_error(int id, const std:: PrintHostUploadNotification* phun = dynamic_cast(notification.get()); if(phun->compare_job_id(id)) { phun->error(); + if (phun->get_host() != host) + phun->set_host(host); wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); break; } diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index c6a24d997..d5dd9b48d 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -203,6 +203,7 @@ public: // print host upload void push_upload_job_notification(int id, float filesize, const std::string& filename, const std::string& host, float percentage = 0); void set_upload_job_notification_percentage(int id, const std::string& filename, const std::string& host, float percentage); + void set_upload_job_notification_host(int id, const std::string& host); void upload_job_notification_show_canceled(int id, const std::string& filename, const std::string& host); void upload_job_notification_show_error(int id, const std::string& filename, const std::string& host); // Download App progress @@ -505,10 +506,12 @@ private: PB_CANCELLED, PB_COMPLETED }; - PrintHostUploadNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, float percentage, int job_id, float filesize) + PrintHostUploadNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, float percentage, int job_id, float filesize, const std::string& filename, const std::string& host) :ProgressBarNotification(n, id_provider, evt_handler) , m_job_id(job_id) , m_file_size(filesize) + , m_filename(filename) + , m_host(host) { m_has_cancel_button = true; set_percentage(percentage); @@ -519,6 +522,8 @@ private: void error() { m_uj_state = UploadJobState::PB_ERROR; m_has_cancel_button = false; init(); } bool compare_job_id(const int other_id) const { return m_job_id == other_id; } bool compare_text(const std::string& text) const override { return false; } + void set_host(const std::string& host) { m_host = host; update({ NotificationType::PrintHostUpload, NotificationLevel::ProgressBarNotificationLevel, 10, get_upload_job_text(m_id, m_filename, m_host)}); } + std::string get_host() const { return m_host; } protected: void init() override; void count_spaces() override; @@ -536,6 +541,8 @@ private: float m_file_size; long m_hover_time{ 0 }; UploadJobState m_uj_state{ UploadJobState::PB_PROGRESS }; + std::string m_filename; + std::string m_host; }; class SlicingProgressNotification : public ProgressBarNotification diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index a09d15882..ec425ae79 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -190,6 +190,7 @@ void PrintHostSendDialog::EndModal(int ret) wxDEFINE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event); wxDEFINE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event); wxDEFINE_EVENT(EVT_PRINTHOST_CANCEL, PrintHostQueueDialog::Event); +wxDEFINE_EVENT(EVT_PRINTHOST_RESOLVE, PrintHostQueueDialog::Event); PrintHostQueueDialog::Event::Event(wxEventType eventType, int winid, size_t job_id) : wxEvent(winid, eventType) @@ -218,6 +219,7 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent) , on_progress_evt(this, EVT_PRINTHOST_PROGRESS, &PrintHostQueueDialog::on_progress, this) , on_error_evt(this, EVT_PRINTHOST_ERROR, &PrintHostQueueDialog::on_error, this) , on_cancel_evt(this, EVT_PRINTHOST_CANCEL, &PrintHostQueueDialog::on_cancel, this) + , on_resolve_evt(this, EVT_PRINTHOST_RESOLVE, &PrintHostQueueDialog::on_resolve, this) { const auto em = GetTextExtent("m").x; @@ -450,6 +452,17 @@ void PrintHostQueueDialog::on_cancel(Event &evt) wxGetApp().notification_manager()->upload_job_notification_show_canceled(evt.job_id + 1, boost::nowide::narrow(nm.GetString()), boost::nowide::narrow(hst.GetString())); } +void PrintHostQueueDialog::on_resolve(Event& evt) +{ + wxCHECK_RET(evt.job_id < (size_t)job_list->GetItemCount(), "Out of bounds access to job list"); + + // wxstring in event is called error, but it should contain new host string. + wxVariant hst(evt.error); + // todo: set variant + job_list->SetValue(hst,evt.job_id,COL_HOST); + wxGetApp().notification_manager()->set_upload_job_notification_host(evt.job_id + 1, boost::nowide::narrow(evt.error)); +} + void PrintHostQueueDialog::get_active_jobs(std::vector>& ret) { int ic = job_list->GetItemCount(); diff --git a/src/slic3r/GUI/PrintHostDialogs.hpp b/src/slic3r/GUI/PrintHostDialogs.hpp index ff3eb6012..9173807b5 100644 --- a/src/slic3r/GUI/PrintHostDialogs.hpp +++ b/src/slic3r/GUI/PrintHostDialogs.hpp @@ -108,6 +108,7 @@ private: EventGuard on_progress_evt; EventGuard on_error_evt; EventGuard on_cancel_evt; + EventGuard on_resolve_evt; JobState get_state(int idx); void set_state(int idx, JobState); @@ -115,6 +116,7 @@ private: void on_progress(Event&); void on_error(Event&); void on_cancel(Event&); + void on_resolve(Event&); // This vector keep adress and filename of uploads. It is used when checking for running uploads during exit. std::vector> upload_names; void save_user_data(int); @@ -124,7 +126,7 @@ private: wxDECLARE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event); wxDECLARE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event); wxDECLARE_EVENT(EVT_PRINTHOST_CANCEL, PrintHostQueueDialog::Event); - +wxDECLARE_EVENT(EVT_PRINTHOST_RESOLVE, PrintHostQueueDialog::Event); }} #endif diff --git a/src/slic3r/Utils/AstroBox.cpp b/src/slic3r/Utils/AstroBox.cpp index 8781549a2..b512e301a 100644 --- a/src/slic3r/Utils/AstroBox.cpp +++ b/src/slic3r/Utils/AstroBox.cpp @@ -92,7 +92,7 @@ wxString AstroBox::get_test_failed_msg (wxString &msg) const % _utf8(L("Note: AstroBox version at least 1.1.0 is required."))).str()); } -bool AstroBox::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const +bool AstroBox::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const { const char *name = get_name(); diff --git a/src/slic3r/Utils/AstroBox.hpp b/src/slic3r/Utils/AstroBox.hpp index 15a8863a9..67fb86130 100644 --- a/src/slic3r/Utils/AstroBox.hpp +++ b/src/slic3r/Utils/AstroBox.hpp @@ -23,7 +23,7 @@ public: bool test(wxString &curl_msg) const override; wxString get_test_ok_msg () const override; wxString get_test_failed_msg (wxString &msg) const override; - bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override; + bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const override; bool has_auto_discovery() const override { return true; } bool can_test() const override { return true; } PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint; } diff --git a/src/slic3r/Utils/Duet.cpp b/src/slic3r/Utils/Duet.cpp index 3293a3ff2..e67e5f31e 100644 --- a/src/slic3r/Utils/Duet.cpp +++ b/src/slic3r/Utils/Duet.cpp @@ -54,7 +54,7 @@ wxString Duet::get_test_failed_msg (wxString &msg) const % std::string(msg.ToUTF8())).str()); } -bool Duet::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const +bool Duet::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const { wxString connect_msg; auto connectionType = connect(connect_msg); diff --git a/src/slic3r/Utils/Duet.hpp b/src/slic3r/Utils/Duet.hpp index edca66ce0..7980994ad 100644 --- a/src/slic3r/Utils/Duet.hpp +++ b/src/slic3r/Utils/Duet.hpp @@ -22,7 +22,7 @@ public: bool test(wxString &curl_msg) const override; wxString get_test_ok_msg() const override; wxString get_test_failed_msg(wxString &msg) const override; - bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override; + bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const override; bool has_auto_discovery() const override { return false; } bool can_test() const override { return true; } PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint | PrintHostPostUploadAction::StartSimulation; } diff --git a/src/slic3r/Utils/FlashAir.cpp b/src/slic3r/Utils/FlashAir.cpp index 2337ac290..612c79eda 100644 --- a/src/slic3r/Utils/FlashAir.cpp +++ b/src/slic3r/Utils/FlashAir.cpp @@ -76,7 +76,7 @@ wxString FlashAir::get_test_failed_msg (wxString &msg) const % _utf8(L("Note: FlashAir with firmware 2.00.02 or newer and activated upload function is required."))).str()); } -bool FlashAir::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const +bool FlashAir::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const { const char *name = get_name(); diff --git a/src/slic3r/Utils/FlashAir.hpp b/src/slic3r/Utils/FlashAir.hpp index 14e3f0015..b961ccfa0 100644 --- a/src/slic3r/Utils/FlashAir.hpp +++ b/src/slic3r/Utils/FlashAir.hpp @@ -23,7 +23,7 @@ public: bool test(wxString &curl_msg) const override; wxString get_test_ok_msg() const override; wxString get_test_failed_msg(wxString &msg) const override; - bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override; + bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const override; bool has_auto_discovery() const override { return false; } bool can_test() const override { return true; } PrintHostPostUploadActions get_post_upload_actions() const override { return {}; } diff --git a/src/slic3r/Utils/MKS.cpp b/src/slic3r/Utils/MKS.cpp index 80a79537d..268b291af 100644 --- a/src/slic3r/Utils/MKS.cpp +++ b/src/slic3r/Utils/MKS.cpp @@ -62,7 +62,7 @@ wxString MKS::get_test_failed_msg(wxString& msg) const % std::string(msg.ToUTF8())).str()); } -bool MKS::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const +bool MKS::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const { bool res = true; diff --git a/src/slic3r/Utils/MKS.hpp b/src/slic3r/Utils/MKS.hpp index 22455436a..53f071fc3 100644 --- a/src/slic3r/Utils/MKS.hpp +++ b/src/slic3r/Utils/MKS.hpp @@ -22,7 +22,7 @@ public: bool test(wxString& curl_msg) const override; wxString get_test_ok_msg() const override; wxString get_test_failed_msg(wxString& msg) const override; - bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override; + bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const override; bool has_auto_discovery() const override { return false; } bool can_test() const override { return true; } PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint; } diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index d180562b8..f9f563ecc 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -118,11 +118,11 @@ bool OctoPrint::test_with_resolved_ip(wxString &msg) const BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url; - auto http = Http::get(std::move(url)); + auto http = Http::get(url);//std::move(url)); set_auth(http); http .on_error([&](std::string body, std::string error, unsigned status) { - BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting version: %2%, HTTP %3%, body: `%4%`") % name % error % status % body; + BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting version at %2% : %3%, HTTP %4%, body: `%5%`") % name % url % error % status % body; res = false; msg = format_error(body, error, status); }) @@ -226,10 +226,10 @@ wxString OctoPrint::get_test_failed_msg (wxString &msg) const % _utf8(L("Note: OctoPrint version at least 1.1.0 is required."))).str()); } -bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const +bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const { #ifndef WIN32 - return upload_inner_with_host(upload_data, prorgess_fn, error_fn); + return upload_inner_with_host(upload_data, prorgess_fn, error_fn, resolve_fn); #else // decide what to do based on m_host - resolve hostname or upload to ip std::vector resolved_addr; @@ -255,30 +255,32 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro if (resolved_addr.empty()) { // no resolved addresses - try system resolving BOOST_LOG_TRIVIAL(error) << "PrusaSlicer failed to resolve hostname " << m_host << " into the IP address. Starting upload with system resolving."; - return upload_inner_with_host(upload_data, prorgess_fn, error_fn); + return upload_inner_with_host(upload_data, prorgess_fn, error_fn, resolve_fn); } else if (resolved_addr.size() == 1) { // one address resolved - upload there - return upload_inner_with_resolved_ip(upload_data, prorgess_fn, error_fn, resolved_addr.front()); + return upload_inner_with_resolved_ip(upload_data, prorgess_fn, error_fn, resolve_fn, resolved_addr.front()); } else if (resolved_addr.size() == 2 && resolved_addr[0].is_v4() != resolved_addr[1].is_v4()) { // there are just 2 addresses and 1 is ip_v4 and other is ip_v6 // try sending to both. (First without verbose, so user doesnt get error msg twice) - if (!upload_inner_with_resolved_ip(upload_data, prorgess_fn, error_fn, resolved_addr.front(), false)) - return upload_inner_with_resolved_ip(upload_data, prorgess_fn, error_fn, resolved_addr.back()); + if (!upload_inner_with_resolved_ip(upload_data, prorgess_fn, error_fn, resolve_fn, resolved_addr.front(), false)) + return upload_inner_with_resolved_ip(upload_data, prorgess_fn, error_fn, resolve_fn, resolved_addr.back()); return true; } else { // There are multiple addresses - user needs to choose which to use. size_t selected_index = resolved_addr.size(); IPListDialog dialog(nullptr, boost::nowide::widen(m_host), resolved_addr, selected_index); if (dialog.ShowModal() == wxID_OK && selected_index < resolved_addr.size()) { - return upload_inner_with_resolved_ip(upload_data, prorgess_fn, error_fn, resolved_addr[selected_index]); + return upload_inner_with_resolved_ip(upload_data, prorgess_fn, error_fn, resolve_fn, resolved_addr[selected_index]); } } return false; #endif // WIN32 } #ifdef WIN32 -bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, const boost::asio::ip::address& resolved_addr, bool verbose /*= true*/) const +bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn, const boost::asio::ip::address& resolved_addr, bool verbose /*= true*/) const { + resolve_fn(boost::nowide::widen(resolved_addr.to_string())); + // If test fails, test_msg_or_host_ip contains the error message. // Otherwise on Windows it contains the resolved IP address of the host. // Test_msg already contains resolved ip and will be cleared on start of test(). @@ -337,7 +339,7 @@ bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, Progr } #endif //WIN32 -bool OctoPrint::upload_inner_with_host(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const +bool OctoPrint::upload_inner_with_host(PrintHostUpload upload_data, ProgressFn prorgess_fn, ResolveFn resolve_fn, ErrorFn error_fn) const { const char* name = get_name(); @@ -372,6 +374,7 @@ bool OctoPrint::upload_inner_with_host(PrintHostUpload upload_data, ProgressFn p // This new address returns in "test_msg_or_host_ip" variable. // Solves troubles of uploades failing with name address. // in original address (m_host) replace host for resolved ip + resolve_fn(test_msg_or_host_ip); url = substitute_host(make_url("api/files/local"), GUI::into_u8(test_msg_or_host_ip)); BOOST_LOG_TRIVIAL(info) << "Upload address after ip resolve: " << url; } diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp index d65bf7340..9511e24a8 100644 --- a/src/slic3r/Utils/OctoPrint.hpp +++ b/src/slic3r/Utils/OctoPrint.hpp @@ -26,7 +26,7 @@ public: bool test(wxString &curl_msg) const override; wxString get_test_ok_msg () const override; wxString get_test_failed_msg (wxString &msg) const override; - bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override; + bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const override; bool has_auto_discovery() const override { return true; } bool can_test() const override { return true; } PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint; } @@ -37,10 +37,10 @@ public: protected: virtual bool validate_version_text(const boost::optional &version_text) const; #ifdef WIN32 - virtual bool upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, const boost::asio::ip::address& resolved_addr, bool verbose = true) const; + virtual bool upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn, const boost::asio::ip::address& resolved_addr, bool verbose = true) const; virtual bool test_with_resolved_ip(wxString& curl_msg) const; #endif - virtual bool upload_inner_with_host(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const; + virtual bool upload_inner_with_host(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const; private: std::string m_host; diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp index 86f6101b6..0ce9892d1 100644 --- a/src/slic3r/Utils/PrintHost.cpp +++ b/src/slic3r/Utils/PrintHost.cpp @@ -93,6 +93,7 @@ struct PrintHostJobQueue::priv void emit_progress(int progress); void emit_error(wxString error); void emit_cancel(size_t id); + void emit_resolve(wxString host); void start_bg_thread(); void stop_bg_thread(); void bg_thread_main(); @@ -131,6 +132,13 @@ void PrintHostJobQueue::priv::emit_cancel(size_t id) wxQueueEvent(queue_dialog, evt); } +void PrintHostJobQueue::priv::emit_resolve(wxString host) +{ + auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_RESOLVE, queue_dialog->GetId(), job_id, host); + wxQueueEvent(queue_dialog, evt); +} + + void PrintHostJobQueue::priv::start_bg_thread() { if (bg_thread.joinable()) { return; } @@ -258,6 +266,9 @@ void PrintHostJobQueue::priv::perform_job(PrintHostJob the_job) [this](Http::Progress progress, bool &cancel) { this->progress_fn(std::move(progress), cancel); }, [this](wxString error) { emit_error(std::move(error)); + }, + [this](wxString host) { + emit_resolve(std::move(host)); } ); diff --git a/src/slic3r/Utils/PrintHost.hpp b/src/slic3r/Utils/PrintHost.hpp index dd22e60b7..5a9fb70df 100644 --- a/src/slic3r/Utils/PrintHost.hpp +++ b/src/slic3r/Utils/PrintHost.hpp @@ -43,13 +43,14 @@ public: typedef Http::ProgressFn ProgressFn; typedef std::function ErrorFn; + typedef ErrorFn ResolveFn; virtual const char* get_name() const = 0; virtual bool test(wxString &curl_msg) const = 0; virtual wxString get_test_ok_msg () const = 0; virtual wxString get_test_failed_msg (wxString &msg) const = 0; - virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const = 0; + virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const = 0; virtual bool has_auto_discovery() const = 0; virtual bool can_test() const = 0; virtual PrintHostPostUploadActions get_post_upload_actions() const = 0; diff --git a/src/slic3r/Utils/Repetier.cpp b/src/slic3r/Utils/Repetier.cpp index 0569d97fa..57c03c556 100644 --- a/src/slic3r/Utils/Repetier.cpp +++ b/src/slic3r/Utils/Repetier.cpp @@ -92,7 +92,7 @@ wxString Repetier::get_test_failed_msg (wxString &msg) const % _utf8(L("Note: Repetier version at least 0.90.0 is required."))).str()); } -bool Repetier::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const +bool Repetier::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const { const char *name = get_name(); diff --git a/src/slic3r/Utils/Repetier.hpp b/src/slic3r/Utils/Repetier.hpp index 6f3310260..8646681e9 100644 --- a/src/slic3r/Utils/Repetier.hpp +++ b/src/slic3r/Utils/Repetier.hpp @@ -23,7 +23,7 @@ public: bool test(wxString &curl_msg) const override; wxString get_test_ok_msg () const override; wxString get_test_failed_msg (wxString &msg) const override; - bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override; + bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const override; bool has_auto_discovery() const override { return false; } bool can_test() const override { return true; } PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint; } From d9058b2a922739da86055b71e1255bd728318954 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 9 May 2022 14:39:26 +0200 Subject: [PATCH 07/13] Cancel upload priority before error showing. --- src/slic3r/Utils/OctoPrint.cpp | 2 +- src/slic3r/Utils/PrintHost.cpp | 41 ++++++++++++++++++++++++++++------ 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index f9f563ecc..6c25f7376 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -339,7 +339,7 @@ bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, Progr } #endif //WIN32 -bool OctoPrint::upload_inner_with_host(PrintHostUpload upload_data, ProgressFn prorgess_fn, ResolveFn resolve_fn, ErrorFn error_fn) const +bool OctoPrint::upload_inner_with_host(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const { const char* name = get_name(); diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp index 0ce9892d1..2a51fc300 100644 --- a/src/slic3r/Utils/PrintHost.cpp +++ b/src/slic3r/Utils/PrintHost.cpp @@ -98,6 +98,7 @@ struct PrintHostJobQueue::priv void stop_bg_thread(); void bg_thread_main(); void progress_fn(Http::Progress progress, bool &cancel); + void error_fn(wxString error); void remove_source(const fs::path &path); void remove_source(); void perform_job(PrintHostJob the_job); @@ -241,6 +242,36 @@ void PrintHostJobQueue::priv::progress_fn(Http::Progress progress, bool &cancel) } } +void PrintHostJobQueue::priv::error_fn(wxString error) +{ + // check if transfer was not canceled before error occured - than do not show the error + bool do_emit_err = true; + if (channel_cancels.size_hint() > 0) { + // Lock both queues + auto cancels = channel_cancels.lock_rw(); + auto jobs = channel_jobs.lock_rw(); + + for (size_t cancel_id : *cancels) { + if (cancel_id == job_id) { + do_emit_err = false; + emit_cancel(job_id); + } + else if (cancel_id > job_id) { + const size_t idx = cancel_id - job_id - 1; + if (idx < jobs->size()) { + jobs->at(idx).cancelled = true; + BOOST_LOG_TRIVIAL(debug) << boost::format("PrintHostJobQueue: Job id %1% cancelled") % cancel_id; + emit_cancel(cancel_id); + } + } + } + cancels->clear(); + } + if (do_emit_err) + emit_error(std::move(error)); +} + + void PrintHostJobQueue::priv::remove_source(const fs::path &path) { if (! path.empty()) { @@ -263,13 +294,9 @@ void PrintHostJobQueue::priv::perform_job(PrintHostJob the_job) emit_progress(0); // Indicate the upload is starting bool success = the_job.printhost->upload(std::move(the_job.upload_data), - [this](Http::Progress progress, bool &cancel) { this->progress_fn(std::move(progress), cancel); }, - [this](wxString error) { - emit_error(std::move(error)); - }, - [this](wxString host) { - emit_resolve(std::move(host)); - } + [this](Http::Progress progress, bool &cancel) { this->progress_fn(std::move(progress), cancel); }, + [this](wxString error) { this->error_fn(std::move(error)); }, + [this](wxString host) { emit_resolve(std::move(host)); } ); if (success) { From 8481de43cc653ec576be3cdefc2d27d1257e02b7 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 9 May 2022 15:35:57 +0200 Subject: [PATCH 08/13] Improved error message if both ipv4 and ipv6 upload fails. --- src/slic3r/Utils/OctoPrint.cpp | 32 ++++++++++++++++++-------------- src/slic3r/Utils/OctoPrint.hpp | 2 +- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index 6c25f7376..e06b1fc62 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -261,9 +261,20 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro return upload_inner_with_resolved_ip(upload_data, prorgess_fn, error_fn, resolve_fn, resolved_addr.front()); } else if (resolved_addr.size() == 2 && resolved_addr[0].is_v4() != resolved_addr[1].is_v4()) { // there are just 2 addresses and 1 is ip_v4 and other is ip_v6 - // try sending to both. (First without verbose, so user doesnt get error msg twice) - if (!upload_inner_with_resolved_ip(upload_data, prorgess_fn, error_fn, resolve_fn, resolved_addr.front(), false)) - return upload_inner_with_resolved_ip(upload_data, prorgess_fn, error_fn, resolve_fn, resolved_addr.back()); + // try sending to both. (Then if both fail, show both error msg after second try) + wxString error_message; + if (!upload_inner_with_resolved_ip(upload_data, prorgess_fn + , [&msg = error_message, resolved_addr](wxString error) { msg = GUI::format_wxstr("%1%: %2%", resolved_addr.front(), error); } + , resolve_fn, resolved_addr.front()) + && + !upload_inner_with_resolved_ip(upload_data, prorgess_fn + , [&msg = error_message, resolved_addr](wxString error) { msg += GUI::format_wxstr("\n%1%: %2%", resolved_addr.back(), error); } + , resolve_fn, resolved_addr.back()) + ) { + + error_fn(error_message); + return false; + } return true; } else { // There are multiple addresses - user needs to choose which to use. @@ -277,7 +288,7 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro #endif // WIN32 } #ifdef WIN32 -bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn, const boost::asio::ip::address& resolved_addr, bool verbose /*= true*/) const +bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn, const boost::asio::ip::address& resolved_addr) const { resolve_fn(boost::nowide::widen(resolved_addr.to_string())); @@ -286,10 +297,7 @@ bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, Progr // Test_msg already contains resolved ip and will be cleared on start of test(). wxString test_msg_or_host_ip = GUI::from_u8(resolved_addr.to_string()); if (!test_with_resolved_ip(test_msg_or_host_ip)) { - if (verbose) - error_fn(std::move(test_msg_or_host_ip)); - else BOOST_LOG_TRIVIAL(info) << test_msg_or_host_ip; - //continue; + error_fn(std::move(test_msg_or_host_ip)); return false; } @@ -316,12 +324,8 @@ bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, Progr BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%: %3%") % name % status % body; }) .on_error([&](std::string body, std::string error, unsigned status) { - if (verbose) { - BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading file: %2%, HTTP %3%, body: `%4%`") % name % error % status % body; - error_fn(format_error(body, error, status)); - } - else - BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Error uploading file: %2%, HTTP %3%, body: `%4%`") % name % error % status % body; + BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading file: %2%, HTTP %3%, body: `%4%`") % name % error % status % body; + error_fn(format_error(body, error, status)); result = false; }) .on_progress([&](Http::Progress progress, bool& cancel) { diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp index 9511e24a8..e1b83bd51 100644 --- a/src/slic3r/Utils/OctoPrint.hpp +++ b/src/slic3r/Utils/OctoPrint.hpp @@ -37,7 +37,7 @@ public: protected: virtual bool validate_version_text(const boost::optional &version_text) const; #ifdef WIN32 - virtual bool upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn, const boost::asio::ip::address& resolved_addr, bool verbose = true) const; + virtual bool upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn, const boost::asio::ip::address& resolved_addr) const; virtual bool test_with_resolved_ip(wxString& curl_msg) const; #endif virtual bool upload_inner_with_host(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const; From a2330e77c268553e3e701c5ca46dd098fbf01a8b Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 9 May 2022 16:58:24 +0200 Subject: [PATCH 09/13] Filter lookup replies by service name --- src/slic3r/Utils/Bonjour.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/slic3r/Utils/Bonjour.cpp b/src/slic3r/Utils/Bonjour.cpp index 87847bee5..11bbcdf5b 100644 --- a/src/slic3r/Utils/Bonjour.cpp +++ b/src/slic3r/Utils/Bonjour.cpp @@ -603,14 +603,14 @@ namespace { std::string strip_service_dn(const std::string& service_name, const std::string& service_dn) { if (service_name.size() <= service_dn.size()) { - return service_name; + return std::string(); } auto needle = service_name.rfind(service_dn); if (needle == service_name.size() - service_dn.size()) { return service_name.substr(0, needle - 1); } else { - return service_name; + return std::string(); } } } // namespace @@ -746,7 +746,10 @@ void LookupSession::handle_receive(const error_code& error, size_t bytes) } const auto& srv = *sdpair.second.srv; + auto service_name = strip_service_dn(sdpair.first, socket->get_service_dn()); + if (service_name.empty()) + continue; std::string path; std::string version; From 9ee6839017d6cb4c7f8671fa4e27538e98fb8224 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 11 May 2022 14:09:39 +0200 Subject: [PATCH 10/13] Resize IP list dialog. --- src/slic3r/GUI/BonjourDialog.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/BonjourDialog.cpp b/src/slic3r/GUI/BonjourDialog.cpp index 69ab23896..d35d8e435 100644 --- a/src/slic3r/GUI/BonjourDialog.cpp +++ b/src/slic3r/GUI/BonjourDialog.cpp @@ -239,15 +239,15 @@ IPListDialog::IPListDialog(wxWindow* parent, const wxString& hostname, const std , m_selected_index (selected_index) { const int em = GUI::wxGetApp().em_unit(); - m_list->SetMinSize(wxSize(20 * em, 30 * em)); + m_list->SetMinSize(wxSize(40 * em, 30 * em)); wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL); - auto* label = new wxStaticText(this, wxID_ANY, GUI::format_wxstr(_L("There are several IP addresses resolving to hostname %1%.\n Please select one that should be used."), hostname)); + auto* label = new wxStaticText(this, wxID_ANY, GUI::format_wxstr(_L("There are several IP addresses resolving to hostname %1%.\nPlease select one that should be used."), hostname)); vsizer->Add(label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, em); m_list->SetSingleStyle(wxLC_SINGLE_SEL); - m_list->AppendColumn(_(L("Address")), wxLIST_FORMAT_LEFT, 20 * em); + m_list->AppendColumn(_(L("Address")), wxLIST_FORMAT_LEFT, 40 * em); for (size_t i = 0; i < ips.size(); i++) auto item = m_list->InsertItem(i, boost::nowide::widen(ips[i].to_string())); From cd30e8f2e7464461e37af45d4720eecec71f237a Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 12 May 2022 14:09:48 +0200 Subject: [PATCH 11/13] get_host_from_url function --- src/slic3r/Utils/OctoPrint.cpp | 46 ++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index e06b1fc62..41deb83bb 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -93,6 +93,38 @@ std::string substitute_host(const std::string& orig_addr, std::string sub_addr) return out; #endif } + +std::string get_host_from_url(const std::string& url_in) +{ + std::string url = url_in; + // add http:// if there is no scheme + size_t double_slash = url.find("//"); + if (double_slash == std::string::npos) + url = "http://" + url; + std::string out = url; + CURLU* hurl = curl_url(); + if (hurl) { + // Parse the input URL. + CURLUcode rc = curl_url_set(hurl, CURLUPART_URL, url.c_str(), 0); + if (rc == CURLUE_OK) { + // Replace the address. + char* host; + rc = curl_url_get(hurl, CURLUPART_HOST, &host, 0); + if (rc == CURLUE_OK) { + out = host; + curl_free(host); + } + else + BOOST_LOG_TRIVIAL(error) << "OctoPrint get_host_from_url: failed to get host form URL " << url; + } + else + BOOST_LOG_TRIVIAL(error) << "OctoPrint get_host_from_url: failed to parse URL " << url; + curl_url_cleanup(hurl); + } + else + BOOST_LOG_TRIVIAL(error) << "OctoPrint get_host_from_url: failed to allocate curl_url"; + return out; +} } //namespace #endif // WIN32 @@ -231,15 +263,17 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro #ifndef WIN32 return upload_inner_with_host(upload_data, prorgess_fn, error_fn, resolve_fn); #else + std::string host = get_host_from_url(m_host); + // decide what to do based on m_host - resolve hostname or upload to ip std::vector resolved_addr; boost::system::error_code ec; - boost::asio::ip::address host_ip = boost::asio::ip::make_address(m_host, ec); + boost::asio::ip::address host_ip = boost::asio::ip::make_address(host, ec); if (!ec) { resolved_addr.push_back(host_ip); } else if ( GUI::get_app_config()->get("allow_ip_resolve") == "1"){ Bonjour("octoprint") - .set_hostname(m_host) + .set_hostname(host) .set_retries(10) // number of rounds of queries send .set_timeout(1) // after each timeout, if there is any answer, the resolving will stop .on_resolve([&ra = resolved_addr](const std::vector& replies) { @@ -259,7 +293,7 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro } else if (resolved_addr.size() == 1) { // one address resolved - upload there return upload_inner_with_resolved_ip(upload_data, prorgess_fn, error_fn, resolve_fn, resolved_addr.front()); - } else if (resolved_addr.size() == 2 && resolved_addr[0].is_v4() != resolved_addr[1].is_v4()) { + } else if (resolved_addr.size() == 2 && resolved_addr[0].is_v4() != resolved_addr[1].is_v4()) { // there are just 2 addresses and 1 is ip_v4 and other is ip_v6 // try sending to both. (Then if both fail, show both error msg after second try) wxString error_message; @@ -307,6 +341,8 @@ bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, Progr std::string url = substitute_host(make_url("api/files/local"), resolved_addr.to_string()); bool result = true; + resolve_fn(boost::nowide::widen(url)); + BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Uploading file %2% at %3%, filename: %4%, path: %5%, print: %6%") % name % upload_data.source_path @@ -315,7 +351,7 @@ bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, Progr % upload_parent_path.string() % (upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false"); - auto http = Http::post(std::move(url)); + auto http = Http::post(url);//std::move(url)); set_auth(http); http.form_add("print", upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false") .form_add("path", upload_parent_path.string()) // XXX: slashes on windows ??? @@ -324,7 +360,7 @@ bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, Progr BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%: %3%") % name % status % body; }) .on_error([&](std::string body, std::string error, unsigned status) { - BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading file: %2%, HTTP %3%, body: `%4%`") % name % error % status % body; + BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading file to %2%: %3%, HTTP %4%, body: `%5%`") % name % url % error % status % body; error_fn(format_error(body, error, status)); result = false; }) From faa4207b1cef62e346878e4171c95a22528ddfdd Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 17 May 2022 12:48:07 +0200 Subject: [PATCH 12/13] OSX build fix --- src/slic3r/GUI/BonjourDialog.cpp | 1 + src/slic3r/GUI/BonjourDialog.hpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/BonjourDialog.cpp b/src/slic3r/GUI/BonjourDialog.cpp index d35d8e435..b416da765 100644 --- a/src/slic3r/GUI/BonjourDialog.cpp +++ b/src/slic3r/GUI/BonjourDialog.cpp @@ -15,6 +15,7 @@ #include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/I18N.hpp" +#include "slic3r/GUI/format.hpp" #include "slic3r/Utils/Bonjour.hpp" namespace Slic3r { diff --git a/src/slic3r/GUI/BonjourDialog.hpp b/src/slic3r/GUI/BonjourDialog.hpp index 6d1af491b..fb177c4b5 100644 --- a/src/slic3r/GUI/BonjourDialog.hpp +++ b/src/slic3r/GUI/BonjourDialog.hpp @@ -12,7 +12,7 @@ class wxListView; class wxStaticText; class wxTimer; class wxTimerEvent; -class boost::asio::ip::address; +class address; namespace Slic3r { From 5c7cb3b55a3ae6cecf5ca640ff49cfc291658889 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Sun, 17 Jul 2022 19:24:29 +0200 Subject: [PATCH 13/13] Delete unused variable --- src/slic3r/GUI/BonjourDialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/BonjourDialog.cpp b/src/slic3r/GUI/BonjourDialog.cpp index b416da765..09d8bdb52 100644 --- a/src/slic3r/GUI/BonjourDialog.cpp +++ b/src/slic3r/GUI/BonjourDialog.cpp @@ -251,7 +251,7 @@ IPListDialog::IPListDialog(wxWindow* parent, const wxString& hostname, const std m_list->AppendColumn(_(L("Address")), wxLIST_FORMAT_LEFT, 40 * em); for (size_t i = 0; i < ips.size(); i++) - auto item = m_list->InsertItem(i, boost::nowide::widen(ips[i].to_string())); + m_list->InsertItem(i, boost::nowide::widen(ips[i].to_string())); m_list->Select(0);