C++ 发送HTTP请求

2023-05-16

HTTPRequest

HTTPRequest是一个用于发出HTTP请求的单头C++库。您可以将它包含在项目中并使用它。HTTPRequest在macOS、Windows、Haiku、BSD和GNU/Linux上进行了测试,但是它应该可以在大多数基于Linux的平台上工作。支持IPv4和IPv6。HTTRequest要求C++ 17或更新版本。

使用

要使用这个库,只需包含HTTPRequest.hpp即可。使用#include "HTTPRequest.hpp"。

GET请求示例

#include "HTTPRequest.hpp" //相关源码在文章末尾

...

//注意:URI地址必须以 http:// 开头,否则不符合头文件校验规则
string uri_time = "http://en.xxx.com/api-ctl/client/curTime/";
string method_time = "GET";
string arguments_time = "";
auto protocol_time = http::InternetProtocol::V4;
http::Request req_time{ uri_time, protocol_time };

string timestamp;
try {
    const auto response_time = req_time.send(method_time, arguments_time, {
    {"Content-Type", "application/json"},
    {"User-Agent", "runscope/0.1"},
    {"Accept", "*/*"}
        }, std::chrono::seconds(2));
    timestamp = json::parse(string{ response_time.body.begin(), response_time.body.end() });
    }
catch (exception& e) {
    //捕获请求失败异常,处理逻辑自行添加
}

POST请求示例

#include "HTTPRequest.hpp" //相关源码在文章末尾

...

//注意:URI地址必须以 http:// 开头,否则不符合头文件校验规则
string uri = "http://en.xxx.com/api-ctl/client/health/";
string method = "POST";
string arguments = string_To_UTF8((char*)encryptStr.c_str());
auto protocol = http::InternetProtocol::V4;
http::Request req{ uri, protocol };

json responseJson;
try {
    const auto response = req.send(method, arguments, {
	{"Content-Type", "application/json"},
	{"User-Agent", "runscope/0.1"},
	{"Accept", "*/*"}
		}, std::chrono::seconds(2));
	responseJson = json::parse(string{ response.body.begin(), response.body.end() });
}
catch (exception& e) {
    //捕获请求失败异常,处理逻辑自行添加
}

HTTPRequest.hpp

//  HTTPRequest

#ifndef HTTPREQUEST_HPP
#define HTTPREQUEST_HPP

#include <cctype>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <algorithm>
#include <array>
#include <chrono>
#include <functional>
#include <map>
#include <memory>
#include <stdexcept>
#include <string>
#include <system_error>
#include <type_traits>
#include <vector>

#if defined(_WIN32) || defined(__CYGWIN__)
#  pragma push_macro("WIN32_LEAN_AND_MEAN")
#  pragma push_macro("NOMINMAX")
#  ifndef WIN32_LEAN_AND_MEAN
#    define WIN32_LEAN_AND_MEAN
#  endif // WIN32_LEAN_AND_MEAN
#  ifndef NOMINMAX
#    define NOMINMAX
#  endif // NOMINMAX
#  include <winsock2.h>
#  if _WIN32_WINNT < _WIN32_WINNT_WINXP
extern "C" char *_strdup(const char *strSource);
#    define strdup _strdup
#    include <wspiapi.h>
#  endif // _WIN32_WINNT < _WIN32_WINNT_WINXP
#  include <ws2tcpip.h>
#  pragma pop_macro("WIN32_LEAN_AND_MEAN")
#  pragma pop_macro("NOMINMAX")
#else
#  include <errno.h>
#  include <fcntl.h>
#  include <netinet/in.h>
#  include <netdb.h>
#  include <sys/select.h>
#  include <sys/socket.h>
#  include <sys/types.h>
#  include <unistd.h>
#endif // defined(_WIN32) || defined(__CYGWIN__)

namespace http
{
    class RequestError final: public std::logic_error
    {
    public:
        using std::logic_error::logic_error;
    };

    class ResponseError final: public std::runtime_error
    {
    public:
        using std::runtime_error::runtime_error;
    };

    enum class InternetProtocol: std::uint8_t
    {
        V4,
        V6
    };

    struct Uri final
    {
        std::string scheme;
        std::string user;
        std::string password;
        std::string host;
        std::string port;
        std::string path;
        std::string query;
        std::string fragment;
    };

    struct HttpVersion final
    {
        uint16_t major;
        uint16_t minor;
    };

    struct Status final
    {
        // RFC 7231, 6. Response Status Codes
        enum Code: std::uint16_t
        {
            Continue = 100,
            SwitchingProtocol = 101,
            Processing = 102,
            EarlyHints = 103,

            Ok = 200,
            Created = 201,
            Accepted = 202,
            NonAuthoritativeInformation = 203,
            NoContent = 204,
            ResetContent = 205,
            PartialContent = 206,
            MultiStatus = 207,
            AlreadyReported = 208,
            ImUsed = 226,

            MultipleChoice = 300,
            MovedPermanently = 301,
            Found = 302,
            SeeOther = 303,
            NotModified = 304,
            UseProxy = 305,
            TemporaryRedirect = 307,
            PermanentRedirect = 308,

            BadRequest = 400,
            Unauthorized = 401,
            PaymentRequired = 402,
            Forbidden = 403,
            NotFound = 404,
            MethodNotAllowed = 405,
            NotAcceptable = 406,
            ProxyAuthenticationRequired = 407,
            RequestTimeout = 408,
            Conflict = 409,
            Gone = 410,
            LengthRequired = 411,
            PreconditionFailed = 412,
            PayloadTooLarge = 413,
            UriTooLong = 414,
            UnsupportedMediaType = 415,
            RangeNotSatisfiable = 416,
            ExpectationFailed = 417,
            MisdirectedRequest = 421,
            UnprocessableEntity = 422,
            Locked = 423,
            FailedDependency = 424,
            TooEarly = 425,
            UpgradeRequired = 426,
            PreconditionRequired = 428,
            TooManyRequests = 429,
            RequestHeaderFieldsTooLarge = 431,
            UnavailableForLegalReasons = 451,

            InternalServerError = 500,
            NotImplemented = 501,
            BadGateway = 502,
            ServiceUnavailable = 503,
            GatewayTimeout = 504,
            HttpVersionNotSupported = 505,
            VariantAlsoNegotiates = 506,
            InsufficientStorage = 507,
            LoopDetected = 508,
            NotExtended = 510,
            NetworkAuthenticationRequired = 511
        };

        HttpVersion httpVersion;
        std::uint16_t code;
        std::string reason;
    };

    using HeaderField = std::pair<std::string, std::string>;
    using HeaderFields = std::vector<HeaderField>;

