/// <summary> /// The start of an Http request /// </summary> /// <param name="sender">The sender.</param> /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param> public void BeginRequest(object sender, EventArgs e) { // begin request events var hArgs = new HttpEventArgs(HttpApplication); RaiseOnBeginRequest(hArgs); var results = new Dictionary<string, JsonResponse>(); var request = HttpContext.Current.Request; var response = HttpContext.Current.Response; // check for Json requests made to the JsonMethodUrl // and clear the requests if(request.FilePath.Equals(JsonMethodUrl)) { // a request was made to the responder Url // deserialize the request and execute the requested methods // gather the results and add them to the results collection // do post - Don't use the request.Form object because it is silly. var workerRequest = (HttpWorkerRequest)HttpContext.Current.GetType().GetProperty("WorkerRequest",BindingFlags.Instance | BindingFlags.NonPublic).GetValue(HttpContext.Current, null); var bodyLength = workerRequest.GetPreloadedEntityBodyLength(); if (bodyLength>0) { var contentType = workerRequest.GetKnownRequestHeader(HttpWorkerRequest.HeaderContentType); if (contentType == null) { throw new NullReferenceException("Header Content-Type cannot be empty. Acceptable post values are multipart/form-data or application/json."); } var filePathsAndMap = ParseRequest(workerRequest); try { JsonResponse.InvokeJsonMethods(filePathsAndMap, ref results); // after methods have been invoked, remove files from temp foreach (var f in filePathsAndMap.Files) { File.Delete(f.Path); } } catch(Exception ex) { var iex = GetInnermostException(ex); var errorResult = new JsonResponse(3, string.Format("Error invoking method. Source: {1}{0} Message: {2}{0} Stack Trace: {3}", Environment.NewLine, iex.Source ,iex.Message, iex.StackTrace)); results.Add("Exception", errorResult); } } else { // if there was no post // do QueryString (get) if (request.QueryString.Keys.Count == 1) { try { var m = new Mapper() {Map = HttpUtility.UrlDecode(request.QueryString.ToString())}; JsonResponse.InvokeJsonMethods(m, ref results); } catch (Exception ex) { var iex = GetInnermostException(ex); var errorResult = new JsonResponse(3, string.Format("Error invoking method. Source: {1}{0} Message: {2}{0} Stack Trace: {3}", Environment.NewLine, iex.Source, iex.Message, iex.StackTrace)); results.Add("Exception", errorResult); } } } } // if one or more Json methods returned a result // respond with that result if (results.Count <= 0) return; foreach(var r in results) { var result = r.Value; if (result.SupressResponse) continue; response.Status = result.Status; response.StatusCode = result.StatusCode; response.ContentType = result.ContentType; response.ContentEncoding = result.ContentEncoding; response.HeaderEncoding = result.HeaderEncoding; if(result.HttpCookie != null) { response.AppendCookie(result.HttpCookie); } if(result.ContentDisposition == JsonContentDisposition.Attachment || result.ContentDisposition == JsonContentDisposition.Normal) { if(result.ContentDisposition == JsonContentDisposition.Attachment) { response.AddHeader("Content-Disposition", string.Format(@"attachment; filename=""{0}""", result.AttachmentFileName)); } if(result.AttachmentContent.Length > 0) { result.ContentLength = result.AttachmentContent.Length; response.AddHeader("Content-Length", result.ContentLength.ToString(CultureInfo.InvariantCulture)); response.Write(result.AttachmentContent); } else if(result.AttachmentStream.Length > 0) { result.ContentLength = (int)result.AttachmentStream.Length; response.AddHeader("Content-Length", result.ContentLength.ToString(CultureInfo.InvariantCulture)); var bytes = new byte[result.AttachmentStream.Length]; result.AttachmentStream.Read(bytes, 0, (int)result.AttachmentStream.Length); response.BinaryWrite(bytes); } response.Flush(); HttpContext.Current.ApplicationInstance.CompleteRequest(); return;// Only one file can be output at a time. } } response.Write(JsonResponse.ToJson(results)); response.Flush(); HttpContext.Current.ApplicationInstance.CompleteRequest(); }
/// <summary> /// Parses the request into files then parses the files into form fields and uploaded files. /// </summary> /// <param name="r">The r.</param> /// <returns></returns> Mapper ParseRequest(HttpWorkerRequest r) { // get the ID of the upload from the querystring if any var qid = r.GetQueryString(); Guid id; if(!Guid.TryParse(qid, out id)) { throw new HttpParseException("Upload contains no ID."); } var d = new HttpUploadStatusEventArgs() { Id = id, StartedOn = DateTime.Now, LastUpdated = DateTime.Now, Message = "Beginning Upload." }; var isIdFound = false; var isMapFound = false; var e = new UTF8Encoding(); // bytes for "Content-Disposition: form-data; " var mapSig = new byte[] { 67,111,110,116,101,110,116,45,68,105,115,112,111, 115,105,116,105,111,110,58,32,102,111,114,109, 45,100,97,116,97,59,32,110,97,109,101,61, 34,109,97,112,34,13,10,13,10 }; var idSig = new byte[] { 67,111,110,116,101,110,116,45,68,105,115,112,111,115,105,116,105, 111,110,58,32,102,111,114,109,45,100,97,116,97,59,32,110, 97,109,101,61,34,105,100,34,13,10,13,10}; var m = new Mapper(); // first 46 bytes = boundary signature const int f = 131072; // 128kb = 131072b var l = r.GetTotalEntityBodyLength(); d.BytesTotal = l; var p = r.GetPreloadedEntityBody(); var b = GetBoundary(p); updateUploadStatus(ref d,l,"Uploading preloaded entity body."); // load stream into temp file var fst = Path.GetTempFileName(); using (var fs = new FileStream(fst, FileMode.OpenOrCreate)) { // write preloaded body to file Debug.Assert(p != null, "GetPreloadedEntityBody != null"); fs.Write(p, 0, p.Length); var c = p.Length; var q = 0; var u = new byte[f]; updateUploadStatus(ref d, l, "Uploading."); while (l - c > q) { q = r.ReadEntityBody(u, 0, f); fs.Write(u, 0, q); c += q; updateUploadStatus(ref d, c, "Uploading."); } if (l - c > 0) { var ux = new byte[l - c]; q = r.ReadEntityBody(ux, 0, l - c); fs.Write(ux, 0, q); } fs.Flush(); fs.Position = 0; updateUploadStatus(ref d, c, "Upload Complete, Parsing upload."); // read the entire file finding all boundaries var s = new List<long>(); while (fs.Position < fs.Length) { s.Add(FindPosition(fs, b, fs.Position)); } fs.Position = 0; // the last boundary is the eof for (var i = 0; s.Count - 1 > i; i++) { // indexes between boundaries - this is the new file size in bytes if (s[i + 1] == -1) { break; } var z = s[i + 1] - s[i]; // this is the size of the object between the boundaries (including current boundary) var g = (z) < f ? z : f; // chunk size (g) = 131072 (f) or next boundary pos - current boundary pos (z) var n = Path.GetTempFileName(); fs.Position = s[i]; // start reading from the beginning position of the object (including boundary) using (var a = new FileStream(n, FileMode.OpenOrCreate)) { q = 0; c = 0; while (z - c > q) { //read blocks while position - mod block size is less than end position q = fs.Read(u, 0, (int)g); a.Write(u, 0, q); c += q; } q = fs.Read(u, 0, (int)z - c); a.Write(u, 0, q); a.Position = 0; // read in form data if (!isMapFound) { if (FindPosition(a, mapSig, 0) > -1) { // this is the map field var mapBytes = new byte[a.Length - a.Position - 2]; // -2 to drop the \r\n a.Read(mapBytes, 0, mapBytes.Length); m.Map = e.GetString(mapBytes); isMapFound = true; continue; } } if (!isIdFound) { if (FindPosition(a, idSig, 0) > -1) { // id is always 36 bytes var idBytes = new byte[36]; a.Read(idBytes, 0, 36); m.Id = Guid.Parse(e.GetString(idBytes)); isIdFound = true; continue; } } // this must be a binary file attachment, rip file apart a.Position = 0; //1) remove boundary while (a.ReadByte() != 13) { } a.Position++; // read past lf //2) parse form data var startOfFormData = a.Position; while (a.ReadByte() != 13) { } a.Position++; // read past lf var endOfFormdata = a.Position; a.Position = startOfFormData; // create a byte array to hold form data var formDataBuffer = new byte[endOfFormdata - startOfFormData]; a.Read(formDataBuffer, 0, formDataBuffer.Length); var formData = e.GetString(formDataBuffer); // form data looks like Content-Disposition: form-data; name="Authentication.CreateAccount_files_0_0"; filename="tiny.gif" //3) parse content type a.Position = endOfFormdata; while (a.ReadByte() != 13) { } a.Position++; // read past lf var endOfContentType = a.Position - 2; // -2 so we don't capture \r\n in content type string var contentTypeBuffer = new byte[endOfContentType - endOfFormdata]; a.Position = endOfFormdata; a.Read(contentTypeBuffer, 0, contentTypeBuffer.Length); var contentType = e.GetString(contentTypeBuffer); //4) remove extra \r\n\r\n a.Position = endOfContentType + 4; //5) the rest is the binary file // read it and store it in a new file var binLength = a.Length - a.Position; var bufferSize = f > binLength ? binLength : f; var remainingChunckSize = binLength % bufferSize; var bytesRead = 0L; var tempFileName = Path.GetTempFileName(); using (var bin = new FileStream(tempFileName, FileMode.OpenOrCreate)) { while (binLength - remainingChunckSize > bytesRead) { var buffer = new byte[bufferSize]; bytesRead += a.Read(buffer, 0, (int)bufferSize); bin.Write(buffer, 0, (int)bufferSize); } if (remainingChunckSize > 0) { var buffer = new byte[remainingChunckSize]; a.Read(buffer, 0, (int)remainingChunckSize); bin.Write(buffer, 0, (int)remainingChunckSize); } // make mapper ref var regex = new Regex(@".*name=""([^""]+)""; filename=""([^""]+)"""); var matches = regex.Match(formData); var methodSig = matches.Groups[1].Value; var oFileName = matches.Groups[2].Value; regex = new Regex(@"file:::(.*)_(\d)_files_(\d+)_(\d+)"); var matchesMetthod = regex.Match(methodSig); var method = matchesMetthod.Groups[1].Value; var methodInstance = matchesMetthod.Groups[2].Value; var fileField = matchesMetthod.Groups[3].Value; var fileNumber = matchesMetthod.Groups[4].Value; var fm = new UploadedFile { ContentType = contentType.Replace("Content-Type: ", ""), // remove the words "Content-Type: " from value Path = tempFileName, FileNumber = int.Parse(fileNumber), Instance = int.Parse(methodInstance), Method = method, FieldNumber = int.Parse(fileField), OriginalFileName = oFileName }; m.Files.Add(fm); } } // delete temp file File.Delete(n); } } File.Delete(fst); d.BytesTotal = 0; d.BytesRead = 0; d.Complete = true; d.LastUpdated = DateTime.Now; d.Message = "Upload Complete."; RaiseOnUploadStatus(d); return m; }