在C#(Unity)中复制cURL命令

时间:2017-11-16 03:12:06

标签: c# http encoding monodevelop roku

我尝试了很多变化 - 我在下面发布的是一个清理版本,我最初开始使用的版本。这是一个帮助实用程序我正在为基于Unity的应用程序编写。它不是游戏,只是一个2D应用程序。

我试图复制这个:

curl -f -s -S --user $(ROKU_DEV_CREDENTIALS) --anyauth -F "mysubmit=Install" -F "archive=@out/Archive.zip" http://$(ROKUIP)/plugin_install > /dev/null

这是我到目前为止(见下面的代码) - 似乎是"有点"工作,但允许auth通过,大约108 KB的数据(wireshark说,整个ZIP,大约2.8 MB,正在发送,但服务器说只收到大约108 KB);我想也许这可能是一个编码问题(应用程序在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();
    }
}

重要提示:

  1. 我已尝试将ZIP加载为FileStream。我试过建一个 原始C#HttpWebRequest。我尝试过匹配Roku的标头 的准确即可。
  2. 我已经使用Fiddler和WireShark来监控网络流量 - 据我所知,流量几乎相同(标头有点不同,会产生不同的数据包偏移)。
  3. 我甚至已经消除了重新组装的数据包(根据wireshark的报告),它们看起来几乎完全相同(主要是标题差异)。
  4. 我已经使用了其他人的Digest Auth实现 - 最后我自己推出了。我使用HEAD到服务器来获取nonce值,然后我使用nonce值来构建我在摘要中使用的下一个POST。据我所知,这似乎有效...但我怀疑这可能是罪魁祸首(当我构建摘要auth头时,Roku设备给我HTTP 100继续,它似乎接受我的摘要身份验证和然后我的POST请求)。
  5. 我也跟着编码一起玩过编码 其他SO建议的建议。我可以得到不同的字节上传 进入Roku的值(例如,原样,我可以使用NO编码 得到大约115700字节报告给我,如果我做UTF16我可以得到 270000~字节)。从wireshark看来,整个ZIP文件 被推到了Roku。 我的#1怀疑是我的编码 搞砸了。
  6. 我有wirehark数据,fiddler数据,以及我可以分享的5种不同的实现(与上面的例子不同)。我甚至可以分享Roku的嵌入式WebApplication所使用的JavaScript,以显示它正在做什么(它是使用digest auth进行的POST - 有点直接)。告诉我你错过了什么 - 我现在已经打了2个星期了,最后还是去寻求帮助!

    有什么想法吗?

1 个答案:

答案 0 :(得分:0)

这个问题归结为“我如何从c#进行http POST”和“我如何从c#进行摘要身份验证” - 如果你谷歌这个,一些S.O.答案很流行,请遵循这些。

编码与它无关,ZIP以原始八位字节的形式提交。

这是故障排除(分而治之)的想法 - 首先教你的应用程序上传小型ZIP,<100k。编写一个简单的BrightScript应用程序(Hello World),看看你的代码可以成功上传。然后专注于更大的尺寸 - 例如,您的客户端可能尝试使用chunked-encodding,而服务器不支持它。我的观点是 - 从简单开始,走向完整的案例