bool CheckIfNoneMatch(HttpRequest objRequest, HttpResponse objResponse, UrlInformation objFile) { string sRequestHeaderIfNoneMatch; string[] sEntityIDs; bool breturn = true; string sreturn = ""; // Checks the If-None-Match header if it was sent with the request. // // returns true if one of the header values matches the file//s entity tag, // or if "*" was sent, // returns false if a header was sent, but does not match the file, or // if no header was sent. // Retrieve If-None-Match Header value from Request (*, meaning any, if none is indicated) sRequestHeaderIfNoneMatch = RetrieveHeader(objRequest, HTTP_HEADER_IF_NONE_MATCH, String.Empty); if (sRequestHeaderIfNoneMatch.Equals(String.Empty)) { // Perform the request normally... breturn = true; } else { if (sRequestHeaderIfNoneMatch.Equals("*")) { // The server must not perform the request objResponse.StatusCode = 412; // Precondition failed breturn = false; } else { // One or more Match IDs where sent by the client software... sEntityIDs = sRequestHeaderIfNoneMatch.Replace("bytes=", "").Split(",".ToCharArray()); // Loop through all entity IDs, finding one which // does not match the current file//s ID will be // enough to satisfy the If-None-Match for (int iLoop = sEntityIDs.GetLowerBound(0); iLoop <= sEntityIDs.GetUpperBound(0); iLoop++) { if (sEntityIDs[iLoop].Trim().Equals(objFile.EntityTag)) { sreturn = sEntityIDs[iLoop]; breturn = false; } } if (!breturn) { // One of the requested entities matches the current file//s tag, objResponse.AppendHeader("ETag", sreturn); objResponse.StatusCode = 304; // Not Modified } } } // return the result... return(breturn); }
bool CheckIfUnmodifiedSince(HttpRequest objRequest, UrlInformation objFile) { string sDate; DateTime dDate; bool breturn; // Checks the If-Unmodified or Unless-Modified-Since header, if // one of them was sent with the request. // // returns true, if the file was not modified since the // indicated date (RFC 1123 format), or // if no header was sent, // returns false, if the file was modified since the indicated date // Retrieve If-Unmodified-Since Header value from Request (Empty if none is indicated) sDate = RetrieveHeader(objRequest, HTTP_HEADER_IF_UNMODIFIED_SINCE, String.Empty); if (sDate.Equals(String.Empty)) { // If-Unmodified-Since was not sent, check Unless-Modified-Since... sDate = RetrieveHeader(objRequest, HTTP_HEADER_UNLESS_MODIFIED_SINCE, String.Empty); } if (sDate.Equals(String.Empty)) { // No date was indicated, // so just give this as true breturn = true; } else { try { // ... to parse the indicated sDate to a datetime value dDate = DateTime.Parse(sDate); // return true if the file was not modified since the indicated date... breturn = objFile.LastWriteTimeUTC < DateTime.Parse(sDate); } catch (Exception) { // Converting the indicated date value failed, return false breturn = false; } } return(breturn); }
bool CheckIfRange(HttpRequest objRequest, UrlInformation objFile) { string sRequestHeaderIfRange; // Checks the If-Range header if it was sent with the request. // // returns true if the header value matches the file//s entity tag, // or if no header was sent, // returns false if a header was sent, but does not match the file. // Retrieve If-Range Header value from Request (objFile.EntityTag if none is indicated) sRequestHeaderIfRange = RetrieveHeader(objRequest, HTTP_HEADER_IF_RANGE, objFile.EntityTag); // If the requested file entity matches the current // file entity, return true return(sRequestHeaderIfRange.Equals(objFile.EntityTag)); }
bool CheckIfMatch(HttpRequest objRequest, UrlInformation objFile) { string sRequestHeaderIfMatch; string[] sEntityIDs; bool breturn = false; // Checks the If-Match header if it was sent with the request. // // returns true if one of the header values matches the file//s entity tag, // or if no header was sent, // returns false if a header was sent, but does not match the file. // Retrieve If-Match Header value from Request (*, meaning any, if none is indicated) sRequestHeaderIfMatch = RetrieveHeader(objRequest, HTTP_HEADER_IF_MATCH, "*"); if (sRequestHeaderIfMatch.Equals("*")) { // The server may perform the request as if the // If-Match header does not exists... breturn = true; } else { // One or more Match IDs where sent by the client software... sEntityIDs = sRequestHeaderIfMatch.Replace("bytes=", "").Split(",".ToCharArray()); // Loop through all entity IDs, finding one // which matches the current file's ID will // be enough to satisfy the If-Match for (int iLoop = sEntityIDs.GetLowerBound(0); iLoop <= sEntityIDs.GetUpperBound(0); iLoop++) { if (sEntityIDs[iLoop].Trim().Equals(objFile.EntityTag)) { breturn = true; } } } // return the result... return(breturn); }
bool CheckIfModifiedSince(HttpRequest objRequest, UrlInformation objFile) { string sDate; DateTime dDate; bool breturn; // Checks the If-Modified header if it was sent with the request. // // returns true, if the file was modified since the // indicated date (RFC 1123 format), or // if no header was sent, // returns false, if the file was not modified since // the indicated date // Retrieve If-Modified-Since Header value from Request (Empty if none is indicated) sDate = RetrieveHeader(objRequest, HTTP_HEADER_IF_MODIFIED_SINCE, string.Empty); if (sDate.Equals(String.Empty)) { // No If-Modified-Since date was indicated, // so just give this as true breturn = true; } else { try { // ... to parse the indicated sDate to a datetime value dDate = DateTime.Parse(sDate); // return true if the file was modified since or at the indicated date... breturn = (objFile.LastWriteTimeUTC >= DateTime.Parse(sDate)); } catch { // Converting the indicated date value failed, return false breturn = false; } } return(breturn); }
public DownloadState ProcessDownload(byte[] pageData, string UrlAddress, string filename) { HttpContext objContext = HttpContext.Current; // The Response object from the Context HttpResponse objResponse = objContext.Response; // The Request object from the Context HttpRequest objRequest = objContext.Request; // File information object... UrlInformation objFile; // Long Arrays for Range values: // ...Begin() contains start positions for each requested Range long[] alRequestedRangesBegin = new long[1]; // ...End() contains end positions for each requested Range long[] alRequestedRangesend = new long[1]; // Response Header value: Content Length... int iResponseContentLength = 0; // The Stream we//re using to download the file in chunks... System.IO.Stream objStream; // Total Bytes to read (per requested range) int iBytesToRead; // Size of the Buffer for chunk-wise reading int iBufferSize = 25000; // The Buffer itself byte[] bBuffer = new byte[iBufferSize]; // Amount of Bytes read int iLengthOfReadChunk = -1; // Indicates if the download was interrupted bool bDownloadBroken = false; // Indicates if this is a range request bool bIsRangeRequest = false; // Indicates if this is a multipart range request bool bMultipart = false; // Loop counter used to iterate through the ranges int iLoop; filename = filename.Replace(' ', '-'); // Content-Disposition value string Content_Disposition_File = "attachment; filename=" + filename + ""; // ToDo - your code here (Determine which file is requested) // Using objRequest, determine which file is requested to // be downloaded, and open objFile with that file: // Example: // objFile = New Download.FileInformation(<Full path to the requested file>) //objFile = new Download.FileInformation(objContext.Server.MapPath("~/download.zip")); objFile = new UrlInformation(pageData, UrlAddress); objFile.ContentType = this.ContentType; // Clear the current output content from the buffer //objResponse.Clear(); if (!(objRequest.HttpMethod.Equals(HTTP_METHOD_GET) || objRequest.HttpMethod.Equals(HTTP_METHOD_HEAD))) { // Currently, only the GET and HEAD methods // are supported... objResponse.StatusCode = 501; // Not implemented } else if (!objFile.Exists) { // The requested file could not be retrieved... objResponse.StatusCode = 404; // Not found } else if (objFile.Length > Int32.MaxValue) { // The file size is too large... objResponse.StatusCode = 413; // Request Entity Too Large } else if (!ParseRequestHeaderRange(objRequest, ref alRequestedRangesBegin, ref alRequestedRangesend, objFile.Length, ref bIsRangeRequest)) { // The Range request contained bad entries objResponse.StatusCode = 400; // Bad Request } else if (!CheckIfModifiedSince(objRequest, objFile)) { // The entity is still unmodified... objResponse.StatusCode = 304; // Not Modified } else if (!CheckIfUnmodifiedSince(objRequest, objFile)) { // The entity was modified since the requested date... objResponse.StatusCode = 412; // Precondition failed } else if (!CheckIfMatch(objRequest, objFile)) { // The entity does not match the request... objResponse.StatusCode = 412; // Precondition failed } else if (!CheckIfNoneMatch(objRequest, objResponse, objFile)) { // The entity does match the none-match request, the response // code was set inside the CheckifNoneMatch function } else { // Preliminary checks where successful... if (bIsRangeRequest && CheckIfRange(objRequest, objFile)) { // This is a Range request... // if the Range arrays contain more than one entry, // it even is a multipart range request... bMultipart = (alRequestedRangesBegin.GetUpperBound(0) > 0); // Loop through each Range to calculate the entire Response length for (iLoop = alRequestedRangesBegin.GetLowerBound(0); iLoop <= alRequestedRangesBegin.GetUpperBound(0); iLoop++) { // The length of the content (for this range) iResponseContentLength += Convert.ToInt32(alRequestedRangesend[iLoop] - alRequestedRangesBegin[iLoop]) + 1; if (bMultipart) { // iResponseContentLength += HTTP_HEADER_Content_Disposition.Length; // if this is a multipart range request, calculate // the length of the intermediate headers to send iResponseContentLength += MULTIPART_BOUNDARY.Length; iResponseContentLength += objFile.ContentType.Length; iResponseContentLength += alRequestedRangesBegin[iLoop].ToString().Length; iResponseContentLength += alRequestedRangesend[iLoop].ToString().Length; iResponseContentLength += objFile.Length.ToString().Length; // 49 is the length of line break and other // needed characters in one multipart header iResponseContentLength += 49; } } if (bMultipart) { // if this is a multipart range request, // we must also calculate the length of // the last intermediate header we must send iResponseContentLength += MULTIPART_BOUNDARY.Length; // 8 is the length of dash and line break characters iResponseContentLength += 8; } else { // This is no multipart range request, so // we must indicate the response Range of // in the initial HTTP Header objResponse.AppendHeader(HTTP_HEADER_CONTENT_RANGE, "bytes " + alRequestedRangesBegin[0].ToString() + "-" + alRequestedRangesend[0].ToString() + "/" + objFile.Length.ToString()); } // Range response objResponse.StatusCode = 206; // Partial Response } else { // This is not a Range request, or the requested Range entity ID // does not match the current entity ID, so start a new download // Indicate the file//s complete size as content length iResponseContentLength = Convert.ToInt32(objFile.Length); // Return a normal OK status... objResponse.StatusCode = 200; } // Write file name into the Response objResponse.AppendHeader(HTTP_HEADER_Content_Disposition, Content_Disposition_File); // Write the content length into the Response objResponse.AppendHeader(HTTP_HEADER_CONTENT_LENGTH, iResponseContentLength.ToString()); // Write the Last-Modified Date into the Response objResponse.AppendHeader(HTTP_HEADER_LAST_MODIFIED, objFile.LastWriteTimeUTC.ToString("r")); // Tell the client software that we accept Range request objResponse.AppendHeader(HTTP_HEADER_ACCEPT_RANGES, HTTP_HEADER_ACCEPT_RANGES_BYTES); // Write the file//s Entity Tag into the Response (in quotes!) objResponse.AppendHeader(HTTP_HEADER_ENTITY_TAG, "\"" + objFile.EntityTag + "\""); // Write the Content Type into the Response if (bMultipart) { // Multipart messages have this special Type. // In this case, the file//s actual mime type is // written into the Response at a later time... objResponse.ContentType = MULTIPART_CONTENTTYPE; } else { // Single part messages have the files content type... objResponse.ContentType = objFile.ContentType; } if (objRequest.HttpMethod.Equals(HTTP_METHOD_HEAD)) { // Only the HEAD was requested, so we can quit the Response right here... } else { // Flush the HEAD information to the client... objResponse.Flush(); // Download is in progress... objFile.State = DownloadState.fsDownloadInProgress; // Open the file as filestream /*objStream = new FileStream(objFile.FullName, FileMode.Open, * FileAccess.Read, * FileShare.Read);*/ objStream = objFile.DataStream; // Now, for each requested range, stream the chunks to the client: for (iLoop = alRequestedRangesBegin.GetLowerBound(0); iLoop <= alRequestedRangesBegin.GetUpperBound(0); iLoop++) { // Move the stream to the desired start position... objStream.Seek(alRequestedRangesBegin[iLoop], SeekOrigin.Begin); // Calculate the total amount of bytes for this range iBytesToRead = Convert.ToInt32(alRequestedRangesend[iLoop] - alRequestedRangesBegin[iLoop]) + 1; if (bMultipart) { // if this is a multipart response, we must add // certain headers before streaming the content: // The multipart boundary objResponse.Output.WriteLine("--" + MULTIPART_BOUNDARY); //objResponse.AppendHeader("--",MULTIPART_BOUNDARY); // The mime type of this part of the content objResponse.Output.WriteLine(HTTP_HEADER_CONTENT_TYPE + ": " + objFile.ContentType); //objResponse.AppendHeader(HTTP_HEADER_CONTENT_TYPE,objFile.ContentType); // The actual range objResponse.Output.WriteLine(HTTP_HEADER_CONTENT_RANGE + ": bytes " + alRequestedRangesBegin[iLoop].ToString() + "-" + alRequestedRangesend[iLoop].ToString() + "/" + objFile.Length.ToString()); /*objResponse.AppendHeader(HTTP_HEADER_CONTENT_RANGE,": bytes " + * alRequestedRangesBegin[iLoop].ToString() + "-" + * alRequestedRangesend[iLoop].ToString() + "/" + * objFile.Length.ToString()); */ // Indicating the end of the intermediate headers objResponse.Output.WriteLine(); } // Now stream the range to the client... while (iBytesToRead > 0) { if (objResponse.IsClientConnected) { // Read a chunk of bytes from the stream iLengthOfReadChunk = objStream.Read(bBuffer, 0, Math.Min(bBuffer.Length, iBytesToRead)); // Write the data to the current output stream. objResponse.OutputStream.Write(bBuffer, 0, iLengthOfReadChunk); // Flush the data to the HTML output. objResponse.Flush(); // Clear the buffer //bBuffer=new byte[iBufferSize]; // Reduce BytesToRead iBytesToRead -= iLengthOfReadChunk; } else { // The client was or has disconneceted from the server... stop downstreaming... iBytesToRead = -1; bDownloadBroken = true; } } // In Multipart responses, mark the end of the part if (bMultipart) { objResponse.Output.WriteLine(); } // No need to proceed to the next part if the // client was disconnected if (bDownloadBroken) { break; } } // At this point, the response was finished or cancelled... if (bDownloadBroken) { // Download is broken... objFile.State = DownloadState.fsDownloadBroken; } else { if (bMultipart) { // In multipart responses, close the response once more with // the boundary and line breaks objResponse.Output.WriteLine("--" + MULTIPART_BOUNDARY + "--"); objResponse.Output.WriteLine(); } // The download was finished objFile.State = DownloadState.fsDownloadFinished; } objStream.Close(); } } //objResponse.End(); //====== return download state ====== return(objFile.State); }