Is WebRequest (System.Net) a safe choice in Unity? - c#

First of all, let me state my problem: my game server does not provide WebAPI (we do not have resources for it now), but rather our client is tring to work like a web browser and I need cookie support for Session ID.
Searching around with Google, I see the best I can do is manually set the headers of request and get the response header. I am ok with that, because I am originally ASP.NET MVC developer.
However, I then realize they use Dictionary for both the request and response. Now that's the problem. We know that the header can be duplicated, in my case is the Set-Cookie.
Then I tried another, and find out UnityWebRequest class, which is still in UnityEngine.Experimental.Networking namespace (so I suppose it is still in beta?), but I try my luck anyway; only sad to realize they also use Dictionary for header items.
So now my only chance is the vanilla .NET WebRequest (in System.Net namespace). However, I see no documentation on the .NET Framework compability in Unity. Can anyone tell me if it is supported on most platform? My main targets are Windows, Android and Web. If possible, even for WebClient would be nicer.
Here is my current solution, which work good in the Unity Editor, but I have yet to test them on other devices. Is there any solution for this?
public class CookieWebRequest
{
private CookieContainer cookieContainer;
public CookieWebRequest()
{
this.cookieContainer = new CookieContainer();
}
public void GetAsync(Uri uri, Action<HttpWebResponse> onFinished)
{
var webRequest = HttpWebRequest.Create(uri) as HttpWebRequest;
webRequest.Method = WebRequestMethods.Http.Get;
webRequest.CookieContainer = this.cookieContainer;
new Thread(() =>
{
HttpWebResponse httpResponse;
try
{
httpResponse = webRequest.GetResponse() as HttpWebResponse;
}
catch (WebException ex)
{
if (onFinished != null)
{
onFinished(ex.Response as HttpWebResponse);
}
return;
}
if (httpResponse.Cookies != null && httpResponse.Cookies.Count > 0)
{
this.cookieContainer.Add(httpResponse.Cookies);
}
if (onFinished != null)
{
onFinished(httpResponse);
}
httpResponse.GetResponseStream().Dispose();
}).Start();
}
}

System.Net.HttpWebRequest and System.Net.WebClient work on most platforms supported by Unity. However when you want to build for the Unity Web Player or WebGL you will run into problems since Unity does not support most of the System.Net networking stuff since javascript does not have direct access to IP Sockets.
WebGL network restictions
As you already mentioned UnityWebRequest or the legacy WWW object from Unity is your best bet. With Unity 5.3 UnityWebRequest work on most platforms including WebGL and the Unity Web player. But as you also already mentioned the complete UnityWebRequest is still experimental but is under constant development and will probably improve in every new update.
The only downside, using the WWW or UnityWebRequest object is (as far as I understood the UnityWebRequest object) that they need to run in the Unity main thread so you will have to use Coroutines instead of pushing the request into a different thread. As long as you do not have millions of webrequest this should not lead into any performance issues of your app. And is probably less error prone.

The bigger issue is that WWW and UnityWebRequest do not support keep-alive on most platforms at the moment (WEBGL might be an exception, actually). Expect any SSL encrypted requests to each have a huge amount of overhead (300+ milliseconds on a good machine).

Related

HTTPWebRequest acts differently from same request sent through browser

I am working with the shift4shop web api. These guys used to be known as threeDCart if that helps anyone. Its an eCommerce platform.
we are trying to apply a promotion code to an open cart.
support has verified there is no api-way to do that.
there is an url that will apply the promotion. This is often emailed to customers so they can apply the promo if they choose to.
we can paste the correct url in chrome, brave, edge, or firefox and it correctly applies the promotion.
We used private tabs for the different browser tests and the browsers were 'cold'. we launched the browser and immediately entered the URL.
We are thinking this eliminates the possibility that there are cookies that are necessary.
https://www.mywebsite.com/continue_order.asp?orderkey=CDC886A7O4Srgyn278668&ApplyPromo=40pro
However, when I try to do this in C#, i get a response that is redirected a page that says 'The cart is empty'.
The promotion is not applied
I am stumped as to how the website would respond differently to the same URL when it comes from a browser as opposed to the c# system.net library.
here is the c# code I am using
using System.Net;
//i really create this using my data, but this is the resulting url
string url = "https://www.mywebsite.com/continue_order.asp?orderkey=CDC886A7O4Srgyn278668&ApplyPromo=40pro"
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
HttpWebResponse response = (HttpWebResponse)req.GetResponse();
string result = "";
using (StreamReader rdr = new StreamReader(response.GetResponseStream()))
{
result = rdr.ReadToEnd();
}
You can also call ".view_cart.asp" w the same parameters and the browsers will cause the promo to be applied.
I have tried setting the method to [ , GET, get ]
There has to be something about the request settings that are preventing this from working.
I do not know what else to try.
Any thoughts are appreciated.
As per shift4shop support, the continue_order.asp has a 302.
the browsers land on continue_order.asp and process that page.
They then continue on to view_order.asp.
The 2 pages together perform functionality that you can not get by just calling continue_order.asp
Thanks to savoy w/ shift4Shop for helping on that.

