protected virtual void ScanHeaderFieldValue(HeaderBuffer headerBuffer, string decapitalizedFieldName, int startOffset) { // argument checks Debug.Assert(headerBuffer != null); string value; switch (decapitalizedFieldName) { case "content-length": value = headerBuffer.ReadFieldASCIIValue(decapitalize: false); this.ContentLength = HeaderBuffer.ParseHeaderFieldValueAsLong(value); break; case "transfer-encoding": value = headerBuffer.ReadFieldASCIIValue(decapitalize: true); if (HeaderBuffer.IsChunkedSpecified(value) == false) { throw MessageBuffer.CreateBadRequestException(); } this.ContentLength = -1; // -1 means 'chunked' break; default: // just skip headerBuffer.SkipField(); break; } }
protected override void ScanHeaderFieldValue(HeaderBuffer headerBuffer, string decapitalizedFieldName, int startOffset) { switch (decapitalizedFieldName) { case "host": // save its value, but its span is unnecessary if (this.HostEndPoint == null) { string hostValue = HeaderBuffer.TrimHeaderFieldValue(headerBuffer.ReadFieldASCIIValue(false)); this.HostEndPoint = Util.ParseEndPoint(hostValue, canOmitPort: true); } else { headerBuffer.SkipField(); } this.HostSpan = new Span(startOffset, headerBuffer.CurrentOffset); break; case "proxy-authorization": // save its span, but its value is unnecessary headerBuffer.SkipField(); this.ProxyAuthorizationSpan = new Span(startOffset, headerBuffer.CurrentOffset); break; default: base.ScanHeaderFieldValue(headerBuffer, decapitalizedFieldName, startOffset); break; } }
protected virtual bool ScanHeaderField(HeaderBuffer headerBuffer) { // argument checks Debug.Assert(headerBuffer != null); // read the first byte bool emptyLine; Func <byte, bool> hasInterest = (b) => { char c = Char.ToLower((char)b); return(IsInterestingHeaderFieldFirstChar(c)); }; byte firstByte = headerBuffer.ReadFieldNameFirstByte(); if (firstByte == MessageBuffer.CR || hasInterest(firstByte) == false) { // no interest, just skip this line emptyLine = headerBuffer.SkipField(firstByte); } else { // scan this field int startOffset = headerBuffer.CurrentOffset - 1; // Note we have already read one byte string decapitalizedFieldName = headerBuffer.ReadFieldName(firstByte); ScanHeaderFieldValue(headerBuffer, decapitalizedFieldName, startOffset); emptyLine = false; } return(emptyLine); }
protected override void ScanStartLine(HeaderBuffer headerBuffer) { // argument checks Debug.Assert(headerBuffer != null); // read items string method = headerBuffer.ReadSpaceSeparatedItem(skipItem: false, decapitalize: false, lastItem: false); int targetStart = headerBuffer.CurrentOffset; string target = headerBuffer.ReadSpaceSeparatedItem(skipItem: false, decapitalize: false, lastItem: false); this.RequestTargetSpan = new Span(targetStart, headerBuffer.CurrentOffset - 1); string httpVersion = headerBuffer.ReadSpaceSeparatedItem(skipItem: false, decapitalize: false, lastItem: true); // set message properties this.Method = method; this.Version = HeaderBuffer.ParseVersion(httpVersion); if (string.IsNullOrEmpty(target) == false) { char firstChar = target[0]; if (firstChar != '/' && firstChar != '*') { // absolute-form or authority-form Uri uri = null; DnsEndPoint hostEndPoint = null; if (target.Contains("://")) { // maybe absolute-form try { uri = new Uri(target); hostEndPoint = new DnsEndPoint(uri.Host, uri.Port); } catch { // continue } } else { // maybe authority-form try { // assume https scheme uri = new Uri($"https://{target}"); hostEndPoint = new DnsEndPoint(uri.Host, uri.Port); uri = null; // this.Uri is not set in case of authority-form } catch { // continue } } this.HostEndPoint = hostEndPoint; this.TargetUri = uri; } } return; }
protected virtual void WriteHeader(Stream output, HeaderBuffer headerBuffer, IEnumerable <MessageBuffer.Modification> modifications) { // argument checks Debug.Assert(output != null); Debug.Assert(headerBuffer != null); // modifications can be null // write message header headerBuffer.WriteHeader(output, modifications); }
protected Message() { // initialize members this.headerBuffer = new HeaderBuffer(); this.bodyBuffer = new BodyBuffer(this.headerBuffer); this.modifications = new List <MessageBuffer.Modification>(); ResetThisClassLevelMessageProperties(); this.ReadingState = MessageReadingState.Error; return; }
public void WriteBody(Stream output) { // argument checks if (output == null) { throw new ArgumentNullException(nameof(output)); } if (output.CanWrite == false) { throw new ArgumentException("It is not writable", nameof(output)); } // state checks if (this.bodyLength == 0) { // no body output.Flush(); return; } // write the body // Note the media where the body is stored depends on its size. if (this.bodyStream != null) { // the body was stored in the stream (large/medium body or chunked body) this.bodyStream.Seek(0, SeekOrigin.Begin); this.bodyStream.CopyTo(output); } else { Debug.Assert(0 <= this.bodyLength && this.bodyLength <= int.MaxValue); int bodyLengthInInt = (int)this.bodyLength; if (this.MemoryBlock != null) { // the body was stored in the memoryBlock (small body) Debug.Assert(this.Next == 0); Debug.Assert(this.Limit == bodyLengthInInt); WriteTo(output, 0, bodyLengthInInt); } else { // the body was stored in the rest of the header buffer (tiny body) HeaderBuffer headerBuffer = this.headerBuffer; // Be careful not to write bytes of the next message. Debug.Assert(bodyLengthInInt <= headerBuffer.Limit - headerBuffer.Next); WriteTo(headerBuffer, output, headerBuffer.Next, bodyLengthInInt); } } output.Flush(); return; }
public BodyBuffer(HeaderBuffer headerBuffer) : base() { // argument checks if (headerBuffer == null) { throw new ArgumentNullException(nameof(headerBuffer)); } // initialize members this.headerBuffer = headerBuffer; return; }
protected bool ReadHeader() { // read header part from the input bool read = false; try { HeaderBuffer headerBuffer = this.headerBuffer; // state checks switch (this.ReadingState) { case MessageReadingState.Error: throw CreateNoIOException(); case MessageReadingState.None: break; default: Reset(); break; } Debug.Assert(this.headerBuffer.CanRead); // read start line ScanStartLine(headerBuffer); // read header fields bool emptyLine; do { emptyLine = ScanHeaderField(headerBuffer); } while (emptyLine == false); // update state int endOfHeaderOffset = headerBuffer.CurrentOffset - 2; // subtract empty line bytes this.EndOfHeaderFields = new Span(endOfHeaderOffset, endOfHeaderOffset); this.ReadingState = MessageReadingState.Header; read = true; } catch (EndOfStreamException) { // no data from input // Note that incomplete data results an exception other than EndOfStreamException. Debug.Assert(this.ReadingState == MessageReadingState.None); Debug.Assert(read == false); // continue } catch { this.ReadingState = MessageReadingState.Error; throw; } return(read); }
private void StoreBody(Stream output, long contentLength) { // argument checks Debug.Assert(output != null); Debug.Assert(output.CanWrite); Debug.Assert(0 <= contentLength); // state checks HeaderBuffer headerBuffer = this.headerBuffer; Debug.Assert(headerBuffer != null); // write body bytes in the header buffer to the output int bodyBytesInHeaderBufferLength = headerBuffer.Limit - headerBuffer.Next; if (bodyBytesInHeaderBufferLength <= contentLength) { WriteTo(headerBuffer, output, headerBuffer.Next, bodyBytesInHeaderBufferLength); } else { // there is data of the next message bodyBytesInHeaderBufferLength = (int)contentLength; WriteTo(headerBuffer, output, headerBuffer.Next, bodyBytesInHeaderBufferLength); headerBuffer.SetPrefetchedBytes(headerBuffer.Next + bodyBytesInHeaderBufferLength); } // write rest of body bytes to the output // the memoryBlock is used as just intermediate buffer instead of storing media byte[] memoryBlock = EnsureMemoryBlockAllocated(); long remaining = contentLength - bodyBytesInHeaderBufferLength; while (0 < remaining) { long count = Math.Min(remaining, memoryBlock.Length); int readCount = ReadBytes(memoryBlock, 0, (int)count); Debug.Assert(0 < readCount); // ReadBytes() throws an exception on end of stream output.Write(memoryBlock, 0, readCount); remaining -= readCount; } return; }
protected override void ScanHeaderFieldValue(HeaderBuffer headerBuffer, string decapitalizedFieldName, int startOffset) { switch (decapitalizedFieldName) { case "content-length": case "transfer-encoding": // Do not parse these header fields in response of HEAD method // otherwise it will be blocked to try to read body stream after this. if (this.Request?.Method != "HEAD") { base.ScanHeaderFieldValue(headerBuffer, decapitalizedFieldName, startOffset); } break; case "connection": // ToDo: exact parsing string value = headerBuffer.ReadFieldASCIIValue(false); if (value.Contains("close")) { this.KeepAliveEnabled = false; } else if (value.Contains("keep-alive")) { this.KeepAliveEnabled = true; } break; case "proxy-authenticate": // save its span and value this.ProxyAuthenticateValue = headerBuffer.ReadFieldASCIIValue(false); this.ProxyAuthenticateSpan = new Span(startOffset, headerBuffer.CurrentOffset); break; default: base.ScanHeaderFieldValue(headerBuffer, decapitalizedFieldName, startOffset); break; } }
protected override void ScanStartLine(HeaderBuffer headerBuffer) { // argument checks Debug.Assert(headerBuffer != null); // read items string version = headerBuffer.ReadSpaceSeparatedItem(skipItem: false, decapitalize: false, lastItem: false); string statusCode = headerBuffer.ReadSpaceSeparatedItem(skipItem: false, decapitalize: false, lastItem: false); headerBuffer.ReadSpaceSeparatedItem(skipItem: true, decapitalize: false, lastItem: true); // set message properties Version httpVersion = HeaderBuffer.ParseVersion(version); this.Version = httpVersion; this.StatusCode = HeaderBuffer.ParseStatusCode(statusCode); if (httpVersion.Major == 1 && httpVersion.Minor == 0) { // in HTTP/1.0, keep-alive is disabled by default this.KeepAliveEnabled = false; } return; }
protected abstract void ScanStartLine(HeaderBuffer headerBuffer);
public void SkipBody(long contentLength) { // argument checks if (contentLength < 0) { throw new ArgumentOutOfRangeException(nameof(contentLength)); } // state checks HeaderBuffer headerBuffer = this.headerBuffer; if (headerBuffer == null) { throw new InvalidOperationException(); } if (this.bodyLength != 0) { throw new InvalidOperationException("This buffer has already handled a message body."); } Debug.Assert(this.bodyStream == null); // skip the body storing its bytes // The media to store body depends on its length and the current margin. Stream bodyStream = null; try { // Note that the some body bytes may be read into the header buffer. // Unread bytes in the header buffer at this point are body bytes. // That is, range [headerBuffer.Next - headerBuffer.Limit). int bodyBytesInHeaderBufferLength = headerBuffer.Limit - headerBuffer.Next; long restLen = contentLength - bodyBytesInHeaderBufferLength; if (restLen <= headerBuffer.Margin) { // The body is to be stored in the rest of header buffer. (tiny body) // read body bytes into the rest of the header buffer if (0 <= restLen) { Debug.Assert(restLen <= int.MaxValue); FillBuffer(headerBuffer, (int)restLen); Debug.Assert(contentLength == headerBuffer.Limit - headerBuffer.Next); Debug.Assert(this.MemoryBlock == null); } else { // there is data of the next message Debug.Assert(contentLength <= int.MaxValue); int offset = headerBuffer.Next + (int)contentLength; headerBuffer.SetPrefetchedBytes(offset); } } else { byte[] memoryBlock = EnsureMemoryBlockAllocated(); if (contentLength <= memoryBlock.Length) { // The body is to be stored in a memory block. (small body) // copy body bytes in the header buffer to this buffer CopyFrom(headerBuffer, headerBuffer.Next, bodyBytesInHeaderBufferLength); // read rest of body bytes Debug.Assert(restLen <= int.MaxValue); FillBuffer((int)restLen); Debug.Assert(contentLength == (this.Limit - this.Next)); } else { // The body is to be stored in a stream. // determine which medium is used to store body, memory or file if (contentLength <= BodyStreamThreshold) { // use memory stream (medium body) Debug.Assert(contentLength <= int.MaxValue); bodyStream = new MemoryStream((int)contentLength); } else { // use temp file stream (large body) bodyStream = Util.CreateTempFileStream(); } StoreBody(bodyStream, contentLength); } } // update state this.bodyLength = contentLength; this.bodyStream = bodyStream; } catch { DisposableUtil.DisposeSuppressingErrors(bodyStream); throw; } return; }