private static void OnHeader(IHttpHeadersSink sink, object?state, ReadOnlySpan <byte> headerName, ReadOnlySpan <byte> headerValue, HttpHeaderFlags flags) { if (headerName.Length > 1 && headerName[0] == ':') { // TODO: check for pseudo headers. } sink.OnHeader(state, headerName, headerValue, flags); }
private static void ParseHeaderNameValue(ReadOnlySpan <byte> line, IHttpHeadersSink sink, object?state) { Debug.Assert(line.Length > 0); int pos = 0; while (line[pos] != (byte)':' && line[pos] != (byte)' ') { pos++; if (pos == line.Length) { // Invalid header line that doesn't contain ':'. throw new Exception(); } } if (pos == 0) { // Invalid empty header name. throw new Exception(); } ReadOnlySpan <byte> name = line.Slice(0, pos); // Eat any trailing whitespace while (line[pos] == (byte)' ') { pos++; if (pos == line.Length) { // Invalid header line that doesn't contain ':'. throw new Exception(); } } if (line[pos++] != ':') { // Invalid header line that doesn't contain ':'. throw new Exception(); } // Skip whitespace after colon while (pos < line.Length && (line[pos] == (byte)' ' || line[pos] == (byte)'\t')) { pos++; } // Note we ignore the return value from TryAddWithoutValidation. If the header can't be added, we silently drop it. ReadOnlySpan <byte> value = line.Slice(pos); sink.OnHeader(state, name, value); }
internal unsafe bool ReadHeadersPortable(Span <byte> buffer, IHttpHeadersSink headersSink, object?state, out int bytesConsumed) { int originalBufferLength = buffer.Length; while (true) { if (buffer.Length == 0) { goto needMore; } int colIdx = buffer.IndexOfAny((byte)':', (byte)'\n'); if (colIdx == -1) { goto needMore; } if (buffer[colIdx] == '\n') { bytesConsumed = originalBufferLength - buffer.Length + colIdx + 1; return(true); } ReadOnlySpan <byte> headerName = buffer.Slice(0, colIdx); int lfIdx = colIdx; // Skip OWS. byte ch; do { if (++lfIdx == buffer.Length) { goto needMore; } ch = buffer[lfIdx]; } while (ch == ' ' || ch == '\t'); Span <byte> valueStart = buffer.Slice(lfIdx); Span <byte> valueIter = valueStart; while (true) { lfIdx = valueIter.IndexOf((byte)'\n'); if (lfIdx == -1 || (lfIdx + 1) == valueIter.Length) { goto needMore; } int crLfIdx = lfIdx != 0 && valueIter[lfIdx - 1] == '\r' ? lfIdx - 1 : lfIdx; if (lfIdx + 1 >= valueIter.Length) { goto needMore; } // Check if header continues on the next line. byte ht = valueIter[lfIdx + 1]; if (ht == '\t' || ht == ' ') { // Replace CRLFHT with SPSPSP and loop. valueIter[crLfIdx] = (byte)' '; valueIter[lfIdx] = (byte)' '; valueIter[lfIdx + 1] = (byte)' '; valueIter = valueIter.Slice(lfIdx + 2); continue; } ReadOnlySpan <byte> headerValue = valueStart.Slice(0, valueStart.Length - valueIter.Length + crLfIdx); headersSink.OnHeader(state, headerName, headerValue); ProcessKnownHeaders(headerName, headerValue); buffer = valueStart.Slice(valueStart.Length - valueIter.Length + lfIdx + 1); break; } } needMore: bytesConsumed = originalBufferLength - buffer.Length; return(false); }