    struct Response final
    {
        Status status;
        HeaderFields headerFields;
        std::vector<std::uint8_t> body;
    };

    inline namespace detail
    {
#if defined(_WIN32) || defined(__CYGWIN__)
        class WinSock final
        {
        public:
            WinSock()
            {
                WSADATA wsaData;
                const auto error = WSAStartup(MAKEWORD(2, 2), &wsaData);
                if (error != 0)
                    throw std::system_error{error, std::system_category(), "WSAStartup failed"};

                if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
                {
                    WSACleanup();
                    throw std::runtime_error{"Invalid WinSock version"};
                }

                started = true;
            }

            ~WinSock()
            {
                if (started) WSACleanup();
            }

            WinSock(WinSock&& other) noexcept:
                started{other.started}
            {
                other.started = false;
            }

            WinSock& operator=(WinSock&& other) noexcept
            {
                if (&other == this) return *this;
                if (started) WSACleanup();
                started = other.started;
                other.started = false;
                return *this;
            }

        private:
            bool started = false;
        };
#endif // defined(_WIN32) || defined(__CYGWIN__)

        inline int getLastError() noexcept
        {
#if defined(_WIN32) || defined(__CYGWIN__)
            return WSAGetLastError();
#else
            return errno;
#endif // defined(_WIN32) || defined(__CYGWIN__)
        }

        constexpr int getAddressFamily(const InternetProtocol internetProtocol)
        {
            return (internetProtocol == InternetProtocol::V4) ? AF_INET :
                (internetProtocol == InternetProtocol::V6) ? AF_INET6 :
                throw RequestError{"Unsupported protocol"};
        }

        class Socket final
        {
        public:
#if defined(_WIN32) || defined(__CYGWIN__)
            using Type = SOCKET;
            static constexpr Type invalid = INVALID_SOCKET;
#else
            using Type = int;
            static constexpr Type invalid = -1;
#endif // defined(_WIN32) || defined(__CYGWIN__)

            explicit Socket(const InternetProtocol internetProtocol):
                endpoint{socket(getAddressFamily(internetProtocol), SOCK_STREAM, IPPROTO_TCP)}
            {
                if (endpoint == invalid)
                    throw std::system_error{getLastError(), std::system_category(), "Failed to create socket"};

#if defined(_WIN32) || defined(__CYGWIN__)
                ULONG mode = 1;
                if (ioctlsocket(endpoint, FIONBIO, &mode) != 0)
                {
                    close();
                    throw std::system_error{WSAGetLastError(), std::system_category(), "Failed to get socket flags"};
                }
#else
                const auto flags = fcntl(endpoint, F_GETFL);
                if (flags == -1)
                {
                    close();
                    throw std::system_error{errno, std::system_category(), "Failed to get socket flags"};
                }

                if (fcntl(endpoint, F_SETFL, flags | O_NONBLOCK) == -1)
                {
                    close();
                    throw std::system_error{errno, std::system_category(), "Failed to set socket flags"};
                }
#endif // defined(_WIN32) || defined(__CYGWIN__)

#ifdef __APPLE__
                const int value = 1;
                if (setsockopt(endpoint, SOL_SOCKET, SO_NOSIGPIPE, &value, sizeof(value)) == -1)
                {
                    close();
                    throw std::system_error{errno, std::system_category(), "Failed to set socket option"};
                }
#endif // __APPLE__
            }

            ~Socket()
            {
                if (endpoint != invalid) close();
            }

            Socket(Socket&& other) noexcept:
                endpoint{other.endpoint}
            {
                other.endpoint = invalid;
            }

            Socket& operator=(Socket&& other) noexcept
            {
                if (&other == this) return *this;
                if (endpoint != invalid) close();
                endpoint = other.endpoint;
                other.endpoint = invalid;
                return *this;
            }

            void connect(const struct sockaddr* address, const socklen_t addressSize, const std::int64_t timeout)
            {
#if defined(_WIN32) || defined(__CYGWIN__)
                auto result = ::connect(endpoint, address, addressSize);
                while (result == -1 && WSAGetLastError() == WSAEINTR)
                    result = ::connect(endpoint, address, addressSize);

                if (result == -1)
                {
                    if (WSAGetLastError() == WSAEWOULDBLOCK)
                    {
                        select(SelectType::write, timeout);

                        char socketErrorPointer[sizeof(int)];
                        socklen_t optionLength = sizeof(socketErrorPointer);
                        if (getsockopt(endpoint, SOL_SOCKET, SO_ERROR, socketErrorPointer, &optionLength) == -1)
                            throw std::system_error{WSAGetLastError(), std::system_category(), "Failed to get socket option"};

                        int socketError;
                        std::memcpy(&socketError, socketErrorPointer, sizeof(socketErrorPointer));

                        if (socketError != 0)
                            throw std::system_error{socketError, std::system_category(), "Failed to connect"};
                    }
                    else
                        throw std::system_error{WSAGetLastError(), std::system_category(), "Failed to connect"};
                }
#else
                auto result = ::connect(endpoint, address, addressSize);
                while (result == -1 && errno == EINTR)
                    result = ::connect(endpoint, address, addressSize);

                if (result == -1)
                {
                    if (errno == EINPROGRESS)
                    {
                        select(SelectType::write, timeout);

                        int socketError;
                        socklen_t optionLength = sizeof(socketError);
                        if (getsockopt(endpoint, SOL_SOCKET, SO_ERROR, &socketError, &optionLength) == -1)
                            throw std::system_error{errno, std::system_category(), "Failed to get socket option"};

                        if (socketError != 0)
                            throw std::system_error{socketError, std::system_category(), "Failed to connect"};
                    }
                    else
                        throw std::system_error{errno, std::system_category(), "Failed to connect"};
                }
#endif // defined(_WIN32) || defined(__CYGWIN__)
            }

            std::size_t send(const void* buffer, const std::size_t length, const std::int64_t timeout)
            {
                select(SelectType::write, timeout);
#if defined(_WIN32) || defined(__CYGWIN__)
                auto result = ::send(endpoint, reinterpret_cast<const char*>(buffer),
                                     static_cast<int>(length), 0);

                while (result == -1 && WSAGetLastError() == WSAEINTR)
                    result = ::send(endpoint, reinterpret_cast<const char*>(buffer),
                                    static_cast<int>(length), 0);

                if (result == -1)
                    throw std::system_error{WSAGetLastError(), std::system_category(), "Failed to send data"};
#else
                auto result = ::send(endpoint, reinterpret_cast<const char*>(buffer),
                                     length, noSignal);

                while (result == -1 && errno == EINTR)
                    result = ::send(endpoint, reinterpret_cast<const char*>(buffer),
                                    length, noSignal);

                if (result == -1)
                    throw std::system_error{errno, std::system_category(), "Failed to send data"};
#endif // defined(_WIN32) || defined(__CYGWIN__)
                return static_cast<std::size_t>(result);
            }

            std::size_t recv(void* buffer, const std::size_t length, const std::int64_t timeout)
            {
                select(SelectType::read, timeout);
#if defined(_WIN32) || defined(__CYGWIN__)
                auto result = ::recv(endpoint, reinterpret_cast<char*>(buffer),
                                     static_cast<int>(length), 0);

                while (result == -1 && WSAGetLastError() == WSAEINTR)
                    result = ::recv(endpoint, reinterpret_cast<char*>(buffer),
                                    static_cast<int>(length), 0);

                if (result == -1)
                    throw std::system_error{WSAGetLastError(), std::system_category(), "Failed to read data"};
#else
                auto result = ::recv(endpoint, reinterpret_cast<char*>(buffer),
                                     length, noSignal);

                while (result == -1 && errno == EINTR)
                    result = ::recv(endpoint, reinterpret_cast<char*>(buffer),
                                    length, noSignal);

                if (result == -1)
                    throw std::system_error{errno, std::system_category(), "Failed to read data"};
#endif // defined(_WIN32) || defined(__CYGWIN__)
                return static_cast<std::size_t>(result);
            }

        private:
            enum class SelectType
            {
                read,
                write
            };

            void select(const SelectType type, const std::int64_t timeout)
            {
                fd_set descriptorSet;
                FD_ZERO(&descriptorSet);
                FD_SET(endpoint, &descriptorSet);

#if defined(_WIN32) || defined(__CYGWIN__)
                TIMEVAL selectTimeout{
                    static_cast<LONG>(timeout / 1000),
                    static_cast<LONG>((timeout % 1000) * 1000)
                };
                auto count = ::select(0,
                                      (type == SelectType::read) ? &descriptorSet : nullptr,
                                      (type == SelectType::write) ? &descriptorSet : nullptr,
                                      nullptr,
                                      (timeout >= 0) ? &selectTimeout : nullptr);

                while (count == -1 && WSAGetLastError() == WSAEINTR)
                    count = ::select(0,
                                     (type == SelectType::read) ? &descriptorSet : nullptr,
                                     (type == SelectType::write) ? &descriptorSet : nullptr,
                                     nullptr,
                                     (timeout >= 0) ? &selectTimeout : nullptr);

                if (count == -1)
                    throw std::system_error{WSAGetLastError(), std::system_category(), "Failed to select socket"};
                else if (count == 0)
                    throw ResponseError{"Request timed out"};
#else
                timeval selectTimeout{
                    static_cast<time_t>(timeout / 1000),
                    static_cast<suseconds_t>((timeout % 1000) * 1000)
                };
                auto count = ::select(endpoint + 1,
                                      (type == SelectType::read) ? &descriptorSet : nullptr,
                                      (type == SelectType::write) ? &descriptorSet : nullptr,
                                      nullptr,
                                      (timeout >= 0) ? &selectTimeout : nullptr);

                while (count == -1 && errno == EINTR)
                    count = ::select(endpoint + 1,
                                     (type == SelectType::read) ? &descriptorSet : nullptr,
                                     (type == SelectType::write) ? &descriptorSet : nullptr,
                                     nullptr,
                                     (timeout >= 0) ? &selectTimeout : nullptr);

                if (count == -1)
                    throw std::system_error{errno, std::system_category(), "Failed to select socket"};
                else if (count == 0)
                    throw ResponseError{"Request timed out"};
#endif // defined(_WIN32) || defined(__CYGWIN__)
            }

            void close() noexcept
            {
#if defined(_WIN32) || defined(__CYGWIN__)
                closesocket(endpoint);
#else
                ::close(endpoint);
#endif // defined(_WIN32) || defined(__CYGWIN__)
            }

#if defined(__unix__) && !defined(__APPLE__) && !defined(__CYGWIN__)
            static constexpr int noSignal = MSG_NOSIGNAL;
#else
            static constexpr int noSignal = 0;
#endif // defined(__unix__) && !defined(__APPLE__)

            Type endpoint = invalid;
        };

        // RFC 7230, 3.2.3. WhiteSpace
        template <typename C>
        constexpr bool isWhiteSpaceChar(const C c) noexcept
        {
            return c == 0x20 || c == 0x09; // space or tab
        };

        // RFC 5234, Appendix B.1. Core Rules
        template <typename C>
        constexpr bool isDigitChar(const C c) noexcept
        {
            return c >= 0x30 && c <= 0x39; // 0 - 9
        }

        // RFC 5234, Appendix B.1. Core Rules
        template <typename C>
        constexpr bool isAlphaChar(const C c) noexcept
        {
            return
                (c >= 0x61 && c <= 0x7A) || // a - z
                (c >= 0x41 && c <= 0x5A); // A - Z
        }

        // RFC 7230, 3.2.6. Field Value Components
        template <typename C>
        constexpr bool isTokenChar(const C c) noexcept
        {
            return c == 0x21 || // !
                c == 0x23 || // #
                c == 0x24 || // $
                c == 0x25 || // %
                c == 0x26 || // &
                c == 0x27 || // '
                c == 0x2A || // *
                c == 0x2B || // +
                c == 0x2D || // -
                c == 0x2E || // .
                c == 0x5E || // ^
                c == 0x5F || // _
                c == 0x60 || // `
                c == 0x7C || // |
                c == 0x7E || // ~
                isDigitChar(c) ||
                isAlphaChar(c);
        };

        // RFC 5234, Appendix B.1. Core Rules
        template <typename C>
        constexpr bool isVisibleChar(const C c) noexcept
        {
            return c >= 0x21 && c <= 0x7E;
        }

        // RFC 7230, Appendix B. Collected ABNF
        template <typename C>
        constexpr bool isObsoleteTextChar(const C c) noexcept
        {
            return static_cast<unsigned char>(c) >= 0x80 &&
                static_cast<unsigned char>(c) <= 0xFF;
        }

        template <class Iterator>
        Iterator skipWhiteSpaces(const Iterator begin, const Iterator end)
        {
            auto i = begin;
            for (i = begin; i != end; ++i)
                if (!isWhiteSpaceChar(*i))
                    break;

            return i;
        }

        // RFC 5234, Appendix B.1. Core Rules
        template <typename T, typename C, typename std::enable_if<std::is_unsigned<T>::value>::type* = nullptr>
        constexpr T digitToUint(const C c)
        {
            // DIGIT
            return (c >= 0x30 && c <= 0x39) ? static_cast<T>(c - 0x30) : // 0 - 9
                throw ResponseError{"Invalid digit"};
        }

        // RFC 5234, Appendix B.1. Core Rules
        template <typename T, typename C, typename std::enable_if<std::is_unsigned<T>::value>::type* = nullptr>
        constexpr T hexDigitToUint(const C c)
        {
            // HEXDIG
            return (c >= 0x30 && c <= 0x39) ? static_cast<T>(c - 0x30) : // 0 - 9
                (c >= 0x41 && c <= 0x46) ? static_cast<T>(c - 0x41) + T(10) : // A - Z
                (c >= 0x61 && c <= 0x66) ? static_cast<T>(c - 0x61) + T(10) : // a - z, some services send lower-case hex digits
                throw ResponseError{"Invalid hex digit"};
        }

        // RFC 3986, 3. Syntax Components
        template <class Iterator>
        Uri parseUri(const Iterator begin, const Iterator end)
        {
            Uri result;

            // RFC 3986, 3.1. Scheme
            auto i = begin;
            if (i == end || !isAlphaChar(*begin))
                throw RequestError{"Invalid scheme"};

            result.scheme.push_back(*i++);

            for (; i != end && (isAlphaChar(*i) || isDigitChar(*i) || *i == '+' || *i == '-' || *i == '.'); ++i)
                result.scheme.push_back(*i);

            if (i == end || *i++ != ':')
                throw RequestError{"Invalid scheme"};
            if (i == end || *i++ != '/')
                throw RequestError{"Invalid scheme"};
            if (i == end || *i++ != '/')
                throw RequestError{"Invalid scheme"};

            // RFC 3986, 3.2. Authority
            std::string authority = std::string(i, end);

            // RFC 3986, 3.5. Fragment
            const auto fragmentPosition = authority.find('#');
            if (fragmentPosition != std::string::npos)
            {
                result.fragment = authority.substr(fragmentPosition + 1);
                authority.resize(fragmentPosition); // remove the fragment part
            }

            // RFC 3986, 3.4. Query
            const auto queryPosition = authority.find('?');
            if (queryPosition != std::string::npos)
            {
                result.query = authority.substr(queryPosition + 1);
                authority.resize(queryPosition); // remove the query part
            }

            // RFC 3986, 3.3. Path
            const auto pathPosition = authority.find('/');
            if (pathPosition != std::string::npos)
            {
                // RFC 3986, 3.3. Path
                result.path = authority.substr(pathPosition);
                authority.resize(pathPosition);
            }
            else
                result.path = "/";

            // RFC 3986, 3.2.1. User Information
            std::string userinfo;
            const auto hostPosition = authority.find('@');
            if (hostPosition != std::string::npos)
            {
                userinfo = authority.substr(0, hostPosition);

                const auto passwordPosition = userinfo.find(':');
                if (passwordPosition != std::string::npos)
                {
                    result.user = userinfo.substr(0, passwordPosition);
                    result.password = userinfo.substr(passwordPosition + 1);
                }
                else
                    result.user = userinfo;

                result.host = authority.substr(hostPosition + 1);
            }
            else
                result.host = authority;

            // RFC 3986, 3.2.2. Host
            const auto portPosition = result.host.find(':');
            if (portPosition != std::string::npos)
            {
                // RFC 3986, 3.2.3. Port
                result.port = result.host.substr(portPosition + 1);
                result.host.resize(portPosition);
            }

            return result;
        }

        // RFC 7230, 2.6. Protocol Versioning
        template <class Iterator>
        std::pair<Iterator, HttpVersion> parseHttpVersion(const Iterator begin, const Iterator end)
        {
            auto i = begin;

            if (i == end || *i++ != 'H')
                throw ResponseError{"Invalid HTTP version"};
            if (i == end || *i++ != 'T')
                throw ResponseError{"Invalid HTTP version"};
            if (i == end || *i++ != 'T')
                throw ResponseError{"Invalid HTTP version"};
            if (i == end || *i++ != 'P')
                throw ResponseError{"Invalid HTTP version"};
            if (i == end || *i++ != '/')
                throw ResponseError{"Invalid HTTP version"};

            if (i == end)
                throw ResponseError{"Invalid HTTP version"};

            const auto majorVersion = digitToUint<std::uint16_t>(*i++);

            if (i == end || *i++ != '.')
                throw ResponseError{"Invalid HTTP version"};

            if (i == end)
                throw ResponseError{"Invalid HTTP version"};

            const auto minorVersion = digitToUint<std::uint16_t>(*i++);

            return {i, HttpVersion{majorVersion, minorVersion}};
        }

        // RFC 7230, 3.1.2. Status Line
        template <class Iterator>
        std::pair<Iterator, std::uint16_t> parseStatusCode(const Iterator begin, const Iterator end)
        {
            std::uint16_t result = 0;

            auto i = begin;
            while (i != end && isDigitChar(*i))
                result = static_cast<std::uint16_t>(result * 10U) + digitToUint<std::uint16_t>(*i++);

            if (std::distance(begin, i) != 3)
                throw ResponseError{"Invalid status code"};

            return {i, result};
        }

        // RFC 7230, 3.1.2. Status Line
        template <class Iterator>
        std::pair<Iterator, std::string> parseReasonPhrase(const Iterator begin, const Iterator end)
        {
            std::string result;

            auto i = begin;
            for (; i != end && (isWhiteSpaceChar(*i) || isVisibleChar(*i) || isObsoleteTextChar(*i)); ++i)
                result.push_back(static_cast<char>(*i));

            return {i, std::move(result)};
        }

        // RFC 7230, 3.2.6. Field Value Components
        template <class Iterator>
        std::pair<Iterator, std::string> parseToken(const Iterator begin, const Iterator end)
        {
            std::string result;

            auto i = begin;
            for (; i != end && isTokenChar(*i); ++i)
                result.push_back(static_cast<char>(*i));

            if (result.empty())
                throw ResponseError{"Invalid token"};

            return {i, std::move(result)};
        }

        // RFC 7230, 3.2. Header Fields
        template <class Iterator>
        std::pair<Iterator, std::string> parseFieldValue(const Iterator begin, const Iterator end)
        {
            std::string result;

            auto i = begin;
            for (; i != end && (isWhiteSpaceChar(*i) || isVisibleChar(*i) || isObsoleteTextChar(*i)); ++i)
                result.push_back(static_cast<char>(*i));

            // trim white spaces
            result.erase(std::find_if(result.rbegin(), result.rend(), [](const char c) noexcept {
                return !isWhiteSpaceChar(c);
            }).base(), result.end());

            return {i, std::move(result)};
        }

        // RFC 7230, 3.2. Header Fields
        template <class Iterator>
        std::pair<Iterator, std::string> parseFieldContent(const Iterator begin, const Iterator end)
        {
            std::string result;

            auto i = begin;

            for (;;)
            {
                const auto fieldValueResult = parseFieldValue(i, end);
                i = fieldValueResult.first;
                result += fieldValueResult.second;

                // Handle obsolete fold as per RFC 7230, 3.2.4. Field Parsing
                // Obsolete folding is known as linear white space (LWS) in RFC 2616, 2.2 Basic Rules
                auto obsoleteFoldIterator = i;
                if (obsoleteFoldIterator == end || *obsoleteFoldIterator++ != '\r')
                    break;

                if (obsoleteFoldIterator == end || *obsoleteFoldIterator++ != '\n')
                    break;

                if (obsoleteFoldIterator == end || !isWhiteSpaceChar(*obsoleteFoldIterator++))
                    break;

                result.push_back(' ');
                i = obsoleteFoldIterator;
            }

            return {i, std::move(result)};
        }

        // RFC 7230, 3.2. Header Fields
        template <class Iterator>
        std::pair<Iterator, HeaderField> parseHeaderField(const Iterator begin, const Iterator end)
        {
            auto tokenResult = parseToken(begin, end);
            auto i = tokenResult.first;
            auto fieldName = std::move(tokenResult.second);

            if (i == end || *i++ != ':')
                throw ResponseError{"Invalid header"};

            i = skipWhiteSpaces(i, end);

            auto valueResult = parseFieldContent(i, end);
            i = valueResult.first;
            auto fieldValue = std::move(valueResult.second);

            if (i == end || *i++ != '\r')
                throw ResponseError{"Invalid header"};

            if (i == end || *i++ != '\n')
                throw ResponseError{"Invalid header"};

            return {i, {std::move(fieldName), std::move(fieldValue)}};
        }

        // RFC 7230, 3.1.2. Status Line
        template <class Iterator>
        std::pair<Iterator, Status> parseStatusLine(const Iterator begin, const Iterator end)
        {
            const auto httpVersionResult = parseHttpVersion(begin, end);
            auto i = httpVersionResult.first;

            if (i == end || *i++ != ' ')
                throw ResponseError{"Invalid status line"};

            const auto statusCodeResult = parseStatusCode(i, end);
            i = statusCodeResult.first;

            if (i == end || *i++ != ' ')
                throw ResponseError{"Invalid status line"};

            auto reasonPhraseResult = parseReasonPhrase(i, end);
            i = reasonPhraseResult.first;

            if (i == end || *i++ != '\r')
                throw ResponseError{"Invalid status line"};

            if (i == end || *i++ != '\n')
                throw ResponseError{"Invalid status line"};

            return {i, Status{
                httpVersionResult.second,
                statusCodeResult.second,
                std::move(reasonPhraseResult.second)
            }};
        }

        // RFC 7230, 4.1. Chunked Transfer Coding
        template <typename T, class Iterator, typename std::enable_if<std::is_unsigned<T>::value>::type* = nullptr>
        T stringToUint(const Iterator begin, const Iterator end)
        {
            T result = 0;
            for (auto i = begin; i != end; ++i)
                result = T(10U) * result + digitToUint<T>(*i);

            return result;
        }

        template <typename T, class Iterator, typename std::enable_if<std::is_unsigned<T>::value>::type* = nullptr>
        T hexStringToUint(const Iterator begin, const Iterator end)
        {
            T result = 0;
            for (auto i = begin; i != end; ++i)
                result = T(16U) * result + hexDigitToUint<T>(*i);

            return result;
        }

        // RFC 7230, 3.1.1. Request Line
        inline std::string encodeRequestLine(const std::string& method, const std::string& target)
        {
            return method + " " + target + " HTTP/1.1\r\n";
        }

        // RFC 7230, 3.2. Header Fields
        inline std::string encodeHeaderFields(const HeaderFields& headerFields)
        {
            std::string result;
            for (const auto& headerField : headerFields)
            {
                if (headerField.first.empty())
                    throw RequestError{"Invalid header field name"};

                for (const auto c : headerField.first)
                    if (!isTokenChar(c))
                        throw RequestError{"Invalid header field name"};

                for (const auto c : headerField.second)
                    if (!isWhiteSpaceChar(c) && !isVisibleChar(c) && !isObsoleteTextChar(c))
                        throw RequestError{"Invalid header field value"};

                result += headerField.first + ": " + headerField.second + "\r\n";
            }

            return result;
        }

        // RFC 4648, 4. Base 64 Encoding
        template <class Iterator>
        std::string encodeBase64(const Iterator begin, const Iterator end)
        {
            constexpr std::array<char, 64> chars{
                'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
                'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
                'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
                'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
                '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
            };

            std::string result;
            std::size_t c = 0;
            std::array<std::uint8_t, 3> charArray;

            for (auto i = begin; i != end; ++i)
            {
                charArray[c++] = static_cast<std::uint8_t>(*i);
                if (c == 3)
                {
                    result += chars[static_cast<std::uint8_t>((charArray[0] & 0xFC) >> 2)];
                    result += chars[static_cast<std::uint8_t>(((charArray[0] & 0x03) << 4) + ((charArray[1] & 0xF0) >> 4))];
                    result += chars[static_cast<std::uint8_t>(((charArray[1] & 0x0F) << 2) + ((charArray[2] & 0xC0) >> 6))];
                    result += chars[static_cast<std::uint8_t>(charArray[2] & 0x3f)];
                    c = 0;
                }
            }

            if (c)
            {
                result += chars[static_cast<std::uint8_t>((charArray[0] & 0xFC) >> 2)];

                if (c == 1)
                    result += chars[static_cast<std::uint8_t>((charArray[0] & 0x03) << 4)];
                else // c == 2
                {
                    result += chars[static_cast<std::uint8_t>(((charArray[0] & 0x03) << 4) + ((charArray[1] & 0xF0) >> 4))];
                    result += chars[static_cast<std::uint8_t>((charArray[1] & 0x0F) << 2)];
                }

                while (++c < 4) result += '='; // padding
            }

            return result;
        }

        inline std::vector<std::uint8_t> encodeHtml(const Uri& uri,
                                                    const std::string& method,
                                                    const std::vector<uint8_t>& body,
                                                    HeaderFields headerFields)
        {
            if (uri.scheme != "http")
                throw RequestError{"Only HTTP scheme is supported"};

            // RFC 7230, 5.3. Request Target
            const std::string requestTarget = uri.path + (uri.query.empty() ? ""  : '?' + uri.query);

            // RFC 7230, 5.4. Host
            headerFields.push_back({"Host", uri.host});

            // RFC 7230, 3.3.2. Content-Length
            headerFields.push_back({"Content-Length", std::to_string(body.size())});

            // RFC 7617, 2. The 'Basic' Authentication Scheme
            if (!uri.user.empty() || !uri.password.empty())
            {
                std::string userinfo = uri.user + ':' + uri.password;
                headerFields.push_back({"Authorization", "Basic " + encodeBase64(userinfo.begin(), userinfo.end())});
            }

            const auto headerData = encodeRequestLine(method, requestTarget) +
                encodeHeaderFields(headerFields) +
                "\r\n";

            std::vector<uint8_t> result(headerData.begin(), headerData.end());
            result.insert(result.end(), body.begin(), body.end());

            return result;
        }
    }

