/// <summary> /// Reads and parses HTTP response from server. /// After return of function HTTP response is read. /// </summary> /// <param name="inStream">Network stream connected to server.</param> /// <param name="defaultKeepAlive">TBD</param> /// <returns>CoreResponseData that describes server response.</returns> private CoreResponseData ParseHTTPResponse(InputNetworkStreamWrapper inStream, bool defaultKeepAlive) { // CoreResponseData keeps all the information of the response. CoreResponseData ret = new CoreResponseData(); // maximumHeadersLength is maximum total length of http header. Basically this is amount // of memory used for headers. int headersLength = m_maxResponseHeadersLen == -1 ? 0x7FFFFFFF : m_maxResponseHeadersLen * 1024; ret.m_shouldClose = !defaultKeepAlive; // Parse the request line. string line = inStream.Read_HTTP_Line(maxHTTPLineLength).Trim(); // Cutoff white spaces int currentOffset = 0; for (; currentOffset < line.Length && ' ' != line[currentOffset]; ++currentOffset) ; // find HTTP version, read http/1.x string httpVersionString = line.Substring(0, currentOffset).ToLower(); if (httpVersionString.Equals("http/1.1")) { ret.m_version = HttpVersion.Version11; } else if (httpVersionString.Equals("http/1.0")) { ret.m_version = HttpVersion.Version10; } else { ret.m_status = WebExceptionStatus.ServerProtocolViolation; ret.m_exceptionMessage = "Unknown http version: " + httpVersionString; return ret; } //advance to the status code for (; currentOffset < line.Length && ' ' == line[currentOffset]; ++currentOffset) ; // Read the status code int codeStart = currentOffset; for (; currentOffset < line.Length && ' ' != line[currentOffset]; ++currentOffset) ; int statusCode = -1; try { string statusCodeStr = line.Substring(codeStart, currentOffset - codeStart); statusCode = Convert.ToInt32(statusCodeStr); } catch (Exception e) { ret.m_status = WebExceptionStatus.ServerProtocolViolation; ret.m_exceptionMessage = "Missing status code in HTTP reply"; ret.m_innerException = e; return ret; } // If we get here - status code should be read. ret.m_statusCode = statusCode; // Advance to the status message. The message is optional for (; currentOffset < line.Length && ' ' != line[currentOffset]; ++currentOffset) ; ret.m_statusDescription = line.Substring(currentOffset); ret.m_headers = new WebHeaderCollection(true); ret.m_chunked = false; ret.m_contentLength = -1; while ((line = inStream.Read_HTTP_Header(maxHTTPLineLength)).Length > 0) { // line.Length is used for the header. Substruct it. headersLength -= line.Length; // If total length used for header is exceeded, we break if (headersLength < 0) { ret.m_status = WebExceptionStatus.ServerProtocolViolation; ret.m_exceptionMessage = "Headers size exceed limit"; return ret; } // Now parse the header. int sepIdx = line.IndexOf(':'); if (sepIdx == -1) { ret.m_status = WebExceptionStatus.ServerProtocolViolation; ret.m_exceptionMessage = "Illegal header format: " + line; return ret; } string headerName = line.Substring(0, sepIdx); string headerValue = line.Substring(sepIdx + 1).TrimStart(null); string matchableHeaderName = headerName.ToLower(); ret.m_headers.AddInternal(headerName, headerValue); if (matchableHeaderName.Equals("content-length")) { try { ret.m_contentLength = Convert.ToInt32(headerValue); // set the response stream length for the input stream, so that an EOF will be read // if the caller tries to read base the response content length inStream.m_BytesLeftInResponse = ret.m_contentLength; } catch (Exception e) { ret.m_status = WebExceptionStatus.ServerProtocolViolation; ret.m_exceptionMessage = "Content length NAN: " + headerValue; ret.m_innerException = e; return ret; } } else if (matchableHeaderName.Equals("transfer-encoding")) { if (headerValue.ToLower().IndexOf("chunked") != -1) { ret.m_chunked = true; } } else if (matchableHeaderName.Equals("connection")) { if (headerValue.ToLower().IndexOf(HttpKnownHeaderValues.close) != -1) { ret.m_shouldClose = true; } } } return ret; }
/// <summary> /// Parses request from client. /// Fills /// - HTTP Verb. /// - HTTP version. /// - Content Length. /// - Fills generic value name pair in WEB header collection. /// </summary> internal void ParseHTTPRequest() { // This is the request line. m_RequestString = m_clientStream.Read_HTTP_Line(HttpWebRequest.maxHTTPLineLength).Trim(); // Split request line into 3 strings - VERB, URL and HTTP version. char[] delimiter = { ' ' }; string[] requestStr = m_RequestString.Split(delimiter); // requestStr should consist of 3 parts. if (requestStr.Length < 3) { throw new ProtocolViolationException("Invalid HTTP request String: " + m_RequestString); } // We have at least 3 strings. Fills the proper fields m_requestVerb = requestStr[0]; m_rawURL = requestStr[1]; // Process third string. It should be either http/1.1 or http/1.0 string httpVerLowerCase = requestStr[2].ToLower(); if (httpVerLowerCase.Equals("http/1.1")) { m_requestHttpVer = HttpVersion.Version11; } else if (httpVerLowerCase.Equals("http/1.0")) { m_requestHttpVer = HttpVersion.Version10; } else { throw new ProtocolViolationException("Unsupported HTTP version: " + requestStr[2]); } // Now it is list of HTTP headers: string line; int headersLen = m_maxResponseHeadersLen; while ((line = m_clientStream.Read_HTTP_Header(HttpWebRequest.maxHTTPLineLength)).Length > 0) { // line.Length is used for the header. Substruct it. headersLen -= line.Length; // If total length used for header is exceeded, we break if (headersLen < 0) { throw new ProtocolViolationException("Http Headers exceeding: " + m_maxResponseHeadersLen); } int sepIdx = line.IndexOf(':'); if (sepIdx == -1) { throw new ProtocolViolationException("Invalid HTTP Header: " + line); } string headerName = line.Substring(0, sepIdx).Trim(); string headerValue = line.Substring(sepIdx + 1).Trim(); string matchableHeaderName = headerName.ToLower(); // Adds new header to collection. m_httpRequestHeaders.AddInternal(headerName, headerValue); // Now we check the value - name pair. For some of them we need to initilize member variables. headerName = headerName.ToLower(); // If it is connection header if (headerName == "connection") { // If value is "Keep-Alive" ( lower case now ), set m_KeepAlive to true; headerValue = headerValue.ToLower(); m_KeepAlive = headerValue == "keep-alive"; } // If user supplied user name and password - parse it and store in m_NetworkCredentials if (headerName == "authorization") { int sepSpace = headerValue.IndexOf(' '); string authType = headerValue.Substring(0, sepSpace); if (authType.ToLower() == "basic") { string authInfo = headerValue.Substring(sepSpace + 1); // authInfo is base64 encoded username and password. byte[] authInfoDecoded = ConvertBase64.FromBase64String(authInfo); char[] authInfoDecChar = System.Text.Encoding.UTF8.GetChars(authInfoDecoded); string strAuthInfo = new string(authInfoDecChar); // The strAuthInfo comes in format username:password. Parse it. int sepColon = strAuthInfo.IndexOf(':'); if (sepColon != -1) { m_NetworkCredentials = new NetworkCredential(strAuthInfo.Substring(0, sepColon), strAuthInfo.Substring(sepColon + 1)); } } } } // Http headers were processed. Now we search for content length. string strContentLen = m_httpRequestHeaders[HttpKnownHeaderNames.ContentLength]; if (strContentLen != null) { try { m_contentLength = Convert.ToInt32(strContentLen); } catch (Exception) { throw new ProtocolViolationException("Invalid content length in request: " + strContentLen); } } }