Реплицируйте команду cURL в C# (Unity)
Я попробовал массу вариантов - то, что я выкладываю ниже, это очищенная версия того, с чего я изначально начал. Это вспомогательная утилита, которую я пишу для Unity-приложения. Это не игра, просто 2-е приложение.
Я пытаюсь повторить это:
curl -f -s -S --user $(ROKU_DEV_CREDENTIALS) --anyauth -F "mysubmit=Install" -F "archive=@out/Archive.zip" http://$(ROKUIP)/plugin_install > /dev/null
Это то, что у меня пока есть (см. Код ниже) - и похоже, что это "своего рода" работает, но позволяет проходить аутентификацию, и требует около 108 КБ данных (wireshark говорит, что ПОЛНЫЙ ZIP, около 2,8 МБ), отправляется, но сервер сообщает, что получено только около 108 КБ); Я подумал, что это может быть проблема с кодировкой (приложение на Windows 10, а Server - встроенный Linux-сервер [Roku Player]).
using System;
using System.Collections;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using UnityEngine;
using UnityEngine.Networking;
public class PushBuild : MonoBehaviour {
public RokuDevice SingleDevice;
public Dictionary<string, RokuDevice> ManyDevice;
public bool startUpload;
// Use this for initialization
void Start() {
}
// Update is called once per frame
void Update() {
if (startUpload)
{
StartCoroutine(Upload());
startUpload = false;
}
}
public void QueueUpPush()
{
startUpload = true;
}
IEnumerator Upload()
{
string[] substr = null;
UnityWebRequest www1 = UnityWebRequest.Head("http://10.0.0.232/plugin_install");
yield return www1.Send();
if (www1.isError)
{
Debug.Log(www1.error);
}
else
{
Dictionary<string, string> responseHeaders = www1.GetResponseHeaders();
string nonce;
string[] split = { "=", "\"" };
bool _null = responseHeaders.TryGetValue("WWW-Authenticate", out nonce);
if (nonce != null)
{
substr = nonce.Split(split, StringSplitOptions.RemoveEmptyEntries);
}
}
WWW _file = new WWW("file:///" + "D:\\Workspace\\Roku\\Tempest\\src\\Archive.zip");
yield return _file;
WWWForm form = new WWWForm();
form.AddBinaryData("archive", _file.bytes, "Archive.zip", "application/x-zip-compressed");
form.AddField("mysubmit", "install");
using (UnityWebRequest www = UnityWebRequest.Post("http://10.0.0.232/plugin_install", form))
{
string ha1 = "rokudev:rokudev:0000";
string ha2 = "POST:/plugin_install";
string responseDigest = CalculateResponseDigest(ha1, ha2, substr[5], "00000000", "aef3fafadfaedfadf", "auth");
string authHeaderVal = string.Format("Digest username=\"rokudev\", realm=\"rokudev\", nonce=\"{0}\", uri=\"/plugin_install\", response=\"{1}\", qop=auth, nc=00000000, cnonce=\"aef3fafadfaedfadf\"", substr[5], responseDigest);
www.SetRequestHeader("Authorization", authHeaderVal);
www.Send();
while (www.uploadProgress < 1.0)
{
Debug.Log("still uploading..." + www.uploadProgress);
yield return null;
}
Debug.Log(www.uploadedBytes);
if (www.isError)
{
Debug.Log(www.error);
}
else
{
Debug.Log("Form upload complete!");
Debug.Log(www.downloadHandler.data);
}
}
}
string CalculateResponseDigest(string ha1, string ha2, string serverNonce, string requestCnt, string clientNonce, string qop)
{
byte[] inputBytesHA1 = Encoding.ASCII.GetBytes(ha1);
byte[] HA1 = MD5.Create().ComputeHash(inputBytesHA1);
StringBuilder _returnValHA1 = new StringBuilder(HA1.Length * 2);
foreach (byte b in HA1)
{
_returnValHA1.AppendFormat("{0:x2}", b);
}
byte[] inputBytesHA2 = Encoding.ASCII.GetBytes(ha2);
byte[] HA2 = MD5.Create().ComputeHash(inputBytesHA2);
StringBuilder _returnValHA2 = new StringBuilder(HA2.Length * 2);
foreach (byte b in HA2)
{
_returnValHA2.AppendFormat("{0:x2}", b);
}
byte[] inputBytesHA3 = Encoding.ASCII.GetBytes(string.Format("{0}:{1}:{2}:{3}:{4}:{5}", _returnValHA1, serverNonce, requestCnt, clientNonce, qop, _returnValHA2));
byte[] HA3 = MD5.Create().ComputeHash(inputBytesHA3);
StringBuilder _returnVal = new StringBuilder(HA3.Length * 2);
foreach (byte b in HA3)
{
_returnVal.AppendFormat("{0:x2}", b);
}
return _returnVal.ToString();
}
}
Важные заметки:
- Я пытался загрузить ZIP как
FileStream
, Я пытался построить сырой C#HttpWebRequest
, Я попыталсяточно подобрать заголовки Roku. - Я использовал Fiddler и WireShark для мониторинга сетевого трафика - насколько я могу судить, трафик практически идентичен (заголовки немного отличаются, что создает разные смещения пакетов).
- Я даже дошел до того, что разобрал повторно собранные пакеты (в соответствии с тем, что сообщает Wireshark), и они кажутся почти идентичными (опять же, в основном различия в заголовках).
- Я использовал реализации других людей для Digest Auth - в конце концов, я сделал свою собственную. Я использую HEAD для сервера, чтобы получить значение nonce, а затем использую это значение nonce для построения моего следующего POST, который используется в дайджесте. Из того, что я могу сказать, похоже, что это работает... но у меня есть подозрения, что это может быть виновником (когда я создаю заголовок аутентификации дайджеста, устройство Roku дает мне HTTP 100 Продолжить, и, кажется, принимает мой дайджест аутентификации и тогда мой POST запрос).
- Я также поиграл с кодировками, следуя советам других рекомендаций SO. Я могу получить различные значения загрузки байтов в Roku (например, как есть, без кодирования, я могу получить около 115700 байт, о которых мне сообщают, если я делаю UTF16, я могу получить около 270000 байт). Из wireshark кажется, что ВЕСЬ ZIP-файл передается в Roku. Мое подозрение #1 состоит в том, что мои кодировки испорчены.
У меня есть данные Wireshark, данные Fiddler, и, как 5 различных реализаций (отличается от приведенного выше примера), которыми я могу поделиться. Я даже могу поделиться JavaScript, который используется встроенным WebApplication от Roku, чтобы показать, что он делает (это POST, использующий дайджест-аутентификацию - прямо скажем). Скажи мне, чего тебе не хватает - я боролся с этим уже 2 недели и наконец сломался, чтобы попросить о помощи!
Есть идеи?
1 ответ
Этот вопрос сводится к тому, "как я могу сделать http POST из C#" и "как мне сделать дайджест-аутентификацию из C#" - если вы гуглите это, сразу появится несколько SO-ответов, следуйте им.
Кодировка не имеет к этому никакого отношения, ZIP представляется в виде необработанных октетов.
Вот идея для устранения неполадок (разделяй и властвуй) - сначала научите ваше приложение загружать небольшие ZIP-файлы, <100k. Напишите тривиальное приложение BrightScript (Hello World) и посмотрите, что ваш код может успешно загрузить. Затем сконцентрируйтесь на больших размерах - это может быть, например, то, что ваш клиент пытается использовать chunked-encodding, а сервер не поддерживает его. Моя точка зрения - начните с простого и идите к полному делу