    class Request final
    {
    public:
        explicit Request(const std::string& uriString,
                         const InternetProtocol protocol = InternetProtocol::V4):
            internetProtocol{protocol},
            uri{parseUri(uriString.begin(), uriString.end())}
        {
        }

        Response send(const std::string& method = "GET",
                      const std::string& body = "",
                      const HeaderFields& headerFields = {},
                      const std::chrono::milliseconds timeout = std::chrono::milliseconds{-1})
        {
            return send(method,
                        std::vector<uint8_t>(body.begin(), body.end()),
                        headerFields,
                        timeout);
        }

        Response send(const std::string& method,
                      const std::vector<uint8_t>& body,
                      const HeaderFields& headerFields = {},
                      const std::chrono::milliseconds timeout = std::chrono::milliseconds{-1})
        {
            const auto stopTime = std::chrono::steady_clock::now() + timeout;

            if (uri.scheme != "http")
                throw RequestError{"Only HTTP scheme is supported"};

            addrinfo hints = {};
            hints.ai_family = getAddressFamily(internetProtocol);
            hints.ai_socktype = SOCK_STREAM;

            const char* port = uri.port.empty() ? "80" : uri.port.c_str();

            addrinfo* info;
            if (getaddrinfo(uri.host.c_str(), port, &hints, &info) != 0)
                throw std::system_error{getLastError(), std::system_category(), "Failed to get address info of " + uri.host};

            const std::unique_ptr<addrinfo, decltype(&freeaddrinfo)> addressInfo{info, freeaddrinfo};

            const auto requestData = encodeHtml(uri, method, body, headerFields);

            Socket socket{internetProtocol};

            const auto getRemainingMilliseconds = [](const std::chrono::steady_clock::time_point time) noexcept -> std::int64_t {
                const auto now = std::chrono::steady_clock::now();
                const auto remainingTime = std::chrono::duration_cast<std::chrono::milliseconds>(time - now);
                return (remainingTime.count() > 0) ? remainingTime.count() : 0;
            };

            // take the first address from the list
            socket.connect(addressInfo->ai_addr, static_cast<socklen_t>(addressInfo->ai_addrlen),
                           (timeout.count() >= 0) ? getRemainingMilliseconds(stopTime) : -1);

            auto remaining = requestData.size();
            auto sendData = requestData.data();

            // send the request
            while (remaining > 0)
            {
                const auto size = socket.send(sendData, remaining,
                                              (timeout.count() >= 0) ? getRemainingMilliseconds(stopTime) : -1);
                remaining -= size;
                sendData += size;
            }

            std::array<std::uint8_t, 4096> tempBuffer;
            constexpr std::array<std::uint8_t, 2> crlf = {'\r', '\n'};
            constexpr std::array<std::uint8_t, 4> headerEnd = {'\r', '\n', '\r', '\n'};
            Response response;
            std::vector<std::uint8_t> responseData;
            bool parsingBody = false;
            bool contentLengthReceived = false;
            std::size_t contentLength = 0U;
            bool chunkedResponse = false;
            std::size_t expectedChunkSize = 0U;
            bool removeCrlfAfterChunk = false;

            // read the response
            for (;;)
            {
                const auto size = socket.recv(tempBuffer.data(), tempBuffer.size(),
                                              (timeout.count() >= 0) ? getRemainingMilliseconds(stopTime) : -1);
                if (size == 0) // disconnected
                    return response;

                responseData.insert(responseData.end(), tempBuffer.begin(), tempBuffer.begin() + size);

                if (!parsingBody)
                {
                    // RFC 7230, 3. Message Format
                    // Empty line indicates the end of the header section (RFC 7230, 2.1. Client/Server Messaging)
                    const auto endIterator = std::search(responseData.cbegin(), responseData.cend(),
                                                         headerEnd.cbegin(), headerEnd.cend());
                    if (endIterator == responseData.cend()) break; // two consecutive CRLFs not found

                    const auto headerBeginIterator = responseData.cbegin();
                    const auto headerEndIterator = endIterator + 2;

                    auto statusLineResult = parseStatusLine(headerBeginIterator, headerEndIterator);
                    auto i = statusLineResult.first;

                    response.status = std::move(statusLineResult.second);

                    for (;;)
                    {
                        auto headerFieldResult = parseHeaderField(i, headerEndIterator);
                        i = headerFieldResult.first;

                        auto fieldName = std::move(headerFieldResult.second.first);
                        const auto toLower = [](const char c) noexcept {
                            return (c >= 'A' && c <= 'Z') ? c - ('A' - 'a') : c;
                        };
                        std::transform(fieldName.begin(), fieldName.end(), fieldName.begin(), toLower);

                        auto fieldValue = std::move(headerFieldResult.second.second);

                        if (fieldName == "transfer-encoding")
                        {
                            // RFC 7230, 3.3.1. Transfer-Encoding
                            if (fieldValue == "chunked")
                                chunkedResponse = true;
                            else
                                throw ResponseError{"Unsupported transfer encoding: " + fieldValue};
                        }
                        else if (fieldName == "content-length")
                        {
                            // RFC 7230, 3.3.2. Content-Length
                            contentLength = stringToUint<std::size_t>(fieldValue.cbegin(), fieldValue.cend());
                            contentLengthReceived = true;
                            response.body.reserve(contentLength);
                        }

                        response.headerFields.push_back({std::move(fieldName), std::move(fieldValue)});

                        if (i == headerEndIterator)
                            break;
                    }

                    responseData.erase(responseData.cbegin(), headerEndIterator + 2);
                    parsingBody = true;
                }

                if (parsingBody)
                {
                    // Content-Length must be ignored if Transfer-Encoding is received (RFC 7230, 3.2. Content-Length)
                    if (chunkedResponse)
                    {
                        // RFC 7230, 4.1. Chunked Transfer Coding
                        for (;;)
                        {
                            if (expectedChunkSize > 0)
                            {
                                const auto toWrite = (std::min)(expectedChunkSize, responseData.size());
                                response.body.insert(response.body.end(), responseData.begin(),
                                                     responseData.begin() + static_cast<std::ptrdiff_t>(toWrite));
                                responseData.erase(responseData.begin(),
                                                   responseData.begin() + static_cast<std::ptrdiff_t>(toWrite));
                                expectedChunkSize -= toWrite;

                                if (expectedChunkSize == 0) removeCrlfAfterChunk = true;
                                if (responseData.empty()) break;
                            }
                            else
                            {
                                if (removeCrlfAfterChunk)
                                {
                                    if (responseData.size() < 2) break;
                                    
                                    if (!std::equal(crlf.begin(), crlf.end(), responseData.begin()))
                                        throw ResponseError{"Invalid chunk"};

                                    removeCrlfAfterChunk = false;
                                    responseData.erase(responseData.begin(), responseData.begin() + 2);
                                }

                                const auto i = std::search(responseData.begin(), responseData.end(),
                                                           crlf.begin(), crlf.end());

                                if (i == responseData.end()) break;

                                expectedChunkSize = detail::hexStringToUint<std::size_t>(responseData.begin(), i);
                                responseData.erase(responseData.begin(), i + 2);

                                if (expectedChunkSize == 0)
                                    return response;
                            }
                        }
                    }
                    else
                    {
                        response.body.insert(response.body.end(), responseData.begin(), responseData.end());
                        responseData.clear();

                        // got the whole content
                        if (contentLengthReceived && response.body.size() >= contentLength)
                            return response;
                    }
                }
            }

            return response;
        }

