diff --git a/CMakeLists.txt b/CMakeLists.txt
index b6c54c12ee..f0b17252fa 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -381,6 +381,8 @@ endif()
if(INDI_BUILD_DRIVERS OR INDI_BUILD_CLIENT OR INDI_BUILD_QT5_CLIENT)
install(FILES
${CMAKE_CURRENT_SOURCE_DIR}/libs/json.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/libs/httplib.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/libs/inicpp.h
${CMAKE_CURRENT_BINARY_DIR}/indiversion.h
DESTINATION ${INCLUDE_INSTALL_DIR}/libindi COMPONENT Devel
)
diff --git a/drivers.xml b/drivers.xml
index d5d0d7c9b2..bbeef1c254 100644
--- a/drivers.xml
+++ b/drivers.xml
@@ -303,6 +303,10 @@
indi_pmc8_telescope
0.5
+
+ indi_planewave_telescope
+ 0.1
+
diff --git a/drivers/telescope/CMakeLists.txt b/drivers/telescope/CMakeLists.txt
index 017677beb5..331a30d84e 100644
--- a/drivers/telescope/CMakeLists.txt
+++ b/drivers/telescope/CMakeLists.txt
@@ -257,3 +257,11 @@ add_executable(indi_skywatcherAltAzMount
target_link_libraries(indi_skywatcherAltAzMount AlignmentDriver indidriver)
install(TARGETS indi_skywatcherAltAzMount RUNTIME DESTINATION bin)
+
+# ########## Planewave Mount ##############
+add_executable(indi_planewave_telescope
+ planewave_mount.cpp)
+
+target_link_libraries(indi_planewave_telescope indidriver)
+
+install(TARGETS indi_planewave_telescope RUNTIME DESTINATION bin)
diff --git a/drivers/telescope/planewave_mount.cpp b/drivers/telescope/planewave_mount.cpp
new file mode 100644
index 0000000000..e2cea26f4a
--- /dev/null
+++ b/drivers/telescope/planewave_mount.cpp
@@ -0,0 +1,435 @@
+/*******************************************************************************
+ Copyright(c) 2023 Jasem Mutlaq. All rights reserved.
+
+ Planewave Mount
+ API Communication
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+ .
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+ .
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*******************************************************************************/
+
+#include "planewave_mount.h"
+
+#include "indicom.h"
+#include "httplib.h"
+#include "connectionplugins/connectiontcp.h"
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+std::unique_ptr PlaneWave_mount(new PlaneWave());
+
+PlaneWave::PlaneWave()
+{
+ setVersion(0, 1);
+
+ SetTelescopeCapability(TELESCOPE_CAN_PARK |
+ TELESCOPE_CAN_SYNC |
+ TELESCOPE_CAN_GOTO |
+ TELESCOPE_CAN_ABORT |
+ TELESCOPE_HAS_TIME |
+ TELESCOPE_HAS_LOCATION |
+ TELESCOPE_HAS_PIER_SIDE,
+ 4);
+
+ setTelescopeConnection(CONNECTION_TCP);
+}
+
+const char *PlaneWave::getDefaultName()
+{
+ return "PlaneWave";
+}
+
+/////////////////////////////////////////////////////////////////////////////
+///
+/////////////////////////////////////////////////////////////////////////////
+bool PlaneWave::initProperties()
+{
+ INDI::Telescope::initProperties();
+
+ // Track Modes
+ AddTrackMode("TRACK_SIDEREAL", "Sidereal", true);
+ AddTrackMode("TRACK_SOLAR", "Solar");
+ AddTrackMode("TRACK_LUNAR", "Lunar");
+ AddTrackMode("TRACK_CUSTOM", "Custom");
+
+ TrackState = SCOPE_IDLE;
+
+ SetParkDataType(PARK_AZ_ALT);
+
+ tcpConnection->setDefaultHost("192.168.1.1");
+ tcpConnection->setDefaultPort(8220);
+
+ addAuxControls();
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+///
+/////////////////////////////////////////////////////////////////////////////
+bool PlaneWave::updateProperties()
+{
+ INDI::Telescope::updateProperties();
+
+ if (isConnected())
+ {
+ defineProperty(FirmwareTP);
+
+ // Initial AZ/AL parking position.
+ if (InitPark())
+ {
+ // If loading parking data is successful, we just set the default parking values.
+ SetAxis1ParkDefault(0);
+ SetAxis2ParkDefault(0);
+ }
+ else
+ {
+ // Otherwise, we set all parking data to default in case no parking data is found.
+ SetAxis1Park(0);
+ SetAxis2Park(0);
+ SetAxis1ParkDefault(0);
+ SetAxis2ParkDefault(0);
+ }
+ }
+ else
+ {
+ deleteProperty(FirmwareTP);
+ }
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+///
+/////////////////////////////////////////////////////////////////////////////
+bool PlaneWave::Handshake()
+{
+ return getStatus();
+}
+
+/////////////////////////////////////////////////////////////////////////////
+///
+/////////////////////////////////////////////////////////////////////////////
+bool PlaneWave::getStatus()
+{
+ return dispatch("/status");
+}
+
+/////////////////////////////////////////////////////////////////////////////
+///
+/////////////////////////////////////////////////////////////////////////////
+bool PlaneWave::Sync(double ra, double dec)
+{
+ INDI_UNUSED(ra);
+ INDI_UNUSED(dec);
+ return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+///
+/////////////////////////////////////////////////////////////////////////////
+bool PlaneWave::Goto(double ra, double dec)
+{
+ httplib::Client cli(tcpConnection->host(), tcpConnection->port());
+ std::string request = "/mount/goto_ra_dec_apparent?ra_hours=" + std::to_string(ra) + "&dec_degs=" + std::to_string(dec);
+ return cli.Get(request);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+///
+/////////////////////////////////////////////////////////////////////////////
+bool PlaneWave::ReadScopeStatus()
+{
+ if (getStatus() == false)
+ return false;
+
+ auto ra = m_Status["mount.ra_apparent_hours"].as();
+ auto de = m_Status["mount.dec_apparent_degs"].as();
+
+ auto isSlewing = m_Status["mount.is_slewing"].as();
+ auto isTracking = m_Status["mount.is_tracking"].as();
+
+ switch (TrackState)
+ {
+ case SCOPE_PARKING:
+ if (!isSlewing)
+ SetParked(true);
+ break;
+ case SCOPE_SLEWING:
+ if (isTracking)
+ {
+ TrackState = SCOPE_TRACKING;
+ SetTrackEnabled(true);
+ }
+ break;
+ default:
+ break;
+ }
+
+ NewRaDec(ra, de);
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+///
+/////////////////////////////////////////////////////////////////////////////
+bool PlaneWave::Park()
+{
+ httplib::Client cli(tcpConnection->host(), tcpConnection->port());
+ std::string request = "/mount/park";
+ return cli.Get(request);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+///
+/////////////////////////////////////////////////////////////////////////////
+bool PlaneWave::UnPark()
+{
+ SetParked(false);
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+///
+/////////////////////////////////////////////////////////////////////////////////////
+bool PlaneWave::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
+{
+ if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
+ {
+
+ }
+
+ return INDI::Telescope::ISNewText(dev, name, texts, names, n);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+///
+/////////////////////////////////////////////////////////////////////////////
+bool PlaneWave::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
+{
+ if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
+ {
+ }
+
+ return INDI::Telescope::ISNewNumber(dev, name, values, names, n);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+///
+/////////////////////////////////////////////////////////////////////////////
+bool PlaneWave::ISNewSwitch(const char *dev, const char *name, ISState * states, char *names[], int n)
+{
+ if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
+ {
+ }
+
+ return INDI::Telescope::ISNewSwitch(dev, name, states, names, n);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+///
+/////////////////////////////////////////////////////////////////////////////
+bool PlaneWave::Abort()
+{
+ httplib::Client cli(tcpConnection->host(), tcpConnection->port());
+ std::string request = "/mount/stop";
+ return cli.Get(request);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+///
+/////////////////////////////////////////////////////////////////////////////
+bool PlaneWave::MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command)
+{
+ INDI_UNUSED(dir);
+ if (TrackState == SCOPE_PARKED)
+ {
+ LOG_ERROR("Please unpark the mount before issuing any motion commands.");
+ return false;
+ }
+
+ if (command == MOTION_START)
+ {
+
+ }
+ else
+ {
+ }
+
+ return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+///
+/////////////////////////////////////////////////////////////////////////////
+bool PlaneWave::MoveWE(INDI_DIR_WE dir, TelescopeMotionCommand command)
+{
+ INDI_UNUSED(dir);
+ if (TrackState == SCOPE_PARKED)
+ {
+ LOG_ERROR("Please unpark the mount before issuing any motion commands.");
+ return false;
+ }
+
+ if (command == MOTION_START)
+ {
+ }
+ else
+ {
+ }
+
+ return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+///
+/////////////////////////////////////////////////////////////////////////////
+bool PlaneWave::updateLocation(double latitude, double longitude, double elevation)
+{
+ INDI_UNUSED(latitude);
+ INDI_UNUSED(longitude);
+ INDI_UNUSED(elevation);
+ return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+///
+/////////////////////////////////////////////////////////////////////////////
+bool PlaneWave::updateTime(ln_date * utc, double utc_offset)
+{
+ INDI_UNUSED(utc);
+ INDI_UNUSED(utc_offset);
+ return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+///
+/////////////////////////////////////////////////////////////////////////////
+bool PlaneWave::SetCurrentPark()
+{
+ return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+///
+/////////////////////////////////////////////////////////////////////////////
+bool PlaneWave::SetDefaultPark()
+{
+ SetAxis1Park(0);
+ SetAxis2Park(0);
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+///
+/////////////////////////////////////////////////////////////////////////////
+bool PlaneWave::SetTrackRate(double raRate, double deRate)
+{
+ INDI_UNUSED(raRate);
+ INDI_UNUSED(deRate);
+ return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+///
+/////////////////////////////////////////////////////////////////////////////
+bool PlaneWave::SetTrackMode(uint8_t mode)
+{
+ double dRA = 0, dDE = 0;
+ if (mode == TRACK_SIDEREAL)
+ dRA = TRACKRATE_SIDEREAL;
+ else if (mode == TRACK_SOLAR)
+ dRA = TRACKRATE_SOLAR;
+ else if (mode == TRACK_LUNAR)
+ dRA = TRACKRATE_LUNAR;
+ else if (mode == TRACK_CUSTOM)
+ {
+ dRA = TrackRateN[AXIS_RA].value;
+ dDE = TrackRateN[AXIS_DE].value;
+ }
+
+ INDI_UNUSED(dRA);
+ INDI_UNUSED(dDE);
+
+ // TODO figure out how to set tracking rate per axis
+ httplib::Client cli(tcpConnection->host(), tcpConnection->port());
+ std::string request = "/mount/tracking_on";
+ return cli.Get(request);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+///
+/////////////////////////////////////////////////////////////////////////////
+bool PlaneWave::SetTrackEnabled(bool enabled)
+{
+ // On engaging track, we simply set the current track mode and it will take care of the rest including custom track rates.
+ if (enabled)
+ return SetTrackMode(IUFindOnSwitchIndex(&TrackModeSP));
+ // Disable tracking
+ else
+ {
+ httplib::Client cli(tcpConnection->host(), tcpConnection->port());
+ std::string request = "/mount/tracking_off";
+ return cli.Get(request);
+ }
+ return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/// Config Items
+/////////////////////////////////////////////////////////////////////////////
+bool PlaneWave::saveConfigItems(FILE *fp)
+{
+ INDI::Telescope::saveConfigItems(fp);
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+///
+/////////////////////////////////////////////////////////////////////////////
+bool PlaneWave::dispatch(const std::string &request)
+{
+ httplib::Client cli(tcpConnection->host(), tcpConnection->port());
+
+ if (auto res = cli.Get(request))
+ {
+ try
+ {
+ ini::IniFile inif;
+ inif.decode("[status]\n" + res->body);
+ if (inif["status"].size() == 0)
+ return false;
+ m_Status = inif["status"];
+ return true;
+ }
+ catch (std::exception &e)
+ {
+ LOGF_ERROR("Failed to process status response: %s", e.what());
+ return false;
+ }
+ }
+ else
+ {
+ LOGF_ERROR("Request %s to %s:%d failed (%d)", request.c_str(), tcpConnection->host(), tcpConnection->port(), res.error());
+ return false;
+ }
+}
diff --git a/drivers/telescope/planewave_mount.h b/drivers/telescope/planewave_mount.h
new file mode 100644
index 0000000000..38f8cc7f90
--- /dev/null
+++ b/drivers/telescope/planewave_mount.h
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ Copyright(c) 2023 Jasem Mutlaq. All rights reserved.
+
+ Planewave Mount
+ API Communication
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+ .
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+ .
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*******************************************************************************/
+
+#pragma once
+
+#include "inditelescope.h"
+#include "indipropertytext.h"
+
+#include "inicpp.h"
+
+class PlaneWave : public INDI::Telescope
+{
+ public:
+ PlaneWave();
+ virtual ~PlaneWave() = default;
+
+ virtual const char *getDefaultName() override;
+ virtual bool Handshake() override;
+ virtual bool ReadScopeStatus() override;
+ virtual bool initProperties() override;
+ virtual bool updateProperties() override;
+
+ virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override;
+ virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override;
+ virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override;
+
+ protected:
+ // Motion
+ virtual bool MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command) override;
+ virtual bool MoveWE(INDI_DIR_WE dir, TelescopeMotionCommand command) override;
+ virtual bool Abort() override;
+
+ // Time & Location
+ virtual bool updateLocation(double latitude, double longitude, double elevation) override;
+ virtual bool updateTime(ln_date *utc, double utc_offset) override;
+
+ // GOTO & SYNC
+ virtual bool Goto(double ra, double dec) override;
+ virtual bool Sync(double ra, double dec) override;
+
+ // Tracking
+ virtual bool SetTrackMode(uint8_t mode) override;
+ virtual bool SetTrackRate(double raRate, double deRate) override;
+ virtual bool SetTrackEnabled(bool enabled) override;
+
+ // Parking
+ virtual bool SetCurrentPark() override;
+ virtual bool SetDefaultPark() override;
+ virtual bool Park() override;
+ virtual bool UnPark() override;
+
+ // Config items
+ virtual bool saveConfigItems(FILE *fp) override;
+
+ private:
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ /// Utility
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ bool getStatus();
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ /// GOTO & SYNC
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ /// Communication
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ bool dispatch(const std::string &request);
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ /// INDI Properties
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // Firmware Version
+ INDI::PropertyText FirmwareTP {1};
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ /// Variables
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ ini::IniSectionBase>> m_Status;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ /// Static Constants
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // 0xA is the stop char
+ static const char DRIVER_STOP_CHAR { 0xA };
+ // Wait up to a maximum of 3 seconds for serial input
+ static constexpr const uint8_t DRIVER_TIMEOUT {3};
+ // Maximum buffer for sending/receving.
+ static constexpr const uint8_t DRIVER_LEN {128};
+};
diff --git a/libs/httplib-LICENSE b/libs/httplib-LICENSE
new file mode 100644
index 0000000000..47c418e072
--- /dev/null
+++ b/libs/httplib-LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2017 yhirose
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/libs/httplib.h b/libs/httplib.h
new file mode 100644
index 0000000000..e055dd443c
--- /dev/null
+++ b/libs/httplib.h
@@ -0,0 +1,8790 @@
+//
+// httplib.h
+//
+// Copyright (c) 2023 Yuji Hirose. All rights reserved.
+// MIT License
+//
+
+#ifndef CPPHTTPLIB_HTTPLIB_H
+#define CPPHTTPLIB_HTTPLIB_H
+
+#define CPPHTTPLIB_VERSION "0.12.4"
+
+/*
+ * Configuration
+ */
+
+#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND
+#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
+#endif
+
+#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT
+#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5
+#endif
+
+#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND
+#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300
+#endif
+
+#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND
+#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0
+#endif
+
+#ifndef CPPHTTPLIB_READ_TIMEOUT_SECOND
+#define CPPHTTPLIB_READ_TIMEOUT_SECOND 5
+#endif
+
+#ifndef CPPHTTPLIB_READ_TIMEOUT_USECOND
+#define CPPHTTPLIB_READ_TIMEOUT_USECOND 0
+#endif
+
+#ifndef CPPHTTPLIB_WRITE_TIMEOUT_SECOND
+#define CPPHTTPLIB_WRITE_TIMEOUT_SECOND 5
+#endif
+
+#ifndef CPPHTTPLIB_WRITE_TIMEOUT_USECOND
+#define CPPHTTPLIB_WRITE_TIMEOUT_USECOND 0
+#endif
+
+#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND
+#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0
+#endif
+
+#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND
+#ifdef _WIN32
+#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 10000
+#else
+#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0
+#endif
+#endif
+
+#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
+#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192
+#endif
+
+#ifndef CPPHTTPLIB_HEADER_MAX_LENGTH
+#define CPPHTTPLIB_HEADER_MAX_LENGTH 8192
+#endif
+
+#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT
+#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20
+#endif
+
+#ifndef CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT
+#define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT 1024
+#endif
+
+#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
+#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits::max)())
+#endif
+
+#ifndef CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH
+#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192
+#endif
+
+#ifndef CPPHTTPLIB_TCP_NODELAY
+#define CPPHTTPLIB_TCP_NODELAY false
+#endif
+
+#ifndef CPPHTTPLIB_RECV_BUFSIZ
+#define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u)
+#endif
+
+#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ
+#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u)
+#endif
+
+#ifndef CPPHTTPLIB_THREAD_POOL_COUNT
+#define CPPHTTPLIB_THREAD_POOL_COUNT \
+ ((std::max)(8u, std::thread::hardware_concurrency() > 0 \
+ ? std::thread::hardware_concurrency() - 1 \
+ : 0))
+#endif
+
+#ifndef CPPHTTPLIB_RECV_FLAGS
+#define CPPHTTPLIB_RECV_FLAGS 0
+#endif
+
+#ifndef CPPHTTPLIB_SEND_FLAGS
+#define CPPHTTPLIB_SEND_FLAGS 0
+#endif
+
+#ifndef CPPHTTPLIB_LISTEN_BACKLOG
+#define CPPHTTPLIB_LISTEN_BACKLOG 5
+#endif
+
+/*
+ * Headers
+ */
+
+#ifdef _WIN32
+#ifndef _CRT_SECURE_NO_WARNINGS
+#define _CRT_SECURE_NO_WARNINGS
+#endif //_CRT_SECURE_NO_WARNINGS
+
+#ifndef _CRT_NONSTDC_NO_DEPRECATE
+#define _CRT_NONSTDC_NO_DEPRECATE
+#endif //_CRT_NONSTDC_NO_DEPRECATE
+
+#if defined(_MSC_VER)
+#if _MSC_VER < 1900
+#error Sorry, Visual Studio versions prior to 2015 are not supported
+#endif
+
+#pragma comment(lib, "ws2_32.lib")
+
+#ifdef _WIN64
+using ssize_t = __int64;
+#else
+using ssize_t = long;
+#endif
+#endif // _MSC_VER
+
+#ifndef S_ISREG
+#define S_ISREG(m) (((m)&S_IFREG) == S_IFREG)
+#endif // S_ISREG
+
+#ifndef S_ISDIR
+#define S_ISDIR(m) (((m)&S_IFDIR) == S_IFDIR)
+#endif // S_ISDIR
+
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif // NOMINMAX
+
+#include
+#include
+#include
+
+#ifndef WSA_FLAG_NO_HANDLE_INHERIT
+#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
+#endif
+
+#ifndef strcasecmp
+#define strcasecmp _stricmp
+#endif // strcasecmp
+
+using socket_t = SOCKET;
+#ifdef CPPHTTPLIB_USE_POLL
+#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout)
+#endif
+
+#else // not _WIN32
+
+#include
+#ifndef _AIX
+#include
+#endif
+#include
+#include
+#include
+#ifdef __linux__
+#include
+#endif
+#include
+#ifdef CPPHTTPLIB_USE_POLL
+#include
+#endif
+#include
+#include
+#include
+#include
+#include
+#include
+
+using socket_t = int;
+#ifndef INVALID_SOCKET
+#define INVALID_SOCKET (-1)
+#endif
+#endif //_WIN32
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include