Не могу войти в cPanel с помощью C# WebRequest

Я изо всех сил пытаюсь разработать класс С# для входа в cPanel на веб-хостинге (Hostgator).

В PHP довольно просто использовать расширение Curl следующим образом:

$url = "http://mysite.com:2082/";

$c = curl_init($url);

curl_setopt($c, CURLOPT_USERPWD, 'user:password');
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($c);
if ($result === false)
    $result = curl_error($c);
curl_close($c);

file_put_contents('log.txt', $result);
//print_r($result);

Теперь вот мой класс С# с различными попытками заставить его работать:

class HTTPHandler
{
    public static string Connect (string url, string userName, string password)
    {
        string result;

        try
        {
            // An initial @ symbol in the password must be escaped
            if (password.Length > 0)
                if (password[0] == '@')
                    password = "\\" + password;

            // Create a request for the URL.        
            WebRequest request = WebRequest.Create(url);
            request.PreAuthenticate = true;
            request.Credentials = new NetworkCredential(userName, password);

            /*
            var credCache = new CredentialCache();
            credCache.Add(new Uri(url), "Basic",
                              new NetworkCredential(userName, password));
            request.Credentials = credCache;
            */

            //request.Method = "POST";
            //request.ContentType = "application/x-www-form-urlencoded";

            /*
            // Create POST data and convert it to a byte array.
            string postData = string.Format("user={0}&pass={1}", userName, password);
            byte[] byteArray = Encoding.UTF8.GetBytes(postData);
            request.ContentLength = byteArray.Length;
            request.ContentType = "application/x-www-form-urlencoded";
            Stream dataStream = request.GetRequestStream();
            dataStream.Write(byteArray, 0, byteArray.Length);
            dataStream.Close();
            */

            // Get the response.
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();

            // Get the stream containing content returned by the server.
            Stream dataStream = response.GetResponseStream();
            // Open the stream using a StreamReader for easy access.
            StreamReader reader = new StreamReader(dataStream);
            // Display the content.
            result = string.Format("Server response:\n{0}\n{1}", response.StatusDescription, reader.ReadToEnd());
            // Cleanup the streams and the response.
            reader.Close();
            dataStream.Close();
            response.Close();
        }
        catch (Exception e)
        {
            result = string.Format("There was an error:\n{0}", e.Message);
        }

        return result;
    }
}

}

Но я продолжаю получать ошибку 401 (Unauthorized) на этапе GetResponse.

Когда я сравниваю переменные $_SERVER на моей тестовой странице локального хоста между отправками PHP и C#, я получаю одни и те же данные, за исключением того, что порт отправителя немного отличается. Критические PHP_AUTH_USER и PHP_AUTH_PW одинаковы.

Моя ОС — Windows 7 64 бит, и я использую Visual C# 2010.

Я предполагаю, что решение действительно простое, но пока я сбит с толку. Но относительный новичок в C#. Я надеюсь, что кто-то может помочь.


person Andy    schedule 22.10.2011    source источник


Ответы (2)


Вам действительно не нужно устанавливать PreAuthenticate, просто позвольте запросу понять это. Также я бы предложил использовать HttpWebRequest вместо WebRequest. Основное отличие состоит в том, что вы можете установить свойство CookieContainer для включения файлов cookie. Это немного сбивает с толку, поскольку по умолчанию файлы cookie отключены, и все, что вам нужно сделать, это установить его на new CookieContainer();, чтобы включить файлы cookie для вашего запроса. Это важно из-за перенаправлений, которые происходят во время аутентификации, и файла cookie авторизации, который записывает тот факт, что вы успешно прошли аутентификацию.

Также примечание о стиле кодирования: убедитесь, что все IDisposable (такие как ответ, поток и читатель) заключены в оператор using().

Также мне непонятно, почему вы экранируете @ в пароле. Запрос должен позаботиться обо всех ваших потребностях в кодировании автоматически.

Полный пример кода:

var request = WebRequest.CreateHttp(url);
request.Credentials = new NetworkCredential(username, password);
request.CookieContainer = new CookieContainer(); // needed to enable cookies

using (var response = (HttpWebResponse)request.GetResponse())
using (var reader = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding(response.CharacterSet)))
    return string.Format("Server response:\n{0}\n{1}", response.StatusDescription, reader.ReadToEnd());

