From 65d3168eb53a6d33e10c92bb1e10d8befd37079f Mon Sep 17 00:00:00 2001 From: peri4 Date: Tue, 26 Nov 2024 18:26:02 +0300 Subject: [PATCH] PICout::withExternalBufferAnd decomposed to PICout::withExternalBufferAndID and PICout::withExternalBufferAnd PIString::toPercentageEncoding/fromPercentageEncoding piStringify() PIHTTPClient support arguments some doc --- doc/examples/picout.cpp | 33 ++++++++++++---------- libs/http_client/pihttpclient.cpp | 11 ++++++++ libs/main/application/pilog.cpp | 2 +- libs/main/core/picout.cpp | 9 ++++-- libs/main/core/picout.h | 35 +++++++++++++++-------- libs/main/http_common/pihttptypes.cpp | 18 ++++++++++++ libs/main/http_common/pihttptypes.h | 6 ++-- libs/main/text/pistring.cpp | 40 +++++++++++++++++++++++++++ libs/main/text/pistring.h | 19 +++++++++++++ main.cpp | 33 ++++++++++------------ 10 files changed, 156 insertions(+), 50 deletions(-) diff --git a/doc/examples/picout.cpp b/doc/examples/picout.cpp index f284829e..808fd262 100644 --- a/doc/examples/picout.cpp +++ b/doc/examples/picout.cpp @@ -1,54 +1,57 @@ #include "pip.h" - + //! [own] -inline PICout operator <<(PICout s, const PIByteArray & ba) { - s.space(); // insert space after previous output - s.quote(); // ONLY if you want to quoted your type +inline PICout operator<<(PICout s, const PIByteArray & ba) { + s.space(); // insert space after previous output + s.quote(); // ONLY if you want to quoted your type s.saveAndSetControls(0); // clear all features and // save them to stack, // now it`s behavior similar to std::cout - + // your output for (uint i = 0; i < ba.size(); ++i) s << ba[i]; - + s.restoreControls(); // restore features from stack - s.quote(); // ONLY if you want to quoted your type + s.quote(); // ONLY if you want to quoted your type return s; } //! [own] +// clang-format off void _() { - //! [0] +using namespace PICoutManipulators; int a = 10, b = 32, c = 11; piCout << a << Hex << b << Bin << c; // 10 20 1011 -piCout << "this" << "is" << Green << "green" << Default << "word"; +piCout << "this" + << "is" << Green << "green" << Default << "word"; // this is green word -PICout(AddSpaces | AddNewLine | AddQuotes) << Tab << "tab and" << "quotes"; +PICout(AddSpaces | AddNewLine | AddQuotes) << Tab << "tab and" + << "quotes"; // "tab and" "quotes" //! [0] - } +// clang-format on //! [notifier] class A: public PIObject { PIOBJECT(A) + public: A() {} - EVENT_HANDLER2(void, pcf, int, id, PIString*, buff) { - piCout << "PICout(" << id << ") finished:" << (*buff); - } + EVENT_HANDLER2(void, pcf, int, id, PIString *, buff) { piCout << "PICout(" << id << ") finished:" << (*buff); } }; int main() { A a; CONNECTU(PICout::Notifier::object(), finished, &a, pcf); PIString buffer = "my buff:"; - PICout(&buffer, 1) << "int 10 ->" << 10 << ", time ->" << PITime::current(); + int my_id = PICout::registerExternalBufferID(); + PICout::withExternalBufferAndID(&buffer, my_id) << "int 10 ->" << 10 << ", time ->" << PITime::current(); return 0; } // PICout( 1 ) finished: my buff:int 10 -> 10 , time -> PITime(14:07:09:000) diff --git a/libs/http_client/pihttpclient.cpp b/libs/http_client/pihttpclient.cpp index 22c55c08..42d5c0ca 100644 --- a/libs/http_client/pihttpclient.cpp +++ b/libs/http_client/pihttpclient.cpp @@ -36,6 +36,16 @@ bool PIHTTPClient::init() { if (is_cancel) return false; PRIVATE->handle = curl_easy_init(); if (!PRIVATE->handle) return false; + auto ait = request.arguments().makeIterator(); + while (ait.next()) { + if (!url.contains('?')) + url.append('?'); + else + url.append('&'); + url.append(ait.key().toPercentageEncoding()); + url.append('='); + url.append(ait.value().toPercentageEncoding()); + } curl_easy_setopt(PRIVATE->handle, CURLOPT_WRITEDATA, this); curl_easy_setopt(PRIVATE->handle, CURLOPT_READDATA, this); curl_easy_setopt(PRIVATE->handle, CURLOPT_XFERINFODATA, this); @@ -156,6 +166,7 @@ PIHTTPClient * PIHTTPClient::create(const PIString & url_, PIHTTP::Method method PIHTTPClient * ret = new PIHTTPClient(); static_cast(ret->request) = req; ret->request.setMethod(method); + ret->reply.setMethod(method); ret->url = url_; return ret; } diff --git a/libs/main/application/pilog.cpp b/libs/main/application/pilog.cpp index e6adc2ec..07e58c67 100644 --- a/libs/main/application/pilog.cpp +++ b/libs/main/application/pilog.cpp @@ -179,7 +179,7 @@ PICout PILog::makePICout(PIObject * context, Level cat) { if (context->name().isNotEmpty()) *buffer += " \"" + context->name() + "\""; *buffer += "] "; } - return PICout::withExternalBuffer(buffer, id_by_cat.value(cat), PICoutManipulators::AddSpaces); + return PICout::withExternalBufferAndID(buffer, id_by_cat.value(cat), PICoutManipulators::AddSpaces); } diff --git a/libs/main/core/picout.cpp b/libs/main/core/picout.cpp index 5a8e73f0..d766cbca 100644 --- a/libs/main/core/picout.cpp +++ b/libs/main/core/picout.cpp @@ -208,7 +208,7 @@ PICout::~PICout() { PICout::__mutex__().unlock(); } if (buffer_) { - ((NotifierObject *)Notifier::object())->finished(id_, buffer_); + if (id_ >= 0) ((NotifierObject *)Notifier::object())->finished(id_, buffer_); } else { getStdStream(stream_).flush(); } @@ -748,7 +748,12 @@ bool PICout::isOutputDeviceActive(PICout::OutputDevice d) { } -PICout PICout::withExternalBuffer(PIString * buffer, int id, PIFlags controls) { +PICout PICout::withExternalBuffer(PIString * buffer, PIFlags controls) { + return withExternalBufferAndID(buffer, -1, controls); +} + + +PICout PICout::withExternalBufferAndID(PIString * buffer, int id, PIFlags controls) { PICout c(controls); c.buffer_ = buffer; c.id_ = id; diff --git a/libs/main/core/picout.h b/libs/main/core/picout.h index e6bcab92..9f2b828d 100644 --- a/libs/main/core/picout.h +++ b/libs/main/core/picout.h @@ -30,15 +30,24 @@ #ifdef DOXYGEN -//! \~english Macro used for conditional (piDebug) output to PICout -//! \~russian Макрос для условного (piDebug) вывода в PICout +//! \~english Macro used for conditional (piDebug) output to PICout(StdOut) +//! \~russian Макрос для условного (piDebug) вывода в PICout(StdOut) # define piCout +//! \~english Macro used for conditional (piDebug) output to PICout(StdErr) +//! \~russian Макрос для условного (piDebug) вывода в PICout(StdErr) +# define piCerr + //! \relatesalso PIObject -//! \~english Macro used for conditional (piDebug && PIObject::debug()) output to PICout for subclasses of PIObject -//! \~russian Макрос для условного (piDebug && PIObject::debug()) вывода в PICout для наследников PIObject +//! \~english Macro used for conditional (piDebug && PIObject::debug()) output to PICout(StdOut) for subclasses of PIObject +//! \~russian Макрос для условного (piDebug && PIObject::debug()) вывода в PICout(StdOut) для наследников PIObject # define piCoutObj +//! \relatesalso PIObject +//! \~english Macro used for conditional (piDebug && PIObject::debug()) output to PICout(StdErr) for subclasses of PIObject +//! \~russian Макрос для условного (piDebug && PIObject::debug()) вывода в PICout(StdErr) для наследников PIObject +# define piCerrObj + #else # define piCout PICout(piDebug, PICoutStdStream::StdOut) # define piCoutObj \ @@ -375,15 +384,19 @@ public: static bool isOutputDeviceActive(OutputDevice d); + //! \~english Construct with external buffer. + //! \~russian Конструктор с внешним буфером. + static PICout withExternalBuffer(PIString * buffer, + PIFlags controls = PICoutManipulators::AddSpaces); + //! \~english Construct with external buffer and ID "id". See \a Notifier for details //! \~russian Конструктор с внешним буфером и ID "id". Подробнее \a Notifier - static PICout withExternalBuffer(PIString * buffer, - int id = 0, - PIFlags controls = PICoutManipulators::AddSpaces | - PICoutManipulators::AddNewLine); + static PICout withExternalBufferAndID(PIString * buffer, + int id, + PIFlags controls = PICoutManipulators::DefaultControls); - //! \~english Returns unique external buffer ID for later use in \a withExternalBuffer() - //! \~russian Возвращает уникальный ID для внешнего буфера для дальнейшего использования в \a withExternalBuffer() + //! \~english Returns unique external buffer ID for later use in \a withExternalBufferAndID() + //! \~russian Возвращает уникальный ID для внешнего буфера для дальнейшего использования в \a withExternalBufferAndID() static int registerExternalBufferID(); static PIMutex & __mutex__(); @@ -397,7 +410,7 @@ private: static OutputDevices devs; PRIVATE_DECLARATION(PIP_EXPORT) bool first_out_ = true, is_copy_ = false, format_changed_ = false, actve_ = true; - int int_base_ = 10, win_attr_ = 0, id_ = 0; + int int_base_ = 10, win_attr_ = 0, id_ = -1; PIString * buffer_ = nullptr; PICoutManipulators::PICoutControls ctrl_ = PICoutManipulators::DefaultControls; PICoutStdStream stream_ = PICoutStdStream::StdOut; diff --git a/libs/main/http_common/pihttptypes.cpp b/libs/main/http_common/pihttptypes.cpp index 2fc6560f..fa2bedf0 100644 --- a/libs/main/http_common/pihttptypes.cpp +++ b/libs/main/http_common/pihttptypes.cpp @@ -24,6 +24,24 @@ PIHTTP::MessageMutable & PIHTTP::MessageMutable::addHeader(const PIString & head } +PIHTTP::MessageMutable & PIHTTP::MessageMutable::removeHeader(const PIString & header) { + m_headers.remove(header); + return *this; +} + + +PIHTTP::MessageMutable & PIHTTP::MessageMutable::addArgument(const PIString & arg, const PIString & value) { + m_arguments[arg] = value; + return *this; +} + + +PIHTTP::MessageMutable & PIHTTP::MessageMutable::removeArgument(const PIString & arg) { + m_arguments.remove(arg); + return *this; +} + + PIHTTP::MessageMutable & PIHTTP::MessageMutable::setMethod(Method m) { m_method = m; return *this; diff --git a/libs/main/http_common/pihttptypes.h b/libs/main/http_common/pihttptypes.h index c93db8bd..fb82dd62 100644 --- a/libs/main/http_common/pihttptypes.h +++ b/libs/main/http_common/pihttptypes.h @@ -37,9 +37,11 @@ public: const PIMap & headers() const { return m_headers; } const PIMap & arguments() const { return m_arguments; } PIMap & headers() { return m_headers; } - PIMap & arguments() { return m_arguments; } MessageMutable & addHeader(const PIString & header, const PIString & value); - void removeHeader(const PIString & header) { m_headers.remove(header); } + MessageMutable & removeHeader(const PIString & header); + PIMap & arguments() { return m_arguments; } + MessageMutable & addArgument(const PIString & arg, const PIString & value); + MessageMutable & removeArgument(const PIString & arg); static MessageMutable fromCode(PIHTTP::Code c); static MessageMutable fromMethod(PIHTTP::Method m); }; diff --git a/libs/main/text/pistring.cpp b/libs/main/text/pistring.cpp index a3959233..a389a998 100644 --- a/libs/main/text/pistring.cpp +++ b/libs/main/text/pistring.cpp @@ -1810,6 +1810,46 @@ PIString & PIString::setReadableSize(llong bytes) { } +PIString PIString::toPercentageEncoding() const { + static const PIString valid = "-._~"_a; + PIString ret; + PIByteArray utf8; + bool ok = false; + for (const auto & c: *this) { + ok = false; + if (c.isAscii()) { + if (c.isAlpha() || c.isDigit()) + ok = true; + else if (valid.contains(c)) + ok = true; + } + if (ok) { + ret.append(c); + } else { + utf8 = PIString(c).toUTF8(); + for (auto u: utf8) + ret.append('%').append(PIString::fromNumber(u, 16).expandLeftTo(2, '0')); + } + } + return ret; +} + + +PIString PIString::fromPercentageEncoding(const PIString & in) { + PIByteArray utf8; + PIChar c; + for (int i = 0; i < in.size_s(); ++i) { + c = in[i]; + if (c == '%') { + utf8 << static_cast(in.mid(i + 1, 2).toInt(16)); + i += 2; + } else + utf8 << static_cast(c.toAscii()); + } + return PIString::fromUTF8(utf8); +} + + PIString & PIString::arg(const PIString & v) { auto ph = minArgPlaceholder(); if (!ph.isEmpty()) replaceAll(ph, v); diff --git a/libs/main/text/pistring.h b/libs/main/text/pistring.h index efcbe031..ffc17c84 100644 --- a/libs/main/text/pistring.h +++ b/libs/main/text/pistring.h @@ -1687,6 +1687,10 @@ public: PIString & setReadableSize(llong bytes); + //! \~english Returns [percentage-encoded](https://en.wikipedia.org/wiki/Percent-encoding) string. + //! \~russian Возвращает [URL-кодированную](https://ru.wikipedia.org/wiki/URL) строку. + PIString toPercentageEncoding() const; + //! \~english Replace all occurances like "%1", "%2", ... with lowest value to "v" and returns this string. //! \~russian Заменяет все вхождения типа "%1", "%2", ... с наименьшим значением на "v" и возвращает эту строку. //! \~\details @@ -1906,6 +1910,10 @@ public: //! \~\sa PIString::setReadableSize() static PIString readableSize(llong bytes); + //! \~english Returns string from [percentage-encoded](https://en.wikipedia.org/wiki/Percent-encoding) "in". + //! \~russian Возвращает строку из [URL-кодированной](https://ru.wikipedia.org/wiki/URL)"in". + static PIString fromPercentageEncoding(const PIString & in); + //! \~english Swaps string `str` other with this string. //! \~russian Меняет строку `str` с этой строкой. //! \~\details @@ -2020,4 +2028,15 @@ inline void piSwap(PIString & f, PIString & s) { f.swap(s); } + +//! \~english Returns string representation of \"v\", using PICout operator<<(T) +//! \~russian Возвращает строковое представление \"v\", используя PICout operator<<(T) +template +inline PIString piStringify(const T & v) { + PIString ret; + PICout::withExternalBuffer(&ret) << v; + return ret; +} + + #endif // PISTRING_H diff --git a/main.cpp b/main.cpp index 6061032f..bff7031d 100644 --- a/main.cpp +++ b/main.cpp @@ -11,13 +11,6 @@ const char * pageTitle = "" "" ""; -template -PIString stringify(const T & v) { - PIString ret; - PICout::withExternalBuffer(&ret, 0, PICoutManipulators::AddSpaces) << v; - return ret; -} - int main(int argc, char * argv[]) { PIHTTPServer server; server.listen({"127.0.0.1:7777"}); @@ -30,9 +23,9 @@ int main(int argc, char * argv[]) { server.registerUnhandled([](const PIHTTP::MessageConst & msg) -> PIHTTP::MessageMutable { PIHTTP::MessageMutable ret; piCout << "server rec:\n\tpath: %1\n\tmethod: %2\n\targs: %3\n\theaders: %4\n\tbody: %5\n"_a.arg(msg.path()) - .arg((int)msg.method()) - .arg(stringify(msg.arguments())) - .arg(PIStringList(msg.headers().map([](PIString k, PIString v) { return k + " = " + v; })).join("\n\t\t")) + .arg(PIHTTP::methodName(msg.method())) + .arg(piStringify(msg.arguments())) + .arg(PIStringList(msg.headers().map([](PIString k, PIString v) { return k + " = " + v; })).join("\n\t\t ")) .arg(PIString::fromUTF8(msg.body())); ret.setCode(PIHTTP::Code::BadRequest); ret.setBody(PIByteArray::fromAscii("hello client! 0123456789")); @@ -40,14 +33,15 @@ int main(int argc, char * argv[]) { }); PIHTTP::MessageMutable req; - req.setBody(PIByteArray::fromAscii("hello server!")); + req.setBody(PIByteArray::fromAscii("hello server!")).addArgument("a0", "val.0").addArgument("a~r1", "знач,1"_u8); auto * c = PIHTTPClient::create("http://u:p@127.0.0.1:7777/api", PIHTTP::Method::Get, req); c->onFinish([](PIHTTP::MessageConst msg) { - piCout << "client rec:\n\tcode: %5\n\tpath: %1\n\tmethod: %2\n\targs: %3\n\tbody: %4\n"_a.arg(msg.path()) - .arg((int)msg.method()) - .arg(stringify(msg.arguments())) - .arg(PIString::fromUTF8(msg.body())) - .arg((int)msg.code()); + piCout << "client rec:\n\tpath: %1\n\tmethod: %2\n\targs: %3\n\theaders: %4\n\tbody: %5\n"_a.arg(msg.path()) + .arg(PIHTTP::methodName(msg.method())) + .arg(piStringify(msg.arguments())) + .arg( + PIStringList(msg.headers().map([](PIString k, PIString v) { return k + " = " + v; })).join("\n\t\t ")) + .arg(PIString::fromUTF8(msg.body())); }) ->onError([c](PIHTTP::MessageConst r) { piCout << "error" << (int)r.code(); @@ -59,9 +53,10 @@ int main(int argc, char * argv[]) { }) ->start(); - // piMSleep(500); - kbd.enableExitCapture(); - WAIT_FOR_EXIT + piMSleep(500); + // kbd.enableExitCapture(); + // WAIT_FOR_EXIT + kbd.stopAndWait(); server.stop(); // c->abort();