简体   繁体   English

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

[英]Replicate cURL command in C# (Unity)

I've tried a ton of variations - what I am posting below is a cleaned up version, of what I originally started with. 我已经尝试了很多变体-我在下面发布的是最初版本的清理版本。 This is a helper utility Im writing for a Unity-based app. 这是我为基于Unity的应用程序编写的帮助实用程序。 It's not a game, just a 2d application. 它不是游戏,而只是2D应用程序。

I'm trying to replicate this: 我正在尝试复制此内容:

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

This is what I have so far (see code below) - and it seems like to be "kinda" working, but allowing the auth to go through, and about 108 KB worth of data (wireshark says that the ENTIRE ZIP, about 2.8 MB, is being sent, but the server is saying only about 108 KB was received); 这就是我到目前为止(请参阅下面的代码)-似乎可以“运行”,但是允许身份验证通过,并且有价值约108 KB的数据(wireshark表示整个ZIP,约2.8 MB ,正在发送,但服务器说仅接收到约108 KB); I thought maybe this might be an encoding problem (app is on Windows 10 and Server is a embedded linux server [Roku Player]). 我以为这可能是编码问题(应用程序在Windows 10上,服务器是嵌入式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();
    }
}

Important Notes: 重要笔记:

  1. I've tried loading the ZIP as a FileStream . 我尝试将ZIP加载为FileStream I've tried building a raw C# HttpWebRequest . 我试图建立一个原始的C# HttpWebRequest I've tried matching the Roku's headers exactly . 我试过完全匹配Roku的标头。
  2. I've used both Fiddler and WireShark to monitor the network traffic - as far as I can tell the traffic is nearly identical (headers are a bit different which creates different packet offsets). 我已经使用了Fiddler和WireShark来监视网络流量-据我所知,流量几乎是相同的(报头有些不同,会产生不同的数据包偏移量)。
  3. I've even gone as far as to diff the reassembled packets (as per what wireshark is reporting) and they appear to be nearly identical (again mostly header differences). 我什至甚至去区分重组的数据包(根据wireshark报告的内容),它们看起来几乎是相同的(再次主要是报头差异)。
  4. I've used other's peoples implementations for Digest Auth - in the end I've rolled my own. 我已经将其他人的实现用于Digest Auth-最后,我推出了自己的实现。 I'm using a HEAD to the server to get the nonce value, then I use that nonce value to build my next POST which is used in the digest. 我正在使用HEAD到服务器以获取现时值,然后使用该现时值来构建摘要中使用的下一个POST。 From what I can tell, this appears to be working... but I have suspicions that this may be the culprit (when I build the digest auth header the Roku device gives me the HTTP 100 Continue and it appears to accept my digest auth and then my POST request). 据我所知,这似乎正在起作用...但是我怀疑这可能是罪魁祸首(当我构建摘要身份验证标头时,Roku设备为我提供了HTTP 100 Continue,它似乎接受了我的摘要身份验证,然后我的POST请求)。
  5. I've played around with the encodings as well, following the advice of other SO recommendations. 在遵循其他SO建议的建议之后,我也尝试了编码。 I can get different byte upload values into the Roku (for instance, as-is, with NO encoding I can get about 115700 bytes reported to me, if I do UTF16 I can get about 270000~ bytes). 我可以在Roku中获得不同的字节上载值(例如,按原样,使用NO编码,我可以获得大约115700个字节的报告给我,如果我执行UTF16,则可以得到大约270000〜个字节)。 From wireshark it appears that the ENTIRE ZIP file is being pushed to the Roku. 从wireshark看来,整个IRE ZIP文件已被推送到Roku。 My #1 suspicion is that my encodings are messed up. 我的#1怀疑是我的编码混乱了。

I have wireshark data, fiddler data, and like 5 different implementations (different from the above example) that I can share. 我有Wireshark数据,Fiddler数据以及可以共享的5种不同实现(与上面的示例不同)。 I can even share the JavaScript used by Roku's embedded WebApplication to show what it's doing (it's a POST using digest auth - kinda straight forward). 我什至可以共享Roku嵌入式Web应用程序使用的JavaScript来显示其功能(这是使用摘要身份验证的POST-直截了当)。 Tell me what you are missing - I've been fighting this for 2 weeks now and finally broken down to ask for help! 告诉我您想念的是什么-我已经为此奋斗了2周,终于分手寻求帮助!

Any ideas? 有任何想法吗?

This question boils down to "how do i do http POST from c#" and "how do i do digest authentication from c#" - if you google this, a few SO answers pop readily, follow these. 这个问题可以归结为“我如何从c#进行http POST”和“我如何从c#进行身份验证”-如果您将其搜索到Google,那么就会弹出一些SO答案,请按照以下步骤操作。

Encoding has nothing do with it, ZIP is submitted as raw octets. 编码与之无关,ZIP作为原始八位字节提交。

Here is idea for troubleshooting (divide & conquer) - first teach your app to upload small ZIPs, <100k. 这是进行故障排除(分而治之)的想法-首先教您的应用上传小于100k的小ZIP。 Write a trivial BrightScript app (Hello World) and see than your code can upload that successfully. 编写一个琐碎的BrightScript应用程序(Hello World),然后看您的代码能否成功上传它。 Then focus on bigger sizes - it might be for example that your client tries using chunked-encodding and server does not support it. 然后专注于更大的尺寸-例如,您的客户端可能尝试使用分块编码,而服务器不支持它。 My point is - start simple and head towards the full case 我的观点是-从简单开始,走向完整案例

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM