kuboon
1/22/2009 - 10:39 AM

GetProxyForUrl.cpp

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"";
}