/// <summary> /// Performs a search /// </summary> /// <param name="dataSource">Traffic data accesssor</param> /// <param name="criteriaSet">Set of criteria for the search</param> /// <param name="result">The search result</param> public void Search(ITrafficDataAccessor dataSource, SearchCriteriaSet criteriaSet, ISearchResult result) { _stopSearch = false; //check the search results cache int hash = GetSearchHash(dataSource, criteriaSet); ICacheable entry = SearchResultCache.Instance.GetEntry(hash); ISearchResult cachedResult = null; if (entry != null) { cachedResult = entry.Reserve() as ISearchResult; entry.Release(); } if (cachedResult == null) { //check for a subset SearchSubset subset = GetSearchSubset(criteriaSet); if (subset == null) { FullSearch(dataSource, criteriaSet, result); } else { SubsetSearch(dataSource, criteriaSet, result, subset); } //cache the search SearchResultCache.Instance.Add(hash, new CacheEntry(result)); } else { result.AddRange(cachedResult); } }
/// <summary> /// Commits a response to the memory cache /// </summary> /// <param name="reqHeaderId"></param> /// <param name="data"></param> private void BufferSaveResponse(int reqHeaderId, byte[] data) { ICacheable entry = RequestDataCache.Instance.GetEntry(_objectId ^ reqHeaderId); if (entry != null) { RequestResponseBytes reqData = entry.Reserve() as RequestResponseBytes; reqData.RawResponse = data; entry.Release(); } }
/// <summary> /// Retrieves the request bytes from disk or from the memory cache /// </summary> /// <param name="requestHeaderId"></param> /// <returns></returns> public byte[] LoadRequestData(int requestHeaderId) { byte[] result = new byte[0]; try { lock (_lockData) //critical section begins { TVRequestInfo reqInfo; if (_requestInfos.TryGetValue(requestHeaderId, out reqInfo) && reqInfo.RequestLength > 0) { //check if the request is already in the buffer ICacheable entry = RequestDataCache.Instance.GetEntry(_objectId ^ requestHeaderId); RequestResponseBytes reqData = null; if (entry != null) { reqData = entry.Reserve() as RequestResponseBytes; entry.Release(); } if (reqData != null && reqData.RawRequest != null) { result = reqData.RawRequest; } else { //load request from disk int length = reqInfo.RequestLength; long startPosition = reqInfo.RequestStartPosition; result = DataRead(startPosition, length); //save request to buffer if is not null if (result.Length != 0) { BufferSaveRequest(requestHeaderId, result); } } if (reqInfo.IsEncrypted && result != null && result.Length > 0) { //decrypt the request result = Encryptor.Decrypt(result); } } } //critical section ends } catch (Exception ex) { SdkSettings.Instance.Logger.Log(TraceLevel.Error, "Cannot load request data for request id: {0} . Stack trace: {1}", requestHeaderId, ex.ToString()); } return(result); }
/// <summary> /// Commits a request to the memory cache /// </summary> /// <param name="reqHeaderId"></param> /// <param name="data"></param> private void BufferSaveRequest(int reqHeaderId, byte[] data) { if (!_cacheEnabled) { return; } ICacheable entry = RequestDataCache.Instance.GetEntry(_objectId ^ reqHeaderId); RequestResponseBytes reqData; if (entry == null) { reqData = new RequestResponseBytes(); reqData.RawRequest = data; RequestDataCache.Instance.Add(_objectId ^ reqHeaderId, new CacheEntry(reqData)); } else { reqData = entry.Reserve() as RequestResponseBytes; reqData.RawRequest = data; entry.Release(); } }
/// <summary> /// Actually gets a matching response from the mock data /// </summary> /// <param name="requestInfo"></param> /// <returns></returns> public HttpResponseInfo SendRequest(HttpRequestInfo requestInfo) { HttpResponseInfo responseInfo = null; string currentRequestString = requestInfo.ToString(); string currentAlertId = Utils.RegexFirstGroupValue(currentRequestString, ALERT_MATCH); TrafficServerMode currentMatchMode = _matchMode; if (!String.IsNullOrEmpty(currentAlertId)) //override the redundancy tuning if we are trying to match a alert { currentMatchMode = TrafficServerMode.BrowserFriendly; } //parse the request variables because we will need them to construct the hash requestInfo.ParseVariables(); TrafficServerResponseSet responseSet = null; //look in the server cache for the request ICacheable entry = TrafficServerCache.Instance.GetEntry(requestInfo.GetHashCode(currentMatchMode)); if (entry != null) { responseSet = entry.Reserve() as TrafficServerResponseSet; entry.Release(); } TrafficServerResponseSet similarRequests = new TrafficServerResponseSet(); if (responseSet == null) { //create a new empty response set responseSet = new TrafficServerResponseSet(); RequestSearcher searcher = new RequestSearcher(); SearchCriteriaSet criteriaSet; criteriaSet = GetCriteriaSet(requestInfo, currentMatchMode); RequestMatches matches = new RequestMatches(); //do the search! searcher.Search(_sourceStore, criteriaSet, matches); //normalize the matches and keep only the ones that have the same variables and values if (matches.Count > 0) { HttpRequestInfo original; HttpRequestInfo found; byte[] requestBytes; int i, n = matches.Count; for (i = 0; i < n & i < MATCHES_LIMIT; i++) { int match = matches[i]; TVRequestInfo header = _sourceStore.GetRequestInfo(match); if (_ignoreAuth) { if ( String.Compare(header.ResponseStatus, "401", true) == 0 || String.Compare(header.ResponseStatus, "407", true) == 0) { HttpServerConsole.Instance.WriteLine(LogMessageType.Warning, "Skipping authentication challenge"); //simply skip 401 matches continue; } } if (String.Compare(header.Description, Resources.TrafficLogProxyDescription, true) == 0) { //is likely that the source store is also the save store and this may be //the current request being saved HttpServerConsole.Instance.WriteLine(LogMessageType.Warning, "Skipping request to traffic store"); continue; } requestBytes = _sourceStore.LoadRequestData(match); string requestString = Constants.DefaultEncoding.GetString(requestBytes); if (String.IsNullOrEmpty(requestString)) { continue; //skip the current match is incorrect } original = new HttpRequestInfo(DynamicElementsRemover.Remove(currentRequestString)); found = new HttpRequestInfo(DynamicElementsRemover.Remove(requestString)); if (RequestMatcher.IsMatch(original, found, TrafficServerMode.Strict)) { responseSet.Add(match); } else if (currentMatchMode != TrafficServerMode.Strict && RequestMatcher.IsMatch(original, found, currentMatchMode)) { similarRequests.Add(match); } } //if no exact requests were found if (responseSet.Count == 0 && similarRequests.Count > 0) { HttpServerConsole.Instance.WriteLine (LogMessageType.Warning, "Warning, exact match was not found for {0} returning a similar request.", requestInfo.RequestLine); } responseSet.AddRange(similarRequests.Matches); } //add this response set to the cache TrafficServerCache.Instance.Add(requestInfo.GetHashCode(currentMatchMode), new CacheEntry(responseSet)); } //get the next response id from the response set int requestId = responseSet.GetNext(); if (requestId == NULL_INDEX) { HttpServerConsole.Instance.WriteLine(LogMessageType.Warning, "(404) Request not found: {0}" , requestInfo.RequestLine); //the request was not found at all, return a 404 return(new HttpResponseInfo(HttpErrorResponse.GenerateHttpErrorResponse(HttpStatusCode.NotFound, "Request Not Found", "<html><head><title>Error code: 404</title><body><h1>Request was not found or variables didn't match.</h1></body></html>"))); } if (requestId != NULL_INDEX) { HttpServerConsole.Instance.WriteLine(LogMessageType.Information, "Returning response from request id: {0}", requestId); byte[] responseBytes = _sourceStore.LoadResponseData(requestId); if (responseBytes != null) { responseInfo = new HttpResponseInfo(responseBytes); if (!String.IsNullOrEmpty(currentAlertId)) { Encoding encoding = HttpUtil.GetEncoding(responseInfo.Headers["Content-Type"]); string responseString = encoding.GetString(responseBytes); responseString = Utils.ReplaceGroups(responseString, ALERT_MATCH, currentAlertId); responseInfo = new HttpResponseInfo(encoding.GetBytes(responseString)); } } //add the request id header responseInfo.Headers.Add("Traffic-Store-Req-Id", requestId.ToString()); } return(responseInfo); }
/// <summary> /// Verifies a request /// </summary> /// <param name="dataSource"></param> /// <param name="header"></param> /// <param name="criteriaSet"></param> /// <param name="result"></param> protected override void RequestMatches(ITrafficDataAccessor dataSource, TVRequestInfo header, SearchCriteriaSet criteriaSet, ISearchResult result) { bool found = false; int hashSum = criteriaSet.DescriptionFilter.GetHashCode(); List <MatchCoordinates> matchCoordinates; //variable used to obtain match coordinates //create a temp list to store all the matches LineMatches tempMatches = new LineMatches(); if (criteriaSet.DescriptionFilter == null || criteriaSet.DescriptionFilter == String.Empty || header.Description.IndexOf(criteriaSet.DescriptionFilter, StringComparison.CurrentCultureIgnoreCase) > -1) { //apply an AND operation with all the search criterias from the last search criteria cached int i, n = criteriaSet.Count, start = criteriaSet.StartCriteriaIndex; for (i = start; i < n; i++) { ISearchCriteria criteria = criteriaSet[i]; found = false; //compose the text to search if (criteria.Context == SearchContext.RequestLine) { if (criteria.Matches(header.RequestLine, 0, out matchCoordinates)) { //if we got here it means that the request line matches //using the request search context since we really care only for request or response found = true; tempMatches.Add(new LineMatch(header.Id, header.RequestLine, matchCoordinates, SearchContext.Request)); } else { found = false; break; //one of the criteria did not match so the end result is false } } else { //search in request first if (criteria.Context == SearchContext.Full || criteria.Context == SearchContext.Request || criteria.Context == SearchContext.RequestBody) { found = SearchInContext(dataSource, header, criteria, SearchContext.Request, ref tempMatches); } if (criteria.Context == SearchContext.Full || criteria.Context == SearchContext.Response) { found |= SearchInContext(dataSource, header, criteria, SearchContext.Response, ref tempMatches); } if (!found) { break; //no match was found in the entire stream } } //if at the end of criteria iteration found is still true cache the hashSum of this criteria //so we can get the sublist of request ids in future searches if (found) { hashSum = hashSum ^ criteria.GetHashCode(); SearchSubset subset = null; ICacheable entry = SearchSubsetsCache.Instance.GetEntry(hashSum); if (entry == null) { subset = new SearchSubset(); subset.Add(header.Id); SearchSubsetsCache.Instance.Add(hashSum, new CacheEntry(subset)); } else { subset = entry.Reserve() as SearchSubset; if (!subset.Contains(header.Id)) { subset.Add(header.Id); } entry.Release(); } } } //end of for, all the criterias were matched or one criteria did not match if (found) { //add all the temp matches to the results result.AddRange(tempMatches); } } }
/// <summary> /// Verifies that a request matches /// </summary> /// <param name="dataSource"></param> /// <param name="header"></param> /// <param name="criteriaSet"></param> /// <param name="result"></param> protected override void RequestMatches(ITrafficDataAccessor dataSource, TVRequestInfo header, SearchCriteriaSet criteriaSet, ISearchResult result) { bool found = true; int hashSum = criteriaSet.DescriptionFilter.GetHashCode(); if (criteriaSet.DescriptionFilter == null || criteriaSet.DescriptionFilter == String.Empty || header.Description.IndexOf(criteriaSet.DescriptionFilter, StringComparison.CurrentCultureIgnoreCase) > -1) { //apply an AND operation with all the search criterias from the last search criteria cached int i, n = criteriaSet.Count, start = criteriaSet.StartCriteriaIndex; for (i = start; i < n; i++) { ISearchCriteria criteria = criteriaSet[i]; //compose the text to search string toBeSearched; if (criteria.Context == SearchContext.RequestLine) { toBeSearched = header.RequestLine; } else if (criteria.Context == SearchContext.RequestBody || criteria.Context == SearchContext.Request) { toBeSearched = Constants.DefaultEncoding.GetString(dataSource.LoadRequestData(header.Id)); if (criteria.Context == SearchContext.RequestBody) { HttpRequestInfo reqInfo = new HttpRequestInfo(toBeSearched); toBeSearched = reqInfo.ContentDataString; } } else if (criteria.Context == SearchContext.Response) { toBeSearched = Constants.DefaultEncoding.GetString(dataSource.LoadResponseData(header.Id)); } else { string request = Constants.DefaultEncoding.GetString(dataSource.LoadRequestData(header.Id)); string response = Constants.DefaultEncoding.GetString(dataSource.LoadResponseData(header.Id)); StringBuilder sb = new StringBuilder(request.Length + response.Length); sb.Append(request); sb.Append(response); toBeSearched = sb.ToString(); } found = criteria.Matches(toBeSearched); if (found) //if found is still true cache the request id as a match for the current set of criterias { hashSum = hashSum ^ criteria.GetHashCode(); ICacheable entry = SearchSubsetsCache.Instance.GetEntry(hashSum); SearchSubset subset; if (entry == null) { subset = new SearchSubset(); subset.Add(header.Id); SearchSubsetsCache.Instance.Add(hashSum, new CacheEntry(subset)); } else { subset = entry.Reserve() as SearchSubset; //if (!subset.Contains(header.Id)) //not checking if the entry exists for performance reasons //{ subset.Add(header.Id); //} entry.Release(); } } else { break; //criteria didn't match, break the loop } } //end of for, all the criterias were matched or one criteria did not match if (found) { //add all the temp matches to the results result.Add(header.Id); } } //end description check }