I am using an Onvif-supported camera and am controlling it using onvif wsdl's (specifically ImagingService, Media10, and PTZ). These work great for controlling most aspects of the camera, but there are a few settings that I want to modify that do not seem to be supported by my camera's version of Onvif.
Onvif works by sending soap requests/packets to the camera. However, when I used Wireshark and observed what happens when I use the software the camera came with, I noticed it works by sending http GET/POST requests containing FormUrlEncoded messages, and one of the fields is labelled session, presumably the session ID for the connection. I was able to recreate the message that the camera's software sends, send it, and confirm that my settings change. However, whenever a new session starts (different ID), it no longer works, and my http response contains:
{"error":{"code":287637505,"message":"Invalid session in request data!"},"result":false}
In other words, hard coding the session Id that I got through wireshark was the only way I could get it to work, but as soon as the session changes it no longer works. I was wondering if there was a way to easily get the session ID, store it as a variable, and then just place it within my messages. I would hope this would be possible either through the HttpClient, or any of the three Onvif clients I am using (MediaClient, ImagingClient, or PTZClient).
I have tried searching through many of the methods/fields in each of the clients above, creating HttpWebRequests, looking through the response messages for GET & POST, looking around where the login credentials are kept, and creating a Cookie with the info.
The way I go about setting everything up is rather simple:
private static readonly HttpClient httpClient = new HttpClient();
var msg = $"{{\"method\":\"configManager.setConfig\",\"params\":{{\"name\":\"VideoInExposure\",\"table\":" +
$"[[{{\"AntiFlicker\":2,\"Compensation\":50,\"DoubleExposure\":0,\"Gain\":50,\"GainMax\":2,\"GainMin\":0," +
$"\"GlareInhibition\":0,\"Iris\":50,\"IrisAuto\":true,\"IrisMax\":30,\"IrisMin\":0,\"Mode\":4,\"RecoveryTime\":900," +
$"\"SlowAutoExposure\":50,\"SlowShutter\":false,\"SlowSpeed\":30,\"Speed\":0,\"Value1\":250,\"Value2\":250," +
$"\"WideDynamicRange\":0,\"WideDynamicRangeMode\":0}}}]],\"options\":[]}},\"id\":6123,\"session\":\"4ec9a21aac415eead620ab163141fa03\"}}";
var response = await httpClient.PostAsync("http://192.168.100.233/RPC2", new StringContent(msg, Encoding.UTF8, "application/x-www-form-urlencoded"));
Where the portion labeled session at the end of the message is the string that I need to obtain.
I was hoping this property would be stored somewhere accessible, either in the client, or within a response if I just do a simple GET command, but I have not yet found this to be the case. Any help is greatly appreciated!
edit: I have also tried getting this value from the soap commands that Onvif already uses, with no luck so far. But if there is a way to do it that way I am very open to it.
Related
I am getting the EWS URL as below:
var response = myAutodiscoverService.GetUserSettings(
"userA#domain.com",
UserSettingName.ExternalEwsUrl,
UserSettingName.InternalEwsUrl,
UserSettingName.EwsSupportedSchemas);
string settingValue;
if (response.TryGetSettingValue(UserSettingName.ExternalEwsUrl, out settingValue)
|| response.TryGetSettingValue(UserSettingName.InternalEwsUrl, out settingValue))
{
// If ExternalEwsUrl is not returned in the response,
// we consider InternalEwsUrl for auto-discover
string ewsurl = settingValue;
}
Now, I am caching this ewsurl I get with this request as recommended since auto-discovery is really slow. What happens when this ewsurl expires (because let's say user's mailbox was moved to a different endpoint). What error should I expect in order to implement a failure mechanism to get the new updated ews url?
Edit: I am using the ews url to subscribe for new mail events on EWS using ExchangeService.SubscribeToPushNotifications So I have another question, suppose if the ews url was changed after user subscribed for notifications, will I get a notification for that user for a new mail event on the callback url of my application?
I seem to recall the ROT from MSFT was you should re-autodiscover every 24 hours, but I've not seen anything recently. As to the Push notifications: since there is a "heartbeat" facility in Push notifications, if you miss two heartbeats in a row, it's safe to say you should tear down the subscription and re-subscribe, which to be safe, implies re-autodiscovery. Of course with O365, since the EWS URL seems (for now) to always be the same, re-autodiscovery is less of a concern.
Edit: after talking it over with a couple IT guys, I've realized it's only the POLL requests that are having issues. I'm fetching the images via GET requests that go through quickly and as expected, whether or not the POLL messages are having issues.
I'm working on a client to interface with an IP camera in C#.
It's all working dandy except that I can get really poor http request performance when I'm not using Fiddler (a web traffic inspection proxy).
I'm using an httpclient to send my requests, this is my code that actually initiates the poll request:
public async Task<bool> SetPoll(int whichpreset)
{
string action = "set";
string resource = presetnames[whichpreset];
string value = presetvalues[whichpreset];
int requestlen = 24 + action.Length + resource.Length + value.Length;
var request = new HttpRequestMessage
{
RequestUri = new Uri("http://" + ipadd + "/res.php"),
Method = HttpMethod.Post,
Content = new FormUrlEncodedContent(new[]{
new KeyValuePair<string,string>("action",action),
new KeyValuePair<string,string>("resource",resource),
new KeyValuePair<string,string>("value",value)
}),
Version = new System.Version("1.1"),
};
HttpResponseMessage mess = await client.SendAsync(request);
if (mess.IsSuccessStatusCode)
{
return true;
}
else
{
return false;
}
}
When Fiddler is up, all my http requests go through quickly, and without a hitch (I'm making about 20 post requests upon connecting). Without it, they only go through as expected ~1/5 of the time, and the rest of the time they're never completed, which is a big issue. Additionally, the initial connection request often takes 1+ minutes when not using Fiddler, and consistently only takes a few seconds when I am, so it doesn't seem to be a timing issue of sending requests too soon after connecting.
This leads me to think that the request, as written, is fairly poorly behaved, and perhaps Fiddler's requests behave better. I'm a newbie to HTTP, so I'm not sure exactly why this would be. My questions:
does Fiddler modify HTTP requests (E.G. different headers, etc.)
as they are sent to the server?
even if it doesn't modify the requests, are Fiddler's requests in
some way better behaved than I'd be getting out of .net 4.0 in C# in
VS2013?
is there a way to improve the behavior of my requests to emulate
whatever Fiddler is doing? Ideally while still working within the
stock HTTP namespace, but I'm open to using others if necessary.
I'll happily furnish more code if helpful (though tomorrow).
Inserting
await Task.Delay(50);
between all requests fixed the problem (I haven't yet tested at different delays). Because Fiddler smoothed the problem out, I suspect it's an issue the camera has with requests sent in too quick of a succession, and fiddler sent them at a more tolerable rate. Because it's an async await, there is no noticeable performance impact, other than it taking a little while to get through all ~20 (30 now) requests through on startup, which is not an issue for my app.
Fiddler installs itself as a system proxy. It is possible that the Fiddler process has better access to the network than your application's process.
Fiddler might be configured to bypass your normal system proxy (check the gateway tab under options) and perhaps the normal system proxy has issues.
Fiddler might be running as a different user with a different network profile, e.g. could be using a different user cert store or different proxy settings such as exclusion list.
Fiddler might be configured to override your hosts file and your hosts file may contain errors.
Your machine might be timing out trying to reach the servers necessary to check for certificate revocation. Fiddler has CRL checking disabled by default (check the HTTPS tab).
Fiddler has a ton of options and the above are just some guesses.
My recommendation would be to check and/or toggle the above options to see if any of them apply. If you can't get anywhere, you may have to forget Fiddler exists and troubleshoot your network problems independently, e.g. by using NSLOOKUP, PING, TRACERT, and possibly TELNET to isolate the problem.
There is nothing in your code sample that suggests a code flaw that could cause intermittent network failures of the kind you are describing. In fact it is hard to imagine any code flaw that would cause that sort of behavior.
I have a web application which is a mesh of a few different servers and 1 server is the front-end server which handles all request external incoming requests.
So some of these request will have to be passed along to different servers and ideally the only thing I want to change is the host and Uri fields of these request. Is there a way to map an entire incoming request to a new outgoing request and just change a few fields?
I tried something like this:
// some controller
public HttpResponseMessage get()
{
return this.Request.Rewrite("192.168.10.13/api/action");
}
//extension method Rewrite
public static HttpResponseMessage Rewrite(this HttpRequestMessage requestIn, string Uri) {
HttpClient httpClient = new HttpClient(new HttpClientHandler());
HttpRequestMessage requestOut = new HttpRequestMessage(requestIn.Method, Uri);
requestOut.Content = requestIn.Content;
var headerCollection = requestIn.Headers.ToDictionary(x => x.Key, y => y.Value);
foreach (var i in headerCollection)
{
requestOut.Headers.Add(i.Key, i.Value);
}
return httpClient.SendAsync(requestOut).Result;
}
The issue I am having is that this has a whole slew of issues. If the request is a get Content shouldn't be set. THe headers are incorrect since it also copies things like host which shouldn't be touched afterwards etc.
Is there an easier way to do something like this?
I had to do this in C# code for a Silverlight solution once. It was not pretty.
What you're wanting is called reverse proxying and application request routing.
First, reverse proxy solutions... they're relatively simple.
Here's Scott Forsyth and Carlos Aguilar Mares guides for creating a reverse proxy using web.config under IIS.
Here's a module some dude named Paul Johnston wrote if you don't like the normal solution. All of these focus on IIS.
Non-IIS reverse proxies are more common for load balancing. Typically they're Apache based or proprietary hardware. They vary from free to expensive as balls. Forgive the slang.
To maintain consistency for the client's perspective you may need more than just a reverse proxy configuration. So before you go down the pure reverse proxy route... there's some considerations.
The servers likely need to share Machine Keys to synchronize view state and other stuff, and share the Session Store too.
If that's not consistent enough, you may want to implement session stickiness through Application Request Routing (look for Server Affinity), such that a given session cookie (or IP address, or maybe have it generate a token cookie) maps the user to the same server on every request.
I also wrote a simple but powerful reverse proxy for asp.net / web api. It does exactly what you need.
You can find it here:
https://github.com/SharpTools/SharpReverseProxy
Just add to your project via nuget and you're good to go. You can even modify on the fly the request, the response, or deny a forwarding due to authentication failure.
Take a look at the source code, it's really easy to implement :)
I'm coming to .net web api from a JavaScript background, and I'm trying to make a proxy to help with a cross domain JSON request. I'm GETing from a server I don't control the source code for, so I can't configure CORS directly. Likewise, it doesn't speak JSONP.
So two questions as I try to get my head around Web API:
1) Is Httpclient the right tool for this job? (if not, what is?)
2) If httpclient IS the right tool, what is an absolute bare bones httpclient config so I can test this out? Not worried about throwing exceptions or anything else other than just GETing API data and feeding it to a jQuery client.
I guess one other piece of information that would be nice would be building username / password authentication into the http request.
Any help is much appreciated, as are links to any good blogs / tutorials / etc that might help as an introduction to this sort of thing. I've watched several today alone, and I'm still not able to get a basic http request going on the server side without resorting to cutting / pasting other people's code.
Thanks in advance!
** EDIT - To make this question a bit more clear, what I'm trying to test is 1) Can the proxy connect to the third party server, which involves authentication via a username and password 2) Can the proxy then respond to the jQuery client request with the JSON data it received from the third party server.
Thanks to all who have taken the time to respond.
HttpClient seems to be ok in this job.
About the minimal config- it depends on what the third party expects. In most cases would work out-of-the-box, but there always may be some minor tweaks like headers and/or auth code.
I have just found some blog entry where some author shows how to test such a proxy and shows the proxy code too. Please see: http://www.davidbreyer.com/programming/2014/10/11/create-fake-responses-to-rest-service-calls-in-c/
You can find info about sending credentials here: How to use credentials in HttpClient in c#?
HTH
EDIT:
this sample code should work (copied from blog above and modified):
public class Proxy
{
public async Task<ExampleDto> GetExample(int id)
{
var client=new HttpClient();
//set some auth here
//set other headers
var response = client.GetAsync(
string.Format("/api/restserviceexample/{0}", id))
.Result.Content.ReadAsAsync<ExampleDto>();
return await response;
}
}
It's so simple that you can just run it and see if the other server responds. If not, you can play with headers - since all the session info and user auth info are sent using ookies and/or headers, all you have to do is to see how it's made with regular browser and then fake it on the server. Probably best tool for this job will be Fiddler.
However - there is one thing to consider. If the other service has special method for authorization (other than passing credentials with each request) the whole thing becomes tricky, since your proxy should perform authorization using their service, then store their auth cookie on the server or propagate them to the browser and attach them with all next requests.
First, you don't need ASP.NET with C# if you really want minimal.
.NET has great http handling without ASP. Check out classes like HttpListener, HttpListenerContext, HttpListenerRequest, etc... Yes, you'll have to write some boilerplate as your application, but these classes are pretty good.
See among others:
http://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=599978
Second, if you want user & password, I'd checkout using oauth authentication so you don't have to deal with them directly. Google Plus, Windows Live, Facebook, etc... all have similar OAuth 2.0 APIs for that. See among others:
http://msdn.microsoft.com/en-us/library/dn659750.aspx
https://developers.google.com/+/web/signin/server-side-flow
https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.2
I'm trying to get the raw data sent to IIS using a HttpHandler. However, because the request is an "GET"-request without the "Content-Length" header set it reports that there is no data to read (TotalBytes), and the inputstream is empty. Is there any way I can plug into the IIS-pipeline (maybe even before the request is parsed) and just kind of take control over the request and read it's raw data? I don't care if I need to parse headers and stuff like that myself, I just want to get my hands on the actual request and tell IIS to ignore this one. Is that at all possible? Cause right now it looks like I need to do the alternative, which is developing a custom standalone server, and I really don't want to do that.
Most web servers will ignore (and rarely give you access to) the body of a GET request, because the HTTP semantics imply that it is to be ignored anyway. You should consider another method (for example POST or PUT).
See this question and the link in this answer:
HTTP GET with request body