GetProxyForUrl.cpp
/**
@file 現在のOSのProxy設定を読み取る
*/
#include "StdAfx.h"
#include <Winhttp.h>
#pragma comment(lib, "Winhttp.lib")
#include <shlwapi.h>
#pragma comment(lib, "shlwapi.lib")
/**
文字列を区切り文字列で分割する。
ex.
SplitString(L"ab<>cd<><>ef<>:", L"<>")
=> "ab", "cd" , "", "ef", ""
@return 分割された文字列。区切りは含まない。
*/
static std::vector<std::wstring> SplitString(
std::wstring input ///< 入力
,const std::wstring& delimiter ///< 区切り文字。空文字のときは空vectorが返る
){
std::vector<std::wstring> ret;
if(delimiter.empty())return ret;
input+=delimiter;
int end, begin=0;
while((end=input.find(delimiter, begin)) !=std::wstring::npos){
ret.push_back(input.substr(begin, end-begin));
begin=end+delimiter.length();
}
return ret;
}
/**
URLからHostNameを取得
ex.
HostNameOf(L"http://www.google.co.jp/hoge/path")
=> www.google.co.jp
*/
static std::wstring HostNameOf(const std::wstring& url){
URL_COMPONENTS urlComp={0};
urlComp.dwStructSize=sizeof(urlComp);
wchar_t hostName[256];
urlComp.lpszHostName=hostName;
urlComp.dwHostNameLength=255;
if(!::WinHttpCrackUrl(url.c_str(), url.length(), 0, &urlComp)){
return L"";
}
return hostName;
}
/**
WINHTTP_CURRENT_USER_IE_PROXY_CONFIG のリソースを自動解放するためのラッパー
*/
class auto_WINHTTP_CURRENT_USER_IE_PROXY_CONFIG:public WINHTTP_CURRENT_USER_IE_PROXY_CONFIG{
public:
auto_WINHTTP_CURRENT_USER_IE_PROXY_CONFIG(){
ZeroMemory(this, sizeof(this));
}
~auto_WINHTTP_CURRENT_USER_IE_PROXY_CONFIG(){
if (lpszAutoConfigUrl)
GlobalFree(lpszAutoConfigUrl);
if (lpszProxy)
GlobalFree(lpszProxy);
if (lpszProxyBypass)
GlobalFree(lpszProxyBypass);
}
};
/**
OSのプロキシ設定を元に、urlに対するプロキシホスト名を返す。
WPADによる自動設定、JavaScriptによる自動構成スクリプト、スキーム別固定設定、例外指定に対応。
スクリプトは本関数内部で呼び出される。
Url毎に異なる値が返されうるので、接続毎に呼び出すこと。
@return "hostname:port" 。直接接続もしくはエラー時は "" を返す。
*/
std::wstring GetProxyForUrl(
const std::wstring& url ///< http(s):// から始まるURL。パスを含んでいても良い。
){
auto_WINHTTP_CURRENT_USER_IE_PROXY_CONFIG iecfg;
if(!WinHttpGetIEProxyConfigForCurrentUser(&iecfg)) {
return L"";
}
if (iecfg.fAutoDetect || iecfg.lpszAutoConfigUrl) {
if (HINTERNET hWinHttp = WinHttpOpen(NULL,
WINHTTP_ACCESS_TYPE_NO_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS,
0)) {
WINHTTP_AUTOPROXY_OPTIONS options={0};
if (iecfg.fAutoDetect) {
options.dwFlags |= WINHTTP_AUTOPROXY_AUTO_DETECT;
options.dwAutoDetectFlags |= WINHTTP_AUTO_DETECT_TYPE_DHCP;
//| WINHTTP_AUTO_DETECT_TYPE_DNS_A;
}
if (iecfg.lpszAutoConfigUrl) {
options.dwFlags |= WINHTTP_AUTOPROXY_CONFIG_URL;
options.lpszAutoConfigUrl = iecfg.lpszAutoConfigUrl;
}
options.fAutoLogonIfChallenged = TRUE;
WINHTTP_PROXY_INFO info={0};
if(WinHttpGetProxyForUrl
(hWinHttp, url.c_str(), &options, &info)){
if(info.lpszProxy){
std::wstring ret=info.lpszProxy;
GlobalFree(info.lpszProxy);
return ret;
}
if (info.lpszProxyBypass)
GlobalFree(info.lpszProxyBypass);
}
WinHttpCloseHandle(hWinHttp);
}
}
if(!iecfg.lpszProxy){
return L"";
}
if(iecfg.lpszProxyBypass){
std::vector<std::wstring> sp=SplitString(iecfg.lpszProxyBypass, L";");
for(std::vector<std::wstring>::iterator it=sp.begin(); it!=sp.end(); ++it){
if(PathMatchSpecW(HostNameOf(url).c_str(),it->c_str()))
return L"";
}
}
if(wcschr(iecfg.lpszProxy, L'=')==NULL){
return iecfg.lpszProxy;
}
bool isHttps=(url.find(L"https:")==0); //begin with https
std::vector<std::wstring> sp=SplitString(iecfg.lpszProxy, L";");
for(std::vector<std::wstring>::iterator it=sp.begin(); it!=sp.end(); ++it){
std::vector<std::wstring> sp2=SplitString(*it, L"=");
if( (isHttps && sp2[0]==L"https")
||(!isHttps&& sp2[0]==L"http") ){
return sp2[1];
}
}
return L"";
}