2015-11-29 00:13:03 +01:00
/ *
_ _ _ ____ _ _____
/ \ _ __ ___ | | __ ( _ ) / ___ | | | _ ___ __ _ _ __ ___ | ___ | __ _ _ __ _ __ ___
/ _ \ | ' __ | / __ | | ' _ \ | | \ ___ \ | __ | / _ \ / _ ` | | ' _ ` _ \ | | _ / _ ` | | ' __ | | ' _ ` _ \
/ ___ \ | | | ( __ | | | | | | ___ ) | | | _ | __ / | ( _ | | | | | | | | | _ | | ( _ | | | | | | | | | |
/ _ / \ _ \ | _ | \ ___ | | _ | | _ | | _ | | ____ / \ __ | \ ___ | \ __ , _ | | _ | | _ | | _ | | _ | \ __ , _ | | _ | | _ | | _ | | _ |
2016-01-16 04:21:36 +01:00
Copyright 2015 - 2016 Ł ukasz "JustArchi" Domeradzki
2015-11-29 00:13:03 +01:00
Contact : JustArchi @JustArchi . net
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
using HtmlAgilityPack ;
2016-04-12 07:40:02 +02:00
using Newtonsoft.Json ;
2015-11-25 16:31:39 +01:00
using Newtonsoft.Json.Linq ;
using System ;
using System.Collections.Generic ;
2016-03-09 03:10:33 +01:00
using System.IO ;
2015-11-25 16:31:39 +01:00
using System.Net ;
using System.Net.Http ;
using System.Threading.Tasks ;
2016-03-15 04:20:28 +01:00
using System.Xml ;
2015-11-25 16:31:39 +01:00
namespace ArchiSteamFarm {
2016-04-12 16:58:45 +02:00
internal sealed class WebBrowser {
2016-01-14 20:37:01 +01:00
internal const byte MaxRetries = 5 ; // Defines maximum number of retries, UrlRequest() does not handle retry by itself (it's app responsibility)
2016-01-14 02:48:56 +01:00
2016-03-15 05:15:22 +01:00
private const byte MaxConnections = 10 ; // Defines maximum number of connections per ServicePoint. Be careful, as it also defines maximum number of sockets in CLOSE_WAIT state
private const byte MaxIdleTime = 15 ; // In seconds, how long socket is allowed to stay in CLOSE_WAIT state after there are no connections to it
2016-01-22 10:19:19 +01:00
private static readonly string DefaultUserAgent = "ArchiSteamFarm/" + Program . Version ;
2015-11-25 16:31:39 +01:00
2016-04-12 16:58:45 +02:00
internal readonly CookieContainer CookieContainer = new CookieContainer ( ) ;
2016-03-06 23:28:56 +01:00
2016-04-12 16:58:45 +02:00
private readonly HttpClient HttpClient ;
private readonly string Identifier ;
2015-12-01 01:34:05 +01:00
2016-04-12 16:58:45 +02:00
internal static void Init ( ) {
2016-01-14 20:37:01 +01:00
// Set max connection limit from default of 2 to desired value
ServicePointManager . DefaultConnectionLimit = MaxConnections ;
// Set max idle time from default of 100 seconds (100 * 1000) to desired value
ServicePointManager . MaxServicePointIdleTime = MaxIdleTime * 1000 ;
2015-12-13 15:25:00 +01:00
2016-01-14 20:37:01 +01:00
// Don't use Expect100Continue, we're sure about our POSTs, save some TCP packets
2015-12-13 15:25:00 +01:00
ServicePointManager . Expect100Continue = false ;
2015-11-25 16:31:39 +01:00
2016-04-12 07:40:02 +02:00
#if ! __MonoCS__
// Reuse ports if possible (since .NET 4.6+)
2016-02-22 18:34:45 +01:00
//ServicePointManager.ReusePort = true;
2016-04-12 07:40:02 +02:00
#endif
2015-11-25 16:31:39 +01:00
}
2016-04-12 16:58:45 +02:00
internal WebBrowser ( string identifier ) {
if ( string . IsNullOrEmpty ( identifier ) ) {
2016-04-12 19:12:45 +02:00
throw new ArgumentNullException ( "identifier" ) ;
2016-04-12 16:58:45 +02:00
}
Identifier = identifier ;
HttpClientHandler httpClientHandler = new HttpClientHandler {
AutomaticDecompression = DecompressionMethods . Deflate | DecompressionMethods . GZip ,
CookieContainer = CookieContainer
} ;
HttpClient = new HttpClient ( httpClientHandler ) {
Timeout = TimeSpan . FromSeconds ( Program . GlobalConfig . HttpTimeout )
} ;
// Most web services expect that UserAgent is set, so we declare it globally
HttpClient . DefaultRequestHeaders . UserAgent . ParseAdd ( DefaultUserAgent ) ;
}
2016-04-29 16:37:42 +02:00
internal async Task < bool > UrlHead ( string request , string referer = null ) {
2015-11-25 16:31:39 +01:00
if ( string . IsNullOrEmpty ( request ) ) {
2016-04-12 07:40:02 +02:00
return false ;
2015-11-25 16:31:39 +01:00
}
2016-04-29 16:37:42 +02:00
using ( HttpResponseMessage response = await UrlHeadToResponse ( request , referer ) . ConfigureAwait ( false ) ) {
2016-04-14 22:23:37 +02:00
return response ! = null ;
2016-04-12 07:40:02 +02:00
}
2015-11-25 16:31:39 +01:00
}
2016-04-29 16:37:42 +02:00
internal async Task < Uri > UrlHeadToUri ( string request , string referer = null ) {
2016-04-12 07:40:02 +02:00
if ( string . IsNullOrEmpty ( request ) ) {
2016-04-29 16:37:42 +02:00
return null ;
2016-04-12 07:40:02 +02:00
}
2016-04-29 16:37:42 +02:00
using ( HttpResponseMessage response = await UrlHeadToResponse ( request , referer ) . ConfigureAwait ( false ) ) {
if ( response = = null ) {
return null ;
}
return response . RequestMessage . RequestUri ;
2016-04-12 07:40:02 +02:00
}
}
2016-04-14 22:23:37 +02:00
internal async Task < string > UrlGetToContent ( string request , string referer = null ) {
2015-11-25 16:31:39 +01:00
if ( string . IsNullOrEmpty ( request ) ) {
return null ;
}
2016-04-14 22:23:37 +02:00
using ( HttpResponseMessage httpResponse = await UrlGetToResponse ( request , referer ) . ConfigureAwait ( false ) ) {
if ( httpResponse = = null ) {
return null ;
}
2015-11-25 16:31:39 +01:00
2016-04-14 22:23:37 +02:00
return await httpResponse . Content . ReadAsStringAsync ( ) . ConfigureAwait ( false ) ;
2015-11-25 16:31:39 +01:00
}
2016-04-12 07:40:02 +02:00
}
2016-04-14 22:23:37 +02:00
internal async Task < byte [ ] > UrlGetToBytes ( string request , string referer = null ) {
2016-04-12 07:40:02 +02:00
if ( string . IsNullOrEmpty ( request ) ) {
2015-11-25 16:31:39 +01:00
return null ;
}
2016-04-14 22:23:37 +02:00
using ( HttpResponseMessage httpResponse = await UrlGetToResponse ( request , referer ) . ConfigureAwait ( false ) ) {
if ( httpResponse = = null ) {
return null ;
}
2016-03-09 03:10:33 +01:00
2016-04-14 22:23:37 +02:00
return await httpResponse . Content . ReadAsByteArrayAsync ( ) . ConfigureAwait ( false ) ;
2016-03-09 03:10:33 +01:00
}
2015-11-25 16:31:39 +01:00
}
2016-04-12 16:58:45 +02:00
internal async Task < HtmlDocument > UrlGetToHtmlDocument ( string request , string referer = null ) {
2015-11-25 16:31:39 +01:00
if ( string . IsNullOrEmpty ( request ) ) {
return null ;
}
2016-04-12 16:58:45 +02:00
string content = await UrlGetToContent ( request , referer ) . ConfigureAwait ( false ) ;
2016-02-22 18:34:45 +01:00
if ( string . IsNullOrEmpty ( content ) ) {
2016-01-14 02:48:56 +01:00
return null ;
}
2016-02-22 18:34:45 +01:00
HtmlDocument htmlDocument = new HtmlDocument ( ) ;
2016-04-22 17:50:01 +02:00
htmlDocument . LoadHtml ( WebUtility . HtmlDecode ( content ) ) ;
2016-02-22 18:34:45 +01:00
return htmlDocument ;
2015-11-25 16:31:39 +01:00
}
2016-04-12 16:58:45 +02:00
internal async Task < JObject > UrlGetToJObject ( string request , string referer = null ) {
2015-11-25 16:31:39 +01:00
if ( string . IsNullOrEmpty ( request ) ) {
return null ;
}
2016-04-12 16:58:45 +02:00
string content = await UrlGetToContent ( request , referer ) . ConfigureAwait ( false ) ;
2016-01-14 02:48:56 +01:00
if ( string . IsNullOrEmpty ( content ) ) {
2015-11-25 16:31:39 +01:00
return null ;
}
2016-01-14 02:48:56 +01:00
JObject jObject ;
try {
jObject = JObject . Parse ( content ) ;
2016-04-12 07:40:02 +02:00
} catch ( JsonException e ) {
2016-04-12 17:09:57 +02:00
Logging . LogGenericException ( e , Identifier ) ;
2015-11-25 16:31:39 +01:00
return null ;
}
2016-01-14 02:48:56 +01:00
return jObject ;
2015-11-25 16:31:39 +01:00
}
2016-04-12 16:58:45 +02:00
internal async Task < XmlDocument > UrlGetToXML ( string request , string referer = null ) {
2016-03-15 04:20:28 +01:00
if ( string . IsNullOrEmpty ( request ) ) {
return null ;
}
2016-04-12 16:58:45 +02:00
string content = await UrlGetToContent ( request , referer ) . ConfigureAwait ( false ) ;
2016-03-15 04:20:28 +01:00
if ( string . IsNullOrEmpty ( content ) ) {
return null ;
}
XmlDocument xmlDocument = new XmlDocument ( ) ;
try {
xmlDocument . LoadXml ( content ) ;
} catch ( XmlException e ) {
2016-04-12 17:09:57 +02:00
Logging . LogGenericException ( e , Identifier ) ;
2016-03-15 04:20:28 +01:00
return null ;
}
return xmlDocument ;
}
2016-04-29 16:37:42 +02:00
internal async Task < bool > UrlPost ( string request , Dictionary < string , string > data = null , string referer = null ) {
if ( string . IsNullOrEmpty ( request ) ) {
return false ;
}
using ( HttpResponseMessage response = await UrlPostToResponse ( request , data , referer ) . ConfigureAwait ( false ) ) {
return response ! = null ;
}
}
2016-04-14 22:23:37 +02:00
private async Task < HttpResponseMessage > UrlGetToResponse ( string request , string referer = null ) {
if ( string . IsNullOrEmpty ( request ) ) {
return null ;
}
return await UrlRequest ( request , HttpMethod . Get , null , referer ) . ConfigureAwait ( false ) ;
}
2016-04-29 16:37:42 +02:00
private async Task < HttpResponseMessage > UrlHeadToResponse ( string request , string referer = null ) {
if ( string . IsNullOrEmpty ( request ) ) {
return null ;
}
return await UrlRequest ( request , HttpMethod . Head , null , referer ) . ConfigureAwait ( false ) ;
}
2016-04-14 22:23:37 +02:00
private async Task < HttpResponseMessage > UrlPostToResponse ( string request , Dictionary < string , string > data = null , string referer = null ) {
if ( string . IsNullOrEmpty ( request ) ) {
return null ;
}
return await UrlRequest ( request , HttpMethod . Post , data , referer ) . ConfigureAwait ( false ) ;
}
2016-04-12 16:58:45 +02:00
private async Task < HttpResponseMessage > UrlRequest ( string request , HttpMethod httpMethod , Dictionary < string , string > data = null , string referer = null ) {
2016-02-22 18:34:45 +01:00
if ( string . IsNullOrEmpty ( request ) | | httpMethod = = null ) {
2015-11-25 16:31:39 +01:00
return null ;
}
2016-04-21 14:48:24 +02:00
if ( request . StartsWith ( "https://" , StringComparison . Ordinal ) & & Program . GlobalConfig . ForceHttp ) {
2016-03-28 15:34:10 +02:00
return null ;
}
2016-02-22 18:34:45 +01:00
HttpResponseMessage responseMessage ;
using ( HttpRequestMessage requestMessage = new HttpRequestMessage ( httpMethod , request ) ) {
2016-03-09 03:10:33 +01:00
if ( data ! = null & & data . Count > 0 ) {
2016-02-22 18:34:45 +01:00
try {
requestMessage . Content = new FormUrlEncodedContent ( data ) ;
} catch ( UriFormatException e ) {
2016-04-12 17:09:57 +02:00
Logging . LogGenericException ( e , Identifier ) ;
2016-02-22 18:34:45 +01:00
return null ;
}
}
2015-11-25 16:31:39 +01:00
2016-03-09 03:10:33 +01:00
if ( ! string . IsNullOrEmpty ( referer ) ) {
2016-02-22 18:34:45 +01:00
requestMessage . Headers . Referrer = new Uri ( referer ) ;
}
2015-11-25 16:31:39 +01:00
2016-02-22 18:34:45 +01:00
try {
2016-04-12 16:58:45 +02:00
responseMessage = await HttpClient . SendAsync ( requestMessage ) . ConfigureAwait ( false ) ;
2016-02-22 18:34:45 +01:00
} catch { // Request failed, we don't need to know the exact reason, swallow exception
return null ;
}
2015-11-25 16:31:39 +01:00
}
2016-04-22 17:50:01 +02:00
if ( responseMessage = = null ) {
return null ;
}
if ( ! responseMessage . IsSuccessStatusCode ) {
2016-03-27 23:07:00 +02:00
if ( Debugging . IsDebugBuild | | Program . GlobalConfig . Debug ) {
2016-04-12 16:58:45 +02:00
Logging . LogGenericError ( "Request: " + request + " failed!" , Identifier ) ;
2016-04-22 17:50:01 +02:00
Logging . LogGenericError ( "Status code: " + responseMessage . StatusCode , Identifier ) ;
Logging . LogGenericError ( "Content: " + Environment . NewLine + await responseMessage . Content . ReadAsStringAsync ( ) . ConfigureAwait ( false ) , Identifier ) ;
2016-03-22 18:45:30 +01:00
}
2016-04-22 17:50:01 +02:00
responseMessage . Dispose ( ) ;
2015-11-25 16:31:39 +01:00
return null ;
}
2016-02-22 18:34:45 +01:00
return responseMessage ;
2015-11-25 16:31:39 +01:00
}
}
}