edit: Извините за все правки. Я писал код по памяти и немного боролся с правильной кодировкой.

person Ilia G    schedule 22.10.2011
comment
Спасибо за Ваш ответ. С моей тестовой страницей на локальном сервере я не получал информацию об имени пользователя и пароле на странице PHP без использования PreAuthenticate. А с Curl мне не нужно было использовать файлы cookie. Я обнаружил, что раньше мне приходилось экранировать начальный символ @ при подключении к cPanel с PHP и Curl. - person Andy; 22.10.2011
comment
Надеюсь, кто-то, у кого есть хостинг Linux с cPanel, сможет протестировать некоторый код C#, который успешно входит в систему. Это не имеет ничего общего с файлами cookie, поскольку вы должны пройти этап GetResponse без ошибок. Просто подражайте моему примеру PHP. - person Andy; 22.10.2011
comment
Спасибо за помощь, но это не сработало для меня. Однако я нашел решение: вам нужно принудительно использовать HTTP-аутентификацию, а не запрашивать учетные данные на веб-странице. Проверьте: ссылка - person Andy; 23.10.2011

Это использование System.Web, где мне пришлось установить свойства проекта для использования полной .NET Framework 4, чтобы получить доступ к этой сборке для HttpUtility и добавить ссылку на System.Web в References.

Я не тестировал все перегруженные методы, но главное — это соединение cPanel, где учетные данные аутентификации добавляются в заголовок http, когда присутствует имя пользователя.

Также для cPanel мне нужно было установить request.AllowAutoRedirect = false; так что я контролирую доступ к странице за страницей, так как мне не удалось захватить файлы cookie.

Вот код для вспомогательного класса HTTP, который я придумал:

class HTTPHandler
{
    // Some default settings
    const string UserAgent = "Bot"; // Change this to something more meaningful
    const int TimeOut = 1000; // Time out in ms

    // Basic connection
    public static string Connect(string url)
    {
        return Connect(url, "", "", UserAgent, "", TimeOut);
    }

    // Connect with post data passed as a key : value pair dictionary
    public static string Connect(string url, Dictionary<string, string> args)
    {
        return Connect(url, "", "", UserAgent, ToQueryString(args), TimeOut);
    }

    // Connect with a custom user agent specified
    public static string Connect(string url, string userAgent)
    {
        return Connect(url, "", "", userAgent, "", TimeOut);
    }

    public static string Connect(string url, string userName, string password, string userAgent, string postData, int timeOut)
    {
        string result;

        try
        {
            // Create a request for the URL.        
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);

            if (userAgent == null)
                userAgent = UserAgent;

            request.UserAgent = userAgent;
            request.Timeout = timeOut;

            if (userName.Length > 0)
            {
                string authInfo = userName + ":" + password;
                authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
                request.Headers["Authorization"] = "Basic " + authInfo;
                request.AllowAutoRedirect = false;
            }

            if (postData.Length > 0)
            {
                request.Method = "POST";
                request.ContentType = "application/x-www-form-urlencoded";

                // Create POST data and convert it to a byte array.
                byte[] byteArray = Encoding.UTF8.GetBytes(postData);
                request.ContentLength = byteArray.Length;
                using (Stream dataStream = request.GetRequestStream())
                {
                    dataStream.Write(byteArray, 0, byteArray.Length);
                }
            }

            // Get the response.
            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
            {
                // Get the stream containing content returned by the server.
                Stream dataStream = response.GetResponseStream();
                // Open the stream using a StreamReader for easy access.
                using (StreamReader reader = new StreamReader(dataStream))
                {
                    result = string.Format("Server response:\n{0}\n{1}", response.StatusDescription, reader.ReadToEnd());
                }
            }
        }

        catch (Exception e)
        {
            result = string.Format("There was an error:\n{0}", e.Message);
        }

        return result;
    }

    public static string ToQueryString(Dictionary<string, string> args)
    {
        List<string> encodedData = new List<string>();

        foreach (KeyValuePair<string, string> pair in args)
        {
            encodedData.Add(HttpUtility.UrlEncode(pair.Key) + "=" + HttpUtility.UrlEncode(pair.Value));
        }

        return String.Join("&", encodedData.ToArray());
    }
}
person Andy    schedule 27.10.2011