 
import inet.url 
namespace inet

var toUnicode = ..string.toUnicode

whttp = class{
		
    ctor( agent = "Mozilla/4.0", proxy,proxyBypass=""){
    	/* 
		HTTP=HTTP://proxyserver:port    
		FTP=FTP://proxyserver:port   
		GOPHER=HTTP://proxyserver:port   
		SOCKS=proxyserver:port 
		多个代理请以空格分开,空字符串表示使用默认代理设置，null表示不使用代理 
		*/  
	    if( #proxy ) 
	   		this.session = Open(toUnicode(agent),3/*WINHTTP_ACCESS_TYPE_NAMED_PROXY*/,toUnicode(proxy),toUnicode(proxyBypass),0); 
	    else
	    	this.session = Open(toUnicode(agent),(!proxy)? 0x1/*_WINHTTP_ACCESS_TYPE_NO_PROXY*/:0x0/*_WINHTTP_ACCESS_TYPE_DEFAULT_PROXY*/,null,null,0);
	   
	    if(!this.session)
			error("inet.whttp初始化失败",2) 
			
		//自动关闭资源
		this._gcdata = ..gcdata(
			_gc = function(){
				this.close();
			} 
		)
		
	}; 
	
	close = function(){  
	    if(this.session){
	    	CloseHandle( this.session ); 
	    	this.session = null; 
	    	this.endReuest();
	    }
	};
	
	endReuest =  function(){
		if(this.connect){
			CloseHandle(this.connect);
			this.connect = null;
		}
		if(this.request){
			CloseHandle(this.request); 
			this.request = null;
		}
	}
	
	beginRequest = function(url,method="GET",referer,accept,flags=0){
		if( ! (#url) )
	    	return null,"未指定网址"
	 
		//分析网址
		var tUrl = ..inet.url.split(url);  
		if(!tUrl)
			return null,"地址格式不正确"
		 
		this.connect = Connect(this.session,toUnicode(tUrl.host) ,tUrl.port,0)  
        if( ! this.connect ) 
        	return null,..lasterr()
	    	
	    //开启HTTPS支持					
	    if( tUrl.schemeNum == 0x4/*_INTERNET_SCHEME_HTTPS*/)
	    	flags |=  0x800000 /*_WINHTTP_FLAG_SECURE*/;

	    if(accept)
			this.accept = accept;

		if(referer)
			this.referer = referer;
			
	    this.request = OpenRequest(this.connect, toUnicode(method),toUnicode( ..string.concat(tUrl.path ,tUrl.extraInfo) ) ,null,toUnicode(this.referer),toUnicode(this.accept),flags   ) 
		if( ! this.request ){
			this.endReuest()
			return null,..lasterr();
		}
		this.referer = url;
		this.headers = this.addHeaders;
		return true;
	}
	send = function(postdata){
		if(!this.request)
			error("无效调用",2) 
			
		this.responseHeaders = null;
		var sendstat = SendRequest(this.request,toUnicode(this.headers),-1,postdata,#postdata,#postdata ,0);
	 	if( (!sendstat)
	 		|| (! ReceiveResponse(this.request))
	 		){ 
	 		this.endReuest()
			return null,..lasterr();
		}
		var ok,status  = QueryHeadersByInt( this.request,0x20000000 | 19 /*_HTTP_QUERY_FLAG_NUMBER | _HTTP_QUERY_STATUS_CODE*/,null,0,4,0)
    	if(!ok){
    		this.endReuest()
			return null,..lasterr();
		}
		return ok,status
	} 
	read = function(){
		var ok,str,size;
		 
        ok,size = QueryDataAvailable( this.request,0);
        if (!ok)   
			return null,..lasterr();
			
        if (!size)
            return null;
            
		ok,str,size = ReadData(this.request,size+1 ,size,0); 
		
		if(!ok)   
			return null,..lasterr(); 
        
		if ( size ) 
			return ..string.left(str,size),size; 
	}
	readHeader = function(name){
		if( ! this.responseHeaders){
			var ok,headers,len =QueryHeadersByString(this.request,0x16,null,0,0,0);  
      		ok,headers,len = QueryHeadersByString(this.request,0x16,null,len+1,len+1,0);
      		if(!ok)   
				return null,..lasterr(); 
			this.responseHeaders = ..string.fromUnicode( headers );
		}
		if(name)
			return ..string.match( this.responseHeaders ,'!\\w<@'+name+'@>\\:*\\s*([^\r\n]+)' )
		return this.responseHeaders
	}
	writeHeader = function(k,v){
		if(!v)
			this.headers = ..string.concat(this.headers,k);
		else{
			this.headers := "";
			this.headers = ..string.replace( this.headers,'!\\w<@'+k+'@>[^\r\n]+','');
			this.headers = ..string.concat( this.headers,#this.headers?'\r\n':"",k,":",v);
		}
	}
	
	post = function(url,postdata,headers,referer,accept ){
		this.headers = ..string.concat( this.headers,headers);  
		if( !(this.headers && ..string.find( this.headers,"@Content-Type:") ) ){   
		    if( postdata[1]=='{'# or  postdata[1]=='['#)  
		    	headers = '\r\nContent-Type:application/json; charset=utf-8';  
		    else 
		    	headers = '\r\nContent-Type:application/x-www-form-urlencoded';  
		}
		else 
			headers = null; 
		
		return this.down(url,postdata,headers ,referer,accept,"POST")
	}
	get = function(url,headers,referer,accept ){
		return this.down(url, ,headers,referer,accept,"GET")
	}
	down = function(url,postdata,headers,referer,accept,method){ 
	    var ok,err = this.beginRequest(url,method,referer,accept); 
	    if(!ok)
	    	return ok,err;
	    
	    this.headers = ..string.concat( this.headers,headers);
	    var ok,status = this.send( postdata );
	  	if(!ok)
    	   return ok,status;
      
    	select(status) {
    		case 200 /*HTTP_STATUS_DENIED*/ {
    			//ok
    		}
/*
    		case _HTTP_STATUS_PROXY_AUTH_REQ { 
        		// Insert code to safely determine cchUserLength and
        		// cchPasswordLength. Insert appropriate error handling code.
    			var varSet = InternetSetOptionPtr(hResourceHandle,_INTERNET_OPTION_PROXY_USERNAME,strUsername,#strUsername+1);
    			var varSet2 = InternetSetOptionPtr(hResourceHandle,_INTERNET_OPTION_PROXY_PASSWORD,strPassword,#strPassword+1);
    		}
    		case _HTTP_STATUS_DENIED {
				// Insert code to safely determine cchUserLength and 
				// cchPasswordLength. Insert error handling code as appropriate.
				InternetSetOption(hResourceHandle, _INTERNET_OPTION_USERNAME,strUsername, #strUsername+1);
				InternetSetOption(hResourceHandle, _INTERNET_OPTION_PASSWORD,strPassword, #strPassword+1);
 
    		} 
*/
    		else{ 
    			this.endReuest()
				return null,status;  
			}   	
    	} 
    
    	
		var ret;
    	if( this.onRecive ){
    		for(str,size in this.eachRead() ){
    			this.onRecive( str,size ) 
    		}
    	}
    	else{
    		var tbuffer = {}; 
		 	//开始下载数据
			for(str,size in this.eachRead() ){ 
				..table.push(tbuffer,str ) ; 
			}  
			ret =  ..string.join(tbuffer)
		}
		this.endReuest();  
		return ret;
	 
	};
	eachRead = function(){
		return this.read 
	} 
 }
		
namespace whttp{ 	
	dll = ..raw.loadDll("winhttp.dll") 
	
	CloseHandle = dll.api("WinHttpCloseHandle","int(POINTER innet)")
	Open = dll.api("WinHttpOpen","pointer(string agent, int dwAccessType,string proxy,str byPass,int flags)" )
	Connect = dll.api("WinHttpConnect","pointer(POINTER innet,str serverName,WORD port,INT reserved)")
	OpenRequest = dll.api("WinHttpOpenRequest","pointer(POINTER conn,string verb,string objectName,string version,string referrer,pointer&  acceptTypes,INT flags )")
	SendRequest = dll.api("WinHttpSendRequest","int(POINTER request,str headers,INT headersLength,pointer optional,INT optionalLength,INT totalLength,INT context)")
	ReceiveResponse= dll.api("WinHttpReceiveResponse","int(POINTER request,pointer reserved)" );
	
	QueryHeadersByString  = dll.api("WinHttpQueryHeaders","int(POINTER request, INT infoLevel,string name,string& buffer,INT &bufferLength, INT & index)")
	QueryHeadersByInt  = dll.api("WinHttpQueryHeaders","int(POINTER request, INT infoLevel,string name,int& buffer,INT &bufferLength, INT & index)")
	
	ReadData  = dll.api("WinHttpReadData","int(POINTER file, string &buffer, INT numberOfBytesToRead, INT &lpdwNumberOfBytesRead)")
	QueryDataAvailable  =  dll.api("WinHttpQueryDataAvailable","int(POINTER hFile,INT &size)")
	
	/*intellisense(whttp)*/
	SetOptionByPtr =  dll.api("WinHttpSetOption","bool(POINTER hInternet,INT option,pointer buffer,pointer length)")
	SetOption =  dll.api("WinHttpSetOption","bool(POINTER hInternet,INT option,struct buffer,INT length)")  
	QueryOption =  dll.api("WinHttpSetOption","bool(POINTER hInternet,INT option,struct &buffer,INT length)") 
	/*end intellisense*/ 
	
}


  

/**intellisense()
!whttp.accept = 指定可选择的文件类型\n默认无需指定,也可以在参数中指定
!whttp.referer = 引用页地址,此属性会自动设置为上次打开的网址\n建议在参数中指定
!whttp.addHeaders = 默认HTTP头
!whttp.headers = 直接设置HTTP头\n会覆盖addHeaders指定的默认头.
!whttp.readHeader() = 获取返回的HTTP头\n以回车换行'\r\n'分隔
!whttp.readHeader("Date") = 获取指定的HTTP头\n参数可以是头信息任意行的任意长度开始部分
!whttp.writeHeader("附加头__") = 以追加模式写入HTTP头
!whttp.writeHeader("名字__","值") = 写入指定HTTP头
!whttp.beginRequest(.(url,method,referer,accept,flags) = 除URL以外,所有参数可选\n参数(URL,"GET"或"POST",引用页,MIME,选项)
!whttp.endReuest() = 关闭请求,与beginRequest配对使用.
!whttp.send(__/*可选输入postdata*/) = 发送请求
!whttp.eachRead = @for(str,size in??.eachRead() ){ 
	table.push(__,str ) ; 
} 
!whttp.get(.(url,http头,引用网址,MIME) = 除URL外,其他为可选参数\n默认使用上一次访问的网址作为下一次的引用网址
!whttp.post(.(url,post数据,http头,引用网址,MIME) = 除URL与post数据以外,其他为可选参数\n默认使用上一次访问的网址作为下一次的引用网址
!whttp.close(.() =  释放资源\nwhttp对象支持自动析构,即使不调用此函数资源也会自动释放. 
inet.whttp(__) = inet.http("Mozilla/4.0", "HTTP://proxyserver:port" ) 
inet.whttp = winhttp支持库
?inet.whttp = !whttp.
end intellisense**/

 
/**intellisense()
_WINHTTP_FLAG_ESCAPE_PERCENT		= 0x4
_WINHTTP_FLAG_NULL_CODEPAGE			= 0x8
_WINHTTP_FLAG_ESCAPE_DISABLE		= 0x40
_WINHTTP_FLAG_ESCAPE_DISABLE_QUERY	= 0x80
_WINHTTP_FLAG_BYPASS_PROXY_CACHE	= 0x100
_WINHTTP_FLAG_REFRESH				= 0x100
_WINHTTP_FLAG_SECURE				= 0x800000
end intellisense**/
