/////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // Note: The SPECWeb99 web app // using System; using System.Collections; using System.Diagnostics; using Microsoft.SingSharp; using Microsoft.SingSharp.Runtime; using Microsoft.Singularity.Channels; using Microsoft.Singularity.Directory; using Microsoft.Singularity.WebApps; using Microsoft.Singularity.WebApps.Contracts; using System.Text; using System.Web; using FileSystem.Utils; using System.Net.IP; using Microsoft.Singularity.V1.Services; using Microsoft.Contracts; using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; using Microsoft.SingSharp.Reflection; using Microsoft.Singularity.Applications; [assembly: Transform(typeof(WebAppResourceTransform))] namespace Microsoft.Singularity.WebApps { [Category("WebApp")] internal sealed class Parameters { [Endpoint] public readonly TRef webAppRef; [Endpoint] public readonly TRef nsRef; reflective private Parameters(); } public class SPECWeb99WebAppIn : IWebApp { private const string ContentDir = "/fs"; private const string PostLogFile = ContentDir + "/postlog"; private const string CustomAdsFile = ContentDir + "/Custom.Ads"; private const string UserPersonalityFile = ContentDir + "/User.Personality"; private const string CookieHeaderName = "Cookie"; private const string CookiePushHeaderName = "Set-Cookie"; private const uint GENDER_MASK = 0x30000000; private const uint AGE_GROUP_MASK = 0x0f000000; private const uint REGION_MASK = 0x00f00000; private const uint INTEREST1_MASK = 0x000ffc00; private const uint INTEREST2_MASK = 0x000003ff; private const int CustomAdsLines = 360; private const int CustomAdsLineLength = 39; // Including \n private const int CustomAdsFileBytes = CustomAdsLines * CustomAdsLineLength; private const int UserPersonalityLines = 360; private const int UserPersonalityLineLength = 15; // Including \n private const int UserPersonalityFileBytes = UserPersonalityLines * UserPersonalityLineLength; private const long PostLogHeaderSize = 11; // Including \n private const long PostLogRecordSize = 139; // Including \n private const int SubstitutionPatternLength = 48; // \nSPECweb99 Dynamic GET & POST Test\n\n

SERVER_SOFTWARE = Singularity Cassini web server\n

REMOTE_ADDR = "); HtmlPreamble2 = Encoding.ASCII.GetBytes("\n

SCRIPT_NAME = SPECWeb99WebApp\n

QUERY_STRING = "); HtmlPreamble3 = Encoding.ASCII.GetBytes("\n

