How to undo Response.Cache.SetNoStore()? - c#

I've a CMS application code which calls Response.Cache.SetNoStore() on all request and if i'm correct, this will be prevents proxies/cdn to cache those pages/content. Therefore, i'm conditionally calling the below code:
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetMaxAge(new TimeSpan(0, 30, 0));
Response.Cache.SetValidUntilExpires(true);
But this doesn't take out the no-store param from the response header, this is the returned http header:
Cache-Control:public, no-store, must-revalidate, max-age=1800
Therefore my question is, how can i take out the nostore param pragmatically? If this isn't possible, how/where can i parse/modify the http-header, because i tried to parsed on PagePreRender event and the nostore param hasn't been applied...which leads to wonder at which life cycle is this appended to the header?

There is a way to undo SetNoStore once you call it. You need to use some creative routing to process the request in a different way or reflection to invoke the built-in reset that is private.
You can get access to HttpCachePolicyWrapper to access the underlying HttpCachePolicy, then assign the internal NoStore field or issue Reset to revert to the default cache policy.
response.Cache.SetNoStore(); // assign no-store
BindingFlags hiddenItems = BindingFlags.NonPublic | BindingFlags.Instance;
var httpCachePolicyWrapper = response.Cache.GetType(); // HttpCachePolicyWrapper type
var httpCache = httpCachePolicyWrapper.InvokeMember("_httpCachePolicy", BindingFlags.GetField | hiddenItems, null, response.Cache, null);
var httpCachePolicy = httpCache.GetType(); // HttpCachePolicy type
// Reset Cache Policy to Default
httpCachePolicy.InvokeMember("Reset", BindingFlags.InvokeMethod | hiddenItems, null, httpCache, null);
var resetAllCachePolicy = httpCachePolicy.InvokeMember("_noStore", BindingFlags.GetField | hiddenItems, null, httpCache, null);
response.Cache.SetNoStore(); // assign no-store
// Undo SetNoStore Cache Policy
httpCachePolicy.InvokeMember("_noStore", BindingFlags.SetField | hiddenItems, null, httpCache, new object[] { false });
var resetNoStoreOnly = httpCachePolicy.InvokeMember("_noStore", BindingFlags.GetField | hiddenItems, null, httpCache, null);

Related

How to check that a certificate was applied in a named HttpClient?

