I am having issue with cache-control. I have one IIS website with multiple host headers. When you browse site number 1 then cache will be set for this site, when you open browser again and go to 2nd site you will see content from first site. How can I determine cache content based on the site user visits? Everything working fine when you have 1 site and host header related to SAME site.
//Set Cacheability
if (!Context.User.Identity.IsAuthenticated && _activeNode.CacheDuration > 0)
{
var eTag = GetETag(_activeNode);
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.Public);
if (IsClientCached(_activeNode.UpdateTimestamp))
{
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.StatusCode = (int)HttpStatusCode.NotModified;
HttpContext.Current.Response.SuppressContent = true;
HttpContext.Current.Response.End();
return;
}
var incomingEtag = HttpContext.Current.Request.Headers["If-None-Match"];
if (String.Compare(incomingEtag, eTag) == 0)
{
HttpContext.Current.Response.StatusCode = (int)HttpStatusCode.NotModified;
HttpContext.Current.Response.SuppressContent = true;
HttpContext.Current.Response.End();
return;
}
HttpContext.Current.Response.Cache.SetExpires(DateTime.Now.ToUniversalTime().AddMinutes(_activeNode.CacheDuration));
HttpContext.Current.Response.Cache.SetMaxAge(new TimeSpan(0, _activeNode.CacheDuration, 0));
HttpContext.Current.Response.Cache.SetLastModified(_activeNode.UpdateTimestamp);
HttpContext.Current.Response.Cache.SetETag(eTag);
}
/// <summary>
/// Gets the ETag.
/// </summary>
/// <param name="node">The node.</param>
/// <returns></returns>
private static string GetETag(Node node)
{
var etag = String.Format("{0}_{1}", node.Site.Id, node.Id);
return "\"" + Encryption.StringToMD5Hash(etag).Replace("-", null) + "\"";
}
/// <summary>
/// Determines whether [is client cached] [the specified content modified].
/// </summary>
/// <param name="contentModified">The content modified.</param>
/// <returns>
/// <c>true</c> if [is client cached] [the specified content modified]; otherwise, <c>false</c>.
/// </returns>
private bool IsClientCached(DateTime contentModified)
{
var header = Request.Headers["If-Modified-Since"];
if (header != null)
{
DateTime isModifiedSince;
if (DateTime.TryParse(header, out isModifiedSince))
{
return isModifiedSince > contentModified;
}
}
return false;
}
Sounds like you should add the host header value to your IsClientCached algorithm
Related
On Safari browser I need to select an Option from the dropdown but the funny thing is that it works for all the browsers except for Safari on Mac OS.
I am using Safari 10.0.3 with selenium webdriver version 3.3.0
I have written the code in C#. Refer the code below -
IWebDriver driver;
driver = new SafariDriver();
List<string> handles = driver.WindowHandles.ToList<string>();
driver.SwitchTo().Window(handles.First());
driver.Navigate().GoToUrl("https://myip/MyPage.aspx");
SelectElement element = new SelectElement(driver.FindElement(By.Id("securityQuestion")));
int totalOptions = element.Options.Count;
Random rnd = new Random();
int rndValue = rnd.Next(1, totalOptions);
element.SelectByIndex(rndValue); // This is not working for Safari browser
driver.FindElement(By.Id("securityAnswer")).SendKeys("test");
driver.FindElement(By.Id("ctl00_Content_btnNext")).Click();
driver.Close();
No error is thrown just that it doesnt select any value from the dropdown.
This is a safaridriver bug. The fix is in WebKit, and being tracked here:
https://bugs.webkit.org/show_bug.cgi?id=174710
As a workaround, you can modify which options of a select are selected using JavaScript and the DOM API.
Try this sample here for JS workaround - implemented as C# extension. This code works on Safari (tested on version 10.1+).
This is not the complete code, just a snippet to make it simple. You can extend it to support any functionality you like.
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Internal;
using OpenQA.Selenium.Support.UI;
namespace Gravity.Plugins.Actions.Extensions
{
public static class SelectExtensions
{
/// <summary>
/// Select the option by the index, as determined by the "index" attribute of the
/// element.
/// </summary>
/// <param name="selectElement">This <see cref="SelectElement"/>.</param>
/// <param name="index">The value of the index attribute of the option to be selected.</param>
public static void JsSelectByIndex(this SelectElement selectElement, int index)
{
// constants
var script = $"options[{index}].selected = true;";
// web element to act on
var onElement = selectElement.WrappedElement;
var onDriver = (IWrapsDriver)onElement;
// execute
((IJavaScriptExecutor)onDriver).ExecuteScript(script, onElement);
}
/// <summary>
/// Select all options by the text displayed.
/// </summary>
/// <param name="selectElement">This <see cref="SelectElement"/>.</param>
/// <param name="text">The text of the option to be selected.</param>
public static void JsSelectByText(this SelectElement selectElement, string text)
{
// constants
var script =
"var options = arguments[0].getElementsByTagName('option');" +
"" +
"for(i = 0; i < options.length; i++) {" +
$" if(options[i].innerText !== '{text}') {{" +
" continue;" +
" }" +
" options[i].selected = true;" +
" break;" +
"}";
// web element to act on
var onElement = selectElement.WrappedElement;
var onDriver = (IWrapsDriver)onElement;
// execute
((IJavaScriptExecutor)onDriver).ExecuteScript(script, onElement);
}
/// <summary>
/// Select an option by the value.
/// </summary>
/// <param name="selectElement"></param>
/// <param name="value">The value of the option to be selected.</param>
public static void JsSelectByValue(this SelectElement selectElement, string value)
{
// constants
var script =
"var options = arguments[0].getElementsByTagName('option');" +
"" +
"for(i = 0; i < options.length; i++) {" +
$" if(options[i].getAttribute('value') !== '{value}') {{" +
" continue;" +
" }" +
" options[i].selected = true;" +
" break;" +
"}";
// web element to act on
var onElement = selectElement.WrappedElement;
var onDriver = (IWrapsDriver)onElement;
// execute
((IJavaScriptExecutor)onDriver).ExecuteScript(script, onElement);
}
}
// Usage sample
public class MySeleniumClass
{
public void DoAutomation()
{
var driver = new ChromeDriver()
{
Url = "https://gravitymvctestapplication.azurewebsites.net/UiControls"
};
var element = driver.FindElement(By.Id("select_menu"));
var selectElement = new SelectElement(element);
selectElement.JsSelectByIndex(1);
selectElement.JsSelectByText("Two");
selectElement.JsSelectByValue("3");
}
}
}
Below is simple code for creating a user in AD. The code is DC un-specific. It doesn't care which DC it creates it on, it'll use the windows default that the server is connected to.
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, Domain, path, ContextOptions.Negotiate, ManagementUsername, ManagementPassword))
{
try
{
using (UserPrincipal up = new UserPrincipal(pc, username, password, true))
{
up.GivenName = firstName; up.Surname = lastName; up.DisplayName = firstName + " " + lastName; up.UserPrincipalName = username + "#" + Domain; up.Save();
}
}
catch (PasswordException) { return null; }
}
The issue is that there is a replication time (usually domains have 15 minutes) of new accounts. This does not work when trying to implement an on-demand account creation when the account is requested by somebody wanting to use it on a workstation connected to a different DC than the server. They end up having to sit in front of the work station for up to 15 minutes being unable to log in.
Question:
Is there a way to connect to a DC based on client IP address to create it on that one? OR is there a way to make the account on all DC's and have the replication be ok with this? OR force the account to replicate programmatically (based on searching through SO, I'm guessing no).
Forest adForest = Forest.GetCurrentForest();
ActiveDirectorySite[] sites = new ActiveDirectorySite[adForest.Sites.Count];
adForest.Sites.CopyTo(sites, 0);
List<ActiveDirectorySubnet> subnets = new List<ActiveDirectorySubnet>();
sites.ToList().ForEach(x =>
{
ActiveDirectorySubnet[] subnetTemp = new ActiveDirectorySubnet[x.Subnets.Count];
x.Subnets.CopyTo(subnetTemp, 0);
subnets.AddRange(subnetTemp);
});
IPAddress address = IPAddress.Parse("IPAddress to look up closest DC");
var currentSubnet = subnets.Where(x => address.IsInRange(x.Name));
var location = currentSubnet.First().Site.Name;
DomainController dc = DomainController.FindOne(new DirectoryContext(DirectoryContextType.Domain, Domain), location);
This gets you the DC associated with that site and domain that is nearest the specified IP address within the topology.
Then you pass the DC IP address to the Principal Context.
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, dc.IPAddress, path, ContextOptions.Negotiate, ManagementUsername, ManagementPassword))
{
try
{
using (UserPrincipal up = new UserPrincipal(pc, username, password, true))
{
up.GivenName = firstName; up.Surname = lastName; up.DisplayName = firstName + " " + lastName; up.UserPrincipalName = username + "#" + Domain; up.Save();
}
}
catch (PasswordException) { return null; }
}
And create a user.
Note: IPAddress functions were done via NetTools IPAddressRange class on github and the following custom extensions of it.
/// <summary>
/// All extensions for IPAddress type
/// </summary>
public static class IPAddressExtension
{
/// <summary>
/// Determine whether this IP address is part of the range/subnet
/// </summary>
/// <param name="range">A range of IPAddresses</param>
/// <returns></returns>
public static bool IsInRange(this IPAddress thisIP, IPAddressRange range)
{
return range.Contains(thisIP);
}
/// <summary>
/// Determine whether this IP address is part of the range/subnet
/// </summary>
/// <param name="range">Can be specified in CIDR/UNI (ex: 192.168.10.0/24) </param>
/// <returns></returns>
public static bool IsInRange(this IPAddress thisIP, string rangeIP)
{
IPAddressRange range = IPAddressRange.Parse(rangeIP);
return range.Contains(thisIP);
}
/// <summary>
/// Determine whether this IP address is part of the range/subnet
/// </summary>
/// <param name="ipBegin">Beginning IP address of range</param>
/// <param name="ipEnd">Ending IP address of range</param>
/// <returns></returns>
public static bool IsInRange(this IPAddress thisIP, IPAddress ipBegin, IPAddress ipEnd)
{
IPAddressRange range = new IPAddressRange(ipBegin, ipEnd);
return range.Contains(thisIP);
}
}
Consider the following ASP.net web api controller method. For demo purpose it returns a cookie. the session-id cookie has a base64 encoded data. When I add the cookie to the response with response.Headers.AddCookies(new CookieHeaderValue[] { cookie }); it url encodes the data (Documentation is here). Is there any way I can attach the cookie without encoding it?
public HttpResponseMessage LogMeIn()
{
var response = Request.CreateResponse<Models.UserAuthResponse>(new Models.UserAuthResponse());
var cookie = new CookieHeaderValue("session-id", "K2QRkQaSnwCBpRrAgI1K3w9kTgbArc+xdIJI64e2hz0=");
cookie.Expires = DateTimeOffset.Now.AddDays(1);
cookie.Domain = ".example.com";
cookie.Path = "/";
response.Headers.AddCookies(new CookieHeaderValue[] { cookie });
return response;
}
I have the same problem because i want to create LTPA Token cookie used by Lotus Domino SSO web applications and this cookie is base64 encoded and use special characters in cookie like "+=....".
I find three way to solve this problem:
OWIN extension in Startup.cs
app.Use((context, next) =>
{
context.Response.Headers....;
//here decode and replace the cookies
return next.Invoke();
});
using javascript after page load with cookies:
document.cookie = encodeURIComponent(document.cookie);
or the best way is to create extension like this:
/// <summary>
///
/// </summary>
public static class CookieExtensions
{
/// <summary>
/// Add a new cookie and value
///
/// </summary>
/// <param name="header"></param>
/// <param name="key"/><param name="value"/>
public static void AppendUrlDecodedCookie(this IHeaderDictionary header, string key, string value)
{
header.AppendValues("Set-Cookie", key + "=" + value + "; path=/");
}
/// <summary>
/// Add a new cookie
///
/// </summary>
/// <param name="header"></param>
/// <param name="key"/><param name="value"/><param name="options"/>
public static void AppendUrlDecodedCookie(this IHeaderDictionary header, string key, string value, CookieOptions options)
{
if (options == null)
throw new ArgumentNullException("options");
bool flag1 = !string.IsNullOrEmpty(options.Domain);
bool flag2 = !string.IsNullOrEmpty(options.Path);
bool hasValue = options.Expires.HasValue;
header.AppendValues("Set-Cookie",
key + "=" + (value ?? string.Empty) + (!flag1 ? (string) null : "; domain=") +
(!flag1 ? (string) null : options.Domain) + (!flag2 ? (string) null : "; path=") +
(!flag2 ? (string) null : options.Path) + (!hasValue ? (string) null : "; expires=") +
(!hasValue
? (string) null
: options.Expires.Value.ToString("ddd, dd-MMM-yyyy HH:mm:ss ",
(IFormatProvider) CultureInfo.InvariantCulture) + "GMT") +
(!options.Secure ? (string) null : "; secure") + (!options.HttpOnly ? (string) null : "; HttpOnly"));
}
}
And you can use it like this:
response.Headers.AppendUrlDecodedCookie("key", "val");
or
response.Headers.AppendUrlDecodedCookie("key", "val", new Microsoft.Owin.CookieOptions
{
Path = "/",
Domain = ="domain.com",
Expires = Date...
});
And this solve the problem.
In a NET Core project, I did an extension method to HttpResponse. In my case, I needed to replace all space characters in order to have a valid cookie value. keep in mind what kind of values you need to save and then update the string properly before creating the setCookieHeaderValue .
public static class CookiesExtensions
{
public static void AppendUnencodedCookie(this HttpResponse response, string key, string value, CookieOptions options)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
response.Cookies.Delete(key);
var setCookieHeaderValue = new SetCookieHeaderValue(key, value.Replace(" ","+"))
{
Domain = options.Domain,
Path = options.Path,
Expires = options.Expires,
MaxAge = options.MaxAge,
Secure = options.Secure,
SameSite = (Microsoft.Net.Http.Headers.SameSiteMode)options.SameSite,
HttpOnly = options.HttpOnly
};
response.Headers[HeaderNames.SetCookie] = StringValues.Concat(response.Headers[HeaderNames.SetCookie], setCookieHeaderValue.ToString());
}
}
Use it like this:
Context.Response.AppendUnencodedCookie(cookieName, cookieValue, options);
I am currently trying to generate a CSV using nhibernate. This error does not occur on my development enviroment but it does on the live site that it's being used on. I have tried fiddling with time out's but this does not seem to have any effect as it's timing out way before it should. The timing is completely random, sometimes it'll be 3 seconds before it times out the next it will be 10 seconds. There doesn't seem to be any real consistancy in the timing.
Stack Trace:
System.Web.HttpException: The remote host closed the connection. The error code is 0x800703E3.
at System.Web.Hosting.IIS7WorkerRequest.RaiseCommunicationError(Int32 result, Boolean throwOnDisconnect)
at System.Web.Hosting.IIS7WorkerRequest.ExplicitFlush()
at System.Web.HttpResponse.Flush(Boolean finalFlush)
at Reports.CustomCSVWriter.WritetoHttpStream(String filename, Boolean header)
The code is as follows:
public class ProductSpreadSheetDownload : CustomCSVWriter
{
protected override string[] GetCollection()
{
Page.Server.ScriptTimeout = 300;
IList<Product> products = new List<Product>();
IStockScheme stockScheme = Fabric.ObjectProvider.Get<IStockScheme>();
ICriteria criteria = CoreHttpModule.Session.CreateCriteria(typeof(Product))
.Add(NHibernate.Expression.Expression.IsNotNull(Product.STOCK_CODE))
.Add(NHibernate.Expression.Expression.Eq(Product.IS_VISIBLE_ON_WEBSITE, true))
.Add(NHibernate.Expression.Expression.Eq(Product.STOCK_TYPE, StockType.StockItem))
.Add(NHibernate.Expression.Expression.Not(NHibernate.Expression.Expression.Like(Product.NAME, "%*%")));
AddCustomCriteria(criteria);
products = criteria.List<Product>();
products = Product.RemoveOrphanedAndUnrooted((List<Product>)products);
Product[] productArray = new Product[products.Count];
products.CopyTo(productArray, 0);
double?[] levels = stockScheme.GetStock(productArray, false);
List<string> productStringList = new List<string>();
IProductMapper mapper = Fabric.ObjectProvider.Get<IProductMapper>();
var rootUrl = Fabric.SettingsProvider.ReadSetting<string>("RootUrl", string.Empty);
string showOutOfStock = Page.Request.QueryString["ShowOutOfStock"];
int minStockLevel = int.MinValue;
if (showOutOfStock == "False")
minStockLevel = 0;
for (int i = 0; i < productArray.Length; i++)
{
if (levels[i] > minStockLevel && levels[i] != null && productArray[i].Parent != null && productArray[i].Parent.IsVisibleOnWebsite)
{
StringBuilder productStringBuilder = new StringBuilder();
productStringBuilder.AppendFormat("{0}, ", CleanString(productArray[i].Name));
productStringBuilder.AppendFormat("{0}, ", CleanString(productArray[i].StockCode));
productStringBuilder.AppendFormat("{0}, ", levels[i]);
productStringBuilder.AppendFormat("{0}, ", mapper.GetUrl(productArray[i]) );
productStringBuilder.AppendFormat("{0}, ", CleanString(productArray[i].Category));
productStringBuilder.AppendFormat("{0}, ", CleanString(productArray[i].SubCategory));
productStringBuilder.AppendFormat("{0}, ", CleanString(mapper.GetText(productArray[i], "Description")));
productStringBuilder.AppendFormat("{0}, ", mapper.GetImageUrl(productArray[i], "Main"));
AddCustomFields(productStringBuilder, mapper);
productStringList.Add(productStringBuilder.ToString().Trim().TrimEnd(','));
}
}
string[] productstrings = new string[productStringList.Count];
productStringList.CopyTo(productstrings, 0);
return productstrings;
}
/// <summary>
/// Override this method to add custom criteria to the feed
/// </summary>
/// <example>
/// criteria.Add(NHibernate.Expression.Expression.Eq(Product.IS_VISIBLE_ON_WEBSITE, true));
/// </example>
protected virtual void AddCustomCriteria(ICriteria criteria) {}
/// <summary>
/// Override this method to add custom fields to the CSV output
/// </summary>
/// <example>
/// productStringBuilder.AppendFormat("{0}, ", mapper.GetImageUrl(productArray[i], "Main"));
/// </example>
protected virtual void AddCustomFields(StringBuilder productStringBuilder, IProductMapper mapper) { }
protected override string Headers()
{
string headers = "Name, Stockcode, Stock_Level, URL, Category, SubCategory, Description, Main Image URL";
return headers;
}
/// <summary>
/// Removes characters that are not safe in a CSV file.
/// </summary>
protected static string CleanString(string stringToClean)
{
return string.IsNullOrEmpty(stringToClean) ? string.Empty : stringToClean.Replace("\n", " ").Replace(',', ' ');
}
}
}
I want to track users who have read my mails.I am doing this but it's not working
I am sending mails to myself in outlook.
Here is my code which sends mails
try
{
string emailTemplateBody = "Hy this is test mail";
emailTemplateBody += "<tr><img src=''http://localhost:52583/HttpModule_using_beacon_images/images/<keyvalue>.aspx'' style=''opacity:0.0; filter:alpha(opacity=0);'' /></tr>";
string templateName = txtTemplateName.Text;
string toEmail = mymailaddress
//// Get unique Key after registring mail to be sent
string key = bl_email_calls.RegisterSystemEmailAudit("1", templateName, DateTime.Now);
emailTemplateBody = emailTemplateBody.Replace("<keyvalue>", key);
//// sending e-mail
bl_email_calls.SendMailMessage(toEmail, templateName, emailTemplateBody, key);
using (var cn = new SqlConnection(ConfigurationManager.ConnectionStrings["webConnectionString"].ToString()))
{
//code to insert record in database; }
Response.Write("Mail sent");
// return false;
}
catch (Exception ex)
{
throw;
}
Here is my HTTP module i have used from http://www.aspnetemail.com/samples/emailtracker/default.aspx[^]
public class HttpModuleClass : IHttpModule
{
//public event EventHandler BeginRequest;
public void Dispose()
{
}
/// <summary>
/// public varibles
/// </summary>
string footerFile = "~/images/footer.png";
//string footerFile = "~/images/ajax-loader.gif";
Email_Calls bl_email_calls = new Email_Calls();
/// <summary>
/// Init methoed
/// </summary>
/// <param name="context"></param>
public void Init(HttpApplication context)
{
context.BeginRequest += new System.EventHandler(GetImage_BeginRequest);
}
/// <summary>
/// handles requests made to server and call update email read time
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
public void GetImage_BeginRequest(object sender, System.EventArgs args)
{
//cast the sender to a HttpApplication object
System.Web.HttpApplication application = (System.Web.HttpApplication)sender;
string url = application.Request.Path; //get the url path
//string pattern = #"/HttpModule/images/(?<key>.*)\.aspx";
//string pattern = #"/HttpModule_using_beacon_images/images/(?<key>.*)\.aspx";
string pattern = #"/HttpModule_using_beacon_images/images/(?<key>.*)\.aspx";
//string pattern = #"~/images/(?<key>.*)\.aspx";
//create the regex to match for beacon images
Regex r = new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);
if (r.IsMatch(url))
{
MatchCollection mc = r.Matches(url);
if ((mc != null) && (mc.Count > 0))
{
string key = (mc[0].Groups["key"].Value);
bl_email_calls.UpdateSystemEmailAuditReadDate(key);
}
//now send the REAL image to the client
//application.Response.ContentType = "image/gif";
application.Response.ContentType = "image/png";
application.Response.WriteFile(application.Request.MapPath(footerFile));
//end the response
application.Response.End();
}
}
}
To request a read receipt, we need add a custom header named 'Disposition-Notification-To'
in this example, read receipts will go back to 'someaddress#mydomain.com'
it's important to note that read receipts will only be sent by those mail clients that
a) support them
and
b)have them enabled.
//Add "Disposition-Notification-To" for Read receipt
mail.Headers.Add("Disposition-Notification-To", "<mail#yahoo.com>");
I got it. I was making some little mistakes.
Actually I was saving the email body into the database so I had to use '' instead of ' and that was making all the trouble. When is removed '' it worked fine.