public void AddBodyHttpData(IInterfaceHttpData data) { Contract.Requires(data != null); if (this.headerFinalized) { throw new ErrorDataEncoderException("Cannot add value once finalized"); } this.bodyListDatas.Add(data); if (!this.isMultipart) { if (data is IAttribute dataAttribute) { try { // name=value& with encoded name and attribute string key = this.EncodeAttribute(dataAttribute.Name, this.charset); string value = this.EncodeAttribute(dataAttribute.Value, this.charset); IAttribute newattribute = this.factory.CreateAttribute(this.request, key, value); this.MultipartHttpDatas.Add(newattribute); this.globalBodySize += newattribute.Name.Length + 1 + newattribute.Length + 1; } catch (IOException e) { throw new ErrorDataEncoderException(e); } } else if (data is IFileUpload fileUpload) { // since not Multipart, only name=filename => Attribute // name=filename& with encoded name and filename string key = this.EncodeAttribute(fileUpload.Name, this.charset); string value = this.EncodeAttribute(fileUpload.FileName, this.charset); IAttribute newattribute = this.factory.CreateAttribute(this.request, key, value); this.MultipartHttpDatas.Add(newattribute); this.globalBodySize += newattribute.Name.Length + 1 + newattribute.Length + 1; } return; } // Logic: // if not Attribute: // add Data to body list // if (duringMixedMode) // add endmixedmultipart delimiter // currentFileUpload = null // duringMixedMode = false; // add multipart delimiter, multipart body header and Data to multipart list // reset currentFileUpload, duringMixedMode // if FileUpload: take care of multiple file for one field => mixed mode // if (duringMixedMode) // if (currentFileUpload.name == data.name) // add mixedmultipart delimiter, mixedmultipart body header and Data to multipart list // else // add endmixedmultipart delimiter, multipart body header and Data to multipart list // currentFileUpload = data // duringMixedMode = false; // else // if (currentFileUpload.name == data.name) // change multipart body header of previous file into multipart list to // mixedmultipart start, mixedmultipart body header // add mixedmultipart delimiter, mixedmultipart body header and Data to multipart list // duringMixedMode = true // else // add multipart delimiter, multipart body header and Data to multipart list // currentFileUpload = data // duringMixedMode = false; // Do not add last delimiter! Could be: // if duringmixedmode: endmixedmultipart + endmultipart // else only endmultipart // if (data is IAttribute attribute) { InternalAttribute internalAttribute; if (this.duringMixedMode) { internalAttribute = new InternalAttribute(this.charset); internalAttribute.AddValue($"\r\n--{this.MultipartMixedBoundary}--"); this.MultipartHttpDatas.Add(internalAttribute); this.MultipartMixedBoundary = null; this.currentFileUpload = null; this.duringMixedMode = false; } internalAttribute = new InternalAttribute(this.charset); if (this.MultipartHttpDatas.Count > 0) { // previously a data field so CRLF internalAttribute.AddValue("\r\n"); } internalAttribute.AddValue($"--{this.MultipartDataBoundary}\r\n"); // content-disposition: form-data; name="field1" internalAttribute.AddValue($"{HttpHeaderNames.ContentDisposition}: {HttpHeaderValues.FormData}; {HttpHeaderValues.Name}=\"{attribute.Name}\"\r\n"); // Add Content-Length: xxx internalAttribute.AddValue($"{HttpHeaderNames.ContentLength}: {attribute.Length}\r\n"); Encoding localcharset = attribute.Charset; if (localcharset != null) { // Content-Type: text/plain; charset=charset internalAttribute.AddValue($"{HttpHeaderNames.ContentType}: {HttpPostBodyUtil.DefaultTextContentType}; {HttpHeaderValues.Charset}={localcharset.WebName}\r\n"); } // CRLF between body header and data internalAttribute.AddValue("\r\n"); this.MultipartHttpDatas.Add(internalAttribute); this.MultipartHttpDatas.Add(data); this.globalBodySize += attribute.Length + internalAttribute.Size; } else if (data is IFileUpload fileUpload) { var internalAttribute = new InternalAttribute(this.charset); if (this.MultipartHttpDatas.Count > 0) { // previously a data field so CRLF internalAttribute.AddValue("\r\n"); } bool localMixed; if (this.duringMixedMode) { if (this.currentFileUpload != null && this.currentFileUpload.Name.Equals(fileUpload.Name)) { // continue a mixed mode localMixed = true; } else { // end a mixed mode // add endmixedmultipart delimiter, multipart body header // and // Data to multipart list internalAttribute.AddValue($"--{this.MultipartMixedBoundary}--"); this.MultipartHttpDatas.Add(internalAttribute); this.MultipartMixedBoundary = null; // start a new one (could be replaced if mixed start again // from here internalAttribute = new InternalAttribute(this.charset); internalAttribute.AddValue("\r\n"); localMixed = false; // new currentFileUpload and no more in Mixed mode this.currentFileUpload = fileUpload; this.duringMixedMode = false; } } else { if (this.encoderMode != EncoderMode.HTML5 && this.currentFileUpload != null && this.currentFileUpload.Name.Equals(fileUpload.Name)) { // create a new mixed mode (from previous file) // change multipart body header of previous file into // multipart list to // mixedmultipart start, mixedmultipart body header // change Internal (size()-2 position in multipartHttpDatas) // from (line starting with *) // --AaB03x // * Content-Disposition: form-data; name="files"; // filename="file1.txt" // Content-Type: text/plain // to (lines starting with *) // --AaB03x // * Content-Disposition: form-data; name="files" // * Content-Type: multipart/mixed; boundary=BbC04y // * // * --BbC04y // * Content-Disposition: attachment; filename="file1.txt" // Content-Type: text/plain this.InitMixedMultipart(); var pastAttribute = (InternalAttribute)this.MultipartHttpDatas[this.MultipartHttpDatas.Count - 2]; // remove past size this.globalBodySize -= pastAttribute.Size; StringBuilder replacement = new StringBuilder( 139 + this.MultipartDataBoundary.Length + this.MultipartMixedBoundary.Length * 2 + fileUpload.FileName.Length + fileUpload.Name.Length) .Append("--") .Append(this.MultipartDataBoundary) .Append("\r\n") .Append(HttpHeaderNames.ContentDisposition) .Append(": ") .Append(HttpHeaderValues.FormData) .Append("; ") .Append(HttpHeaderValues.Name) .Append("=\"") .Append(fileUpload.Name) .Append("\"\r\n") .Append(HttpHeaderNames.ContentType) .Append(": ") .Append(HttpHeaderValues.MultipartMixed) .Append("; ") .Append(HttpHeaderValues.Boundary) .Append('=') .Append(this.MultipartMixedBoundary) .Append("\r\n\r\n") .Append("--") .Append(this.MultipartMixedBoundary) .Append("\r\n") .Append(HttpHeaderNames.ContentDisposition) .Append(": ") .Append(HttpHeaderValues.Attachment); if (fileUpload.FileName.Length > 0) { replacement.Append("; ") .Append(HttpHeaderValues.FileName) .Append("=\"") .Append(fileUpload.FileName) .Append('"'); } replacement.Append("\r\n"); pastAttribute.SetValue(replacement.ToString(), 1); pastAttribute.SetValue("", 2); // update past size this.globalBodySize += pastAttribute.Size; // now continue // add mixedmultipart delimiter, mixedmultipart body header // and // Data to multipart list localMixed = true; this.duringMixedMode = true; } else { // a simple new multipart // add multipart delimiter, multipart body header and Data // to multipart list localMixed = false; this.currentFileUpload = fileUpload; this.duringMixedMode = false; } } if (localMixed) { // add mixedmultipart delimiter, mixedmultipart body header and // Data to multipart list internalAttribute.AddValue($"--{this.MultipartMixedBoundary}\r\n"); if (fileUpload.FileName.Length == 0) { // Content-Disposition: attachment internalAttribute.AddValue($"{HttpHeaderNames.ContentDisposition}: {HttpHeaderValues.Attachment}\r\n"); } else { // Content-Disposition: attachment; filename="file1.txt" internalAttribute.AddValue($"{HttpHeaderNames.ContentDisposition}: {HttpHeaderValues.Attachment}; {HttpHeaderValues.FileName}=\"{fileUpload.FileName}\"\r\n"); } } else { internalAttribute.AddValue($"--{this.MultipartDataBoundary}\r\n"); if (fileUpload.FileName.Length == 0) { // Content-Disposition: form-data; name="files"; internalAttribute.AddValue($"{HttpHeaderNames.ContentDisposition}: {HttpHeaderValues.FormData}; {HttpHeaderValues.Name}=\"{fileUpload.Name}\"\r\n"); } else { // Content-Disposition: form-data; name="files"; // filename="file1.txt" internalAttribute.AddValue($"{HttpHeaderNames.ContentDisposition}: {HttpHeaderValues.FormData}; {HttpHeaderValues.Name}=\"{fileUpload.Name}\"; {HttpHeaderValues.FileName}=\"{fileUpload.FileName}\"\r\n"); } } // Add Content-Length: xxx internalAttribute.AddValue($"{HttpHeaderNames.ContentLength}: {fileUpload.Length}\r\n"); // Content-Type: image/gif // Content-Type: text/plain; charset=ISO-8859-1 // Content-Transfer-Encoding: binary internalAttribute.AddValue($"{HttpHeaderNames.ContentType}: {fileUpload.ContentType}"); string contentTransferEncoding = fileUpload.ContentTransferEncoding; if (contentTransferEncoding != null && contentTransferEncoding.Equals(HttpPostBodyUtil.TransferEncodingMechanism.Binary.Value)) { internalAttribute.AddValue($"\r\n{HttpHeaderNames.ContentTransferEncoding}: {HttpPostBodyUtil.TransferEncodingMechanism.Binary.Value}\r\n\r\n"); } else if (fileUpload.Charset != null) { internalAttribute.AddValue($"; {HttpHeaderValues.Charset}={fileUpload.Charset.WebName}\r\n\r\n"); } else { internalAttribute.AddValue("\r\n\r\n"); } this.MultipartHttpDatas.Add(internalAttribute); this.MultipartHttpDatas.Add(data); this.globalBodySize += fileUpload.Length + internalAttribute.Size; } }
public IHttpRequest FinalizeRequest() { // Finalize the multipartHttpDatas if (!this.headerFinalized) { if (this.isMultipart) { var attribute = new InternalAttribute(this.charset); if (this.duringMixedMode) { attribute.AddValue($"\r\n--{this.MultipartMixedBoundary}--"); } attribute.AddValue($"\r\n--{this.MultipartDataBoundary}--\r\n"); this.MultipartHttpDatas.Add(attribute); this.MultipartMixedBoundary = null; this.currentFileUpload = null; this.duringMixedMode = false; this.globalBodySize += attribute.Size; } this.headerFinalized = true; } else { throw new ErrorDataEncoderException("Header already encoded"); } HttpHeaders headers = this.request.Headers; IList <ICharSequence> contentTypes = headers.GetAll(HttpHeaderNames.ContentType); IList <ICharSequence> transferEncoding = headers.GetAll(HttpHeaderNames.TransferEncoding); if (contentTypes != null) { headers.Remove(HttpHeaderNames.ContentType); foreach (ICharSequence contentType in contentTypes) { // "multipart/form-data; boundary=--89421926422648" string lowercased = contentType.ToString().ToLower(); if (lowercased.StartsWith(HttpHeaderValues.MultipartFormData.ToString()) || lowercased.StartsWith(HttpHeaderValues.ApplicationXWwwFormUrlencoded.ToString())) { // ignore } else { headers.Add(HttpHeaderNames.ContentType, contentType); } } } if (this.isMultipart) { string value = $"{HttpHeaderValues.MultipartFormData}; {HttpHeaderValues.Boundary}={this.MultipartDataBoundary}"; headers.Add(HttpHeaderNames.ContentType, value); } else { // Not multipart headers.Add(HttpHeaderNames.ContentType, HttpHeaderValues.ApplicationXWwwFormUrlencoded); } // Now consider size for chunk or not long realSize = this.globalBodySize; if (this.isMultipart) { this.iterator = new ListIterator(this.MultipartHttpDatas); } else { realSize -= 1; // last '&' removed this.iterator = new ListIterator(this.MultipartHttpDatas); } headers.Set(HttpHeaderNames.ContentLength, Convert.ToString(realSize)); if (realSize > HttpPostBodyUtil.ChunkSize || this.isMultipart) { this.isChunked = true; if (transferEncoding != null) { headers.Remove(HttpHeaderNames.TransferEncoding); foreach (ICharSequence v in transferEncoding) { if (HttpHeaderValues.Chunked.ContentEqualsIgnoreCase(v)) { // ignore } else { headers.Add(HttpHeaderNames.TransferEncoding, v); } } } HttpUtil.SetTransferEncodingChunked(this.request, true); // wrap to hide the possible content return(new WrappedHttpRequest(this.request)); } else { // get the only one body and set it to the request IHttpContent chunk = this.NextChunk(); if (this.request is IFullHttpRequest fullRequest) { IByteBuffer chunkContent = chunk.Content; if (!ReferenceEquals(fullRequest.Content, chunkContent)) { fullRequest.Content.Clear(); fullRequest.Content.WriteBytes(chunkContent); chunkContent.Release(); } return(fullRequest); } else { return(new WrappedFullHttpRequest(this.request, chunk)); } } }
public void AddBodyHttpData(IInterfaceHttpData data) { if (data is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.data); } if (this.headerFinalized) { ThrowHelper.ThrowErrorDataEncoderException_CannotAddValue(); } this.bodyListDatas.Add(data); if (!this.isMultipart) { switch (data) { case IAttribute dataAttribute: try { // name=value& with encoded name and attribute string key = this.EncodeAttribute(dataAttribute.Name, this.charset); string value = this.EncodeAttribute(dataAttribute.Value, this.charset); IAttribute newattribute = this.factory.CreateAttribute(this.request, key, value); this.MultipartHttpDatas.Add(newattribute); this.globalBodySize += newattribute.Name.Length + 1 + newattribute.Length + 1; } catch (IOException e) { ThrowHelper.ThrowErrorDataEncoderException(e); } break; case IFileUpload fileUpload: // since not Multipart, only name=filename => Attribute // name=filename& with encoded name and filename string key0 = this.EncodeAttribute(fileUpload.Name, this.charset); string value0 = this.EncodeAttribute(fileUpload.FileName, this.charset); IAttribute newattribute0 = this.factory.CreateAttribute(this.request, key0, value0); this.MultipartHttpDatas.Add(newattribute0); this.globalBodySize += newattribute0.Name.Length + 1 + newattribute0.Length + 1; break; } return; } // Logic: // if not Attribute: // add Data to body list // if (duringMixedMode) // add endmixedmultipart delimiter // currentFileUpload = null // duringMixedMode = false; // add multipart delimiter, multipart body header and Data to multipart list // reset currentFileUpload, duringMixedMode // if FileUpload: take care of multiple file for one field => mixed mode // if (duringMixedMode) // if (currentFileUpload.name == data.name) // add mixedmultipart delimiter, mixedmultipart body header and Data to multipart list // else // add endmixedmultipart delimiter, multipart body header and Data to multipart list // currentFileUpload = data // duringMixedMode = false; // else // if (currentFileUpload.name == data.name) // change multipart body header of previous file into multipart list to // mixedmultipart start, mixedmultipart body header // add mixedmultipart delimiter, mixedmultipart body header and Data to multipart list // duringMixedMode = true // else // add multipart delimiter, multipart body header and Data to multipart list // currentFileUpload = data // duringMixedMode = false; // Do not add last delimiter! Could be: // if duringmixedmode: endmixedmultipart + endmultipart // else only endmultipart // if (data is IAttribute attribute) { InternalAttribute internalAttribute; if (this.duringMixedMode) { internalAttribute = new InternalAttribute(this.charset); internalAttribute.AddValue($"\r\n--{this.MultipartMixedBoundary}--"); this.MultipartHttpDatas.Add(internalAttribute); this.MultipartMixedBoundary = null; this.currentFileUpload = null; this.duringMixedMode = false; } internalAttribute = new InternalAttribute(this.charset); if ((uint)this.MultipartHttpDatas.Count > 0u) { // previously a data field so CRLF internalAttribute.AddValue("\r\n"); } internalAttribute.AddValue($"--{this.MultipartDataBoundary}\r\n"); // content-disposition: form-data; name="field1" internalAttribute.AddValue($"{HttpHeaderNames.ContentDisposition}: {HttpHeaderValues.FormData}; {HttpHeaderValues.Name}=\"{attribute.Name}\"\r\n"); // Add Content-Length: xxx internalAttribute.AddValue($"{HttpHeaderNames.ContentLength}: {attribute.Length}\r\n"); Encoding localcharset = attribute.Charset; if (localcharset is object) { // Content-Type: text/plain; charset=charset internalAttribute.AddValue($"{HttpHeaderNames.ContentType}: {HttpPostBodyUtil.DefaultTextContentType}; {HttpHeaderValues.Charset}={localcharset.WebName}\r\n"); } // CRLF between body header and data internalAttribute.AddValue("\r\n"); this.MultipartHttpDatas.Add(internalAttribute); this.MultipartHttpDatas.Add(data); this.globalBodySize += attribute.Length + internalAttribute.Size; } else if (data is IFileUpload fileUpload) { var internalAttribute = new InternalAttribute(this.charset); if ((uint)this.MultipartHttpDatas.Count > 0u) { // previously a data field so CRLF internalAttribute.AddValue("\r\n"); } bool localMixed; if (this.duringMixedMode) { if (this.currentFileUpload is object && string.Equals(this.currentFileUpload.Name, fileUpload.Name #if NETCOREAPP_3_0_GREATER || NETSTANDARD_2_0_GREATER )) #else , StringComparison.Ordinal)) #endif { // continue a mixed mode localMixed = true; } else { // end a mixed mode // add endmixedmultipart delimiter, multipart body header // and // Data to multipart list internalAttribute.AddValue($"--{this.MultipartMixedBoundary}--"); this.MultipartHttpDatas.Add(internalAttribute); this.MultipartMixedBoundary = null; // start a new one (could be replaced if mixed start again // from here internalAttribute = new InternalAttribute(this.charset); internalAttribute.AddValue("\r\n"); localMixed = false; // new currentFileUpload and no more in Mixed mode this.currentFileUpload = fileUpload; this.duringMixedMode = false; } } else { if (this.encoderMode != EncoderMode.HTML5 && this.currentFileUpload is object && string.Equals(this.currentFileUpload.Name, fileUpload.Name #if NETCOREAPP_3_0_GREATER || NETSTANDARD_2_0_GREATER )) #else , StringComparison.Ordinal)) #endif { // create a new mixed mode (from previous file) // change multipart body header of previous file into // multipart list to // mixedmultipart start, mixedmultipart body header // change Internal (size()-2 position in multipartHttpDatas) // from (line starting with *) // --AaB03x // * Content-Disposition: form-data; name="files"; // filename="file1.txt" // Content-Type: text/plain // to (lines starting with *) // --AaB03x // * Content-Disposition: form-data; name="files" // * Content-Type: multipart/mixed; boundary=BbC04y // * // * --BbC04y // * Content-Disposition: attachment; filename="file1.txt" // Content-Type: text/plain this.InitMixedMultipart(); var pastAttribute = (InternalAttribute)this.MultipartHttpDatas[this.MultipartHttpDatas.Count - 2]; // remove past size this.globalBodySize -= pastAttribute.Size; var replacement = StringBuilderManager.Allocate( 139 + this.MultipartDataBoundary.Length + this.MultipartMixedBoundary.Length * 2 + fileUpload.FileName.Length + fileUpload.Name.Length) .Append("--") .Append(this.MultipartDataBoundary) .Append("\r\n") .Append(HttpHeaderNames.ContentDisposition) .Append(": ") .Append(HttpHeaderValues.FormData) .Append("; ") .Append(HttpHeaderValues.Name) .Append("=\"") .Append(fileUpload.Name) .Append("\"\r\n") .Append(HttpHeaderNames.ContentType) .Append(": ") .Append(HttpHeaderValues.MultipartMixed) .Append("; ") .Append(HttpHeaderValues.Boundary) .Append('=') .Append(this.MultipartMixedBoundary) .Append("\r\n\r\n") .Append("--") .Append(this.MultipartMixedBoundary) .Append("\r\n") .Append(HttpHeaderNames.ContentDisposition) .Append(": ") .Append(HttpHeaderValues.Attachment); if ((uint)fileUpload.FileName.Length > 0u) { replacement.Append("; ") .Append(HttpHeaderValues.FileName) .Append("=\"") .Append(this.currentFileUpload.FileName) .Append('"'); } replacement.Append("\r\n"); pastAttribute.SetValue(StringBuilderManager.ReturnAndFree(replacement), 1); pastAttribute.SetValue("", 2); // update past size this.globalBodySize += pastAttribute.Size; // now continue // add mixedmultipart delimiter, mixedmultipart body header // and // Data to multipart list localMixed = true; this.duringMixedMode = true; }