/*++ Routine Description: Assembles the data/headers for an HTTP request into a buffer Arguments: none. Return Value: none. --*/ internal void UpdateHeaders() { GlobalLog.Enter("HttpWebRequest#" + ValidationHelper.HashString(this) + "::UpdateHeaders"); GlobalLog.ThreadContract(ThreadKinds.Unknown, "HttpWebRequest#" + ValidationHelper.HashString(this) + "::UpdateHeaders"); #if TRAVE _HttpRequestHeaders.Set("Cur-Hash-ID", ValidationHelper.HashString(this)); _HttpRequestHeaders.AddInternal("Hash-ID", ValidationHelper.HashString(this)); #endif string hostString; // All WebsocketRequests (even with scheme http) are tunneled through proxies bool isTunnelRequestForHttp = IsTunnelRequest && _OriginUri.Scheme == Uri.UriSchemeHttp; // For CONNECT requests the proxy is not able to identify whether the origin Uri is using http or // https-scheme. According to the 4.0 behavior in HttpWebRequest proxies assume http-scheme when // calculating the default port. A CONNECT request sent form HttpWebRequest in .Net 4.0 for // https://hostname/someresource.htm looks like: // // CONNECT hostname:443 HTTP/1.1 // Host: hostname // Proxy-Connection: Keep-Alive // // This means the Host header should contain port 80 - even if the origin Uri is using http-scheme, // because the proxy has no way to identify the scheme for the origin Uri and assumes https in this // case, because tunneling SSL/TLS through proxies is the most common use case. // // WININET is always emitting the default port for CONNECT requests. if (UseCustomHost) { hostString = GetSafeHostAndPort(_HostUri, _HostHasPort || isTunnelRequestForHttp, false); } else { hostString = GetSafeHostAndPort(isTunnelRequestForHttp, false); } // Set HostName Header // Do unusual encoding so that host header value gets serialized to wire properly HostHeaderString hhs = new HostHeaderString(hostString); string host = WebHeaderCollection.HeaderEncoding.GetString(hhs.Bytes, 0, hhs.ByteCount); _HttpRequestHeaders.ChangeInternal( HttpKnownHeaderNames.Host, host); // about to create the headers we're going to send. Check if any // modules want to inspect or modify them if (_CookieContainer != null) { CookieModule.OnSendingHeaders(this); } GlobalLog.Leave("HttpWebRequest#" + ValidationHelper.HashString(this) + "::UpdateHeaders"); }
/*++ Routine Description: Assembles the status line for an HTTP request specifically to a proxy... Arguments: headersSize - size of the Header string that we send after this request line Return Value: int - number of bytes written out --*/ private int GenerateProxyRequestLine(int headersSize) { // // Handle Proxy Case, i.e. "GET http://hostname-outside-of-proxy.somedomain.edu:999" // consider handling other schemes // // Note that we will take the scheme off the URI, hence may issue other but http request // through the proxy on redirect. While this is not allowed by RFC, // this it used to work in IE and we have already tested permissions for that destination Uri. if ((object)_Uri.Scheme == (object)Uri.UriSchemeFtp) { // FTP return GenerateFtpProxyRequestLine(headersSize); } // HTTP int offset = 0; string scheme = _Uri.GetComponents(UriComponents.Scheme | UriComponents.KeepDelimiter, UriFormat.UriEscaped); HostHeaderString host = new HostHeaderString(GetSafeHostAndPort(false, true)); string path = _Uri.GetComponents(UriComponents.Path | UriComponents.Query, UriFormat.UriEscaped); int writeBufferLength = CurrentMethod.Name.Length + scheme.Length + host.ByteCount + path.Length + RequestLineConstantSize + headersSize; _WriteBuffer = new byte[writeBufferLength]; offset = Encoding.ASCII.GetBytes(CurrentMethod.Name, 0, CurrentMethod.Name.Length, WriteBuffer, 0); WriteBuffer[offset++] = (byte)' '; offset += Encoding.ASCII.GetBytes(scheme, 0, scheme.Length, WriteBuffer, offset); host.Copy(WriteBuffer, offset); offset += host.ByteCount; offset += Encoding.ASCII.GetBytes(path, 0, path.Length, WriteBuffer, offset); WriteBuffer[offset++] = (byte)' '; return offset; }
private int GenerateFtpProxyRequestLine(int headersSize) { // Special handling for FTP via HTTP proxy int offset = 0; string scheme = _Uri.GetComponents(UriComponents.Scheme | UriComponents.KeepDelimiter, UriFormat.UriEscaped); string userInfo = _Uri.GetComponents(UriComponents.UserInfo | UriComponents.KeepDelimiter, UriFormat.UriEscaped); HostHeaderString host = new HostHeaderString(GetSafeHostAndPort(false, true)); string path = _Uri.GetComponents(UriComponents.Path | UriComponents.Query, UriFormat.UriEscaped); if (userInfo == "") { // No userinfo so see if we can add from Credentials string username = null; string password = null; NetworkCredential networkCreds = Credentials.GetCredential(_Uri, "basic"); if (networkCreds != null && (object)networkCreds != (object)FtpWebRequest.DefaultNetworkCredential) { username = networkCreds.InternalGetDomainUserName(); password = networkCreds.InternalGetPassword(); password = (password == null) ? string.Empty : password; } if (username != null) { // For FTP proxy we don't escape the username and password strings // Since some servers don't seem to support it // Only escape the absolute minimum that is required for // a valid Uri which is (: \ / ? # %) username = username.Replace(":", "%3A"); password = password.Replace(":", "%3A"); username = username.Replace("\\", "%5C"); password = password.Replace("\\", "%5C"); username = username.Replace("/", "%2F"); password = password.Replace("/", "%2F"); username = username.Replace("?", "%3F"); password = password.Replace("?", "%3F"); username = username.Replace("#", "%23"); password = password.Replace("#", "%23"); username = username.Replace("%", "%25"); password = password.Replace("%", "%25"); username = username.Replace("@", "%40"); password = password.Replace("@", "%40"); // build complete userinfo userInfo = username + ":" + password + "@"; } } // construct request line from components int writeBufferLength = CurrentMethod.Name.Length + scheme.Length + userInfo.Length + host.ByteCount + path.Length + RequestLineConstantSize + headersSize; _WriteBuffer = new byte[writeBufferLength]; offset = Encoding.ASCII.GetBytes(CurrentMethod.Name, 0, CurrentMethod.Name.Length, WriteBuffer, 0); WriteBuffer[offset++] = (byte)' '; offset += Encoding.ASCII.GetBytes(scheme, 0, scheme.Length, WriteBuffer, offset); offset += Encoding.ASCII.GetBytes(userInfo, 0, userInfo.Length, WriteBuffer, offset); host.Copy(WriteBuffer, offset); offset += host.ByteCount; offset += Encoding.ASCII.GetBytes(path, 0, path.Length, WriteBuffer, offset); WriteBuffer[offset++] = (byte)' '; return offset; }
/*++ Routine Description: Assembles the status line for an HTTP request specifically for CONNECT style verbs, that create a pipe Arguments: headersSize - size of the Header string that we send after this request line Return Value: int - number of bytes written out --*/ private int GenerateConnectRequestLine(int headersSize) { int offset = 0; HostHeaderString host = new HostHeaderString(GetSafeHostAndPort(true, true)); // // Handle Connect Case, i.e. "CONNECT hostname.domain.edu:999" // int writeBufferLength = CurrentMethod.Name.Length + host.ByteCount + RequestLineConstantSize + headersSize; _WriteBuffer = new byte[writeBufferLength]; offset = Encoding.ASCII.GetBytes(CurrentMethod.Name, 0, CurrentMethod.Name.Length, WriteBuffer, 0); WriteBuffer[offset++] = (byte)' '; host.Copy(WriteBuffer, offset); offset += host.ByteCount; WriteBuffer[offset++] = (byte)' '; return offset; }