First Webrequest is very slow

My problem is when using WebClient or WebRequest/HttpRequest that the first request often takes a bit more than 40 seconds and all subsequent ones only around 250 milliseconds regardless of size (if it is serveral k characters or only 40). Im only trying to get JSON data from some endpoint.
with WebClient I use wc.DownloadString("some address");
with WeRequest I use (HttpWebRequest) WebRequest.Create("some address") and webRequest.GetResponse(); with according stream handling afterwards
I already looked around for a Solution, and found:
WebRequest.DefaultWebProxy = GlobalProxySelection.GetEmptyWebProxy();
WebRequest.DefaultWebProxy = null;
webclient.Proxy = null;
webclient.Proxy = GlobalProxySelection.GetEmptyWebProxy();
The above mentionen options are NOT reliable working for me.
Are there any other ways to reliable fix this huge delay for first Request? I would prefere a solution without external libs if possible.
As a note I use .Net Framework 4.5.

Making a post request in Unity on Windows Phone 8

I'm trying to do a post request on windows phone 8 from the Unity Platform. I do not want to use the unity WWW method as this blocks rendering (and is not thread safe).
The following code works in the editor and on Android, but when building it for WP8 I get the following error.
System.Byte[] System.Net.WebClient::UploadData(System.String,System.String,System.Byte[])` doesn't exist in target framework.
The reason for this error is explained here
It’s because Windows Phone 8 uses a different flavor of .NET called .NET for Windows Phone which is missing some of the types available on other platforms. You’ll have to either replace these types with different ones or implement them yourself.
- http://docs.unity3d.com/Manual/wp8-faq.html
This is my code
using (WebClient client = new WebClient())
{
client.Encoding = System.Text.Encoding.UTF8;
client.Headers[HttpRequestHeader.ContentType] = "application/json";
byte[] requestData = new byte[0];
string jsonRequest = "{}";
if (data != null)
{
string tempRequest = Converter.SerializeToString (data);
jsonRequest = "{\"Data\": \"" + tempRequest + "\"}";
requestData = System.Text.Encoding.UTF8.GetBytes(jsonRequest);
}
// below line of code is the culprit
byte[] returnedData = client.UploadData(url, "POST", requestData);
if(returnedData.Length > 0)
{
// do stuff
}
}
I've also tried WebRequests, but GetResponse() breaks it, and HttpClient does not exist.
So, how do I post data in Unity, without using WWW, on windows phone 8?
UPDATE AS PER COMMENT REQUEST - WebRequests
This code, using HttpWebRequest works in the editor and on Android, but on windows phone throws the errors listed below the code.
var request = (System.Net.HttpWebRequest) System.Net.WebRequest.Create(url);
request.ContentType = "application/json";
request.Method = "POST";
var sw = new System.IO.StreamWriter(request.GetRequestStream(), System.Text.Encoding.UTF8);
sw.Write(jsonRequest); // jsonRequest is same variable as in above code, string with json object.
sw.Close();
var re = request.GetResponse();
string resultString = "";
using (var outputStream = new System.IO.StreamReader(re.GetResponseStream(), System.Text.Encoding.UTF8))
{
resultString = outputStream.ReadToEnd();
}
if(resultString.Length > 0)
{}
Error 1:
Error: method System.IO.Stream System.Net.HttpWebRequest::GetRequestStream() doesn't exist in target framework.
Error 2:
System.Net.WebResponse System.Net.HttpWebRequest::GetResponse() doesn't exist in target framework.
UPDATE WITH MORE DETAILS - UploadStringAsync
Using this code to make an async request it again works great in the editor, errors are thrown on the WP8.
bool isCompleted = false;
byte[] returnedData = null;
client.UploadDataCompleted +=
new UploadDataCompletedEventHandler((object sender, UploadDataCompletedEventArgs e) =>
{
Debug.Log("return event");
returnedData = e.Result;
isCompleted =true;
});
Debug.Log("async call start");
client.UploadDataAsync(new Uri(url), requestData);
while(isCompleted == false){
Thread.Sleep(100);
}
if(returnedData.Length > 0)
{}
Error 1
method System.Void System.Net.WebClient::add_UploadDataCompleted(System.Net.UploadDataCompletedEventHandler) doesn't exist in target framework.
Error 2
Error: method System.Void System.Net.WebClient::UploadDataAsync(System.Uri,System.Byte[]) doesn't exist in target framework.
Error 3
Error: type System.Net.UploadDataCompletedEventArgs doesn't exist in target framework.
Error 4
Error: method System.Byte[] System.Net.UploadDataCompletedEventArgs::get_Result() doesn't exist in target framework.
I don't know about any potential restrictions placed on you by Unity, but Windows Phone 8 has the WebClient.UploadStringAsync method and the WebClient.UploadStringCompleted event for doing just this.
HttpWebRequest should also work (again, I don't know about any Unity limitations - see comment above asking for clarification).
Go it to work on the windows phone using the code below. Compiles and runs in the editor, on Android and on WP8 (yay!). Haven't tried it on iOS yet.
Also wrote a post about it here: Create Web requests for unity that work on all platforms, even WP8
/// <summary>
/// Make post request to url with given paramaters
/// </summary>
/// <param name="url">URL to post data to http://server.com/method </param>
/// <param name="data">{ Data: data }</param>
/// <returns>string server response</returns>
public string PostData(string url, string data)
{
// json request, hard coded right now but use "data" paramater to set this value.
string jsonRequest = "{\"Data\": \"data\"}"; // the json request
var request = System.Net.WebRequest.Create(url) as System.Net.HttpWebRequest;
// this could be different for your server
request.ContentType = "application/json";
// i want to do post and not get
request.Method = "POST";
// used to check if async call is complete
bool isRequestCallComplete = false;
// store the response in this
string responseString = string.Empty;
request.BeginGetRequestStream(ar =>
{
var requestStream = request.EndGetRequestStream(ar);
using (var sw = new System.IO.StreamWriter(requestStream))
{
// write the request data to the server
sw.Write(jsonRequest);
// force write of all content
sw.Flush();
}
request.BeginGetResponse(a =>
{
var response = request.EndGetResponse(a);
var responseStream = response.GetResponseStream();
using (var sr = new System.IO.StreamReader(responseStream))
{
// read in the servers response right here.
responseString = sr.ReadToEnd();
}
// set this to true so the while loop at the end stops looping.
isRequestCallComplete = true;
}, null);
}, null);
// wait for request to complete before continuing
// probably want to add some sort of time out to this
// so that the request is stopped after X seconds.
while (isRequestCallComplete == false) { Thread.Sleep(50); }
return responseString;
}
You cannot use these frameworks from unity because they are threaded / async / use another version of .Net
In short :
Unity runs on a version of mono which accepts only .Net 3.5
Unity does not allow you to start threads, tasks or anything close to real asynchronicity
You can implement those though.
To do that on windows phone you will need to create a class library (two in fact) what unity calls a plugin
So why two dlls?
Because the editor needs a mockup dll (or the actual one if you want to implement it for desktop as well) that implements the same interface as the dll which is compatible on windows phone.
To be clear :
The dll for the editor (in Assets/Plugins) needs to be compatible .Net 3.5 and desktop
The dll placed in Assets/plugins/WP8 needs to be compatible windows phone .Net subset (as i remember you can be .Net 4.5 with no pb)
So you should implement that functionality in a dll, then reference that in unity, and finally call it directly.
All is explained here : http://docs.unity3d.com/Manual/wp8-plugins-guide-csharp.html
It is simpler than it looks like.
I know you said you don't want to use the WWW class, but the reasons you gave to dont make sense IMO.
If you use a Coroutine to check the ".isDone" flag and not check it by using the Thread.Sleep() it wont block rendering and is thread safe (since unity only gives you access to its info on a single thread).
If you want to access the return data on another thread you just need to read that data in the main unity thread and then pass it along to whatever thread you want to use it on.
Unity by design is all runs on a single thread to make life easier for less experienced programmers. Though you should be able to create multiple threads using .NET threading, you will not be able to communicate directly with any Unity components, but you can pass the data back to Unity and wait until Update or another call by Unity to use the data. I know this doesnt answer your question exactly but if you cant solve it I think it would be worthwhile to look again at the WWW class and the best practices in using it.

Some help posting to ASP.NET Web API with C#

I have been playing with ASP.NET Web API. I am looking to see can I post to a method I have built which simply returns back the object I have POSTED:
On The Accounts Controller:
// POST /api/accounts
public Account Post(Account account)
{
return account;
}
Code Used To Post:
public void PostAccount()
{
// http://local_ip/api/accounts
var uri = string.Format("{0}", webServiceRoot);
var acc = new Account();
acc.AccountID = "8";
acc.AccountName = "Mitchel Cars";
acc.AccountNumber = "600123801";
acc.SubscriptionKey = "2535-8254-8568-1192";
acc.ValidUntil = DateTime.Now;
var request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = "POST";
request.ContentType = "application/xml";
request.ContentLength = 800;
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Account));
xmlSerializer.Serialize(request.GetRequestStream(), acc);
var response = (HttpWebResponse)request.GetResponse();
XmlSerializer serializer = new XmlSerializer(typeof(Account));
var newAcc = (Account)serializer.Deserialize(response.GetResponseStream());
}
I have removed any error checking or any boiler plate code to make it easier to read. This is strictly a spike just to under stand to to actually POST. My understanding is that you should write to the GetRequestStream(). All the reading and such seems to work ok but I never here back from the request.GetResponse();
If I do a simple get it works fine. I did see you can use a class called HTTPClient for doing this stuff but I can't use it as I need to get this working for WinForms, Silverlight and Windows Phone all based on .Net 3.5
Any help pushing POCO's to the server would be a great help, cheers!
ADDITIONAL INFO:
Currently I get no error, the test app just hangs.
If I turn off the WebAPI project I get a server not found response.
I have not changed any routes or any of that.
Gets to the same controller work.
You will need to close the response stream. Most examples I see also show setting the content length. You may be better to serialize to a memory stream and then use the length of that stream as the Content-Length. Unfortunately in .net 3.5 there is no CopyStream so you may have to write that yourself.
If you want to use the HttpClient, you can install the download the REST starter Kit. and use the DLLs as external DLLs
http://forums.asp.net/t/1680252.aspx/1

Fast HTTP call ASP.Net

I'm sending an HTTPWebRequest to a 3rd party with the code below. The response takes between 2 and 22 seconds to come back. The 3rd party claims that once they receive it, they are sending back a response immediately, and that none of their other partners are reporting any delays (but I'm not sure I believe them -- they've lied before).
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://www.example.com");
request.Timeout = 38000;
request.Method = "POST";
request.ContentType = "text/xml";
StreamWriter streamOut = new StreamWriter(request.GetRequestStream(), System.Text.Encoding.ASCII);
streamOut.Write(XMLToSend); // XMLToSend is just a string that is maybe 1kb in size
streamOut.Close();
HttpWebResponse resp = null;
resp = (HttpWebResponse)request.GetResponse(); // This line takes between 2 and 22 seconds to return.
StreamReader responseReader = new StreamReader(resp.GetResponseStream(), Encoding.UTF8);
Response = responseReader.ReadToEnd(); // Response is merely a string to hold the response.
Is there any reason that the code above would just...pause? The code is running in a very solid hosting provider (Rackspace Intensive Segment), and the machine it is on isn't being used for anything else. I'm merely testing some code that we are about to put into production. So, it's not that the machine is taxed, and given that it is Rackspace and we are paying a boatload, I doubt it is their network either.
I'm just trying to make sure that my code is as fast as possible, and that I'm not doing anything stupid, because in a few weeks, this code will be ramped up to run 20,000 requests to this 3rd part every hour.
Try doing a flush before you close.
streamOut.Flush();
streamOut.Close();
Also download microsoft network monitor to see for certain if the hold up is you or them, you can download it here...
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=983b941d-06cb-4658-b7f6-3088333d062f&displaylang=en
There is a few things that I would do:
I would profile the code above and get some definitive timings.
Implement the using statements in order to dispose of resources correctly.
Write the code in an async style there's going to be an awful lot of IO wait once its ramped.
Can you hit the URL in a regular ole browser? How fast is that?
Can you hit other URL's (not your partner's) in this code? How fast is that?
It is entirely possible you're getting bitten by the 'latency bug' where even an instant response from your partner results in unpredictable delays from your perspective.
Another thought: I noticed the https in your URL. Is it any faster with http?

Categories

Resources