개발관련/C&C++

HttpClient

Diademata 2018. 5. 25. 19:33
반응형

code : https://github.com/EomTaeWook/Cpp-Util/tree/master/Util/Web


윈도우 함수 Wrapping


NS.h


#pragma once

#ifndef HTTP_H

#define HTTP_H

#define NS_WEB_HTTP_BEGIN namespace Util { namespace Web { namespace Http {

#define NS_WEB_HTTP_END } } } 

#define USING_WEB_HTTP using namespace Util::Web::Http;


#define NS_WEB_BEGIN namespace Util { namespace Web {

#define NS_WEB_END } }

#define USING_WEB_HTTP using namespace Util::Web::Http;

#define USING_WEB using namespace Util::Web;

#endif


WebEnum.h


#pragma once

#include "NS.h"


NS_WEB_BEGIN


enum class Method

{

Get,

Post,

Put,

Delete

};


NS_WEB_END


Url.h


#pragma once

#pragma once

#include "NS.h"

#include <string>

#include <algorithm>

NS_WEB_BEGIN

class Url

{

public:

Url();

Url(std::string url);

Url(std::wstring url);

virtual ~Url();

private:

std::wstring _path;

std::wstring _protocol;

std::wstring _host;

int _port;

public:

void Parse(std::string url);

void Parse(std::wstring url);

std::wstring GetPath();

std::wstring GetProtocol();

std::wstring GetHost();

int GetPort();

};

inline Url::Url()

{

}

inline Url::Url(std::string url)

{

Parse(std::wstring().assign(url.begin(), url.end()));

}

inline Url::Url(std::wstring url)

{

Parse(url);

}

inline Url::~Url()

{

}

inline void Url::Parse(std::string url)

{

Parse(std::wstring().assign(url.begin(), url.end()));

}

inline void Url::Parse(std::wstring url)

{

if (url.size() == 0) return;

auto beginIdx = url.find(L"://");

if (beginIdx != std::wstring::npos)

{

_protocol = url.substr(0, beginIdx).c_str();

std::transform(_protocol.begin(), _protocol.end(), _protocol.begin(), ::tolower);

beginIdx += 3;

}

else

{

_protocol = L"http";

beginIdx = 0;

}

auto endIdx = url.find(L"/", beginIdx);

if (endIdx != std::wstring::npos)

{

_host = url.substr(beginIdx, endIdx - beginIdx);

std::transform(_host.begin(), _host.end(), _host.begin(), ::tolower);

beginIdx = endIdx;

}

else

_host = url.substr(beginIdx, url.size() - beginIdx);


endIdx = _host.rfind(L":");

if (endIdx != std::wstring::npos)

{

_port = std::stoi(_host.substr(endIdx + 1, _host.size() - endIdx));

_host.erase(endIdx, _host.size() - endIdx);

}

else

_port = 80;

if (beginIdx != 0)

{

_path = url.substr(beginIdx, url.size() - beginIdx);

std::transform(_path.begin(), _path.end(), _path.begin(), ::tolower);

}

}

inline std::wstring Url::GetPath()

{

return _path;

}

inline std::wstring Url::GetProtocol()

{

return _protocol;

}

inline std::wstring Url::GetHost()

{

return _host;

}

inline int Url::GetPort()

{

return _port;

}

NS_WEB_END


HttpClient.h


#pragma once

#include <Windows.h>

#include <winhttp.h>

#include "WebEnum.h"

#include "Url.h"

#include <string>

#include <functional>

#pragma comment(lib ,"winhttp.lib")

NS_WEB_HTTP_BEGIN

class HttpClient

{

public:

HttpClient();

virtual ~HttpClient();

private:

HINTERNET _session;

HINTERNET _connect;

HINTERNET _request;

int _protocolType;

char _buffer[2048];

Url _url;

std::wstring _header;

std::function<void(int, std::string&)> _callback;

std::string _response;

int _responseCode;

public:

void Init(std::string url);

bool SendRequestAsync(std::string requestData = "", Method method = Method::Get, std::function<void(int, std::string&)> callback = nullptr);

std::string SendRequest(std::string requestData = "", Method method = Method::Get);

void HeaderAppend(std::string header);

private:

void Invoke(unsigned long code, void* info, unsigned long length);

void Close();

private:

static void __stdcall Invoke(HINTERNET handle, DWORD_PTR context, DWORD status, void* info, DWORD infoLength);

};

inline HttpClient::HttpClient()

{

_protocolType = 0;

}

inline HttpClient::~HttpClient()

{

Close();

}

NS_WEB_HTTP_END


HttpClient.cpp


#include "HttpClient.h"

NS_WEB_HTTP_BEGIN

void HttpClient::Init(std::string url)

{

try

{

_url.Parse(url);

if (_url.GetProtocol() == L"https")

_protocolType = WINHTTP_FLAG_SECURE;

}

catch (...)

{

throw std::exception("InitException");

}

}

std::string HttpClient::SendRequest(std::string requestData, Method method)

