/// <devdoc> /// <para>Dumps a byte array to the log</para> /// </devdoc> internal static void Dump(TraceSource traceSource, object obj, string method, byte[] buffer, int offset, int length) { if (!ValidateSettings(traceSource, TraceEventType.Verbose)) { return; } if (buffer == null) { PrintLine(traceSource, TraceEventType.Verbose, 0, "(null)"); return; } if (offset > buffer.Length) { PrintLine(traceSource, TraceEventType.Verbose, 0, "(offset out of range)"); return; } PrintLine(traceSource, TraceEventType.Verbose, 0, "Data from " + GetObjectName(obj) + "#" + HashString(obj) + "::" + method); int maxDumpSize = GetMaxDumpSizeSetting(traceSource); if (length > maxDumpSize) { PrintLine(traceSource, TraceEventType.Verbose, 0, "(printing " + maxDumpSize.ToString(NumberFormatInfo.InvariantInfo) + " out of " + length.ToString(NumberFormatInfo.InvariantInfo) + ")"); length = maxDumpSize; } if ((length < 0) || (length > buffer.Length - offset)) { length = buffer.Length - offset; } if (GetUseProtocolTextSetting(traceSource)) { string output = "<<" + HeaderEncoding.GetString(buffer, offset, length) + ">>"; PrintLine(traceSource, TraceEventType.Verbose, 0, output); return; } do { int n = Math.Min(length, 16); string disp = String.Format(CultureInfo.CurrentCulture, "{0:X8} : ", offset); for (int i = 0; i < n; ++i) { disp += String.Format(CultureInfo.CurrentCulture, "{0:X2}", buffer[offset + i]) + ((i == 7) ? '-' : ' '); } for (int i = n; i < 16; ++i) { disp += " "; } disp += ": "; for (int i = 0; i < n; ++i) { disp += ((buffer[offset + i] < 0x20) || (buffer[offset + i] > 0x7e)) ? '.' : (char)(buffer[offset + i]); } PrintLine(traceSource, TraceEventType.Verbose, 0, disp); offset += n; length -= n; } while (length > 0); }
// These methods require the HTTP_REQUEST to still be pinned in its original location. internal string?GetVerb() { var verb = NativeRequest->Verb; if (verb > HttpApiTypes.HTTP_VERB.HttpVerbUnknown && verb < HttpApiTypes.HTTP_VERB.HttpVerbMaximum) { return(HttpApiTypes.HttpVerbs[(int)verb]); } else if (verb == HttpApiTypes.HTTP_VERB.HttpVerbUnknown && NativeRequest->pUnknownVerb != null) { // Never use Latin1 for the VERB return(HeaderEncoding.GetString(NativeRequest->pUnknownVerb, NativeRequest->UnknownVerbLength, useLatin1: false)); } return(null); }
internal DataParseStatus ParseHeaders(byte[] buffer, int size, ref int unparsed, ref int totalResponseHeadersLength, int maximumResponseHeadersLength) { int headerNameStartOffset = -1; int headerNameEndOffset = -1; int headerValueStartOffset = -1; int headerValueEndOffset = -1; int numberOfLf = -1; int index = unparsed; bool spaceAfterLf; string headerMultiLineValue; string headerName; string headerValue; // we need this because this method is entered multiple times. int localTotalResponseHeadersLength = totalResponseHeadersLength; DataParseStatus parseStatus = DataParseStatus.Invalid; GlobalLog.Enter("WebHeaderCollection::ParseHeaders(): size:" + size.ToString() + ", unparsed:" + unparsed.ToString() + " buffer:[" + Encoding.ASCII.GetString(buffer, unparsed, Math.Min(256, size - unparsed)) + "]"); //GlobalLog.Print("WebHeaderCollection::ParseHeaders(): advancing. index=" + index.ToString() + " current character='" + ((char)buffer[index]).ToString() + "':" + ((int)buffer[index]).ToString() + " next character='" + ((char)buffer[index+1]).ToString() + "':" + ((int)buffer[index+1]).ToString()); //GlobalLog.Print("WebHeaderCollection::ParseHeaders(): headerName:[" + headerName + "], headerValue:[" + headerValue + "], headerMultiLineValue:[" + headerMultiLineValue + "] numberOfLf=" + numberOfLf.ToString() + " index=" + index.ToString()); //GlobalLog.Print("WebHeaderCollection::ParseHeaders(): headerNameStartOffset=" + headerNameStartOffset + " headerNameEndOffset=" + headerNameEndOffset + " headerValueStartOffset=" + headerValueStartOffset + " headerValueEndOffset=" + headerValueEndOffset); // // according to RFC216 a header can have the following syntax: // // message-header = field-name ":" [ field-value ] // field-name = token // field-value = *( field-content | LWS ) // field-content = <the OCTETs making up the field-value and consisting of either *TEXT or combinations of token, separators, and quoted-string> // TEXT = <any OCTET except CTLs, but including LWS> // CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)> // SP = <US-ASCII SP, space (32)> // HT = <US-ASCII HT, horizontal-tab (9)> // CR = <US-ASCII CR, carriage return (13)> // LF = <US-ASCII LF, linefeed (10)> // LWS = [CR LF] 1*( SP | HT ) // CHAR = <any US-ASCII character (octets 0 - 127)> // token = 1*<any CHAR except CTLs or separators> // separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT // quoted-string = ( <"> *(qdtext | quoted-pair ) <"> ) // qdtext = <any TEXT except <">> // quoted-pair = "\" CHAR // // // At each iteration of the following loop we expect to parse a single HTTP header entirely. // for (;;) { // // trim leading whitespaces (LWS) just for extra robustness, in fact if there are leading white spaces then: // 1) it could be that after the status line we might have spaces. handle this. // 2) this should have been detected to be a multiline header so there'll be no spaces and we'll spend some time here. // headerName = string.Empty; headerValue = string.Empty; spaceAfterLf = false; headerMultiLineValue = null; if (Count == 0) { // // so, restrict this extra trimming only on the first header line // while (index < size && (buffer[index] == ' ' || buffer[index] == '\t')) { index++; if (maximumResponseHeadersLength >= 0 && ++localTotalResponseHeadersLength >= maximumResponseHeadersLength) { parseStatus = DataParseStatus.DataTooBig; goto quit; } } if (index == size) { // // we reached the end of the buffer. ask for more data. // parseStatus = DataParseStatus.NeedMoreData; goto quit; } } // // what we have here is the beginning of a new header // headerNameStartOffset = index; while (index < size && (buffer[index] != ':' && buffer[index] != '\n')) { if (buffer[index] > ' ') { // // if thre's an illegal character we should return DataParseStatus.Invalid // instead we choose to be flexible, try to trim it, but include it in the string // headerNameEndOffset = index; } index++; if (maximumResponseHeadersLength >= 0 && ++localTotalResponseHeadersLength >= maximumResponseHeadersLength) { parseStatus = DataParseStatus.DataTooBig; goto quit; } } if (index == size) { // // we reached the end of the buffer. ask for more data. // parseStatus = DataParseStatus.NeedMoreData; goto quit; } startOfValue: // // skip all [' ','\t',':','\r','\n'] characters until HeaderValue starts // if we didn't find any headers yet, we set numberOfLf to 1 // so that we take the '\n' from the status line into account // numberOfLf = (Count == 0 && headerNameEndOffset < 0) ? 1 : 0; while (index < size && numberOfLf < 2 && (buffer[index] <= ' ' || buffer[index] == ':')) { if (buffer[index] == '\n') { numberOfLf++; spaceAfterLf = index + 1 < size && (buffer[index + 1] == ' ' || buffer[index + 1] == '\t'); } index++; if (maximumResponseHeadersLength >= 0 && ++localTotalResponseHeadersLength >= maximumResponseHeadersLength) { parseStatus = DataParseStatus.DataTooBig; goto quit; } } if (numberOfLf == 2 || (numberOfLf == 1 && !spaceAfterLf)) { // // if we've counted two '\n' we got at the end of the headers even if we're past the end of the buffer // if we've counted one '\n' and the first character after that was a ' ' or a '\t' // no matter if we found a ':' or not, treat this as an empty header name. // goto addHeader; } if (index == size) { // // we reached the end of the buffer. ask for more data. // parseStatus = DataParseStatus.NeedMoreData; goto quit; } headerValueStartOffset = index; while (index < size && buffer[index] != '\n') { if (buffer[index] > ' ') { headerValueEndOffset = index; } index++; if (maximumResponseHeadersLength >= 0 && ++localTotalResponseHeadersLength >= maximumResponseHeadersLength) { parseStatus = DataParseStatus.DataTooBig; goto quit; } } if (index == size) { // // we reached the end of the buffer. ask for more data. // parseStatus = DataParseStatus.NeedMoreData; goto quit; } // // at this point we found either a '\n' or the end of the headers // hence we are at the end of the Header Line. 4 options: // 1) need more data // 2) if we find two '\n' => end of headers // 3) if we find one '\n' and a ' ' or a '\t' => multiline header // 4) if we find one '\n' and a valid char => next header // numberOfLf = 0; while (index < size && numberOfLf < 2 && (buffer[index] == '\r' || buffer[index] == '\n')) { if (buffer[index] == '\n') { numberOfLf++; } index++; if (maximumResponseHeadersLength >= 0 && ++localTotalResponseHeadersLength >= maximumResponseHeadersLength) { parseStatus = DataParseStatus.DataTooBig; goto quit; } } if (index == size && numberOfLf < 2) { // // we reached the end of the buffer but not of the headers. ask for more data. // parseStatus = DataParseStatus.NeedMoreData; goto quit; } addHeader: if (headerValueStartOffset >= 0 && headerValueStartOffset > headerNameEndOffset && headerValueEndOffset >= headerValueStartOffset) { // // Encoding fastest way to build the UNICODE string off the byte[] // headerValue = HeaderEncoding.GetString(buffer, headerValueStartOffset, headerValueEndOffset - headerValueStartOffset + 1); } // // if we got here from the beginning of the for loop, headerMultiLineValue will be null // otherwise it will contain the headerValue constructed for the multiline header // add this line as well to it, separated by a single space // headerMultiLineValue = (headerMultiLineValue == null ? headerValue : headerMultiLineValue + " " + headerValue); if (index < size && numberOfLf == 1 && (buffer[index] == ' ' || buffer[index] == '\t')) { // // since we found only one Lf and the next header line begins with a Lws, // this is the beginning of a multiline header. // parse the next line into headerValue later it will be added to headerMultiLineValue // index++; if (maximumResponseHeadersLength >= 0 && ++localTotalResponseHeadersLength >= maximumResponseHeadersLength) { parseStatus = DataParseStatus.DataTooBig; goto quit; } goto startOfValue; } if (headerNameStartOffset >= 0 && headerNameEndOffset >= headerNameStartOffset) { // // Encoding is the fastest way to build the UNICODE string off the byte[] // headerName = HeaderEncoding.GetString(buffer, headerNameStartOffset, headerNameEndOffset - headerNameStartOffset + 1); } // // now it's finally safe to add the header if we have a name for it // if (headerName.Length > 0) { // // the base clasee will check for pre-existing headerValue and append // it using commas as indicated in the RFC // GlobalLog.Print("WebHeaderCollection::ParseHeaders() calling base.Add() key:[" + headerName + "], value:[" + headerMultiLineValue + "]"); base.Add(headerName, headerMultiLineValue); } // // and update unparsed // totalResponseHeadersLength = localTotalResponseHeadersLength; unparsed = index; if (numberOfLf == 2) { parseStatus = DataParseStatus.Done; goto quit; } } // for (;;) quit: GlobalLog.Leave("WebHeaderCollection::ParseHeaders() returning parseStatus:" + parseStatus.ToString()); return(parseStatus); }