\n");
            HtmlPostamble = Encoding.ASCII.GetBytes("\n
\n\n"); PostLogInitData = Encoding.ASCII.GetBytes(" 0\n"); MatchPattern = Encoding.ASCII.GetBytes("(dsImp); #endif base(); assert PostLogInitData.Length == (int)PostLogHeaderSize; start = DateTime.Now; } DateTime start; bool isRecording = false; bool isDone = false; public void ProcessRequest(IHttpRequest! request) { // //TimeSpan beenRunning = DateTime.Now-start; //if (beenRunning.TotalSeconds > 300 && !isRecording && !isDone) { // Console.WriteLine("\n\n\nRECORDING\n\n\n"); // isRecording = true; // WebAppFSUtils.recording = true; // FileUtils.recording = true; //} //else if (beenRunning.TotalSeconds > 330 && isRecording && !isDone) { // Console.WriteLine("\n\n\nSTOP RECORDING\n\n\n"); // isDone = true; // WebAppFSUtils.recording = false; // FileUtils.recording = false; // float bindTime = (float) FileUtils.bindTime.Ticks / TimeSpan.TicksPerSecond; // float readTime = (float) WebAppFSUtils.readTime.Ticks / TimeSpan.TicksPerSecond; // Console.WriteLine("Bind: count: "+FileUtils.bindCount+", time: "+bindTime+"\n"+ // "Read: count: "+WebAppFSUtils.readCount+", time: "+readTime); //} // string uri = request.GetUriPath(); if ((uri == null) || (uri.Length == 0)) { uri = "/"; } if (uri == "/dynamic-content") { HandleDynamicRequest(request); } else { // Treat as a request for a static file WebAppFSUtils.ServeFSPath(uri, request); } } /////////////////////////////////////////////////////////////////////// // // Helper code // private void WriteResponseHtmlPreamble(IHttpRequest! request) { request.SendStatus(200, "OK"); request.SendHeader("Content-type", "text/html"); request.SendBodyData(HtmlPreamble1); request.SendBodyData(Encoding.ASCII.GetBytes(request.GetRemoteAddress())); request.SendBodyData(HtmlPreamble2); string queryString = request.GetQueryString(); if (queryString != null) { request.SendBodyData(Encoding.ASCII.GetBytes(queryString)); } else { request.SendBodyData(Encoding.ASCII.GetBytes("")); } request.SendBodyData(HtmlPreamble3); } private void WriteResponseHtmlPostamble(IHttpRequest! request) { request.SendBodyData(HtmlPostamble); } private void WriteMessageResponse(IHttpRequest! request, string! message) { WriteResponseHtmlPreamble(request); if ((message != null) && (message.Length > 0)) { request.SendBodyData(Encoding.ASCII.GetBytes(message)); } WriteResponseHtmlPostamble(request); } private void WriteFileResponse(IHttpRequest! request, string! filePath) { FileContract.Imp fileImp = FileUtils.OpenFile(filePath); if (fileImp != null) { WriteFileResponse(request, fileImp); delete fileImp; } else { WriteMessageResponse(request, "Couldn't open file: " + filePath); } } private void WriteFileResponse(IHttpRequest! request, FileContract.Imp! fileImp) { WriteResponseHtmlPreamble(request); WebAppFSUtils.TransmitFileContents(fileImp, request); WriteResponseHtmlPostamble(request); } private void HandleDynamicRequest(IHttpRequest! request) { string verb = request.GetVerb(); if (verb == "GET") { HandleGET(request); } else if (verb == "POST") { HandlePOST(request); } else { WriteMessageResponse(request, "Unknown HTTP verb: " + verb); } request.Done(); } private bool ParseCookie(string! cookie, out uint myUser, out uint lastAd, out string rejectString) { const string cookiePreamble = "my_cookie=user_id="; const string lastAdPreamble = "last_ad="; rejectString = null; myUser = 0; lastAd = 0; if (!cookie.StartsWith(cookiePreamble)) { rejectString = "Invalid cookie format (bad preamble): \"" + cookie + "\""; return false; } int nextAmp = cookie.IndexOf('&', cookiePreamble.Length); if (nextAmp == -1) { rejectString = "Invalid cookie format (no ampersand): \"" + cookie + "\""; return false; } string myUserStr = cookie.Substring(cookiePreamble.Length, nextAmp - cookiePreamble.Length); try { myUser = UInt32.Parse(myUserStr); } catch (Exception) { rejectString = "Invalid cookie format (bad user_id value): \"" + cookie + "\""; return false; } string remainderStr = cookie.Substring(nextAmp + 1); if (!remainderStr.StartsWith(lastAdPreamble)) { rejectString = "Invalid cookie format (no last_ad field): \"" + cookie + "\""; return false; } try { lastAd = UInt32.Parse(remainderStr.Substring(lastAdPreamble.Length)); } catch (Exception) { rejectString = "Invalid cookie format (bad last_ad value): \"" + cookie + "\""; return false; } return true; } private uint Get1970Time() { TimeSpan delta1970 = DateTime.Now - new DateTime(1970, 1, 1); return (uint)delta1970.TotalSeconds; } /////////////////////////////////////////////////////////////////////// // // Dynamic GET // private void HandleGET(IHttpRequest! request) { const string commandPrefix = "command/"; string queryString = request.GetQueryString(); if ((queryString != null) && (queryString.Length > 0)) { if (queryString.StartsWith(commandPrefix)) { HandleCommand(request, queryString.Substring(commandPrefix.Length)); } else { // We must serve the file corresponding to // the queryString. We may or may not have to // perform substitution, depending on whether // a cookie is provided. string filePath = String.Format("{0}{1}", ContentDir, queryString); string cookie = request.GetHeader(CookieHeaderName); if ((cookie == null) || (cookie.Length == 0)) { // Just serve the file normally WriteFileResponse(request, filePath); } else { HandleSubstitution(request, filePath, cookie); } } } else { WriteMessageResponse(request, "A query string is required with a GET operation"); } } /////////////////////////////////////////////////////////////////////// // // Housekeeping commands // // Validate string only contains integers private static string CheckDigits(string s) { if (s == null) return null; for (int i = 0; i < s.Length; i++) { if (s[i] < '0' || s[i] > '9') { return null; } } return s; } private static bool RunCommand(string[]! commandLine) { try { Process! process = new Process(commandLine); process.Start(); process.Join(); // XXX could check exit status, but process will print // error to stdout. process.Dispose(true); return true; } catch (ProcessCreateException) { return false; } } private void HandleReset(IHttpRequest! request, string[]! arguments) { string maxLoad = null; string pointTime = null; string maxThreads = null; string expired1 = null; string expired2 = null; string urlRoot = null; for (int i = 0; i < arguments.Length; i++) { int remain = arguments.Length - i - 1; // All values are key-value, except expiry list which // has two values. If there's not at least one argument // it's a bogus request. if (remain == 0) break; if (arguments[i] == "maxload") { maxLoad = CheckDigits(arguments[++i]); if (maxLoad == null) { WriteMessageResponse(request, "Bad 'maxload' element in GET request"); return; } } else if (arguments[i] == "pttime") { pointTime = CheckDigits(arguments[++i]); if (pointTime == null) { WriteMessageResponse(request, "Bad 'pttime' in element GET request"); return; } } else if (arguments[i] == "maxthread") { maxThreads = CheckDigits(arguments[++i]); if (maxThreads == null) { WriteMessageResponse(request, "Bad 'maxthread' in element GET request"); return; } } else if (arguments[i] == "exp") { if (remain < 2) { WriteMessageResponse(request, "Bad 'expired_list' element in GET request"); return; } expired1 = CheckDigits(arguments[++i]); if (expired1 == null) { WriteMessageResponse(request, "Bad 'expired_list[0]' element in GET request"); return; } expired2 = CheckDigits(arguments[++i]); if (expired2 == null) { WriteMessageResponse(request, "Bad 'expired_list[1]' element in GET request"); return; } } else if (arguments[i] == "urlroot") { urlRoot = arguments[++i]; } } if (maxLoad == null) { WriteMessageResponse(request, "Missing 'maxload' element in GET request."); return; } else if (pointTime == null) { WriteMessageResponse(request, "Missing 'pttime' element in GET request."); return; } else if (maxThreads == null) { WriteMessageResponse(request, "Missing 'maxthreads' element in GET request."); return; } else if ((expired1 == null) || (expired2 == null)) { WriteMessageResponse(request, "Missing 'expired_list' element in GET request."); return; } else if (urlRoot == null) { WriteMessageResponse(request, "Missing 'url_root' element in GET request."); return; } // This might fail if the file didn't already exist FileUtils.DeleteFile(CustomAdsFile); string []! cadgen = new string[] { "/init/cadgen99", "-C", ContentDir, "-e", pointTime, "-t", maxThreads, expired1, expired2}; if (RunCommand(cadgen) == false) { WriteMessageResponse(request, "Failed to run cadgen."); return; } if (!customAdsFile.Refresh()) { WriteMessageResponse(request, "Failed to load ads file"); return; } if (((!)customAdsFile.FileData).Length < CustomAdsFileBytes) { WriteMessageResponse(request, "Custom ads file was too small after generation"); return; } // This might fail if the file didn't already exist FileUtils.DeleteFile(UserPersonalityFile); string []! upfgen = new string[] {"/init/upfgen99", "-C", ContentDir, "-n", maxLoad, "-t", maxThreads}; if (RunCommand(upfgen) == false) { WriteMessageResponse(request, "Failed to run upfgen."); return; } if (!userPersonalityFile.Refresh()) { WriteMessageResponse(request, "Failed to load user personality file"); return; } if (!ResetPostLog()) { WriteMessageResponse(request, "Failed to reset the PostLog file"); return; } // Signal success with a blank message page WriteMessageResponse(request, ""); } private void HandleCommand(IHttpRequest! request, string! command) { if (command == "Fetch") { // Lock on this to protect the PostLog lock (this) { FileContract.Imp fileImp = FileUtils.OpenFile(PostLogFile); if (fileImp != null) { WriteFileResponse(request, fileImp); delete fileImp; } else { WriteMessageResponse(request, "Error opening PostLog file"); } } } else if (command.StartsWith("Reset&")) { HandleReset(request, command.Split(new char [] { '&', '=', ',' })); } } /////////////////////////////////////////////////////////////////////// // // Dynamic POST support // private void HandlePOST(IHttpRequest! request) { string cookie = request.GetHeader(CookieHeaderName); if ((cookie == null) || (cookie.Length == 0)) { WriteMessageResponse(request, "POST requires a 'Cookie' header"); return; } byte[] bodyData = request.GetBodyData(); if ((bodyData == null) || (bodyData.Length == 0)) { WriteMessageResponse(request, "POST requires body data"); return; } // // Parse the POST data // string urlRoot = null; int dirNum = -1, classNum = -1, fileNum = -1, clientNum = -1; string body = Encoding.ASCII.GetString(bodyData); string[] fragments = body.Split(new char[] { '=', '&' } ); for (int i = 0; i < fragments.Length; i += 2) { if (fragments[i] == "urlroot") { urlRoot = fragments[i + 1]; } else if (fragments[i] == "dir") { try { dirNum = Int32.Parse(fragments[i + 1]); } catch (Exception) { WriteMessageResponse(request, "Bad 'dir' element in POST data"); return; } } else if (fragments[i] == "class") { try { classNum = Int32.Parse(fragments[i + 1]); } catch (Exception) { WriteMessageResponse(request, "Bad 'class' element in POST data"); return; } } else if (fragments[i] == "num") { try { fileNum = Int32.Parse(fragments[i + 1]); } catch (Exception) { WriteMessageResponse(request, "Bad 'num' element in POST data"); return; } } else if (fragments[i] == "client") { try { clientNum = Int32.Parse(fragments[i + 1]); } catch (Exception) { WriteMessageResponse(request, "Bad 'client' element in POST data"); return; } } else { WriteMessageResponse(request, "Invalid POST data item \"" + fragments[i] + "\""); return; } } if (urlRoot == null) { WriteMessageResponse(request, "Missing 'urlroot' item in POST data"); return; } else if (dirNum == -1) { WriteMessageResponse(request, "Missing 'dir' item in POST data"); return; } else if (classNum == -1) { WriteMessageResponse(request, "Missing 'class' item in POST data"); return; } else if (fileNum == -1) { WriteMessageResponse(request, "Missing 'num' item in POST data"); return; } else if (clientNum == -1) { WriteMessageResponse(request, "Missing 'client' item in POST data"); return; } // // Parse the cookie data // string cookieReject; uint myCookie, lastAd; if (!ParseCookie(cookie, out myCookie, out lastAd, out cookieReject)) { WriteMessageResponse(request, (!)cookieReject); return; } string filePath = String.Format("{0}{1}dir{2}/class{3}_{4}", ContentDir, urlRoot, dirNum.ToString("d05"), classNum, fileNum); // Write out all the parsed data if (!AppendToPostLog((uint)dirNum, (uint)classNum, (uint)fileNum, (uint)clientNum, filePath, (uint)myCookie)) { WriteMessageResponse(request, "Error writing to PostLog file"); return; } FileContract.Imp fileImp = FileUtils.OpenFile(filePath); if (fileImp != null) { request.SendHeader(CookiePushHeaderName, "my_cookie=" + myCookie.ToString()); WriteFileResponse(request, fileImp); delete fileImp; } else { WriteMessageResponse(request, "Couldn't open file: \"" + filePath + "\""); return; } } private bool ResetPostLog() { lock (this) { // OK to ignore the result of this; the file // may not exist. FileUtils.DeleteFile(PostLogFile); if (FileUtils.CreateFile(PostLogFile) != 0) { return false; } FileContract.Imp fileImp = FileUtils.OpenFile(PostLogFile); if (fileImp == null) { return false; } try { long written = FileUtils.Write(fileImp, 0, PostLogInitData); if (written < PostLogInitData.Length) { return false; } } finally { delete fileImp; } } return true; } private bool AppendToPostLog(uint dirNum, uint classNum, uint fileNum, uint clientNum, string fileName, uint cookieNum) { lock (this) { FileContract.Imp fileImp = FileUtils.OpenFile(PostLogFile); if (fileImp == null) { return false; } try { // First, read in the header so we can increment the record count byte[] header = new byte[PostLogHeaderSize]; if (FileUtils.Read(fileImp, 0,PostLogHeaderSize , 0, header) != PostLogHeaderSize) { return false; } int numExistingRecords; try { numExistingRecords = Int32.Parse( Encoding.ASCII.GetString(header, 0, PostLogHeaderSize - 1)); } catch (Exception) { return false; } // Increment the record count and rewrite the header string newHeaderStr = String.Format("{0,10}\n", numExistingRecords + 1); byte[] newHeader = Encoding.ASCII.GetBytes(newHeaderStr); assert newHeader.Length == (int)PostLogHeaderSize; if (FileUtils.Write(fileImp, 0, newHeader) != PostLogHeaderSize) { return false; } // Write a new record to the appropriate offset in the file long newRecordOffset = (numExistingRecords * PostLogRecordSize) + PostLogHeaderSize; string newEntryStr = String.Format( "{0,10} {1,10} {2,10} {3,5} {4,2} {5,2} {6,10} {7,-60} {8,10} {9,10}\n", numExistingRecords + 1, Get1970Time(), ProcessService.GetCurrentProcessId(), dirNum, classNum, fileNum, clientNum, fileName, ProcessService.GetCurrentProcessId(), cookieNum); byte[] newEntry = Encoding.ASCII.GetBytes(newEntryStr); assert newEntry.Length == (int)PostLogRecordSize; if (FileUtils.Write(fileImp, newRecordOffset, newEntry) != PostLogRecordSize) { return false; } } finally { delete fileImp; } } return true; } /////////////////////////////////////////////////////////////////////// // // Substitution / ad-rotation support // private void SubstituteAt(byte[]! in ExHeap buffer, int offset, uint adId) { assert offset + SubstitutionPatternLength <= buffer.Length; string! num1 = (!)(adId / 36).ToString("d05"); string! num2 = (!)((adId % 36) / 9).ToString(); string! num3 = (!)(adId % 9).ToString(); for (int i = 0; i < 5; i++) { buffer[offset + SubstituteNum1Pos + i] = (byte)num1[i]; } buffer[offset + SubstituteNum2Pos] = (byte)num2[0]; // should be a single char buffer[offset + SubstituteNum3Pos] = (byte)num3[0]; // ditto } private int SubstituteInBlock(byte[]! in ExHeap buffer, uint maxBytes, uint adId) { // NOTE: the idea here is to find the randomly placed // markers in the file block and doctor them. The only // difficulty arises if there is a match of the prefix // of our search pattern at the end of a block (that is, // the pattern to match straddles a block boundary). // As a naive solution to this, if we detect such a case, we // return an amount to step back in the file so the next block // will contain the entire match-pattern. If we were smarter // we could keep state between invocations, instead. int startMatch = 0; int matchDepth = 0; int matchPatternLength = MatchPattern.Length; // Make this unsafe to skip the insane overhead of non-hoisted // ExHeap vector accesses unsafe { byte *pSrc = &buffer[0]; fixed (byte *pMatch = &MatchPattern[0]) { while (startMatch < maxBytes) { if (pSrc[startMatch] == pMatch[0]) { do { ++matchDepth; if (matchDepth == matchPatternLength) { // Found a match! SubstituteAt(buffer, startMatch, adId); } if (startMatch + matchDepth >= maxBytes) { // We were in the middle of a match // but hit the end of the buffer. // Step back by one disk sector return 512; } } while (pSrc[startMatch + matchDepth] == pMatch[matchDepth]); startMatch += matchDepth; matchDepth = 0; } else { startMatch++; } } } } return 0; } private void WriteSubstitutedFile(IHttpRequest! request, string! filePath, uint adId) { FileContract.Imp fileImp = FileUtils.OpenFile(filePath); if (fileImp == null) { WriteMessageResponse(request, "Couldn't open file " + filePath); return; } WriteResponseHtmlPreamble(request); try { long readSize = 1024 * 16; // 16KB long readOffset = 0; bool keepReading = true; do { byte[] in ExHeap buf = new[ExHeap] byte[readSize]; fileImp.SendRead(buf, 0, readOffset, readSize); switch receive { case fileImp.AckRead( _buf, long bytesRead, int error) : if ((error != 0) || (_buf == null)) { if (_buf != null) { delete _buf; } keepReading = false; } else { byte[]! in ExHeap original = _buf; bool atEnd = (bytesRead < readSize); if (bytesRead > 0) { int stepBack = SubstituteInBlock(original, (uint)bytesRead, adId); // Ignore any partial matches that occur at the very // end of the file if (atEnd) { stepBack = 0; } if ((stepBack > 0) || (bytesRead < readSize)) { // Discard the excess portion of the block delete Bitter.SplitOff(ref original, (int)(bytesRead - stepBack)); } request.SendBodyData(original); readOffset += (bytesRead - stepBack); } else { // By coincidence, our previous read took us to // exactly the end of the file. We should // optimize this away somehow delete original; } keepReading = !atEnd; } break; case fileImp.ChannelClosed() : keepReading = false; break; } } while (keepReading); } finally { delete fileImp; } WriteResponseHtmlPostamble(request); } private bool RetrieveAdData(uint adIndex, out uint demographics, out uint weightings, out uint minMatch, out uint expiry) { demographics = weightings = minMatch = expiry = 0; byte[] fileData = customAdsFile.FileData; if (fileData == null) { return false; } if (fileData.Length < CustomAdsFileBytes) { return false; } try { int startLine = (int)adIndex * (int)CustomAdsLineLength; // debug! string wholeLine = Encoding.ASCII.GetString(fileData, startLine, CustomAdsLineLength); if (wholeLine == null) { DebugStub.Break(); } string demoStr = Encoding.ASCII.GetString(fileData, startLine + 6, 8); string weightStr = Encoding.ASCII.GetString(fileData, startLine + 15, 8); string minMatchStr = Encoding.ASCII.GetString(fileData, startLine + 24, 3); string expiryStr = Encoding.ASCII.GetString(fileData, startLine + 28, 10); demographics = UInt32.Parse(demoStr, System.Globalization.NumberStyles.AllowHexSpecifier | System.Globalization.NumberStyles.AllowLeadingWhite); weightings = UInt32.Parse(weightStr, System.Globalization.NumberStyles.AllowHexSpecifier | System.Globalization.NumberStyles.AllowLeadingWhite); minMatch = UInt32.Parse(minMatchStr, System.Globalization.NumberStyles.AllowLeadingWhite); expiry = UInt32.Parse(expiryStr, System.Globalization.NumberStyles.AllowLeadingWhite); } catch (Exception) { return false; } return true; } private bool RetrieveUserPersonality(uint userIndex, out uint demographics) { demographics = 0; byte[] fileData = userPersonalityFile.FileData; if (fileData == null) { return false; } if (fileData.Length < UserPersonalityFileBytes) { return false; } try { int startLine = (int)userIndex * (int)UserPersonalityLineLength; // debug! string wholeLine = Encoding.ASCII.GetString(fileData, startLine, UserPersonalityLineLength); if (wholeLine == null) { DebugStub.Break(); } string demoStr = Encoding.ASCII.GetString(fileData, startLine + 6, 8); demographics = UInt32.Parse(demoStr, System.Globalization.NumberStyles.AllowHexSpecifier | System.Globalization.NumberStyles.AllowLeadingWhite); } catch (Exception) { return false; } return true; } private uint GenderWt(uint demVal) { return (demVal & 0x000f0000) >> 16; } private uint AgeGroupWt(uint demVal) { return (demVal & 0x0000f000) >> 12; } private uint RegionWt(uint demVal) { return (demVal & 0x00000f00) >> 8; } private uint InterestWt(uint demVal) { return (demVal & 0x000000f0) >> 4; } private uint Interest2Wt(uint demVal) { return (demVal & 0x0000000f); } private void HandleSubstitution(IHttpRequest! request, string! filePath, string! cookie) { bool isSmallFile = (filePath.IndexOf("class1") != -1) || (filePath.IndexOf("class2") != -1); string cookieReject; uint userId, lastAd; if (!ParseCookie(cookie, out userId, out lastAd, out cookieReject)) { WriteMessageResponse(request, (!)cookieReject); return; } if (userId < 10000) { WriteMessageResponse(request, "UserID was less than 10,000"); return; } uint userIndex = userId - 10000; uint userPersonality; uint adIndex = lastAd + 1; if (adIndex >= CustomAdsLines) { adIndex = 0; } if (!RetrieveUserPersonality(userIndex, out userPersonality)) { // BUGBUG: the spec is unclear here; there is no adId to use. request.SendHeader(CookiePushHeaderName, "found_cookie=Ad_Id=-1&Ad_weight=00&Expired=1"); WriteFileResponse(request, filePath); } uint adWeight = 0; bool expired = false; while (adIndex != lastAd) { adWeight = 0; expired = false; uint adDemographics, weightings, minMatch, expiry; bool gotAd = RetrieveAdData(adIndex, out adDemographics, out weightings, out minMatch, out expiry); assert gotAd; // we checked the cached adfile above uint combinedDemographics = userPersonality & adDemographics; if ((combinedDemographics & GENDER_MASK) != 0) { adWeight = adWeight + GenderWt(adDemographics); } if ((combinedDemographics & AGE_GROUP_MASK) != 0) { adWeight = adWeight + AgeGroupWt(adDemographics); } if ((combinedDemographics & REGION_MASK) != 0) { adWeight = adWeight + RegionWt(adDemographics); } if ((combinedDemographics & INTEREST1_MASK) != 0) { adWeight = adWeight + InterestWt(adDemographics); } if ((combinedDemographics & INTEREST2_MASK) != 0) { adWeight = adWeight + Interest2Wt(adDemographics); } if (adWeight >= minMatch) { // Found an acceptable match uint now1970Delta = Get1970Time(); if (now1970Delta > expiry) { expired = true; } request.SendHeader(CookiePushHeaderName, "found_cookie=Ad_id=" + adIndex + "&Ad_weight=" + adWeight + "&Expired=" + (expired ? "1" : "0")); if (isSmallFile) { WriteSubstitutedFile(request, filePath, adIndex); return; } else { WriteFileResponse(request, filePath); return; } } adIndex++; if (adIndex >= CustomAdsLines) { adIndex = 0; // wrap around } } request.SendHeader(CookiePushHeaderName, "found_cookie=Ad_id=" + adIndex + "&Ad_weight=" + adWeight + "&Expired=" + (expired ? "1" : "0")); if (isSmallFile) { WriteSubstitutedFile(request, filePath, adIndex); } else { WriteFileResponse(request, filePath); } } /////////////////////////////////////////////////////////////////////// // // The code below gets used when this webapp is compiled // to a stand-alone executable // internal static int AppMain(Parameters! config) { //Endpoint * in ExHeap ep = Process.GetStartupEndpoint(0); WebAppContract.Exp conn = config.webAppRef.Acquire(); if (conn == null) { // Wrong contract type! return -1; } conn.SendWebAppReady(); SPECWeb99WebAppIn webApp = new SPECWeb99WebAppIn(); Driver.ServiceChannel(webApp, conn); delete conn; return 0; } } }