    private:
#if defined(_WIN32) || defined(__CYGWIN__)
        WinSock winSock;
#endif // defined(_WIN32) || defined(__CYGWIN__)
        InternetProtocol internetProtocol;
        Uri uri;
    };
}

#endif // HTTPREQUEST_HPP

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ 发送HTTP请求 的相关文章

  • 如何将 POST 请求内容保存为 .NET 中的文件 [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我有一个客户端应用程序POST请求a
  • Node.JS Web 服务器中的安全性

    所以 我正在学习 Node JS 到目前为止我很喜欢它 我已经有几个项目在工作了 我想我可以在其中使用nodejs 不过 我担心安全问题 如果我使用 Node JS http 模块编写自定义 Web 服务器 我是否可能非常容易受到攻击 Ap
  • HttpGet 401 状态代码后跟 200 状态代码

    我使用 Apachage HttpComponent 访问 Web 服务时遇到奇怪的行为 我可以访问服务器日志 当我尝试连接到服务器并执行 httpGet 命令时 我可以在日志中首先看到 401 状态 http 未经授权 然后看到 200
  • 如何在C++中使用Curl获取HTTP响应字符串

    我对 HTTP 命令和 libcurl 库非常陌生 我知道如何获取 HTTP 响应代码 但不知道如何获取 HTTP 响应字符串 以下是我为获取响应代码而编写的代码片段 任何有关如何获取响应字符串的帮助将不胜感激 curl easy seto
  • Angular4如何使用flatMap链接forkJoin

    我所处的情况是 我需要进行 5 个可以并行执行的 http 调用 在这五个调用之后需要执行另一个 http 调用 我在前 5 个中使用了 forkJoin 但我不知道如何链接 flatMap 或其他函数 forkJoin firstObse
  • 在 Heroku 上获取客户端的真实 IP 地址

    在任何 Heroku 堆栈上 我想获取客户端的 IP 我的第一次尝试可能是 request headers REMOTE ADDR 当然 这是行不通的 因为所有请求都是通过代理传递的 所以替代方法是使用 request headers X
  • 如何使用独立的 Jetty 进行服务器推送

    我正在尝试使用独立的 Jetty 在静态网站上测试服务器推送功能 我的网站由一个index html 1个CSS 一堆图像组成 目录结构为 Album index html style css images image 1 png a se
  • Angular JS 在调用新的 $http 之前取消 $http 调用

    在 Angular JS 1 1 5 中 您可以取消之前启动的 http 调用 这两个link1 https stackoverflow com questions 16962232 in angularjs how to stop ong
  • HTTP部分上传、断点续传的标准方法

    我正在开发 http 客户端 服务器框架 并寻找处理部分上传的正确方法 与使用带有 Range 标头的 GET 方法进行下载相同 但是 HTTP PUT 并不打算恢复 据我所知 PATCH 方法不接受 Range 标头 有没有办法通过 HT
  • Chrome 在传输一定量的数据后挂起 - 等待可用的套接字

    我有一个浏览器游戏 最近我开始向游戏添加音频 Chrome 无法加载整个页面并卡在 91 requests 8 1 MB transferred 并且不再加载任何内容 它甚至破坏了所有其他选项卡中的网站 说Waiting for avail
  • 外部依赖错误的 HTTP 状态代码

    当服务器与外部 API 通信出现问题时 返回的正确 HTTP 状态代码是什么 假设客户端向我的服务器 A 发送有效请求 然后 A 查询服务器 B 的 API 以便执行某些操作 然而 B 的 API 当前抛出 500 错误或因某种原因无法访问
  • HTTP代理服务器

    我开始研究一个用 C 编写的非常基本的 HTTP 代理服务器 Edit 只是转发我的请求的东西 此时我很难理解如何继续 任何帮助都会有益的 看一眼micro proxy http acme com software micro proxy
  • Django 响应总是用 text/html 分块无法设置内容长度

    在我的Django应用程序的views py中 我在尝试设置以下HTTP标头字段后返回一个HttpResponse对象 Create a Response Object with the content to return response
  • 编写每个处理程序中间件

    我希望从处理程序中提取一些重复的逻辑 并将其放入一些每个处理程序的中间件中 特别是 CSRF 检查 检查现有会话值 即身份验证或预览页面 等 我读了关于此的几篇文章 http justinas org writing http middle
  • HTTPWebResponse 响应字符串被截断

    应用程序正在与 REST 服务通信 Fiddler 显示作为 Apps 响应传入的完整良好 XML 响应 该应用程序位于法属波利尼西亚 在新西兰也有一个相同的副本 因此主要嫌疑人似乎在编码 但我们已经检查过 但空手而归 查看流读取器的输出字
  • 收到“路径‘OPTIONS’被禁止”。 ASP.NET网站异常

    我收到错误System Web HttpException Path OPTIONS is forbidden 自从我们将网站转移到新的服务器设置以来 我无法重新创建该错误 但我每天至少会收到几次有关此异常的电子邮件 有什么想法可能导致此问
  • 如何在android中创建nanohttpd服务器?

    其实我也查过一些问题 然后去github上找了一下 但我是新来的 我无法理解这个例子 我想在android中创建http服务器 这样我就可以在PC浏览器中访问它 我有一个类扩展 nanohttpd 的实例 但服务器无法工作 不知道为什么 我
  • Python Requests 库重定向新 url

    我一直在浏览 Python 请求文档 但看不到我想要实现的任何功能 在我的脚本中我设置allow redirects True 我想知道该页面是否已重定向到其他内容 新的 URL 是什么 例如 如果起始 URL 为 www google c
  • 是否可以使用 PHP 重定向发送 POST 数据?

    更新 这不是重复的如何使用 PHP 发送 POST 请求 https stackoverflow com questions 5647461 how do i send a post request with php 那里的解决方案对我不起
  • ExoPlayer2 - 如何使 HTTP 301 重定向工作?

    我开始使用 ExoPlayer 来传输一些音频 一切都很顺利 直到我遇到一个带有 301 永久移动 重定向的 URL ExoPlayer2 默认情况下不处理该问题 我已经看过这个线程 https github com google ExoP

随机推荐

  • 激光SLAM 算法匹配原理

    文章目录 1 数据获取 xff1a 2 数据关联 xff1a 3 匹配 xff1a 4 位姿更新 xff1a 5 地图更新 xff1a 6 循环 xff1a 数据关联二维激光局部定位算法原理 激光SLAM xff08 Simultaneou
  • 【ORB_SLAM2 CMakeLists.txt 文件详解】

    文章目录 ORB SLAM2 CMakeLists txt 文件详解 set CMAKE LIBRARY OUTPUT DIRECTORY PROJECT SOURCE DIR lib add library PROJECT NAME SH
  • 数据结构知识体系框架图

  • [TM4C123单片机实践] 配置SSI并驱动DAC7811显示正弦波

    这几天做电赛学习了TM4C123 单片机 总得来说 结合官方例程与参考手册 加上一个好的示波器 效率会高很多 TI的SSI 实际上就是SPI 我门先熟悉一下SPI SPI 就是在主机与从机之间用来传输数据的 通过TX RX传输数据 通过CS
  • 压力BMP180传感器时序详解

    BMP180是一种高精度数字压力传感器 xff0c BMP180的超低功耗 xff0c 低电压电子元件经过优化 xff0c 适用于移动电话 xff0c PDA xff0c GPS导航设备和户外设备 UP 61 压力数据 xff08 16到1
  • C3D行为识别(一):UCF101视频数据集预处理

    行为识别 xff08 一 xff09 xff1a UCF101视频数据集预处理 文章目录 行为识别 xff08 一 xff09 xff1a UCF101视频数据集预处理1 数据集介绍1 1 UCF101 2 UCF101预处理2 1 划分t
  • Linux学习-坑多就慢慢填

    Linux学习 坑多就慢慢填 刚开始想找个资料丰富的开发板学习Linux xff0c 那时正点原子和野火都上架了NXP芯片的开发板 xff0c 因为经常用正点原子的硬件 教程和论坛 xff0c 毅然选择了正点原子的NAND版 xff0c 实
  • 树莓派 I2C通信,控制多个I2C从设备

    1 需要安装 i2c tools工具 在黑框输入 xff1a sudo apt get install i2c tools 2 后续编程使用python库 在黑框输入 xff1a sudo apt get install python sm
  • 软件框架详解

    软件框架至少包含以下组成部分 xff1a xff08 1 xff09 一系列完成计算的模块 xff0c 在此称为构件 xff08 2 xff09 构件之间的关系与交互机制 xff08 3 xff09 一系列可变点 xff08 也称热点 xf
  • oauth2四种模式的流程图

    参照https www ruanyifeng com blog 2019 04 oauth grant types html 以下为自己的理解整理 xff0c 授权码和密码模式是用户使用第三方平台账号授权给客户端应用 xff0c 来获取受第
  • Linux 应用开发 - 必须掌握的 5 个底层 IO 函数

    底层输入输出 xff08 Low Level Input Output xff09 这篇博客主要介绍 Linux 原生的 IO 操作 xff08 Low IO xff09 xff0c 你可能会想不是有跨平台的 ANSI C 可以使用么 xf
  • 为什么选择嵌入式方向

    本文出自 同济大学软件学院院长谈 嵌入式 方向选择 xff0c 个人感觉不错 xff0c 故转之 嵌入式系统无疑是当前最热门最有发展前途的 IT应用领域之一 嵌入式系统用在一些特定专用设备上 xff0c 通常这些设备的 硬件资源 xff08
  • 关于Apache的目录浏览功能

    近日 xff0c 公司有个需求需要使用到Apache的目录浏览功能 但是遇到了一点问题 xff0c 所以出现了这篇文章 linux安装Apache很简单 xff0c 使用yum安装即可 命令 xff1a yum y install http
  • Linux 下的make命令与Makefile

    概述 博客内容包含linux下make命令的使用与makefile的书写规则等 xff0c 希望通过本文档使读者对make命令makefile文件有进一步了解 xff0c 由于鄙人经验学识有限文档中会有描述不准确以及理解偏差 xff0c 欢
  • 人工智能会取代程序员吗?

    多亏了人工智能 xff08 AI xff09 xff0c 软件将在未来自行编写 至少 xff0c 这就是谷歌首席执行官桑达尔 皮查伊 xff08 Sundar Pichai xff09 对软件开发未来的看法 他是对的 xff01 这并不能使
  • 人工智能和5G在无人机技术中的作用

    技术为我们提供了许多引人入胜的设备和发明 xff0c 无人机就是其中之一 无人机 xff0c 在技术术语中也称为无人机 xff08 UAV xff09 xff0c 自2007年首次出现以来越来越受欢迎 这些小工具最初是手动和远程控制的 无人
  • 惠普暗影精灵笔记本开机自动打开小键盘,冷热启动均支持

  • STM32F4 使用结构体配置功能

    1 IIC配置 void IIC Mode Config void I2C InitTypeDef I2C InitStructure I2C InitStructure I2C Mode 61 I2C Mode I2C IIC模式 I2C
  • 执行 sudo apt-get update 报错:仓库 xxx 没有release文件 / 不再含有release文件

    输入命令 sudo apt get update 报错 这里可以看到有两个问题 xff0c 一个是 ubuntu自己的源连不上了 第二三个红框框 xff0c 一个是 vmware 这个软件 第一个红框框 首先解决第一个问题 archive
  • C++ 发送HTTP请求

    HTTPRequest HTTPRequest是一个用于发出HTTP请求的单头C 43 43 库 您可以将它包含在项目中并使用它 HTTPRequest在macOS Windows Haiku BSD和GNU Linux上进行了测试 xff