مكتبة HTTP/HTTPS عبر النظام الأساسي لرأس ملف واحد فقط C++11.
إنه سهل الإعداد للغاية. فقط قم بتضمين ملف httplib.h في التعليمات البرمجية الخاصة بك!
مهم
تستخدم هذه المكتبة الإدخال/الإخراج بمقبس "الحظر". إذا كنت تبحث عن مكتبة ذات إدخال/إخراج بمقبس "غير محظور"، فهذه ليست المكتبة التي تريدها.
#define CPPHTTPLIB_OPENSSL_SUPPORT#include "path/to/httplib.h"// HTTPhttplib::Server svr;// HTTPShttplib::SSLServer svr; svr.Get("/hi", [](const httplib::Request &, httplib::Response &res) { res.set_content("مرحبا بالعالم!"، "نص/عادي"); }); svr.listen("0.0.0.0", 8080);
#define CPPHTTPLIB_OPENSSL_SUPPORT#include "path/to/httplib.h"// HTTPhttplib::Client cli("http://cpp-httplib-server.yhirose.repl.co");// HTTPShttplib::Client cli(" https://cpp-httplib-server.yhirose.repl.co");auto res = cli.Get("/hi"); الدقة->الحالة؛ الدقة->الجسم؛
يتوفر دعم SSL مع CPPHTTPLIB_OPENSSL_SUPPORT
. ينبغي ربط libssl
و libcrypto
.
ملحوظة
يدعم cpp-httplib حاليًا الإصدار 3.0 أو الأحدث فقط. يرجى الاطلاع على هذه الصفحة للحصول على مزيد من المعلومات.
نصيحة
بالنسبة لنظام التشغيل macOS: يمكن لـ cpp-httplib الآن استخدام شهادات النظام مع CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
. يجب ربط CoreFoundation
و Security
بـ -framework
.
#define CPPHTTPLIB_OPENSSL_SUPPORT#include "path/to/httplib.h"// Serverhttplib::SSLServer svr("./cert.pem", "./key.pem");// Clienthttplib::Client cli("https: //المضيف المحلي:1234"); // مخطط + hosthttplib::SSLClient cli("localhost:1234"); // hosthttplib::SSLClient cli("localhost"، 1234); // مضيف، منفذ// استخدم حزمة CA الخاصة بكcli.set_ca_cert_path("./ca-bundle.crt");// تعطيل التحقق من الشهادةcli.enable_server_certificate_verification(false);// تعطيل التحقق من المضيفcli.enable_server_host_verification(false);
ملحوظة
عند استخدام SSL، يبدو من المستحيل تجنب SIGPIPE في جميع الحالات، لأنه في بعض أنظمة التشغيل، لا يمكن منع SIGPIPE إلا على أساس كل رسالة، ولكن لا توجد طريقة لجعل مكتبة OpenSSL تفعل ذلك لاتصالاتها الداخلية. إذا كان برنامجك بحاجة إلى تجنب إنهائه على SIGPIPE، فقد تكون الطريقة العامة الوحيدة هي إعداد معالج إشارة لـ SIGPIPE للتعامل معه أو تجاهله بنفسك.
#تتضمن <httplib.h>int main(void) { باستخدام مساحة الاسم httplib؛ خادم SVR. svr.Get("/hi", [](const Request& req, Response& res) { res.set_content("مرحبا بالعالم!"، "نص/عادي"); }); // مطابقة مسار الطلب مع التعبير العادي // واستخراج لقطاته svr.Get(R"(/numbers/(d+))), [&](const Request& req, Response& res) { auto number = req.matches[1]; res.set_content(numbers, "text/plain"); }); // التقط الجزء الثاني من مسار الطلب باعتباره معلمة مسار "المعرف". svr.Get("/users/:id", [&](const Request& req, Response& res) { auto user_id = req.path_params.at("id"); res.set_content(user_id, "text/plain"); }); // استخراج القيم من رؤوس HTTP ومعلمات استعلام URL svr.Get("/body-header-param", [](const Request& req, Response& res) { if (req.has_header("Content-Length")) { auto val = req.get_header_value("Content-Length" ); } if (req.has_param("key")) { auto val = req.get_param_value("key"); } res.set_content(req.body, "text/plain"); }); svr.Get("/stop"، [&](const Request& req, Response& res) { svr.stop(); }); svr.listen("المضيف المحلي"، 1234); }
يتم أيضًا دعم طرق Post
Put
Delete
Options
.
int port = svr.bind_to_any_port("0.0.0.0"); svr.listen_after_bind();
// Mount / to ./www Directoryauto ret = svr.set_mount_point("/", "./www");if (!ret) { // الدليل الأساسي المحدد غير موجود...}// Mount / عام إلى ./www Directoryret = svr.set_mount_point("/public"، "./www")؛// Mount /public إلى ./www1 و./www2 دليل ret = svr.set_mount_point("/public", "./www1"); // الترتيب الأول للبحث ret = svr.set_mount_point("/public", "./www2"); // الترتيب الثاني للبحث// إزالة التثبيت /ret = svr.remove_mount_point("/");// إزالة التثبيت /publicret = svr.remove_mount_point("/public");
// ملحق الملف المحدد من قبل المستخدم ونوع MIME mappingssvr.set_file_extension_and_mimetype_mapping("cc", "text/xc"); svr.set_file_extension_and_mimetype_mapping("cpp"، "text/xc"); svr.set_file_extension_and_mimetype_mapping("hh", "text/xh");
فيما يلي تعيينات مضمنة:
امتداد | نوع MIME | امتداد | نوع MIME |
---|---|---|---|
المغلق | النص/المغلق | mpga | الصوت/mpeg |
ملف CSV | نص/ملف CSV | weba | الصوت/ويب |
رسالة قصيرة | نص/عادي | wav | الصوت/الموجة |
vtt | نص/فت | otf | الخط/otf |
أتش تي أم أل، هتم | نص/أتش تي أم أل | ttf | الخط/ttf |
apng | صورة/apng | woff | الخط/وف |
avif | صورة/أفيف | woff2 | الخط/woff2 |
bmp | صورة/bmp | 7z | التطبيق/x-7z-مضغوط |
gif | صورة / جيف | ذرة | التطبيق/الذرة+XML |
png | صورة / بابوا نيو غينيا | قوات الدفاع الشعبي | التطبيق/pdf |
SVG | الصورة/svg+xml | مجس، شبيبة | التطبيق/جافا سكريبت |
webp | صورة/ويب | json | application/json |
ico | الصورة/x-أيقونة | آر إس إس | التطبيق / آر إس إس + XML |
تيف | صورة / شجار | قطران | التطبيق/x-القطران |
شجار | صورة / شجار | أتش تي أم أل، إكس إتش تي | التطبيق/xhtml+xml |
الحياة السياسية في فرنسا، JPG | صورة/JPEG | com.xslt | التطبيق/xslt+xml |
mp4 | فيديو/mp4 | xml | التطبيق/xml |
mpeg | فيديو/MPEG | gz | التطبيق/gzip |
webm | فيديو/ويب | أَزِيز | التطبيق/الرمز البريدي |
mp3 | الصوت/mp3 | com.wasm | التطبيق/واسم |
تحذير
طرق خادم الملفات الثابتة هذه ليست آمنة لمؤشر الترابط.
// يتم استدعاء المعالج مباشرة قبل إرسال الاستجابة إلى العملاءvr.set_file_request_handler([](const Request &req, Response &res) { ... });
svr.set_logger([](const auto& req, const auto& res) { your_logger(req, res); });
svr.set_error_handler([](const auto& req, auto& res) { auto fmt = "<p>حالة الخطأ: <span style='color:red;'>%d</span></p>"; char buf [BUFSIZ]; snprintf(buf, sizeof(buf), fmt, res.status); res.set_content(buf, "text/html"); });
يتم استدعاء معالج الاستثناء إذا قام معالج توجيه المستخدم بإلقاء خطأ.
svr.set_exception_handler([](const auto& req, auto& res, std::exception_ptr ep) { auto fmt = "<h1>خطأ 500</h1><p>%s</p>"; char buf[BUFSIZ] حاول { std::rethrow_exception(ep); } Catch (std::exception &e) { snprintf(buf, sizeof(buf), fmt, e.what()); } Catch (...) { // راجع الملاحظة التالية snprintf(buf, sizeof(buf), fmt, "Unknown Exception"); } res.set_content(buf, "text/html"); res.status = StatusCode::InternalServerError_500; });
حذر
إذا لم تقم بتوفير كتلة catch (...)
لمؤشر الاستثناء المعاد طرحه، فسيؤدي الاستثناء الذي لم يتم اكتشافه إلى تعطل الخادم. احرص!
svr.set_pre_routing_handler([](const auto& req, auto& res) { if (req.path == "/hello") { res.set_content("world", "text/html"); إرجاع الخادم::HandlerResponse::Handled; } return Server::HandlerResponse::Unhandled; });
svr.set_post_routing_handler([](const auto& req, auto& res) { res.set_header("ADDITIONAL_HEADER"، "value"); });
svr.Post("/multipart", [&](const auto& req, auto& res) { auto size = req.files.size(); auto ret = req.has_file("name1"); const auto& file = req. get_file_value("name1"); // file.filename;
svr.Post("/content_receiver"، [&](const Request &req, Response &res, const ContentReader &content_reader) { if (req.is_multipart_form_data()) { // ملاحظة: يتم حظر `content_reader` حتى تتم قراءة كل حقل بيانات في النموذج ملفات MultipartFormDataItems؛ محتوى_قارئ( [&](const MultipartFormData &file) { files.push_back(file); عودة صحيحة؛ }, [&](const char *data, size_t data_length) { files.back().content.append(data, data_length); عودة صحيحة؛ }); } آخر { الأمراض المنقولة جنسيا::سلسلة الجسم؛ content_reader([&](const char *data, size_t data_length) { body.append(data, data_length); عودة صحيحة؛ }); } });
حجم ثابت DATA_CHUNK_SIZE = 4؛ svr.Get("/stream", [&](const Request &req, Response &res) { auto data = new std::string("abcdefg"); res.set_content_provider( data->size(), // طول المحتوى "نص/عادي"، // نوع المحتوى [&, data](size_t offset, size_t length, DataSink &sink) { const auto &d = *data; وعة.write(&d[إزاحة], std::min(length, DATA_CHUNK_SIZE)); عودة صحيحة؛ // قم بإرجاع "خطأ" إذا كنت تريد إلغاء العملية. }, [البيانات](نجاح منطقي) {حذف البيانات؛ }); });
بدون طول المحتوى:
svr.Get("/stream", [&](طلب ثابت &req, استجابة &res) { res.set_content_provider( "text/plain"، // نوع المحتوى [&](size_t offset, DataSink &sink) { إذا (/* لا تزال هناك بيانات */) { بيانات std::vector<char>; // تحضير البيانات... Sink.write(data.data(), data.size()); } آخر { تم () ؛ // لا مزيد من البيانات } إرجاع صحيح؛ // قم بإرجاع "خطأ" إذا كنت تريد إلغاء العملية. }); });
svr.Get("/chunked", [&](طلب ثابت& req, استجابة& الدقة) { res.set_chunked_content_provider("نص/عادي"، [](size_t إزاحة، DataSink &sink) { وعة.write("123", 3); وعة.write("345", 3); وعة.write("789", 3); تم () ؛ // لا يتم إرجاع المزيد من البيانات بشكل صحيح؛ // قم بإرجاع "خطأ" إذا كنت تريد إلغاء العملية. } ); });
مع مقطورة:
svr.Get("/chunked", [&](طلب ثابت& req, استجابة& الدقة) { res.set_header("Trailer", "Dummy1, Dummy2"); res.set_chunked_content_provider("نص/عادي"، [](size_t إزاحة، DataSink &sink) { وعة.write("123", 3); وعة.write("345", 3); وعة.write("789", 3); بالوعة.done_with_trailer({ {"دمية1"، "دميةفال1"}، {"دمية2"، "دميةفال2"} }); عودة صحيحة؛ } ); });
svr.Get("/content", [&](طلب ثابت &req, استجابة &res) { res.set_file_content("./path/to/conent.html"); }); svr.Get("/content", [&](طلب ثابت &req, استجابة &res) { res.set_file_content("./path/to/conent", "text/html"); });
بشكل افتراضي، يرسل الخادم استجابة 100 Continue
لرأس Expect: 100-continue
.
// أرسل "417 فشل التوقع" Response.svr.set_expect_100_continue_handler([](const Request &req, Response &res) { return StatusCode::ExpectationFailed_417; });
// أرسل الحالة النهائية دون قراءة الرسالة body.svr.set_expect_100_continue_handler([](const Request &req, Response &res) { return res.status = StatusCode::Unauthorized_401; });
svr.set_keep_alive_max_count(2); // الافتراضي هو 5svr.set_keep_alive_timeout(10); // الافتراضي هو 5
svr.set_read_timeout(5, 0); // 5 ثوانيsvr.set_write_timeout(5, 0); // 5 ثوانيsvr.set_idle_interval(0, 100000); // 100 مللي ثانية
svr.set_payload_max_length(1024 * 1024 * 512); // 512 ميجابايت
ملحوظة
عندما يكون نوع محتوى نص الطلب هو "www-form-urlencoded"، يجب ألا يتجاوز طول الحمولة الفعلي CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH
.
يرجى الاطلاع على مثال الخادم ومثال العميل.
يتم استخدام ThreadPool
كقائمة انتظار مهام افتراضية ، وعدد الخيوط الافتراضي هو 8، أو std::thread::hardware_concurrency()
. يمكنك تغييره باستخدام CPPHTTPLIB_THREAD_POOL_COUNT
.
إذا كنت تريد ضبط عدد الخيوط في وقت التشغيل، فلا توجد طريقة مناسبة... ولكن إليك الطريقة.
svr.new_task_queue = [] { return new ThreadPool(12); };
يمكنك أيضًا توفير معلمة اختيارية لتحديد الحد الأقصى لعدد الطلبات المعلقة، أي accept()
بواسطة المستمع ولكنها لا تزال تنتظر الخدمة بواسطة سلاسل العمليات العاملة.
svr.new_task_queue = [] { return new ThreadPool(/*num_threads=*/12, /*max_queued_requests=*/18); };
الحد الافتراضي هو 0 (غير محدود). بمجرد الوصول إلى الحد الأقصى، سيقوم المستمع بإيقاف اتصال العميل.
يمكنك توفير تنفيذ تجمع مؤشرات الترابط الخاص بك وفقًا لاحتياجاتك.
فئة YourThreadPoolTaskQueue: TaskQueue العامة {public: YourThreadPoolTaskQueue(size_t n) { pool_.start_with_thread_count(n); } Virtual bool enqueue(std::function<void()> fn) override { /* يُرجع صحيحًا إذا كانت المهمة مدرجة بالفعل في قائمة الانتظار، أو يُرجع خطأ * إذا كان على المتصل إسقاط الاتصال المقابل. */ إرجاع Pool_.enqueue(fn); } تجاوز باطلة افتراضية () { pool_.shutdown_graceously(); }خاص: YourThreadPoolpool_; }; svr.new_task_queue = [] { return new YourThreadPoolTaskQueue(12); };
#تتضمن <httplib.h>#تتضمن <iostream>int main(void) { httplib::Client cli("localhost", 1234); if (auto res = cli.Get("/hi")) { if (res->status == StatusCode::OK_200) { std::cout << res->body << std::endl; } } else { خطأ تلقائي = res.error(); std::cout << "خطأ HTTP: " << httplib::to_string(err) << std::endl; } }
نصيحة
أصبح المُنشئ ذو سلسلة منفذ مضيف المخطط مدعومًا الآن!
httplib::Client cli("localhost"); httplib::Client cli("localhost:8080"); httplib::Client cli("http://localhost"); httplib::Client cli("http://localhost:8080"); httplib::Client cli("https://localhost"); httplib::SSLClient cli("localhost");
فيما يلي قائمة الأخطاء من Result::error()
.
خطأ في التعداد { النجاح = 0، مجهول، اتصال، بيندIPAddress, يقرأ، يكتب، تجاوز عدد إعادة التوجيه، ملغى، اتصال SSL، شهادات تحميل SSL, التحقق من خادم SSL، أحرف حدود متعددة الأجزاء غير مدعومة، ضغط، مهلة الاتصال, };
httplib::رؤوس الرؤوس = { { "قبول التشفير"، "gzip، انكماش" } };auto res = cli.Get("/hi"، headers);
أو
auto res = cli.Get("/hi"، {{"Accept-Encoding"، "gzip، deflate"}});
أو
cli.set_default_headers({ { "قبول التشفير"، "gzip، انكماش" } });auto res = cli.Get("/hi");
res = cli.Post("/post", "text", "text/plain"); res = cli.Post("/person", "name=john1¬e=coder", "application/x-www-form-urlencoded");
httplib::Params Params; params.emplace("name", "john"); params.emplace("note", "coder");auto res = cli.Post("/post", params);
أو
httplib::بارامس بارامز{ { "الاسم"، "جون" }، { "ملاحظة"، "المبرمج" } };auto res = cli.Post("/post", params);
httplib::MultipartFormDataItems العناصر = { { "نص 1"، "النص الافتراضي"، ""، "" }، { "نص 2"، "أωb"، ""، "" }، { "ملف1"، "hnennlnlnon"، "hello.txt"، "نص/عادي" }، { "file2"، "{n "world"، صحيحn}n"، "world.json"، "application/json" }، { "file3"، ""، ""، "application/octet-stream" }، };auto res = cli.Post("/multipart"، items);
res = cli.Put("/resource/foo"، "text"، "text/plain")؛
res = cli.Delete("/resource/foo");
الدقة = cli.Options("*"); res = cli.Options("/resource/foo");
cli.set_connection_timeout(0, 300000); // 300 مللي ثانيةcli.set_read_timeout(5, 0); // 5 ثوانيcli.set_write_timeout(5, 0); // 5 ثواني
std::string body;auto res = cli.Get("/large-data"، [&](const char *data, size_t data_length) { body.append(data, data_length); عودة صحيحة؛ });
std::string body;auto res = cli.Get( "/stream", Headers(), [&](const Response &response) { EXPECT_EQ(StatusCode::OK_200, Response.status); عودة صحيحة؛ // قم بإرجاع "خطأ" إذا كنت تريد إلغاء الطلب. }, [&](const char *data, size_t data_length) { body.append(data, data_length); عودة صحيحة؛ // قم بإرجاع "خطأ" إذا كنت تريد إلغاء الطلب. });
std::string body = ...;auto res = cli.Post( "/stream", body.size(), [](إزاحة size_t، طول size_t، حوض البيانات &sink) { Sink.write(body.data() + offset, length); عودة صحيحة؛ // قم بإرجاع "خطأ" إذا كنت تريد إلغاء الطلب. }, "نص/عادي");
الدقة التلقائية = cli.Post("/stream"، [](size_t إزاحة، DataSink &sink) { Sink.os << "البيانات المقسمة 1"; Sink.os << "البيانات المقسمة 2"; Sink.os << "البيانات المقسمة 3"; تم () ؛ عودة صحيحة؛ // قم بإرجاع "خطأ" إذا كنت تريد إلغاء الطلب. }, "نص/عادي");
httplib::Client cli(url, port);// المطبوعات: 0/000 بايت => 50% Completeauto res = cli.Get("/", [](uint64_t len, uint64_t Total) { printf("%lld / %lld بايت => %d%% مكتملn"، إجمالي لين, (كثافة العمليات)(لين*100/الإجمالي)); عودة صحيحة؛ // قم بإرجاع "خطأ" إذا كنت تريد إلغاء الطلب.} );
// Basic Authenticationcli.set_basic_auth("user", "pass");// Digest Authenticationcli.set_digest_auth("user", "pass");// Bearer Token Authenticationcli.set_bearer_token_auth("token");
ملحوظة
OpenSSL مطلوب لمصادقة الملخص.
cli.set_proxy("host", port);// المصادقة الأساسيةcli.set_proxy_basic_auth("user", "pass");// ملخص Authenticationcli.set_proxy_digest_auth("user", "pass");// مصادقة الرمز المميز لحاملcli.set_proxy_bearer_token_auth ("يمر")؛
ملحوظة
OpenSSL مطلوب لمصادقة الملخص.
httplib::Client cli("httpbin.org");auto res = cli.Get("/range/32", { httplib::make_range_header({{1, 10}}) // 'النطاق: بايت=1- 10'});// res->يجب أن تكون الحالة 206.// res->body يجب أن يكون "bcdefghijk".
httplib::make_range_header({{1, 10}, {20, -1}}) // 'النطاق: بايت=1-10, 20-'httplib::make_range_header({{100, 199}, {500, 599) }}) // 'النطاق: البايتات=100-199، 500-599'httplib::make_range_header({{0, 0}, {-1, 1}}) // 'النطاق: بايت=0-0, -1'
httplib::Client cli("localhost", 1234); cli.Get("/hello"); // مع "الاتصال: إغلاق"cli.set_keep_alive(true); cli.Get("/world"); cli.set_keep_alive(false); cli.Get("/last-request"); // مع "الاتصال: إغلاق"
httplib::Client cli("yahoo.com");auto res = cli.Get("/"); الدقة->الحالة؛ // 301cli.set_follow_location(true); الدقة = cli.Get("/"); الدقة->الحالة؛ // 200
ملحوظة
هذه الميزة غير متوفرة على نظام التشغيل Windows حتى الآن.
cli.set_interface("eth0"); // اسم الواجهة أو عنوان IP أو اسم المضيف
يمكن للخادم تطبيق الضغط على محتويات نوع MIME التالية:
جميع أنواع النص باستثناء النص/دفق الحدث
الصورة/svg+xml
التطبيق/جافا سكريبت
application/json
التطبيق/xml
التطبيق/xhtml+xml
يتوفر ضغط "gzip" مع CPPHTTPLIB_ZLIB_SUPPORT
. يجب ربط libz
.
ضغط Brotli متاح مع CPPHTTPLIB_BROTLI_SUPPORT
. يجب ربط المكتبات الضرورية. يرجى الاطلاع على https://github.com/google/brotli لمزيد من التفاصيل.
cli.set_compress(true); res = cli.Post("/resource/foo", "...", "text/plain");
cli.set_decompress(false); res = cli.Get("/resource/foo", {{"Accept-Encoding", "gzip, deflate, br"}}); الدقة->الجسم؛ // البيانات المضغوطة
poll
بدلاً من select
يتم استخدام select
استدعاء النظام كإعداد افتراضي نظرًا لأنه مدعوم على نطاق أوسع. إذا كنت تريد السماح لـ cpp-httplib باستخدام poll
بدلاً من ذلك، فيمكنك القيام بذلك باستخدام CPPHTTPLIB_USE_POLL
.
يتوفر دعم Unix Domain Switch على أنظمة Linux وmacOS.
// Serverhttplib::Server svr("./my-socket.sock"); svr.set_address_family(AF_UNIX).listen("./my-socket.sock"، 80);// Clienthttplib::Client cli("./my-socket.sock"); cli.set_address_family(AF_UNIX);
يمكن أن يكون "my-socket.sock" مسارًا نسبيًا أو مسارًا مطلقًا. يجب أن يكون لدى تطبيقك الأذونات المناسبة للمسار. يمكنك أيضًا استخدام عنوان مأخذ التوصيل المجرد على Linux. لاستخدام عنوان مأخذ توصيل مجرد، قم بإضافة بايت فارغ ('x00') إلى المسار.
$ ./split.py -husage: Split.py [-h] [-e EXTENSION] [-o OUT]يقسم هذا البرنامج النصي httplib.h إلى أجزاء .h و.cc. وسيطات اختيارية: -h، --help show رسالة المساعدة هذه والخروج -e EXTENSION، --extension EXTENSION ملحق ملف التنفيذ (الافتراضي: cc) -o OUT، --out OUT مكان كتابة الملفات (الافتراضي: out)$ ./split.py كتب خارج/httplib.h و خارج/httplib.cc
يتوفر ملف Dockerfile لخادم HTTP الثابت. رقم المنفذ لخادم HTTP هذا هو 80، وهو يخدم ملفات ثابتة من دليل /html
في الحاوية.
> docker build -t cpp-httplib-server ....> docker run --rm -it -p 8080:80 -v ./docker/html:/html cpp-httplib-server خدمة HTTP على 0.0.0.0 المنفذ 80 ... 192.168.65.1 - - [31/Aug/2024:21:33:56 +0000] "GET / HTTP/1.1" 200 599 "-" "curl/8.7.1"192.168.65.1 - - [31/Aug/2024 :21:34:26 +0000] "احصل على / HTTP/1.1" 200 599 "-" "Mozilla/5.0 ..."192.168.65.1 - - [31/Aug/2024:21:34:26 +0000] "الحصول على /favicon.ico HTTP/1.1" 404 152 " -" "موزيلا/5.0..."
من دوكر هب
> تشغيل عامل الميناء --rm -it -p 8080:80 -v ./docker/html:/html yhirose4dockerhub/cpp-httplib-server ...> تشغيل عامل الميناء --init --rm -it -p 8080:80 -v ./docker/html:/html cpp-httplib-server خدمة HTTP على 0.0.0.0 المنفذ 80 ... 192.168.65.1 - - [31/Aug/2024:21:33:56 +0000] "GET / HTTP/1.1" 200 599 "-" "curl/8.7.1"192.168.65.1 - - [31/Aug/2024 :21:34:26 +0000] "احصل على / HTTP/1.1" 200 599 "-" "Mozilla/5.0 ..."192.168.65.1 - - [31/Aug/2024:21:34:26 +0000] "الحصول على /favicon.ico HTTP/1.1" 404 152 " -" "موزيلا/5.0..."
لا يمكن لـ g++ 4.8 والإصدارات الأقدم إنشاء هذه المكتبة نظرًا لأن <regex>
في الإصدارات معطلة.
قم بتضمين httplib.h
قبل Windows.h
أو قم بتضمين Windows.h
عن طريق تعريف WIN32_LEAN_AND_MEAN
مسبقًا.
#تتضمن <httplib.h>#تتضمن <Windows.h>
#تعريف WIN32_LEAN_AND_MEAN#تتضمن <Windows.h>#تتضمن <httplib.h>
ملحوظة
يدعم cpp-httplib رسميًا أحدث إصدار من Visual Studio فقط. قد يعمل مع الإصدارات السابقة من Visual Studio، لكن لم يعد بإمكاني التحقق من ذلك. نرحب دائمًا بطلبات السحب للإصدارات الأقدم من Visual Studio ما لم تخالف توافق C++ 11.
ملحوظة
Windows 8 أو أقل، Visual Studio 2013 أو أقل، وCygwin وMSYS2 بما في ذلك MinGW غير مدعومة ولم يتم اختبارها.
ترخيص معهد ماساتشوستس للتكنولوجيا (© 2024 يوجي هيروس)
قدم هؤلاء الأشخاص مساهمات كبيرة لصقل هذه المكتبة إلى مستوى آخر تمامًا من لعبة بسيطة!