private static string VerifyBoundary(string boundary) { if ((null == boundary) || (70 < boundary.Length)) { throw Error.BatchStreamInvalidDelimiter(boundary); } foreach (char c in boundary) { if ((127 < (int)c) || Char.IsWhiteSpace(c) || Char.IsControl(c)) { throw Error.BatchStreamInvalidDelimiter(boundary); } } return("--" + boundary); }
internal bool MoveNext() { #region dispose previous content stream if (null == this.reader || this.disposeWithContentStreamDispose) { return(false); } if (null != this.contentStream) { this.contentStream.Dispose(); } Debug.Assert(0 <= this.byteLength, "negative byteLength"); Debug.Assert(0 <= this.batchLength, "negative batchLength"); #endregion #region initialize start state to EndBatch or EndChangeSet switch (this.batchState) { case BatchStreamState.EndBatch: Debug.Assert(null == this.batchBoundary, "non-null batch boundary"); Debug.Assert(null == this.changesetBoundary, "non-null changesetBoundary boundary"); throw Error.BatchStreamInvalidBatchFormat(); case BatchStreamState.Get: case BatchStreamState.GetResponse: this.ClearPreviousOperationInformation(); goto case BatchStreamState.StartBatch; case BatchStreamState.StartBatch: case BatchStreamState.EndChangeSet: Debug.Assert(null != this.batchBoundary, "null batch boundary"); Debug.Assert(null == this.changesetBoundary, "non-null changeset boundary"); this.batchState = BatchStreamState.EndBatch; this.batchLength = Int32.MaxValue; break; case BatchStreamState.BeginChangeSet: Debug.Assert(null != this.batchBoundary, "null batch boundary"); Debug.Assert(null != this.contentHeaders, "null contentHeaders"); Debug.Assert(null != this.changesetBoundary, "null changeset boundary"); this.contentHeaders = null; this.changesetEncoding = null; this.batchState = BatchStreamState.EndChangeSet; break; case BatchStreamState.ChangeResponse: case BatchStreamState.Delete: Debug.Assert(null != this.changesetBoundary, "null changeset boundary"); this.ClearPreviousOperationInformation(); this.batchState = BatchStreamState.EndChangeSet; break; case BatchStreamState.Post: case BatchStreamState.Put: case BatchStreamState.Merge: Debug.Assert(null != this.changesetBoundary, "null changeset boundary"); this.batchState = BatchStreamState.EndChangeSet; break; default: Debug.Assert(false, "unknown state"); throw Error.BatchStreamInvalidBatchFormat(); } Debug.Assert(null == this.contentHeaders, "non-null content headers"); Debug.Assert(null == this.contentStream, "non-null content stream"); Debug.Assert(null == this.statusCode, "non-null statusCode"); Debug.Assert( this.batchState == BatchStreamState.EndBatch || this.batchState == BatchStreamState.EndChangeSet, "unexpected state at start"); #endregion #region read --delimiter string delimiter = this.ReadLine(); if (String.IsNullOrEmpty(delimiter)) { delimiter = this.ReadLine(); } if (String.IsNullOrEmpty(delimiter)) { throw Error.BatchStreamInvalidBatchFormat(); } if (delimiter.EndsWith("--", StringComparison.Ordinal)) { delimiter = delimiter.Substring(0, delimiter.Length - 2); if ((null != this.changesetBoundary) && (delimiter == this.changesetBoundary)) { Debug.Assert(this.batchState == BatchStreamState.EndChangeSet, "bad changeset boundary state"); this.changesetBoundary = null; return(true); } else if (delimiter == this.batchBoundary) { if (BatchStreamState.EndChangeSet == this.batchState) { throw Error.BatchStreamMissingEndChangesetDelimiter(); } this.changesetBoundary = null; this.batchBoundary = null; if (this.byteLength != 0) { throw Error.BatchStreamMoreDataAfterEndOfBatch(); } return(false); } else { throw Error.BatchStreamInvalidDelimiter(delimiter); } } else if ((null != this.changesetBoundary) && (delimiter == this.changesetBoundary)) { Debug.Assert(this.batchState == BatchStreamState.EndChangeSet, "bad changeset boundary state"); } else if (delimiter == this.batchBoundary) { if (this.batchState != BatchStreamState.EndBatch) { if (this.batchState == BatchStreamState.EndChangeSet) { throw Error.BatchStreamMissingEndChangesetDelimiter(); } else { throw Error.BatchStreamInvalidBatchFormat(); } } } else { throw Error.BatchStreamInvalidDelimiter(delimiter); } #endregion #region read header with values in this form (([^:]*:.*)\r\n)*\r\n this.ReadContentHeaders(); #endregion #region should start changeset? string contentType; bool readHttpHeaders = false; if (this.contentHeaders.TryGetValue(XmlConstants.HttpContentType, out contentType)) { if (String.Equals(contentType, XmlConstants.MimeApplicationHttp, StringComparison.OrdinalIgnoreCase)) { if (this.contentHeaders.Count != 2) { throw Error.BatchStreamInvalidNumberOfHeadersAtOperationStart( XmlConstants.HttpContentType, XmlConstants.HttpContentTransferEncoding); } string transferEncoding; if (!this.contentHeaders.TryGetValue(XmlConstants.HttpContentTransferEncoding, out transferEncoding) || XmlConstants.BatchRequestContentTransferEncoding != transferEncoding) { throw Error.BatchStreamMissingOrInvalidContentEncodingHeader( XmlConstants.HttpContentTransferEncoding, XmlConstants.BatchRequestContentTransferEncoding); } readHttpHeaders = true; } else if (BatchStreamState.EndBatch == this.batchState) { string boundary; Encoding encoding; if (GetBoundaryAndEncodingFromMultipartMixedContentType(contentType, out boundary, out encoding)) { this.changesetBoundary = VerifyBoundary(boundary); this.changesetEncoding = encoding; this.batchState = BatchStreamState.BeginChangeSet; } else { throw Error.BatchStreamInvalidContentTypeSpecified( XmlConstants.HttpContentType, contentType, XmlConstants.MimeApplicationHttp, XmlConstants.MimeMultiPartMixed); } if (this.contentHeaders.Count > 2 || (this.contentHeaders.Count == 2 && !this.contentHeaders.ContainsKey(XmlConstants.HttpContentLength))) { throw Error.BatchStreamInvalidNumberOfHeadersAtChangeSetStart(XmlConstants.HttpContentType, XmlConstants.HttpContentLength); } } else { throw Error.BatchStreamInvalidContentTypeSpecified( XmlConstants.HttpContentType, contentType, XmlConstants.MimeApplicationHttp, XmlConstants.MimeMultiPartMixed); } } else { throw Error.BatchStreamMissingContentTypeHeader(XmlConstants.HttpContentType); } #endregion #region what is the operation and uri? if (readHttpHeaders) { this.ReadHttpHeaders(); this.contentHeaders.TryGetValue(XmlConstants.HttpContentType, out contentType); } #endregion #region does content have a fixed length? string text = null; int length = -1; if (this.contentHeaders.TryGetValue(XmlConstants.HttpContentLength, out text)) { length = Int32.Parse(text, CultureInfo.InvariantCulture); if (length < 0) { throw Error.BatchStreamInvalidContentLengthSpecified(text); } if (this.batchState == BatchStreamState.BeginChangeSet) { this.batchLength = length; } else if (length != 0) { Debug.Assert( this.batchState == BatchStreamState.Delete || this.batchState == BatchStreamState.Get || this.batchState == BatchStreamState.Post || this.batchState == BatchStreamState.Put || this.batchState == BatchStreamState.Merge, "unexpected contentlength location"); this.contentStream = new StreamWithLength(this, length); } } else { if (this.batchState == BatchStreamState.EndBatch) { this.batchLength = Int32.MaxValue; } if (this.batchState != BatchStreamState.BeginChangeSet) { this.contentStream = new StreamWithDelimiter(this); } } #endregion Debug.Assert( this.batchState == BatchStreamState.BeginChangeSet || (this.batchRequest && (this.batchState == BatchStreamState.Delete || this.batchState == BatchStreamState.Get || this.batchState == BatchStreamState.Post || this.batchState == BatchStreamState.Put || this.batchState == BatchStreamState.Merge)) || (!this.batchRequest && (this.batchState == BatchStreamState.GetResponse || this.batchState == BatchStreamState.ChangeResponse)), "unexpected state at return"); #region enforce if contentStream is expected, caller needs to enforce if contentStream is not expected if (null == this.contentStream) { switch (this.batchState) { case BatchStreamState.BeginChangeSet: case BatchStreamState.Delete: case BatchStreamState.Get: case BatchStreamState.ChangeResponse: case BatchStreamState.GetResponse: break; case BatchStreamState.Post: case BatchStreamState.Put: case BatchStreamState.Merge: default: throw Error.BatchStreamContentExpected(this.batchState); } } #endregion #region enforce if contentType not is expected, caller needs to enforce if contentType is expected if (!String.IsNullOrEmpty(contentType)) { switch (this.batchState) { case BatchStreamState.BeginChangeSet: case BatchStreamState.Post: case BatchStreamState.Put: case BatchStreamState.Merge: case BatchStreamState.GetResponse: case BatchStreamState.ChangeResponse: break; case BatchStreamState.Get: case BatchStreamState.Delete: default: throw Error.BatchStreamContentUnexpected(this.batchState); } } #endregion return(true); }