#include "HttpClient.h" #include #include #include "curl/curl.h" #include "Base.h" /**************************************************************************/ #define USER_CURL 1 struct RequestSink { std::string* reqData; size_t sendSize; }; struct ResponseSink { std::string* respData; std::vector* respHeaders; }; static size_t onRequest(void *ptr, size_t size, size_t nmemb, void *stream) { RequestSink* reqSink = reinterpret_cast(stream); std::string* reqData = reqSink->reqData; size_t sendSize = reqSink->sendSize; size_t actualSize = reqData->size() - sendSize; if (actualSize > 4096) { actualSize = 4096; } memcpy_s(ptr, size * nmemb, &((*reqData)[sendSize]), actualSize); reqSink->sendSize += actualSize; return actualSize; } static size_t onResponse(void *ptr, size_t size, size_t nmemb, void *stream) { ResponseSink* respSink = reinterpret_cast(stream); std::string* respData = respSink->respData; respData->append((char*)ptr, size * nmemb); return (size * nmemb); } static size_t onHeaderCallback(char *buffer, size_t size, size_t nitems, void *userdata) { std::string str(buffer, size * nitems); size_t index = str.rfind('\r'); std::string header = str.substr(0, index); if (false == header.empty()) { ResponseSink* respSink = reinterpret_cast(userdata); respSink->respHeaders->push_back(header.substr(0, index)); } return (size * nitems); } HttpClient::HttpClient(const std::wstring& user_agent) : m_user_agent(user_agent) , m_hSession(NULL) , m_hConnect(NULL) , m_hRequest(NULL) , m_proxyIP("") , m_proxyPort(1080) { } HttpClient::~HttpClient() { http_close(); } size_t req_reply(void *ptr, size_t size, size_t nmemb, void *stream) { std::string *str = (std::string*)stream; (*str).append((char*)ptr, size * nmemb); return (size * nmemb); } void HttpClient::setProxy(const std::string& ip, unsigned short port) { m_proxyIP = ip; m_proxyPort = port; } DWORD HttpClient::http_get(const std::wstring& url , const std::vector& headers, std::string& resp_data) { #ifndef USER_CURL DWORD ret = request(url, L"GET", headers, std::string(), resp_data); http_close(); return ret; #else std::string url_temp = Wide2UTF8(url); CURL* curl = curl_easy_init(); CURLcode res = CURLE_OK; if (curl) { std::vector respHeaders; ResponseSink sink = { &resp_data, &respHeaders }; // set params curl_easy_setopt(curl, CURLOPT_URL, url_temp.c_str()); // url curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, onResponse); curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&sink); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, onHeaderCallback); curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *)&sink); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5000); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5000); if (false == m_proxyIP.empty()) { curl_easy_setopt(curl, CURLOPT_PROXY, m_proxyIP.c_str()); curl_easy_setopt(curl, CURLOPT_PROXYPORT, m_proxyPort); curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5_HOSTNAME); } struct curl_slist *headerlist = NULL; size_t headerlength = headers.size(); for (int i = 0; i < headerlength; i++) { headerlist = curl_slist_append(headerlist, Wide2UTF8(headers[i]).c_str()); } curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); // start req res = curl_easy_perform(curl); curl_slist_free_all(headerlist); } if (NULL != curl) { curl_easy_cleanup(curl); curl = NULL; } return res; #endif // !USER_CURL } DWORD HttpClient::http_post(const std::wstring& url , const std::vector& headers, const std::string& body, std::string& resp_data) { #ifndef USER_CURL DWORD ret = request(url, L"POST", headers, body, resp_data); http_close(); return ret; #else std::string url_temp = Wide2UTF8(url); CURL* curl = curl_easy_init(); CURLcode res = CURLE_OK; if (curl) { RequestSink reqSink = { const_cast(&body), 0 }; std::vector respHeaders; ResponseSink respSink = { &resp_data, &respHeaders }; // set params curl_easy_setopt(curl, CURLOPT_POST, 1); // post req curl_easy_setopt(curl, CURLOPT_URL, url_temp.c_str()); // url curl_easy_setopt(curl, CURLOPT_READFUNCTION, onRequest); // 调用重写的读文件流函数 curl_easy_setopt(curl, CURLOPT_READDATA, (void *)&reqSink); // 往read_file()函数中传入用户自定义的数据类型 curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (curl_off_t)body.size()); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, onResponse); curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&respSink); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, onHeaderCallback); curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *)&respSink); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5000); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5000); if (false == m_proxyIP.empty()) { curl_easy_setopt(curl, CURLOPT_PROXY, m_proxyIP.c_str()); curl_easy_setopt(curl, CURLOPT_PROXYPORT, m_proxyPort); curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5_HOSTNAME); } struct curl_slist *headerlist = NULL; size_t headerlength = headers.size(); for (int i = 0; i < headerlength; i++) { headerlist = curl_slist_append(headerlist, Wide2UTF8(headers[i]).c_str()); } curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); // start req res = curl_easy_perform(curl); curl_slist_free_all(headerlist); } if (NULL != curl) { curl_easy_cleanup(curl); curl = NULL; } return res; #endif } DWORD HttpClient::http_put(const std::wstring& url , const std::vector& headers, const std::string& body, std::string& resp_data) { DWORD ret = request(url, L"PUT", headers, body, resp_data); http_close(); return ret; } void HttpClient::http_close() { if (m_hRequest) { WinHttpCloseHandle(m_hRequest); m_hRequest = NULL; } if (m_hConnect) { WinHttpCloseHandle(m_hConnect); m_hConnect = NULL; } if (m_hSession) { WinHttpCloseHandle(m_hSession); m_hSession = NULL; } } DWORD HttpClient::request(const std::wstring& url, const std::wstring& method , const std::vector& headers, const std::string& body, std::string& resp_data) { assert(NULL == m_hSession && NULL == m_hConnect && NULL == m_hRequest); std::wstring host_name; std::wstring url_path; URL_COMPONENTS url_comp = { 0 }; url_comp.dwStructSize = sizeof(url_comp); host_name.resize(url.size()); url_path.resize(url.size()); url_comp.lpszHostName = const_cast(host_name.data()); url_comp.dwHostNameLength = host_name.size(); url_comp.lpszUrlPath = const_cast(url_path.data()); url_comp.dwUrlPathLength = url_path.size(); if (FALSE == ::WinHttpCrackUrl(url.c_str(), static_cast(url.size()), 0, &url_comp)) { return ::GetLastError(); } m_hSession = ::WinHttpOpen(m_user_agent.c_str() , WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); if (NULL == m_hSession) { return ::GetLastError(); } m_hConnect = ::WinHttpConnect(m_hSession, host_name.c_str(), url_comp.nPort, 0); if (NULL == m_hConnect) { return ::GetLastError(); } DWORD flags = (INTERNET_SCHEME_HTTP == url_comp.nScheme ? 0 : WINHTTP_FLAG_SECURE); m_hRequest = ::WinHttpOpenRequest(m_hConnect, method.c_str(), url_path.c_str(), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, flags); if (NULL == m_hRequest) { return ::GetLastError(); } for (std::vector::const_iterator it = headers.begin(); headers.end() != it; ++it) { ::WinHttpAddRequestHeaders(m_hRequest, it->c_str(), (ULONG)-1L, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_COALESCE); } if (0 == method.compare(L"GET")) { ::WinHttpSendRequest(m_hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0); } else if (0 == method.compare(L"POST")) { const void* body_data = reinterpret_cast(body.c_str()); ::WinHttpSendRequest(m_hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, const_cast(body_data), body.size(), body.size(), 0); } else if (0 == method.compare(L"PUT")) { const void* body_data = reinterpret_cast(body.c_str()); ::WinHttpSendRequest(m_hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, const_cast(body_data), body.size(), body.size(), 0); } if (ERROR_SUCCESS != ::GetLastError()) { return ::GetLastError(); } if (FALSE == ::WinHttpReceiveResponse(m_hRequest, NULL)) { return ::GetLastError(); } WCHAR status_code[16] = { 0 }; DWORD buffer_length = _countof(status_code); if (FALSE == ::WinHttpQueryHeaders(m_hRequest, WINHTTP_QUERY_STATUS_CODE , WINHTTP_HEADER_NAME_BY_INDEX, status_code, &buffer_length , WINHTTP_NO_HEADER_INDEX)) { return ::GetLastError(); } DWORD size = 0; while (TRUE == ::WinHttpQueryDataAvailable(m_hRequest, &size)) { if (0 == size) { break; } std::unique_ptr buffer(new char[size]); if (NULL == buffer.get()) { break; } DWORD lpdwNumberOfBytesRead = 0; if (TRUE == ::WinHttpReadData(m_hRequest, buffer.get(), size, &lpdwNumberOfBytesRead)) { resp_data.append(buffer.get(), static_cast(lpdwNumberOfBytesRead)); } else { return ::GetLastError(); } } const WCHAR ok_status_code[] = { L'2', L'0', L'0', L'\0' }; if (0 != ::_wcsicmp(ok_status_code, status_code)) { return EcHttpCodeError; } return ERROR_SUCCESS; }