{

try

{

_session = WinHttpOpen(L"0",

WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,

WINHTTP_NO_PROXY_NAME,

WINHTTP_NO_PROXY_BYPASS,

0);

if (_session)

{

_connect = WinHttpConnect(_session,

_url.GetHost().c_str(),

_url.GetPort(),

0);

}

std::wstring type = L"GET";

std::wstring path = _url.GetPath().c_str();

switch (method)

{

case Method::Get:

path.append(requestData.begin(), requestData.end());

requestData.clear();

break;

case Method::Post:

type = L"POST";

break;

case Method::Put:

type = L"PUT";

break;

case Method::Delete:

type = L"Delete";

break;

}


_request = WinHttpOpenRequest(_connect,

type.c_str(),

path.c_str(),

NULL,

WINHTTP_NO_REFERER,

WINHTTP_DEFAULT_ACCEPT_TYPES,

_protocolType);


if (_header.size() == 0)

_header = L"content-type:application/x-www-form-urlencoded";


WinHttpAddRequestHeaders(_request, _header.c_str(), (DWORD)_header.size(), WINHTTP_ADDREQ_FLAG_ADD);


if (WinHttpSendRequest(_request,

WINHTTP_NO_ADDITIONAL_HEADERS,

0,

(LPVOID)requestData.c_str(),

(DWORD)requestData.size(),

(DWORD)requestData.size(),

(DWORD_PTR)this))

{

if (WinHttpReceiveResponse(_request, NULL))

{

unsigned long readSize = 0;

do

{

WinHttpQueryDataAvailable(_request, &readSize);

if (readSize <= 0) break;

WinHttpReadData(_request, _buffer, sizeof(_buffer), &readSize);

_response.append(_buffer, readSize);

readSize = 0;

} while (readSize > 0);

}

}

else

{

throw std::exception();

}

}

catch (...)

{

Close();

throw std::exception("RequestException");

}

Close();

return _response;

}

bool HttpClient::SendRequestAsync(std::string requestData, Method method, std::function<void(int, std::string&)> callback)

{

try

{

_session = WinHttpOpen(L"0",

WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,

WINHTTP_NO_PROXY_NAME,

WINHTTP_NO_PROXY_BYPASS,

WINHTTP_FLAG_ASYNC);


if (_session)

{

_connect = WinHttpConnect(_session,

_url.GetHost().c_str(),

_url.GetPort(),

0);

}


std::wstring type = L"GET";

std::wstring path = _url.GetPath().c_str();

_callback = callback;

switch (method)

{

case Method::Get:

path.append(requestData.begin(), requestData.end());

requestData.clear();

break;

case Method::Post:

type = L"POST";

break;

case Method::Put:

type = L"PUT";

break;

case Method::Delete:

type = L"Delete";

break;

}


_request = WinHttpOpenRequest(_connect,

type.c_str(),

path.c_str(),

NULL,

WINHTTP_NO_REFERER,

WINHTTP_DEFAULT_ACCEPT_TYPES,

_protocolType);


if (_header.size() == 0)

_header = L"content-type:application/x-www-form-urlencoded";


WinHttpAddRequestHeaders(_request, _header.c_str(), (DWORD)_header.size(), WINHTTP_ADDREQ_FLAG_ADD);


WinHttpSetStatusCallback(_request,

HttpClient::Invoke,

WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS,

0);


auto result = WinHttpSendRequest(_request,

WINHTTP_NO_ADDITIONAL_HEADERS,

0,

(LPVOID)requestData.c_str(),

(DWORD)requestData.size(),

(DWORD)requestData.size(),

(DWORD_PTR)this);

return result;

}

catch (...)

{

throw std::exception("RequestException");

}

}

void HttpClient::HeaderAppend(std::string header)

{

if (header.size() == 0) return;

auto data = std::wstring().assign(header.begin(), header.end());

_header.append(data + L"\n");

}

void HttpClient::Close()

{

if (_request) WinHttpCloseHandle(_request);

if (_connect) WinHttpCloseHandle(_connect);

if (_session) WinHttpCloseHandle(_session);

}


void HttpClient::Invoke(unsigned long code, void* info, unsigned long length)

{

switch (code)

{

case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:

WinHttpReceiveResponse(_request, NULL);

break;

case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE:

{

unsigned long status = 0;

unsigned long statusSize = sizeof(unsigned long);

std::string response;

WinHttpQueryHeaders(_request,

WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,

WINHTTP_HEADER_NAME_BY_INDEX,

&status,

&statusSize,

WINHTTP_NO_HEADER_INDEX);

_responseCode = status;

WinHttpQueryDataAvailable(_request, NULL);

break;

}

case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:

{

WinHttpReadData(_request, &_buffer, sizeof(_buffer), NULL);

break;

}

case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:

{

if (length != 0)

{

_response.append(_buffer, length);

WinHttpQueryDataAvailable(_request, NULL);

}

else

{

if (_callback != nullptr)

{

_callback(_responseCode, _response);

this->Close();

}

}

break;

}

case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:

if (_callback != nullptr)

{

_callback(code, _response.append("WINHTTP_CALLBACK_STATUS_REQUEST_ERROR"));

this->Close();

}

break;

}

}

void __stdcall HttpClient::Invoke(HINTERNET handle, DWORD_PTR context, DWORD status, void* info, DWORD infoLength)

{

auto client = reinterpret_cast<HttpClient*>(context);

if (client != NULL)

client->Invoke(status, info, infoLength);

}

NS_WEB_HTTP_END

반응형

'개발관련 > C&C++' 카테고리의 다른 글

반복자(iterator)가 포함된 Queue  (0) 2018.09.08
IOCP Socket Client 구현  (0) 2018.05.31
MSMQ(MS MessageQueue)  (0) 2018.05.15
Functional 이용한 델리게이트  (0) 2018.04.26
IOCP Socket Server 구현  (0) 2018.04.16