private void ParseChunkedTrailer(SocketInput input) { var scan = input.ConsumingStart(); var consumed = scan; try { var ch1 = scan.Take(); var ch2 = scan.Take(); if (ch1 == -1 || ch2 == -1) { return; } else if (ch1 == '\r' && ch2 == '\n') { consumed = scan; _mode = Mode.Complete; } else { _mode = Mode.TrailerHeaders; } } finally { input.ConsumingComplete(consumed, scan); } }
private void ParseChunkedPrefix(SocketInput input) { var scan = input.ConsumingStart(); var consumed = scan; try { var ch1 = scan.Take(); var ch2 = scan.Take(); if (ch1 == -1 || ch2 == -1) { return; } var chunkSize = CalculateChunkSize(ch1, 0); ch1 = ch2; do { if (ch1 == ';') { consumed = scan; _inputLength = chunkSize; _mode = Mode.Extension; return; } ch2 = scan.Take(); if (ch2 == -1) { return; } if (ch1 == '\r' && ch2 == '\n') { consumed = scan; _inputLength = chunkSize; if (chunkSize > 0) { _mode = Mode.Data; } else { _mode = Mode.Trailer; } return; } chunkSize = CalculateChunkSize(ch1, chunkSize); ch1 = ch2; } while (ch1 != -1); } finally { input.ConsumingComplete(consumed, scan); } }
private void ParseChunkedSuffix(SocketInput input) { var scan = input.ConsumingStart(); var consumed = scan; try { var ch1 = scan.Take(); var ch2 = scan.Take(); if (ch1 == -1 || ch2 == -1) { return; } else if (ch1 == '\r' && ch2 == '\n') { consumed = scan; _mode = Mode.Prefix; } else { ThrowBadRequestException("Bad chunk suffix"); } } finally { input.ConsumingComplete(consumed, scan); } }
public Connection(ListenerContext context, UvStreamHandle socket) : base(context) { _socket = socket; socket.Connection = this; ConnectionControl = this; ConnectionId = GenerateConnectionId(Interlocked.Increment(ref _lastConnectionId)); _rawSocketInput = new SocketInput(Memory, ThreadPool); _rawSocketOutput = new SocketOutput(Thread, _socket, Memory, this, ConnectionId, Log, ThreadPool, WriteReqPool); }
private void ParseExtension(SocketInput input) { var scan = input.ConsumingStart(); var consumed = scan; try { // Chunk-extensions not currently parsed // Just drain the data do { if (scan.Seek(ref _vectorCRs) == -1) { // End marker not found yet consumed = scan; return; } ; var ch1 = scan.Take(); var ch2 = scan.Take(); if (ch2 == '\n') { consumed = scan; if (_inputLength > 0) { _mode = Mode.Data; } else { _mode = Mode.Trailer; } } else if (ch2 == -1) { return; } } while (_mode == Mode.Extension); } finally { input.ConsumingComplete(consumed, scan); } }
public Task StopAsync() { lock (_stateLock) { switch (_connectionState) { case ConnectionState.SocketClosed: return(TaskUtilities.CompletedTask); case ConnectionState.CreatingFrame: _connectionState = ConnectionState.ToDisconnect; break; case ConnectionState.Open: _frame.Stop(); SocketInput.CompleteAwaiting(); break; } _socketClosedTcs = new TaskCompletionSource <object>(); return(_socketClosedTcs.Task); } }
private int ReadChunkedData(SocketInput input, byte[] buffer, int offset, int count) { var scan = input.ConsumingStart(); int actual; try { var limit = buffer == null ? _inputLength : Math.Min(count, _inputLength); scan = scan.CopyTo(buffer, offset, limit, out actual); _inputLength -= actual; } finally { input.ConsumingComplete(scan, scan); } if (_inputLength == 0) { _mode = Mode.Suffix; } return(actual); }
private static async Task <int> ReadAsyncAwaited(this SocketInput input, byte[] buffer, int offset, int count) { while (true) { await input; var fin = input.RemoteIntakeFin; var begin = input.ConsumingStart(); int actual; var end = begin.CopyTo(buffer, offset, count, out actual); input.ConsumingComplete(end, end); if (actual != 0) { return(actual); } else if (fin) { return(0); } } }
public static ValueTask <int> ReadAsync(this SocketInput input, byte[] buffer, int offset, int count) { while (input.IsCompleted) { var fin = input.RemoteIntakeFin; var begin = input.ConsumingStart(); int actual; var end = begin.CopyTo(buffer, offset, count, out actual); input.ConsumingComplete(end, end); if (actual != 0) { return(actual); } else if (fin) { return(0); } } return(input.ReadAsyncAwaited(buffer, offset, count)); }
private async Task <int> ReadStateMachineAsync(SocketInput input, ArraySegment <byte> buffer, CancellationToken cancellationToken) { while (_mode < Mode.Trailer) { while (_mode == Mode.Prefix) { var fin = input.RemoteIntakeFin; ParseChunkedPrefix(input); if (_mode != Mode.Prefix) { break; } else if (fin) { ThrowChunkedRequestIncomplete(); } await input; } while (_mode == Mode.Extension) { var fin = input.RemoteIntakeFin; ParseExtension(input); if (_mode != Mode.Extension) { break; } else if (fin) { ThrowChunkedRequestIncomplete(); } await input; } while (_mode == Mode.Data) { var fin = input.RemoteIntakeFin; int actual = ReadChunkedData(input, buffer.Array, buffer.Offset, buffer.Count); if (actual != 0) { return(actual); } else if (_mode != Mode.Data) { break; } else if (fin) { ThrowChunkedRequestIncomplete(); } await input; } while (_mode == Mode.Suffix) { var fin = input.RemoteIntakeFin; ParseChunkedSuffix(input); if (_mode != Mode.Suffix) { break; } else if (fin) { ThrowChunkedRequestIncomplete(); } await input; } } // Chunks finished, parse trailers while (_mode == Mode.Trailer) { var fin = input.RemoteIntakeFin; ParseChunkedTrailer(input); if (_mode != Mode.Trailer) { break; } else if (fin) { ThrowChunkedRequestIncomplete(); } await input; } if (_mode == Mode.TrailerHeaders) { while (!_context.TakeMessageHeaders(input, _requestHeaders)) { if (input.RemoteIntakeFin) { if (_context.TakeMessageHeaders(input, _requestHeaders)) { break; } else { ThrowChunkedRequestIncomplete(); } } await input; } _mode = Mode.Complete; } return(0); }
public bool TakeMessageHeaders(SocketInput input, FrameRequestHeaders requestHeaders) { var scan = input.ConsumingStart(); var consumed = scan; try { int chFirst; int chSecond; while (!scan.IsEnd) { var beginName = scan; if (scan.Seek(ref _vectorColons, ref _vectorCRs) == -1) { return(false); } var endName = scan; chFirst = scan.Take(); var beginValue = scan; chSecond = scan.Take(); if (chFirst == -1 || chSecond == -1) { return(false); } if (chFirst == '\r') { if (chSecond == '\n') { consumed = scan; return(true); } ReportCorruptedHttpRequest(new BadHttpRequestException("Headers corrupted, invalid header sequence.")); // Headers corrupted, parsing headers is complete return(true); } while ( chSecond == ' ' || chSecond == '\t' || chSecond == '\r' || chSecond == '\n') { if (chSecond == '\r') { var scanAhead = scan; var chAhead = scanAhead.Take(); if (chAhead == -1) { return(false); } else if (chAhead == '\n') { chAhead = scanAhead.Take(); if (chAhead == -1) { return(false); } else if (chAhead != ' ' && chAhead != '\t') { // If the "\r\n" isn't part of "linear whitespace", // then this header has no value. break; } } } beginValue = scan; chSecond = scan.Take(); if (chSecond == -1) { return(false); } } scan = beginValue; var wrapping = false; while (!scan.IsEnd) { if (scan.Seek(ref _vectorCRs) == -1) { // no "\r" in sight, burn used bytes and go back to await more data return(false); } var endValue = scan; chFirst = scan.Take(); // expecting: \r chSecond = scan.Take(); // expecting: \n if (chSecond == -1) { return(false); } else if (chSecond != '\n') { // "\r" was all by itself, move just after it and try again scan = endValue; scan.Take(); continue; } var chThird = scan.Peek(); if (chThird == -1) { return(false); } else if (chThird == ' ' || chThird == '\t') { // special case, "\r\n " or "\r\n\t". // this is considered wrapping"linear whitespace" and is actually part of the header value // continue past this for the next wrapping = true; continue; } var name = beginName.GetArraySegment(endName); var value = beginValue.GetAsciiString(endValue); if (wrapping) { value = value.Replace("\r\n", " "); } consumed = scan; requestHeaders.Append(name.Array, name.Offset, name.Count, value); break; } } return(false); } finally { input.ConsumingComplete(consumed, scan); } }
protected bool TakeStartLine(SocketInput input) { var scan = input.ConsumingStart(); var consumed = scan; try { string method; var begin = scan; if (!begin.GetKnownMethod(ref scan, out method)) { if (scan.Seek(ref _vectorSpaces) == -1) { return(false); } method = begin.GetAsciiString(scan); scan.Take(); } begin = scan; var needDecode = false; var chFound = scan.Seek(ref _vectorSpaces, ref _vectorQuestionMarks, ref _vectorPercentages); if (chFound == -1) { return(false); } else if (chFound == '%') { needDecode = true; chFound = scan.Seek(ref _vectorSpaces, ref _vectorQuestionMarks); if (chFound == -1) { return(false); } } var pathBegin = begin; var pathEnd = scan; var queryString = ""; if (chFound == '?') { begin = scan; if (scan.Seek(ref _vectorSpaces) != ' ') { return(false); } queryString = begin.GetAsciiString(scan); } scan.Take(); begin = scan; string httpVersion; if (!begin.GetKnownVersion(ref scan, out httpVersion)) { scan = begin; if (scan.Seek(ref _vectorCRs) == -1) { return(false); } httpVersion = begin.GetAsciiString(scan); scan.Take(); } if (scan.Take() != '\n') { return(false); } // URIs are always encoded/escaped to ASCII https://tools.ietf.org/html/rfc3986#page-11 // Multibyte Internationalized Resource Identifiers (IRIs) are first converted to utf8; // then encoded/escaped to ASCII https://www.ietf.org/rfc/rfc3987.txt "Mapping of IRIs to URIs" string requestUrlPath; if (needDecode) { // URI was encoded, unescape and then parse as utf8 pathEnd = UrlPathDecoder.Unescape(pathBegin, pathEnd); requestUrlPath = pathBegin.GetUtf8String(pathEnd); requestUrlPath = PathNormalizer.NormalizeToNFC(requestUrlPath); } else { // URI wasn't encoded, parse as ASCII requestUrlPath = pathBegin.GetAsciiString(pathEnd); } requestUrlPath = PathNormalizer.RemoveDotSegments(requestUrlPath); consumed = scan; Method = method; RequestUri = requestUrlPath; QueryString = queryString; HttpVersion = httpVersion; bool caseMatches; if (!string.IsNullOrEmpty(_pathBase) && (requestUrlPath.Length == _pathBase.Length || (requestUrlPath.Length > _pathBase.Length && requestUrlPath[_pathBase.Length] == '/')) && RequestUrlStartsWithPathBase(requestUrlPath, out caseMatches)) { PathBase = caseMatches ? _pathBase : requestUrlPath.Substring(0, _pathBase.Length); Path = requestUrlPath.Substring(_pathBase.Length); } else { Path = requestUrlPath; } return(true); } finally { input.ConsumingComplete(consumed, scan); } }