I have registered a named HttpClient in my app. I would like to add a test before i use this client to ensure that it has the certificate applied.
var clientCertificate = new X509Certificate2(pathToCert, passwordToCert);
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(clientCertificate);
services.AddHttpClient(name, client =>
{
client.BaseAddress = new Uri(hostName);
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("User-Agent", userAgent);
}).ConfigurePrimaryHttpMessageHandler(() => handler)
.UseHttpClientMetrics()
.SetHandlerLifetime(TimeSpan.FromMinutes(5)) //Set lifetime to five minutes
.AddPolicyHandler(RetryPolicy.GetRetryPolicy());
The issue is I cant seem to find it anywhere in the client object that says it has the message handler.
_client = httpClientFactory.CreateClient(_settings.Name);
I dont seem to have access to any of the parameters that start with _
It's not possible out of the box, but you can use the FieldInfo.class from System.Reflections to get the needed information:
_client = httpClientFactory.CreateClient(_settings.Name);
var handler = _client.BaseType.GetField("_handler", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(_client) as HttpClientHandler;
With that procedure, you should be able to get the other fields, which are not accessable by default, too. You just need to make sure, from which class/type the property is from.

I got an error "System.Net.Http.WinHttpException: The operation timed" after i migrated to .NET Core 2.2

I am migrating my API to .NET Core 2.2 and in my application is calling another wsdl (WCF) service. Upon calling that service, I'm getting an error saying
System.Net.Http.WinHttpException: The operation timed
Is there something wrong with the way i migrated? It is perfectly working in my previous solution running at .net 4.5
Here is the full inner text message.
InnerException: System.Net.Http.HttpRequestException: An error occurred while sending the request.
System.Net.Http.WinHttpException: The operation timed out
at System.Threading.Tasks.RendezvousAwaitable``1.GetResult()
at System.Net.Http.WinHttpHandler.StartRequest(WinHttpRequestState state)
--- End of inner exception stack trace --->
at System.ServiceModel.Channels.ServiceModelHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.FinishSendAsyncUnbuffered(Task1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
at System.ServiceModel.Channels.HttpChannelFactory1.HttpClientRequestChannel.HttpClientChannelAsyncRequest.SendRequestAsync(Message message, TimeoutHelper timeoutHelper)
Does your soap request take longer than 30 seconds? If yes, you need to know that default timeout in .NET Core for soap request is 30 seconds.
It's a little tricky to change to timeout, but someone already figured it how:
static void Main(string[] args)
{
var client = new SimpleServiceClient();
client.OpenAsync().GetAwaiter().GetResult();
client.DelayedResponseAsync(2000).GetAwaiter().GetResult();
var channel = client.InnerChannel;
var httpChannelFactory = client.InnerChannel.GetProperty<IChannelFactory>();
var cacheField = httpChannelFactory.GetType().GetField("_httpClientCache", BindingFlags.NonPublic | BindingFlags.Instance);
var httpClientCache = cacheField.GetValue(httpChannelFactory);
var cacheDictionaryField = httpClientCache.GetType().GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance);
IDictionary cacheDictionary = (IDictionary)cacheDictionaryField.GetValue(httpClientCache);
foreach(var cacheKey in cacheDictionary.Keys)
{
var cacheEntry = cacheDictionary[cacheKey];
var valueField = cacheEntry.GetType().GetField("value", BindingFlags.NonPublic | BindingFlags.Instance);
HttpClient httpClient = (HttpClient)valueField.GetValue(cacheEntry);
FixHttpClient(httpClient);
}
client.DelayedResponseAsync(50000).GetAwaiter().GetResult();
Console.WriteLine("Done");
Console.ReadLine();
}
private static void FixHttpClient(HttpClient httpClient)
{
var handlerField = typeof(HttpMessageInvoker).GetField("_handler", BindingFlags.NonPublic | BindingFlags.Instance);
DelegatingHandler delegatingHandler = (DelegatingHandler)handlerField.GetValue(httpClient); // Should be of type ServiceModelHttpMessageHandler
WinHttpHandler winHttpHandler = (WinHttpHandler)delegatingHandler.InnerHandler;
WinHttpHandler newHandler = new WinHttpHandler();
newHandler.ServerCredentials = winHttpHandler.ServerCredentials;
newHandler.CookieUsePolicy = winHttpHandler.CookieUsePolicy;
newHandler.ClientCertificates.AddRange(winHttpHandler.ClientCertificates);
newHandler.ServerCertificateValidationCallback = winHttpHandler.ServerCertificateValidationCallback;
newHandler.Proxy = winHttpHandler.Proxy;
newHandler.AutomaticDecompression = winHttpHandler.AutomaticDecompression;
newHandler.PreAuthenticate = winHttpHandler.PreAuthenticate;
newHandler.CookieContainer = winHttpHandler.CookieContainer;
// Fix the timeouts
newHandler.ReceiveHeadersTimeout = Timeout.InfiniteTimeSpan;
newHandler.ReceiveDataTimeout = Timeout.InfiniteTimeSpan;
newHandler.SendTimeout = Timeout.InfiniteTimeSpan;
var servicemodelHttpHandlerInnerHandlerField = delegatingHandler.GetType().GetField("_innerHandler", BindingFlags.NonPublic | BindingFlags.Instance);
servicemodelHttpHandlerInnerHandlerField.SetValue(delegatingHandler, newHandler);
var delegatingHandlerInnerHandlerField = typeof(DelegatingHandler).GetField("_innerHandler", BindingFlags.NonPublic | BindingFlags.Instance);
delegatingHandlerInnerHandlerField.SetValue(delegatingHandler, newHandler);
}
So eaily, pass your HttpClient to
Code directly copied from this gist.

EWS failes with Could not create SSL/TLS secure channel

I'm trying to fetch free / busy using EWS
Ive installed the latest nuget package Microsoft.Exchange.WebServices
I'm also setting everything I know of to ignore cert. errors:
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.Ssl3;
The code I'm using to fetch appointments:
//Set up the service with correct credentials.
var service = new ExchangeService(ExchangeVersion.Exchange2007_SP1)
{
Credentials = new WebCredentials(Account, Password, Domain),
TraceEnabled = true,
EnableScpLookup = false
};
service.Url = new Uri(ServiceUrl);
// Create a list of attendees.
var attendees = Contacts.Select(contact => new AttendeeInfo { SmtpAddress = contact.Email, AttendeeType = MeetingAttendeeType.Required }).ToList();
// Specify availability options.
var myOptions = new AvailabilityOptions
{
MeetingDuration = 30,
RequestedFreeBusyView = FreeBusyViewType.DetailedMerged,
DetailedSuggestionsWindow = new TimeWindow(DateTime.Now, DateTime.Now.AddDays(Days))
};
// Return a set of free/busy times.
var freeBusyResults = service.GetUserAvailability(attendees, new TimeWindow(DateTime.Now, DateTime.Now.AddDays(Days)),AvailabilityData.FreeBusyAndSuggestions, myOptions);
This code works for 5 out of 6 exchange servers I have, but one of them gives "The request failed. The request was aborted: Could not create SSL/TLS secure channel." error message.
If I set up fiddler to act as an proxy for the call, and tell fiddler to decrypt, everything works.
I just want to ignore ALL ssl errors and get the data, how do I do that?
The Managed API is just using HTTPWebRequest as the underlying class to do the Request/response. If it works with fiddler mostly likely your problem is environmental/Client related. I would suggest you enable tracing https://blogs.msdn.microsoft.com/dgorti/2005/09/18/using-system-net-tracing/ you should then be able to see what happens at the lower level when it fails.

Issue with System.Uri

I'm having an unexpected behavior with the System.Uri class.
When an instance of System.Uri is created, and the UrlString has some patterns like ..., or ...#, or .#, the System.Uri removes all repeated . characters.
This is weird, but I believe this behavior is based on RFC 2396.
The problem begins when I try to download the HTML from this URL: http://www.submarino.com.br/produto/1/23853463/mundo+segundo+steve+jobs,+o:+as+frases+mais+inspiradoras+...
and the System.Uri removes all the repeated .s. As the web site doesn't recognize the "New URL," it redirects to the rriginal URL. Then a "System.Net.WebException: Too many automatic redirections were attempted" is thrown and the page is never reached.
How can I solve this issue?
You can use reflection to remove that particular attribute. Use this before your Uri call:
MethodInfo getSyntax = typeof(UriParser).GetMethod("GetSyntax", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
FieldInfo flagsField = typeof(UriParser).GetField("m_Flags", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
if (getSyntax != null && flagsField != null)
{
foreach (string scheme in new[] { "http", "https" })
{
UriParser parser = (UriParser)getSyntax.Invoke(null, new object[] { scheme });
if (parser != null)
{
int flagsValue = (int)flagsField.GetValue(parser);
// Clear the CanonicalizeAsFilePath attribute
if ((flagsValue & 0x1000000) != 0)
flagsField.SetValue(parser, flagsValue & ~0x1000000);
}
}
}
It has been reported to Connect before.

How to Modify HTTP Header of a request using C#?

I was trying to modify a HTTP Header using C#. I tried to manipulate the Request.Headers on Page preinit event. But when i try to set anything to the Headers, i get PlatformNotSupportedException. Since We can not set a new NameValueCollection to Reqeust.Headers, I tried to set the value using following code:
Request.Headers.Set(HttpRequestHeader.UserAgent.ToString(), "some value");
Any idea how can this be achieved?
Try this:
HttpContext.Current.Request.Headers["User-Agent"] = "Some Value";
EDIT:
This could be your reason:
http://bigjimindc.blogspot.com/2007/07/ms-kb928365-aspnet-requestheadersadd.html
There is a code snippet in that, which adds a new header to the Request.Headers. Verified on Windows 7 32 bit OS too.
But you might want to replace the line:
HttpApplication objApp = (HttpApplication)r_objSender;
with:
HttpApplication objApp = (HttpApplication)HttpContext.Current.ApplicationInstance;
EDIT:
To replace the existing Header value, use:
t.InvokeMember("BaseSet", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, headers, new object[] { "Host", item });
where "Host" is a Header name.
Adding the complete (working) code from the linked blog - incase that blog vanishes
HttpApplication objApp = (HttpApplication)HttpContext.Current.ApplicationInstance;
HttpRequest Request = (HttpContext)objApp.Context.Request;
//get a reference
NameValueCollection headers = Request.Headers;
//get a type
Type t = headers.GetType();
System.Collections.ArrayList item = new System.Collections.ArrayList();
t.InvokeMember("MakeReadWrite",BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance,null,headers,null);
t.InvokeMember("InvalidateCachedArrays",BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance,null,headers,null);
item.Add("CUSTOM_HEADER_VALUE");
t.InvokeMember("BaseAdd",BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance,null,headers, new object[]{"CUSTOM_HEADER_NAME",item});
t.InvokeMember("MakeReadOnly",BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance,null,headers,null);

Categories