2019-02-16 17:34:17 +01:00
// _ _ _ ____ _ _____
2017-11-18 17:27:06 +01:00
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
2019-01-14 19:11:17 +01:00
// |
2020-02-01 23:33:35 +01:00
// Copyright 2015-2020 Łukasz "JustArchi" Domeradzki
2018-07-27 04:52:14 +02:00
// Contact: JustArchi@JustArchi.net
2019-01-14 19:11:17 +01:00
// |
2018-07-27 04:52:14 +02:00
// 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
2019-01-14 19:11:17 +01:00
// |
2018-07-27 04:52:14 +02:00
// http://www.apache.org/licenses/LICENSE-2.0
2019-01-14 19:11:17 +01:00
// |
2018-07-27 04:52:14 +02:00
// 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.
2015-11-29 00:13:03 +01:00
2015-11-25 16:31:39 +01:00
using System ;
using System.Collections.Generic ;
2017-12-16 11:34:04 +01:00
using System.IO ;
2020-02-24 21:11:54 +01:00
using System.Linq ;
2015-11-25 16:31:39 +01:00
using System.Net ;
using System.Net.Http ;
2020-06-06 16:34:20 +02:00
using System.Text ;
2015-11-25 16:31:39 +01:00
using System.Threading.Tasks ;
2016-03-15 04:20:28 +01:00
using System.Xml ;
2020-04-02 18:01:55 +03:00
using AngleSharp ;
using AngleSharp.Dom ;
2017-01-06 15:32:12 +01:00
using ArchiSteamFarm.Localization ;
2018-09-08 01:03:55 +02:00
using ArchiSteamFarm.NLog ;
2019-01-10 22:33:07 +01:00
using JetBrains.Annotations ;
2016-11-24 07:32:16 +01:00
using Newtonsoft.Json ;
2015-11-25 16:31:39 +01:00
namespace ArchiSteamFarm {
2019-01-10 22:33:07 +01:00
public sealed class WebBrowser : IDisposable {
2019-07-05 11:39:19 +02:00
[PublicAPI]
public const byte MaxTries = 5 ; // Defines maximum number of recommended tries for a single request
2019-05-19 23:03:40 +02:00
internal const byte MaxConnections = 5 ; // Defines maximum number of connections per ServicePoint. Be careful, as it also defines maximum number of sockets in CLOSE_WAIT state
2016-01-14 02:48:56 +01:00
2017-07-09 09:09:46 +02:00
private const byte ExtendedTimeoutMultiplier = 10 ; // Defines multiplier of timeout for WebBrowsers dealing with huge data (ASF update)
private const byte MaxIdleTime = 15 ; // Defines in seconds, how long socket is allowed to stay in CLOSE_WAIT state after there are no connections to it
2016-03-15 05:15:22 +01:00
2019-07-05 11:39:19 +02:00
[PublicAPI]
public TimeSpan Timeout = > HttpClient . Timeout ;
2016-03-06 23:28:56 +01:00
2019-07-05 11:39:19 +02:00
internal readonly CookieContainer CookieContainer = new CookieContainer ( ) ;
2017-09-09 20:24:57 +02:00
2016-11-06 12:06:02 +01:00
private readonly ArchiLogger ArchiLogger ;
2016-04-12 16:58:45 +02:00
private readonly HttpClient HttpClient ;
2019-01-02 18:09:07 +01:00
private readonly HttpClientHandler HttpClientHandler ;
2015-12-01 01:34:05 +01:00
2020-08-22 21:41:01 +02:00
internal WebBrowser ( ArchiLogger archiLogger , IWebProxy ? webProxy = null , bool extendedTimeout = false ) {
2017-03-14 08:20:29 -03:00
ArchiLogger = archiLogger ? ? throw new ArgumentNullException ( nameof ( archiLogger ) ) ;
2016-11-24 07:32:16 +01:00
2019-01-02 18:09:07 +01:00
HttpClientHandler = new HttpClientHandler {
2018-09-08 00:46:40 +02:00
AllowAutoRedirect = false , // This must be false if we want to handle custom redirection schemes such as "steammobile://"
2020-04-17 23:44:36 +02:00
2020-08-22 21:41:01 +02:00
#if NETFRAMEWORK
2016-11-24 07:46:37 +01:00
AutomaticDecompression = DecompressionMethods . Deflate | DecompressionMethods . GZip ,
2020-08-22 21:41:01 +02:00
#else
AutomaticDecompression = DecompressionMethods . All ,
2020-04-17 23:44:36 +02:00
#endif
2019-01-02 18:09:07 +01:00
CookieContainer = CookieContainer
2016-11-24 07:46:37 +01:00
} ;
2019-01-02 18:09:07 +01:00
if ( webProxy ! = null ) {
HttpClientHandler . Proxy = webProxy ;
HttpClientHandler . UseProxy = true ;
}
2018-06-03 07:51:20 +02:00
if ( ! RuntimeCompatibility . IsRunningOnMono ) {
2019-01-02 18:09:07 +01:00
HttpClientHandler . MaxConnectionsPerServer = MaxConnections ;
2018-06-03 07:51:20 +02:00
}
2019-01-02 18:09:07 +01:00
HttpClient = GenerateDisposableHttpClient ( extendedTimeout ) ;
}
2016-11-24 07:32:16 +01:00
2019-01-02 18:09:07 +01:00
public void Dispose ( ) {
HttpClient . Dispose ( ) ;
HttpClientHandler . Dispose ( ) ;
2016-11-24 07:32:16 +01:00
}
2019-01-17 16:37:16 +01:00
[PublicAPI]
public HttpClient GenerateDisposableHttpClient ( bool extendedTimeout = false ) {
2020-08-22 21:41:01 +02:00
if ( ASF . GlobalConfig = = null ) {
throw new ArgumentNullException ( nameof ( ASF . GlobalConfig ) ) ;
}
2019-02-11 03:52:14 +01:00
HttpClient result = new HttpClient ( HttpClientHandler , false ) {
2019-09-27 20:43:11 +02:00
#if ! NETFRAMEWORK
2020-02-21 18:33:21 +03:00
DefaultRequestVersion = HttpVersion . Version20 ,
2019-09-27 20:43:11 +02:00
#endif
2019-01-17 16:37:16 +01:00
Timeout = TimeSpan . FromSeconds ( extendedTimeout ? ExtendedTimeoutMultiplier * ASF . GlobalConfig . ConnectionTimeout : ASF . GlobalConfig . ConnectionTimeout )
} ;
// Most web services expect that UserAgent is set, so we declare it globally
// If you by any chance came here with a very "clever" idea of hiding your ass by changing default ASF user-agent then here is a very good advice from me: don't, for your own safety - you've been warned
result . DefaultRequestHeaders . UserAgent . ParseAdd ( SharedInfo . PublicIdentifier + "/" + SharedInfo . Version + " (+" + SharedInfo . ProjectURL + ")" ) ;
return result ;
}
2019-01-10 22:33:07 +01:00
[PublicAPI]
2020-08-22 21:41:01 +02:00
public async Task < HtmlDocumentResponse ? > UrlGetToHtmlDocument ( string request , string? referer = null , ERequestOptions requestOptions = ERequestOptions . None , byte maxTries = MaxTries ) {
2018-03-09 23:48:47 +01:00
if ( string . IsNullOrEmpty ( request ) | | ( maxTries = = 0 ) ) {
2020-08-22 21:41:01 +02:00
throw new ArgumentNullException ( nameof ( request ) + " || " + nameof ( maxTries ) ) ;
2016-05-30 01:57:06 +02:00
}
2020-08-22 21:41:01 +02:00
HtmlDocumentResponse ? result = null ;
2018-12-15 00:27:15 +01:00
2020-05-27 14:45:27 +02:00
for ( byte i = 0 ; i < maxTries ; i + + ) {
2020-08-22 21:41:01 +02:00
await using StreamResponse ? response = await UrlGetToStream ( request , referer , requestOptions | ERequestOptions . ReturnClientErrors , 1 ) . ConfigureAwait ( false ) ;
2020-04-07 23:12:01 +03:00
2020-05-27 15:42:12 +03:00
if ( response ? . StatusCode . IsClientErrorCode ( ) = = true ) {
2020-04-07 23:12:01 +03:00
if ( requestOptions . HasFlag ( ERequestOptions . ReturnClientErrors ) ) {
result = new HtmlDocumentResponse ( response ) ;
}
break ;
}
2020-05-27 15:42:12 +03:00
if ( response ? . Content = = null ) {
2020-04-07 23:12:01 +03:00
continue ;
}
try {
result = await HtmlDocumentResponse . Create ( response ) . ConfigureAwait ( false ) ;
} catch ( Exception e ) {
ArchiLogger . LogGenericWarningException ( e ) ;
continue ;
}
return result ;
}
if ( maxTries > 1 ) {
ArchiLogger . LogGenericWarning ( string . Format ( Strings . ErrorRequestFailedTooManyTimes , maxTries ) ) ;
ArchiLogger . LogGenericDebug ( string . Format ( Strings . ErrorFailingRequest , request ) ) ;
}
return result ;
2016-05-30 01:57:06 +02:00
}
2019-01-10 22:33:07 +01:00
[PublicAPI]
2020-08-22 21:41:01 +02:00
public async Task < ObjectResponse < T > ? > UrlGetToJsonObject < T > ( string request , string? referer = null , ERequestOptions requestOptions = ERequestOptions . None , byte maxTries = MaxTries ) where T : class {
2018-03-09 23:48:47 +01:00
if ( string . IsNullOrEmpty ( request ) | | ( maxTries = = 0 ) ) {
2020-08-22 21:41:01 +02:00
throw new ArgumentNullException ( nameof ( request ) + " || " + nameof ( maxTries ) ) ;
2016-05-30 01:57:06 +02:00
}
2020-08-22 21:41:01 +02:00
ObjectResponse < T > ? result = null ;
2019-04-05 16:24:02 +02:00
2018-11-28 02:30:50 +01:00
for ( byte i = 0 ; i < maxTries ; i + + ) {
2020-08-22 21:41:01 +02:00
await using StreamResponse ? response = await UrlGetToStream ( request , referer , requestOptions | ERequestOptions . ReturnClientErrors , 1 ) . ConfigureAwait ( false ) ;
2018-11-28 02:30:50 +01:00
2020-05-27 15:42:12 +03:00
if ( response ? . StatusCode . IsClientErrorCode ( ) = = true ) {
2019-04-05 16:24:02 +02:00
if ( requestOptions . HasFlag ( ERequestOptions . ReturnClientErrors ) ) {
result = new ObjectResponse < T > ( response ) ;
}
break ;
}
2020-05-27 15:42:12 +03:00
if ( response ? . Content = = null ) {
2018-11-28 02:30:50 +01:00
continue ;
}
2016-05-30 01:57:06 +02:00
2020-08-22 21:41:01 +02:00
T ? obj ;
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
2018-11-28 02:30:50 +01:00
try {
2020-04-02 18:01:55 +03:00
using StreamReader streamReader = new StreamReader ( response . Content ) ;
using JsonReader jsonReader = new JsonTextReader ( streamReader ) ;
JsonSerializer serializer = new JsonSerializer ( ) ;
obj = serializer . Deserialize < T > ( jsonReader ) ;
2020-08-22 21:41:01 +02:00
if ( obj = = null ) {
ArchiLogger . LogGenericWarning ( string . Format ( Strings . ErrorIsEmpty , nameof ( obj ) ) ) ;
continue ;
}
2020-04-07 23:12:01 +03:00
} catch ( Exception e ) {
2018-11-28 02:30:50 +01:00
ArchiLogger . LogGenericWarningException ( e ) ;
2017-04-05 14:16:47 +02:00
2018-11-28 02:30:50 +01:00
continue ;
2017-04-05 14:16:47 +02:00
}
2018-11-28 02:30:50 +01:00
return new ObjectResponse < T > ( response , obj ) ;
2016-04-12 07:40:02 +02:00
}
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
2018-11-28 02:30:50 +01:00
if ( maxTries > 1 ) {
ArchiLogger . LogGenericWarning ( string . Format ( Strings . ErrorRequestFailedTooManyTimes , maxTries ) ) ;
ArchiLogger . LogGenericDebug ( string . Format ( Strings . ErrorFailingRequest , request ) ) ;
}
2019-04-05 16:24:02 +02:00
return result ;
2016-04-12 07:40:02 +02:00
}
2019-01-10 22:33:07 +01:00
[PublicAPI]
2020-08-22 21:41:01 +02:00
public async Task < XmlDocumentResponse ? > UrlGetToXmlDocument ( string request , string? referer = null , ERequestOptions requestOptions = ERequestOptions . None , byte maxTries = MaxTries ) {
2018-03-09 23:48:47 +01:00
if ( string . IsNullOrEmpty ( request ) | | ( maxTries = = 0 ) ) {
2020-08-22 21:41:01 +02:00
throw new ArgumentNullException ( nameof ( request ) + " || " + nameof ( maxTries ) ) ;
2016-05-30 01:57:06 +02:00
}
2020-08-22 21:41:01 +02:00
XmlDocumentResponse ? result = null ;
2019-04-05 16:24:02 +02:00
2018-11-28 02:30:50 +01:00
for ( byte i = 0 ; i < maxTries ; i + + ) {
2020-08-22 21:41:01 +02:00
await using StreamResponse ? response = await UrlGetToStream ( request , referer , requestOptions | ERequestOptions . ReturnClientErrors , 1 ) . ConfigureAwait ( false ) ;
2019-04-05 16:24:02 +02:00
2020-05-27 15:42:12 +03:00
if ( response ? . StatusCode . IsClientErrorCode ( ) = = true ) {
2019-04-05 16:24:02 +02:00
if ( requestOptions . HasFlag ( ERequestOptions . ReturnClientErrors ) ) {
result = new XmlDocumentResponse ( response ) ;
}
break ;
}
2016-11-24 07:32:16 +01:00
2020-05-27 15:42:12 +03:00
if ( response ? . Content = = null ) {
2018-11-28 02:30:50 +01:00
continue ;
}
2016-11-24 07:32:16 +01:00
2018-11-28 02:30:50 +01:00
XmlDocument xmlDocument = new XmlDocument ( ) ;
try {
2020-04-02 18:01:55 +03:00
xmlDocument . Load ( response . Content ) ;
2018-11-28 02:30:50 +01:00
} catch ( XmlException e ) {
ArchiLogger . LogGenericWarningException ( e ) ;
2018-12-15 00:27:15 +01:00
2018-11-28 02:30:50 +01:00
continue ;
}
return new XmlDocumentResponse ( response , xmlDocument ) ;
}
if ( maxTries > 1 ) {
ArchiLogger . LogGenericWarning ( string . Format ( Strings . ErrorRequestFailedTooManyTimes , maxTries ) ) ;
ArchiLogger . LogGenericDebug ( string . Format ( Strings . ErrorFailingRequest , request ) ) ;
2016-11-24 07:32:16 +01:00
}
2019-04-05 16:24:02 +02:00
return result ;
2016-11-24 07:32:16 +01:00
}
2019-01-10 22:33:07 +01:00
[PublicAPI]
2020-08-22 21:41:01 +02:00
public async Task < BasicResponse ? > UrlHead ( string request , string? referer = null , ERequestOptions requestOptions = ERequestOptions . None , byte maxTries = MaxTries ) {
2018-03-09 23:48:47 +01:00
if ( string . IsNullOrEmpty ( request ) | | ( maxTries = = 0 ) ) {
2020-08-22 21:41:01 +02:00
throw new ArgumentNullException ( nameof ( request ) + " || " + nameof ( maxTries ) ) ;
2016-11-24 07:32:16 +01:00
}
2020-08-22 21:41:01 +02:00
BasicResponse ? result = null ;
2019-04-05 16:24:02 +02:00
2018-03-09 23:48:47 +01:00
for ( byte i = 0 ; i < maxTries ; i + + ) {
2020-08-22 21:41:01 +02:00
using HttpResponseMessage ? response = await InternalHead ( request , referer ) . ConfigureAwait ( false ) ;
2016-11-24 07:32:16 +01:00
2019-09-27 20:43:11 +02:00
if ( response = = null ) {
continue ;
}
2019-04-05 16:24:02 +02:00
2019-09-27 20:43:11 +02:00
if ( response . StatusCode . IsClientErrorCode ( ) ) {
if ( requestOptions . HasFlag ( ERequestOptions . ReturnClientErrors ) ) {
result = new BasicResponse ( response ) ;
2019-04-05 16:24:02 +02:00
}
2019-09-27 20:43:11 +02:00
break ;
2018-03-09 23:48:47 +01:00
}
2019-09-27 20:43:11 +02:00
return new BasicResponse ( response ) ;
2016-12-04 01:07:37 +01:00
}
2018-03-10 02:33:50 +01:00
if ( maxTries > 1 ) {
ArchiLogger . LogGenericWarning ( string . Format ( Strings . ErrorRequestFailedTooManyTimes , maxTries ) ) ;
ArchiLogger . LogGenericDebug ( string . Format ( Strings . ErrorFailingRequest , request ) ) ;
}
2019-04-05 16:24:02 +02:00
return result ;
2016-12-04 01:07:37 +01:00
}
2019-01-10 22:33:07 +01:00
[PublicAPI]
2020-08-22 21:41:01 +02:00
public async Task < BasicResponse ? > UrlPost < T > ( string request , T ? data = null , string? referer = null , ERequestOptions requestOptions = ERequestOptions . None , byte maxTries = MaxTries ) where T : class {
2018-03-09 23:48:47 +01:00
if ( string . IsNullOrEmpty ( request ) | | ( maxTries = = 0 ) ) {
2020-08-22 21:41:01 +02:00
throw new ArgumentNullException ( nameof ( request ) + " || " + nameof ( maxTries ) ) ;
2016-05-30 01:57:06 +02:00
}
2020-08-22 21:41:01 +02:00
BasicResponse ? result = null ;
2019-04-05 16:24:02 +02:00
2018-03-09 23:48:47 +01:00
for ( byte i = 0 ; i < maxTries ; i + + ) {
2020-08-22 21:41:01 +02:00
using HttpResponseMessage ? response = await InternalPost ( request , data , referer ) . ConfigureAwait ( false ) ;
2016-05-30 01:57:06 +02:00
2019-09-27 20:43:11 +02:00
if ( response = = null ) {
continue ;
}
2019-04-05 16:24:02 +02:00
2019-09-27 20:43:11 +02:00
if ( response . StatusCode . IsClientErrorCode ( ) ) {
if ( requestOptions . HasFlag ( ERequestOptions . ReturnClientErrors ) ) {
result = new BasicResponse ( response ) ;
2019-04-05 16:24:02 +02:00
}
2019-09-27 20:43:11 +02:00
break ;
2018-03-09 23:48:47 +01:00
}
2019-09-27 20:43:11 +02:00
return new BasicResponse ( response ) ;
2016-05-30 01:57:06 +02:00
}
2018-03-10 02:33:50 +01:00
if ( maxTries > 1 ) {
ArchiLogger . LogGenericWarning ( string . Format ( Strings . ErrorRequestFailedTooManyTimes , maxTries ) ) ;
ArchiLogger . LogGenericDebug ( string . Format ( Strings . ErrorFailingRequest , request ) ) ;
}
2019-04-05 16:24:02 +02:00
return result ;
2016-05-30 01:57:06 +02:00
}
2019-01-10 22:33:07 +01:00
[PublicAPI]
2020-08-22 21:41:01 +02:00
public async Task < HtmlDocumentResponse ? > UrlPostToHtmlDocument < T > ( string request , T ? data = null , string? referer = null , ERequestOptions requestOptions = ERequestOptions . None , byte maxTries = MaxTries ) where T : class {
2018-03-09 23:48:47 +01:00
if ( string . IsNullOrEmpty ( request ) | | ( maxTries = = 0 ) ) {
2020-08-22 21:41:01 +02:00
throw new ArgumentNullException ( nameof ( request ) + " || " + nameof ( maxTries ) ) ;
2016-11-17 21:30:17 +01:00
}
2020-08-22 21:41:01 +02:00
HtmlDocumentResponse ? result = null ;
2020-04-07 23:12:01 +03:00
2020-05-27 14:45:27 +02:00
for ( byte i = 0 ; i < maxTries ; i + + ) {
2020-08-22 21:41:01 +02:00
await using StreamResponse ? response = await UrlPostToStream ( request , data , referer , requestOptions | ERequestOptions . ReturnClientErrors , 1 ) . ConfigureAwait ( false ) ;
2020-04-07 23:12:01 +03:00
2020-05-27 15:42:12 +03:00
if ( response ? . StatusCode . IsClientErrorCode ( ) = = true ) {
2020-04-07 23:12:01 +03:00
if ( requestOptions . HasFlag ( ERequestOptions . ReturnClientErrors ) ) {
result = new HtmlDocumentResponse ( response ) ;
}
break ;
}
2020-05-27 15:42:12 +03:00
if ( response ? . Content = = null ) {
2020-04-07 23:12:01 +03:00
continue ;
}
try {
result = await HtmlDocumentResponse . Create ( response ) . ConfigureAwait ( false ) ;
} catch ( Exception e ) {
ArchiLogger . LogGenericWarningException ( e ) ;
continue ;
}
return result ;
}
if ( maxTries > 1 ) {
ArchiLogger . LogGenericWarning ( string . Format ( Strings . ErrorRequestFailedTooManyTimes , maxTries ) ) ;
ArchiLogger . LogGenericDebug ( string . Format ( Strings . ErrorFailingRequest , request ) ) ;
}
2018-12-15 00:27:15 +01:00
2020-04-07 23:12:01 +03:00
return result ;
2016-11-17 21:30:17 +01:00
}
2019-01-10 22:33:07 +01:00
[PublicAPI]
2020-08-22 21:41:01 +02:00
public async Task < ObjectResponse < TResult > ? > UrlPostToJsonObject < TResult , TData > ( string request , TData ? data = null , string? referer = null , ERequestOptions requestOptions = ERequestOptions . None , byte maxTries = MaxTries ) where TResult : class where TData : class {
2018-03-09 23:48:47 +01:00
if ( string . IsNullOrEmpty ( request ) | | ( maxTries = = 0 ) ) {
2020-08-22 21:41:01 +02:00
throw new ArgumentNullException ( nameof ( request ) + " || " + nameof ( maxTries ) ) ;
2016-07-16 21:03:39 +02:00
}
2020-08-22 21:41:01 +02:00
ObjectResponse < TResult > ? result = null ;
2019-04-05 16:24:02 +02:00
2018-11-28 02:30:50 +01:00
for ( byte i = 0 ; i < maxTries ; i + + ) {
2020-08-22 21:41:01 +02:00
await using StreamResponse ? response = await UrlPostToStream ( request , data , referer , requestOptions | ERequestOptions . ReturnClientErrors , 1 ) . ConfigureAwait ( false ) ;
2019-04-05 16:24:02 +02:00
2020-05-27 15:42:12 +03:00
if ( response ? . StatusCode . IsClientErrorCode ( ) = = true ) {
2019-04-05 16:24:02 +02:00
if ( requestOptions . HasFlag ( ERequestOptions . ReturnClientErrors ) ) {
2020-06-06 16:34:20 +02:00
result = new ObjectResponse < TResult > ( response ) ;
2019-04-05 16:24:02 +02:00
}
break ;
}
2020-05-27 15:42:12 +03:00
if ( response ? . Content = = null ) {
2018-11-28 02:30:50 +01:00
continue ;
}
2020-08-22 21:41:01 +02:00
TResult ? obj ;
2018-11-28 02:30:50 +01:00
try {
2020-04-02 18:01:55 +03:00
using StreamReader steamReader = new StreamReader ( response . Content ) ;
using JsonReader jsonReader = new JsonTextReader ( steamReader ) ;
JsonSerializer serializer = new JsonSerializer ( ) ;
2020-06-06 16:34:20 +02:00
obj = serializer . Deserialize < TResult > ( jsonReader ) ;
2020-08-22 21:41:01 +02:00
if ( obj = = null ) {
ArchiLogger . LogGenericWarning ( string . Format ( Strings . ErrorIsEmpty , nameof ( obj ) ) ) ;
continue ;
}
2020-04-07 23:12:01 +03:00
} catch ( Exception e ) {
2018-11-28 02:30:50 +01:00
ArchiLogger . LogGenericWarningException ( e ) ;
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
2018-11-28 02:30:50 +01:00
continue ;
2017-04-05 14:16:47 +02:00
}
2020-06-06 16:34:20 +02:00
return new ObjectResponse < TResult > ( response , obj ) ;
2016-07-16 21:03:39 +02:00
}
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
2018-11-28 02:30:50 +01:00
if ( maxTries > 1 ) {
ArchiLogger . LogGenericWarning ( string . Format ( Strings . ErrorRequestFailedTooManyTimes , maxTries ) ) ;
ArchiLogger . LogGenericDebug ( string . Format ( Strings . ErrorFailingRequest , request ) ) ;
}
2019-04-05 16:24:02 +02:00
return result ;
2016-07-16 21:03:39 +02:00
}
2019-01-10 22:33:07 +01:00
internal static void Init ( ) {
// 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 ;
// Don't use Expect100Continue, we're sure about our POSTs, save some TCP packets
ServicePointManager . Expect100Continue = false ;
// Reuse ports if possible
if ( ! RuntimeCompatibility . IsRunningOnMono ) {
ServicePointManager . ReusePort = true ;
}
}
2020-08-23 14:07:22 +03:00
internal async Task < BinaryResponse ? > UrlGetToBinary ( string request , string? referer = null , ERequestOptions requestOptions = ERequestOptions . None , byte maxTries = MaxTries , IProgress < int > ? progressReporter = null ) {
2019-01-10 22:33:07 +01:00
if ( string . IsNullOrEmpty ( request ) | | ( maxTries = = 0 ) ) {
2020-08-22 21:41:01 +02:00
throw new ArgumentNullException ( nameof ( request ) + " || " + nameof ( maxTries ) ) ;
2019-01-10 22:33:07 +01:00
}
2020-08-22 21:41:01 +02:00
BinaryResponse ? result = null ;
2019-04-05 16:24:02 +02:00
2019-01-10 22:33:07 +01:00
for ( byte i = 0 ; i < maxTries ; i + + ) {
const byte printPercentage = 10 ;
const byte maxBatches = 99 / printPercentage ;
2020-08-22 21:41:01 +02:00
await using StreamResponse ? response = await UrlGetToStream ( request , referer , requestOptions | ERequestOptions . ReturnClientErrors , 1 ) . ConfigureAwait ( false ) ;
2019-01-10 22:33:07 +01:00
2020-05-27 15:42:12 +03:00
if ( response ? . StatusCode . IsClientErrorCode ( ) = = true ) {
2019-09-27 20:43:11 +02:00
if ( requestOptions . HasFlag ( ERequestOptions . ReturnClientErrors ) ) {
result = new BinaryResponse ( response ) ;
2019-04-05 16:24:02 +02:00
}
2019-09-27 20:43:11 +02:00
break ;
}
2019-01-10 22:33:07 +01:00
2020-05-27 15:42:12 +03:00
if ( response ? . Content = = null ) {
continue ;
}
2020-08-23 14:07:22 +03:00
progressReporter ? . Report ( 0 ) ;
2020-08-22 21:41:01 +02:00
#if NETFRAMEWORK
using MemoryStream ms = new MemoryStream ( ( int ) response . Length ) ;
#else
await using MemoryStream ms = new MemoryStream ( ( int ) response . Length ) ;
2020-04-18 18:33:27 +02:00
#endif
2019-01-10 22:33:07 +01:00
2019-09-27 20:43:11 +02:00
try {
byte batch = 0 ;
uint readThisBatch = 0 ;
byte [ ] buffer = new byte [ 8192 ] ; // This is HttpClient's buffer, using more doesn't make sense
2019-01-10 22:33:07 +01:00
2020-04-18 18:35:37 +02:00
while ( response . Content . CanRead ) {
int read = await response . Content . ReadAsync ( buffer , 0 , buffer . Length ) . ConfigureAwait ( false ) ;
2019-01-10 22:33:07 +01:00
2019-09-27 20:43:11 +02:00
if ( read = = 0 ) {
break ;
}
2019-01-10 22:33:07 +01:00
2019-09-27 20:43:11 +02:00
await ms . WriteAsync ( buffer , 0 , read ) . ConfigureAwait ( false ) ;
2019-01-10 22:33:07 +01:00
2020-04-02 18:01:55 +03:00
if ( ( response . Length = = 0 ) | | ( batch > = maxBatches ) ) {
2019-09-27 20:43:11 +02:00
continue ;
2019-01-10 22:33:07 +01:00
}
2019-09-27 20:43:11 +02:00
readThisBatch + = ( uint ) read ;
2020-04-02 18:01:55 +03:00
if ( readThisBatch < response . Length / printPercentage ) {
2019-09-27 20:43:11 +02:00
continue ;
}
2019-01-10 22:33:07 +01:00
2020-04-02 18:01:55 +03:00
readThisBatch - = response . Length / printPercentage ;
2020-08-23 14:07:22 +03:00
progressReporter ? . Report ( + + batch * printPercentage ) ;
2019-01-10 22:33:07 +01:00
}
2019-09-27 20:43:11 +02:00
} catch ( Exception e ) {
ArchiLogger . LogGenericDebuggingException ( e ) ;
return null ;
2019-01-10 22:33:07 +01:00
}
2019-09-27 20:43:11 +02:00
2020-08-23 14:07:22 +03:00
progressReporter ? . Report ( 100 ) ;
2019-09-27 20:43:11 +02:00
return new BinaryResponse ( response , ms . ToArray ( ) ) ;
2019-01-10 22:33:07 +01:00
}
if ( maxTries > 1 ) {
ArchiLogger . LogGenericWarning ( string . Format ( Strings . ErrorRequestFailedTooManyTimes , maxTries ) ) ;
ArchiLogger . LogGenericDebug ( string . Format ( Strings . ErrorFailingRequest , request ) ) ;
}
2019-04-05 16:24:02 +02:00
return result ;
2019-01-10 22:33:07 +01:00
}
2020-08-22 21:41:01 +02:00
internal async Task < StringResponse ? > UrlGetToString ( string request , string? referer = null , ERequestOptions requestOptions = ERequestOptions . None , byte maxTries = MaxTries ) {
2019-01-10 22:33:07 +01:00
if ( string . IsNullOrEmpty ( request ) | | ( maxTries = = 0 ) ) {
2020-08-22 21:41:01 +02:00
throw new ArgumentNullException ( nameof ( request ) + " || " + nameof ( maxTries ) ) ;
2019-01-10 22:33:07 +01:00
}
2020-08-22 21:41:01 +02:00
StringResponse ? result = null ;
2019-04-05 16:24:02 +02:00
2019-01-10 22:33:07 +01:00
for ( byte i = 0 ; i < maxTries ; i + + ) {
2020-08-22 21:41:01 +02:00
using HttpResponseMessage ? response = await InternalGet ( request , referer ) . ConfigureAwait ( false ) ;
2019-01-10 22:33:07 +01:00
2020-05-27 15:42:12 +03:00
if ( response ? . StatusCode . IsClientErrorCode ( ) = = true ) {
2019-09-27 20:43:11 +02:00
if ( requestOptions . HasFlag ( ERequestOptions . ReturnClientErrors ) ) {
result = new StringResponse ( response ) ;
2019-04-05 16:24:02 +02:00
}
2019-09-27 20:43:11 +02:00
break ;
2019-01-10 22:33:07 +01:00
}
2019-09-27 20:43:11 +02:00
2020-05-27 15:42:12 +03:00
if ( response ? . Content = = null ) {
continue ;
}
2019-09-27 20:43:11 +02:00
return new StringResponse ( response , await response . Content . ReadAsStringAsync ( ) . ConfigureAwait ( false ) ) ;
2019-01-10 22:33:07 +01:00
}
if ( maxTries > 1 ) {
ArchiLogger . LogGenericWarning ( string . Format ( Strings . ErrorRequestFailedTooManyTimes , maxTries ) ) ;
ArchiLogger . LogGenericDebug ( string . Format ( Strings . ErrorFailingRequest , request ) ) ;
}
2019-04-05 16:24:02 +02:00
return result ;
2019-01-10 22:33:07 +01:00
}
2020-08-22 21:41:01 +02:00
private async Task < HttpResponseMessage ? > InternalGet ( string request , string? referer = null , HttpCompletionOption httpCompletionOption = HttpCompletionOption . ResponseContentRead ) {
2016-03-15 04:20:28 +01:00
if ( string . IsNullOrEmpty ( request ) ) {
2020-08-22 21:41:01 +02:00
throw new ArgumentNullException ( nameof ( request ) ) ;
2016-03-15 04:20:28 +01:00
}
2020-06-06 16:34:20 +02:00
return await InternalRequest < object > ( new Uri ( request ) , HttpMethod . Get , null , referer , httpCompletionOption ) . ConfigureAwait ( false ) ;
2016-03-15 04:20:28 +01:00
}
2020-08-22 21:41:01 +02:00
private async Task < HttpResponseMessage ? > InternalHead ( string request , string? referer = null ) {
2016-04-29 16:37:42 +02:00
if ( string . IsNullOrEmpty ( request ) ) {
2020-08-22 21:41:01 +02:00
throw new ArgumentNullException ( nameof ( request ) ) ;
2016-05-30 01:57:06 +02:00
}
2020-06-06 16:34:20 +02:00
return await InternalRequest < object > ( new Uri ( request ) , HttpMethod . Head , null , referer ) . ConfigureAwait ( false ) ;
2016-05-30 01:57:06 +02:00
}
2020-08-22 21:41:01 +02:00
private async Task < HttpResponseMessage ? > InternalPost < T > ( string request , T ? data = null , string? referer = null , HttpCompletionOption httpCompletionOption = HttpCompletionOption . ResponseContentRead ) where T : class {
2016-04-14 22:23:37 +02:00
if ( string . IsNullOrEmpty ( request ) ) {
2020-08-22 21:41:01 +02:00
throw new ArgumentNullException ( nameof ( request ) ) ;
2016-04-14 22:23:37 +02:00
}
2020-04-02 18:01:55 +03:00
return await InternalRequest ( new Uri ( request ) , HttpMethod . Post , data , referer , httpCompletionOption ) . ConfigureAwait ( false ) ;
2016-04-14 22:23:37 +02:00
}
2020-08-22 21:41:01 +02:00
private async Task < HttpResponseMessage ? > InternalRequest < T > ( Uri requestUri , HttpMethod httpMethod , T ? data = null , string? referer = null , HttpCompletionOption httpCompletionOption = HttpCompletionOption . ResponseContentRead , byte maxRedirections = MaxTries ) where T : class {
2017-06-26 04:02:11 +02:00
if ( ( requestUri = = null ) | | ( httpMethod = = null ) ) {
2020-08-22 21:41:01 +02:00
throw new ArgumentNullException ( nameof ( requestUri ) + " || " + nameof ( httpMethod ) ) ;
2015-11-25 16:31:39 +01:00
}
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
HttpResponseMessage response ;
using ( HttpRequestMessage request = new HttpRequestMessage ( httpMethod , requestUri ) ) {
2020-02-21 17:07:05 +01:00
#if ! NETFRAMEWORK
2020-02-21 17:10:42 +01:00
request . Version = HttpClient . DefaultRequestVersion ;
2020-02-21 17:07:05 +01:00
#endif
2016-07-16 21:03:39 +02:00
if ( data ! = null ) {
2020-06-06 16:34:20 +02:00
switch ( data ) {
2020-06-07 18:09:01 +03:00
case HttpContent content :
request . Content = content ;
break ;
2020-06-06 16:34:20 +02:00
case IReadOnlyCollection < KeyValuePair < string , string > > dictionary :
try {
request . Content = new FormUrlEncodedContent ( dictionary ) ;
} catch ( UriFormatException ) {
request . Content = new StringContent ( string . Join ( "&" , dictionary . Select ( kv = > WebUtility . UrlEncode ( kv . Key ) + "=" + WebUtility . UrlEncode ( kv . Value ) ) ) , null , "application/x-www-form-urlencoded" ) ;
}
break ;
case string text :
request . Content = new StringContent ( text ) ;
break ;
default :
request . Content = new StringContent ( JsonConvert . SerializeObject ( data ) , Encoding . UTF8 , "application/json" ) ;
break ;
2016-02-22 18:34:45 +01:00
}
}
2015-11-25 16:31:39 +01:00
2016-03-09 03:10:33 +01:00
if ( ! string . IsNullOrEmpty ( referer ) ) {
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
request . Headers . Referrer = new Uri ( referer ) ;
2016-02-22 18:34:45 +01:00
}
2015-11-25 16:31:39 +01:00
2018-04-11 18:32:45 +02:00
if ( Debugging . IsUserDebugging ) {
ArchiLogger . LogGenericDebug ( httpMethod + " " + requestUri ) ;
}
2016-02-22 18:34:45 +01:00
try {
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
response = await HttpClient . SendAsync ( request , httpCompletionOption ) . ConfigureAwait ( false ) ;
2016-06-11 03:39:25 +02:00
} catch ( Exception e ) {
2017-11-26 19:08:48 +01:00
ArchiLogger . LogGenericDebuggingException ( e ) ;
2018-12-15 00:27:15 +01:00
2016-02-22 18:34:45 +01:00
return null ;
}
2015-11-25 16:31:39 +01:00
}
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
if ( response = = null ) {
2018-04-11 22:23:31 +02:00
if ( Debugging . IsUserDebugging ) {
ArchiLogger . LogGenericDebug ( "null <- " + httpMethod + " " + requestUri ) ;
}
2016-04-22 17:50:01 +02:00
return null ;
}
2018-04-11 22:23:31 +02:00
if ( Debugging . IsUserDebugging ) {
ArchiLogger . LogGenericDebug ( response . StatusCode + " <- " + httpMethod + " " + requestUri ) ;
}
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
if ( response . IsSuccessStatusCode ) {
return response ;
2016-05-13 06:32:42 +02:00
}
2018-09-08 00:46:40 +02:00
// WARNING: We still have not disposed response by now, make sure to dispose it ASAP if we're not returning it!
2018-02-17 04:07:26 +01:00
if ( ( response . StatusCode > = HttpStatusCode . Ambiguous ) & & ( response . StatusCode < HttpStatusCode . BadRequest ) & & ( maxRedirections > 0 ) ) {
2018-02-17 03:57:09 +01:00
Uri redirectUri = response . Headers . Location ;
if ( redirectUri . IsAbsoluteUri ) {
switch ( redirectUri . Scheme ) {
case "http" :
case "https" :
break ;
case "steammobile" :
// Those redirections are invalid, but we're aware of that and we have extra logic for them
return response ;
default :
// We have no clue about those, but maybe HttpClient can handle them for us
2020-08-22 21:41:01 +02:00
ArchiLogger . LogGenericError ( string . Format ( Strings . WarningUnknownValuePleaseReport , nameof ( redirectUri . Scheme ) , redirectUri . Scheme ) ) ;
2018-12-15 00:27:15 +01:00
2018-02-17 03:57:09 +01:00
break ;
2017-06-26 04:02:11 +02:00
}
} else {
2019-01-07 19:01:09 +01:00
redirectUri = new Uri ( requestUri , redirectUri ) ;
}
2020-07-23 21:36:05 +02:00
switch ( response . StatusCode ) {
case HttpStatusCode . SeeOther :
// Per https://tools.ietf.org/html/rfc7231#section-6.4.4, a 303 redirect should be performed with a GET request
httpMethod = HttpMethod . Get ;
// Data doesn't make any sense for a GET request, clear it
data = null ;
break ;
}
2019-06-19 16:42:04 +02:00
response . Dispose ( ) ;
2019-01-07 19:01:09 +01:00
// Per https://tools.ietf.org/html/rfc7231#section-7.1.2, a redirect location without a fragment should inherit the fragment from the original URI
if ( ! string . IsNullOrEmpty ( requestUri . Fragment ) & & string . IsNullOrEmpty ( redirectUri . Fragment ) ) {
redirectUri = new UriBuilder ( redirectUri ) { Fragment = requestUri . Fragment } . Uri ;
2018-02-17 03:57:09 +01:00
}
2017-06-26 04:02:11 +02:00
2018-03-09 23:48:47 +01:00
return await InternalRequest ( redirectUri , httpMethod , data , referer , httpCompletionOption , - - maxRedirections ) . ConfigureAwait ( false ) ;
2018-02-17 03:57:09 +01:00
}
2020-07-03 18:54:13 +02:00
if ( ! Debugging . IsUserDebugging ) {
ArchiLogger . LogGenericDebug ( response . StatusCode + " <- " + httpMethod + " " + requestUri ) ;
}
2019-04-05 16:44:31 +02:00
if ( response . StatusCode . IsClientErrorCode ( ) ) {
2019-06-19 14:14:35 +02:00
if ( Debugging . IsUserDebugging ) {
ArchiLogger . LogGenericDebug ( string . Format ( Strings . Content , await response . Content . ReadAsStringAsync ( ) . ConfigureAwait ( false ) ) ) ;
}
2019-04-05 16:24:02 +02:00
// Do not retry on client errors
return response ;
}
2018-04-13 09:17:27 +02:00
using ( response ) {
if ( Debugging . IsUserDebugging ) {
ArchiLogger . LogGenericDebug ( string . Format ( Strings . Content , await response . Content . ReadAsStringAsync ( ) . ConfigureAwait ( false ) ) ) ;
}
return null ;
}
2015-11-25 16:31:39 +01:00
}
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
2020-08-22 21:41:01 +02:00
private async Task < StreamResponse ? > UrlGetToStream ( string request , string? referer = null , ERequestOptions requestOptions = ERequestOptions . None , byte maxTries = MaxTries ) {
2018-03-09 23:48:47 +01:00
if ( string . IsNullOrEmpty ( request ) | | ( maxTries = = 0 ) ) {
2020-08-22 21:41:01 +02:00
throw new ArgumentNullException ( nameof ( request ) + " || " + nameof ( maxTries ) ) ;
2018-03-09 23:48:47 +01:00
}
2020-08-22 21:41:01 +02:00
StreamResponse ? result = null ;
2019-04-05 16:24:02 +02:00
2018-03-09 23:48:47 +01:00
for ( byte i = 0 ; i < maxTries ; i + + ) {
2020-08-22 21:41:01 +02:00
HttpResponseMessage ? response = await InternalGet ( request , referer , HttpCompletionOption . ResponseHeadersRead ) . ConfigureAwait ( false ) ;
2018-03-09 23:48:47 +01:00
2020-05-27 15:42:12 +03:00
if ( response ? . StatusCode . IsClientErrorCode ( ) = = true ) {
2019-09-27 20:43:11 +02:00
if ( requestOptions . HasFlag ( ERequestOptions . ReturnClientErrors ) ) {
2020-04-02 18:01:55 +03:00
result = new StreamResponse ( response ) ;
2019-04-05 16:24:02 +02:00
}
2019-09-27 20:43:11 +02:00
break ;
2018-03-09 23:48:47 +01:00
}
2019-09-27 20:43:11 +02:00
2020-05-27 15:42:12 +03:00
if ( response ? . Content = = null ) {
continue ;
}
2020-04-02 18:01:55 +03:00
return new StreamResponse ( response , await response . Content . ReadAsStreamAsync ( ) . ConfigureAwait ( false ) ) ;
}
if ( maxTries > 1 ) {
ArchiLogger . LogGenericWarning ( string . Format ( Strings . ErrorRequestFailedTooManyTimes , maxTries ) ) ;
ArchiLogger . LogGenericDebug ( string . Format ( Strings . ErrorFailingRequest , request ) ) ;
}
return result ;
}
2020-08-22 21:41:01 +02:00
private async Task < StreamResponse ? > UrlPostToStream < T > ( string request , T ? data = null , string? referer = null , ERequestOptions requestOptions = ERequestOptions . None , byte maxTries = MaxTries ) where T : class {
2020-04-02 18:01:55 +03:00
if ( string . IsNullOrEmpty ( request ) | | ( maxTries = = 0 ) ) {
2020-08-22 21:41:01 +02:00
throw new ArgumentNullException ( nameof ( request ) + " || " + nameof ( maxTries ) ) ;
2020-04-02 18:01:55 +03:00
}
2020-08-22 21:41:01 +02:00
StreamResponse ? result = null ;
2020-04-02 18:01:55 +03:00
for ( byte i = 0 ; i < maxTries ; i + + ) {
2020-08-22 21:41:01 +02:00
HttpResponseMessage ? response = await InternalPost ( request , data , referer , HttpCompletionOption . ResponseHeadersRead ) . ConfigureAwait ( false ) ;
2020-04-02 18:01:55 +03:00
2020-05-27 15:42:12 +03:00
if ( response ? . StatusCode . IsClientErrorCode ( ) = = true ) {
2020-04-02 18:01:55 +03:00
if ( requestOptions . HasFlag ( ERequestOptions . ReturnClientErrors ) ) {
result = new StreamResponse ( response ) ;
}
break ;
}
2020-05-27 15:42:12 +03:00
if ( response ? . Content = = null ) {
continue ;
}
2020-04-02 18:01:55 +03:00
return new StreamResponse ( response , await response . Content . ReadAsStreamAsync ( ) . ConfigureAwait ( false ) ) ;
2018-03-09 23:48:47 +01:00
}
2018-03-10 02:33:50 +01:00
if ( maxTries > 1 ) {
ArchiLogger . LogGenericWarning ( string . Format ( Strings . ErrorRequestFailedTooManyTimes , maxTries ) ) ;
ArchiLogger . LogGenericDebug ( string . Format ( Strings . ErrorFailingRequest , request ) ) ;
}
2019-04-05 16:24:02 +02:00
return result ;
2018-03-09 23:48:47 +01:00
}
2019-01-10 22:33:07 +01:00
public class BasicResponse {
2019-04-05 16:24:02 +02:00
[PublicAPI]
public readonly HttpStatusCode StatusCode ;
2018-02-17 03:57:09 +01:00
internal readonly Uri FinalUri ;
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
2020-08-22 21:41:01 +02:00
internal BasicResponse ( HttpResponseMessage httpResponseMessage ) {
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
if ( httpResponseMessage = = null ) {
throw new ArgumentNullException ( nameof ( httpResponseMessage ) ) ;
}
2018-02-17 03:57:09 +01:00
FinalUri = httpResponseMessage . Headers . Location ? ? httpResponseMessage . RequestMessage . RequestUri ;
2019-04-05 16:24:02 +02:00
StatusCode = httpResponseMessage . StatusCode ;
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
}
2020-08-22 21:41:01 +02:00
internal BasicResponse ( BasicResponse basicResponse ) {
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
if ( basicResponse = = null ) {
throw new ArgumentNullException ( nameof ( basicResponse ) ) ;
}
2018-02-17 03:57:09 +01:00
FinalUri = basicResponse . FinalUri ;
2019-04-05 16:24:02 +02:00
StatusCode = basicResponse . StatusCode ;
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
}
}
2020-04-18 15:22:59 +02:00
public sealed class HtmlDocumentResponse : BasicResponse , IDisposable {
2019-01-10 22:33:07 +01:00
[PublicAPI]
2020-08-22 21:41:01 +02:00
public readonly IDocument ? Content ;
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
2020-08-22 21:41:01 +02:00
internal HtmlDocumentResponse ( BasicResponse basicResponse ) : base ( basicResponse ) {
2020-04-08 12:50:58 +02:00
if ( basicResponse = = null ) {
throw new ArgumentNullException ( nameof ( basicResponse ) ) ;
}
}
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
2020-08-22 21:41:01 +02:00
private HtmlDocumentResponse ( StreamResponse streamResponse , IDocument document ) : this ( streamResponse ) {
2020-04-07 23:12:01 +03:00
if ( ( streamResponse = = null ) | | ( document = = null ) ) {
throw new ArgumentNullException ( nameof ( streamResponse ) + " || " + nameof ( document ) ) ;
}
Content = document ;
}
2020-04-18 15:22:59 +02:00
public void Dispose ( ) = > Content ? . Dispose ( ) ;
2020-08-22 21:41:01 +02:00
internal static async Task < HtmlDocumentResponse ? > Create ( StreamResponse streamResponse ) {
2020-04-02 18:01:55 +03:00
if ( streamResponse = = null ) {
throw new ArgumentNullException ( nameof ( streamResponse ) ) ;
2019-04-10 18:35:21 +02:00
}
2020-04-02 18:01:55 +03:00
2020-04-18 00:34:10 +02:00
IBrowsingContext context = BrowsingContext . New ( ) ;
2020-04-02 18:01:55 +03:00
2020-04-07 23:12:01 +03:00
try {
2020-04-18 00:35:45 +02:00
IDocument document = await context . OpenAsync ( req = > req . Content ( streamResponse . Content , true ) ) . ConfigureAwait ( false ) ;
2020-04-07 23:12:01 +03:00
return new HtmlDocumentResponse ( streamResponse , document ) ;
} catch ( Exception e ) {
ASF . ArchiLogger . LogGenericWarningException ( e ) ;
return null ;
}
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
}
}
2020-08-22 21:41:01 +02:00
public sealed class ObjectResponse < T > : BasicResponse where T : class {
2019-01-10 22:33:07 +01:00
[PublicAPI]
2020-08-22 21:41:01 +02:00
public readonly T ? Content ;
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
2020-08-22 21:41:01 +02:00
internal ObjectResponse ( StreamResponse streamResponse , T content ) : this ( streamResponse ) {
2020-04-02 18:01:55 +03:00
if ( streamResponse = = null ) {
throw new ArgumentNullException ( nameof ( streamResponse ) ) ;
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
}
2019-01-10 22:33:07 +01:00
Content = content ;
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
}
2019-04-05 16:24:02 +02:00
2020-08-22 21:41:01 +02:00
internal ObjectResponse ( BasicResponse basicResponse ) : base ( basicResponse ) {
2020-04-08 12:50:58 +02:00
if ( basicResponse = = null ) {
throw new ArgumentNullException ( nameof ( basicResponse ) ) ;
}
}
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
}
2019-01-10 22:33:07 +01:00
public sealed class XmlDocumentResponse : BasicResponse {
[PublicAPI]
2020-08-22 21:41:01 +02:00
public readonly XmlDocument ? Content ;
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
2020-08-22 21:41:01 +02:00
internal XmlDocumentResponse ( StreamResponse streamResponse , XmlDocument content ) : this ( streamResponse ) {
2020-04-02 18:01:55 +03:00
if ( streamResponse = = null ) {
throw new ArgumentNullException ( nameof ( streamResponse ) ) ;
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
}
Content = content ;
}
2019-04-05 16:24:02 +02:00
2020-08-22 21:41:01 +02:00
internal XmlDocumentResponse ( BasicResponse basicResponse ) : base ( basicResponse ) {
2020-04-08 12:50:58 +02:00
if ( basicResponse = = null ) {
throw new ArgumentNullException ( nameof ( basicResponse ) ) ;
}
}
2019-04-05 16:24:02 +02:00
}
[Flags]
public enum ERequestOptions : byte {
None = 0 ,
ReturnClientErrors = 1
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
}
2019-01-10 22:33:07 +01:00
internal sealed class BinaryResponse : BasicResponse {
2020-08-22 21:41:01 +02:00
internal readonly byte [ ] ? Content ;
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
2020-08-22 21:41:01 +02:00
internal BinaryResponse ( BasicResponse basicResponse , byte [ ] content ) : this ( basicResponse ) {
2020-04-02 18:01:55 +03:00
if ( ( basicResponse = = null ) | | ( content = = null ) ) {
throw new ArgumentNullException ( nameof ( basicResponse ) + " || " + nameof ( content ) ) ;
}
Content = content ;
}
2020-08-22 21:41:01 +02:00
internal BinaryResponse ( BasicResponse basicResponse ) : base ( basicResponse ) {
2020-04-08 12:50:58 +02:00
if ( basicResponse = = null ) {
throw new ArgumentNullException ( nameof ( basicResponse ) ) ;
}
}
2020-04-02 18:01:55 +03:00
}
2020-04-18 17:52:11 +02:00
internal sealed class StreamResponse : BasicResponse , IAsyncDisposable {
2020-08-22 21:41:01 +02:00
internal readonly Stream ? Content ;
2020-04-02 18:01:55 +03:00
internal readonly uint Length ;
2020-04-02 17:16:05 +02:00
2020-04-02 18:01:55 +03:00
private readonly HttpResponseMessage ResponseMessage ;
2020-08-22 21:41:01 +02:00
internal StreamResponse ( HttpResponseMessage httpResponseMessage , Stream content ) : this ( httpResponseMessage ) {
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
if ( ( httpResponseMessage = = null ) | | ( content = = null ) ) {
throw new ArgumentNullException ( nameof ( httpResponseMessage ) + " || " + nameof ( content ) ) ;
}
Content = content ;
2020-04-08 12:50:58 +02:00
}
2020-08-22 21:41:01 +02:00
internal StreamResponse ( HttpResponseMessage httpResponseMessage ) : base ( httpResponseMessage ) {
2020-04-08 12:50:58 +02:00
if ( httpResponseMessage = = null ) {
throw new ArgumentNullException ( nameof ( httpResponseMessage ) ) ;
}
2020-04-02 18:01:55 +03:00
Length = ( uint ) httpResponseMessage . Content . Headers . ContentLength . GetValueOrDefault ( ) ;
ResponseMessage = httpResponseMessage ;
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
}
2019-04-05 16:24:02 +02:00
2020-04-18 17:52:11 +02:00
public async ValueTask DisposeAsync ( ) {
if ( Content ! = null ) {
await Content . DisposeAsync ( ) . ConfigureAwait ( false ) ;
}
2020-04-08 12:52:01 +02:00
2020-04-02 18:01:55 +03:00
ResponseMessage . Dispose ( ) ;
}
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
}
2019-01-10 22:33:07 +01:00
internal sealed class StringResponse : BasicResponse {
2020-08-22 21:41:01 +02:00
internal readonly string? Content ;
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
2020-08-22 21:41:01 +02:00
internal StringResponse ( HttpResponseMessage httpResponseMessage , string content ) : this ( httpResponseMessage ) {
2019-01-10 22:33:07 +01:00
if ( ( httpResponseMessage = = null ) | | ( content = = null ) ) {
throw new ArgumentNullException ( nameof ( httpResponseMessage ) + " || " + nameof ( content ) ) ;
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
}
Content = content ;
}
2019-04-05 16:24:02 +02:00
2020-08-22 21:41:01 +02:00
internal StringResponse ( HttpResponseMessage httpResponseMessage ) : base ( httpResponseMessage ) {
2020-04-08 12:50:58 +02:00
if ( httpResponseMessage = = null ) {
throw new ArgumentNullException ( nameof ( httpResponseMessage ) ) ;
}
}
Rewrite entire ASF HTTP stack
Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
2018-02-16 15:49:18 +01:00
}
2015-11-25 16:31:39 +01:00
}
2018-07-28 05:17:52 +02:00
}