I'm trying to call a web service from a c# application, with sessionID.
In order to do this I need to set the "Domain" header in a cookie.
In Fiddler it looks like - "ASP.NET_SessionId=izdtd4tbzczsa3nlt5ujrbf5" (no domain is specified in the cookie).
The web service is at - "http://[some ip goes here]:8989/MyAPI.asmx".
I've tried:
http://[ip] ,
http://[ip]:8989 ,
http://[ip]:8989/MyAPI.asmx
All of these cause runtime error.
I've also tried the ip alone (i.e. 100.10.10.10) , which doesn't cause a runtime error, and sets the cookie, but the cookie is never sent when I invoke a web method.
Here's my code for setting the domain:
if (!string.IsNullOrEmpty(currentSessionID))
{
req.CookieContainer=new CookieContainer();
Cookie cookie = new Cookie("ASP.NET_SessionId", currentSessionID);
cookie.Domain = GetCookieUrl(); //<- What should this be?
req.CookieContainer.Add(cookie);
}
So what should the domain be?
Thanks.
I believe it should simply be [ip]. Drop the http:// part of what you've tried.
According to this page on MSDN, your code should be
cookie.Domain = "100.10.10.10";
Next, exactly what error are you getting? Also, are you confusing a Compile error with a Runtime error? I find it hard to believe you are getting a compilation error as Domain is a String property which means you can put pretty much anything into it.
Finally, why are you sending a cookie to a web service? The normal way is to pass everything in the form post or on the query string.
Update
BTW, if you absolutely must add a cookie to the header in order to pass it to a web service, the way you do this is (taken from here):
byte[] buffer = Encoding.ASCII.GetBytes("fareId=123456"); //the data you want to send to the web service
HttpWebRequest WebReq = (HttpWebRequest)WebRequest.Create(url);
WebReq.Method = "POST";
WebReq.ContentType = "application/x-www-form-urlencoded";
WebReq.ContentLength = buffer.Length;
WebReq.Headers["Cookie"] = "ASP.NET_SessionId=izdtd4tbzczsa3nlt5ujrbf5"
Stream PostData = WebReq.GetRequestStream();
Note that this sets the header inline with the request without instantiating a "cookie" object. The Domain property of a cookie is to help ensure the cookie is only sent to the domain listed. However, if you are initiating the request and trying to append a cookie to it, then the best way is to just add it as a string to the request headers.
The reason the cookie was not sent is that the request's content length should be set after adding the cookie, and not before.
The domain is the ip alone.
// Simple function to get cookie domain
private string GetCookieDomain(string uri)
{
Uri req_uri = new Uri(uri);
return req_uri.GetComponents(UriComponents.Host, UriFormat.Unescaped);
}
Related
I have already read many articles about the topic but I can't find solution.
So please don't mark this question as duplicate because other solutions won't work and are out to date.
I have a web application with a page containing a GridView (one button per row).
The button will create a HttpWebRequest (or WebClient, it's the same) and get its html.
I tried using one cookie or all the cookies but I have no success.
This is the code:
String path = Request.Url.GetLeftPart(UriPartial.Authority) + VirtualPathUtility.ToAbsolute("~/");
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(path + "MyPage.aspx");
CookieContainer cookieContainer = new CookieContainer();
HttpCookie httpCookie = HttpContext.Current.Request.Cookies.Get("ASP.NET_SessionId");
if (httpCookie != null)
{
Cookie myCookie = new Cookie();
// Convert between the System.Net.Cookie to a System.Web.HttpCookie...
myCookie.Domain = webRequest.RequestUri.Host;
myCookie.Expires = httpCookie.Expires;
myCookie.Name = httpCookie.Name;
myCookie.Path = httpCookie.Path;
myCookie.Secure = httpCookie.Secure;
myCookie.Value = httpCookie.Value;
cookieContainer.Add(myCookie);
}
webRequest.CookieContainer = cookieContainer;
string responseHTML = string.Empty;
using (HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse())
{
using (Stream responseStream = response.GetResponseStream())
{
using (StreamReader responseReader = new StreamReader(responseStream))
{
responseHTML = responseReader.ReadToEnd();
}
}
}
webRequest.GetResponse will get timeout.
I think the problem is the domain (localhost), i know it's not possible but i have not any domain and i won't create a fake one in web.config. Moreover i have tried using a fake domain without success.
Without the following line
webRequest.CookieContainer = cookieContainer;
it works nicely without sharing session.
I would remember domain must be set otherwise i will received the relative error.
Session access must be serialized. When you use ASP.NET session, it is necessary to "serialize" HTTP requests to avoid threading issues. If two or more requests were processed in parallel, that would mean two threads could change or read session variables at the same time, which could cause a variety of issues.
The good news: ASP.NET will serialize the requests for you, automatically. If you send a second request with the same ASP.NET_SessionId, it will wait until the first one has completed.
The bad news: That means that a mechanism like the one you are attempting will not work. Your web request runs in the context of one HTTP request that is already in progress; it will block any additional HTTP requests until it is completed, including the request that you are sending via WebRequest.
More good news: If your page reads session data and does not write it, it can specify a hint that will allow two threads to run concurrently. Try adding this to both pages (the page your code is behind and the page that your code is attempting to access):
<% #Page EnableSessionState="ReadOnly" %>
If ASP.NET recognizes that the session needs are read-only, it'll allow two read-only threads to run at the same time with the same session ID.
If you need read/write access in either page, you are out of luck.
An alternative would be to use HttpServerUtility.Transfer instead. The role of the first page would change. Instead of serving as a proxy to the second page, it hands off control to the second page. By putting the pages in series, you avoid any issues with parallelism.
Example:
Server.Transfer("MyPage.aspx");
My app communicates with an internal web API that requires authentication.
When I send the request I get the 401 challenge as expected, the handshake occurs, the authenticated request is re-sent and everything continues fine.
However, I know that the auth is required. Why do I have to wait for the challenge? Can I force the request to send the credentials in the first request?
My request generation is like this:
private static HttpWebRequest BuildRequest(string url, string methodType)
{
var request = HttpWebRequest.CreateHttp(url);
request.PreAuthenticate = true;
request.AuthenticationLevel = AuthenticationLevel.MutualAuthRequested;
request.Credentials = CredentialCache.DefaultNetworkCredentials;
request.Proxy.Credentials = CredentialCache.DefaultNetworkCredentials;
request.ContentType = CONTENT_TYPE;
request.Method = methodType;
request.UserAgent = BuildUserAgent();
return request;
}
Even with this code, the auth header isn't included in the initial request.
I know how to include the auth info with basic.... what I want to do is to use Windows Auth of the user executing the app (so I can't store the password in a config file).
UPDATE I also tried using a HttpClient and its own .Credentials property with the same result: no auth header is added to the initial request.
The only way I could get the auth header in was to hack it in manually using basic authentication (which won't fly for this use-case)
Ntlm is a challenge/response based authentication protocol. You need to make the first request so that the server can issue the challenge then in the subsequent request the client sends the response to the challenge. The server then verifies this response with the domain controller by giving it the challenge and the response that the client sent.
Without knowing the challenge you can't send the response which is why 2 requests are needed.
Basic authentication is password based so you can short circuit this by sending the credentials with the first request but in my experience this can be a problem for some servers to handle.
More details available here:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa378749(v=vs.85).aspx
I'm not 100% sure, but I suspect that there is no way around this; it's simply the way HttpWebRequest works.
In the online .NET source, function DoSubmitRequestProcessing which is here, you can see this comment just after the start of the function, line 1731:
// We have a response of some sort, see if we need to resubmit
// it do to authentication, redirection or something
// else, then handle clearing out state and draining out old response.
A little further down (line 1795) (some lines removed for brevity)
if (resubmit)
{
if (CacheProtocol != null && _HttpResponse != null) CacheProtocol.Reset();
ClearRequestForResubmit(ntlmFollowupRequest);
...
}
And in ClearRequestForResubmit line 5891:
// We're uploading and need to resubmit for Authentication or Redirect.
and then (Line 5923):
// The second NTLM request is required to use the same connection, don't close it
if (ntlmFollowupRequest) {....}
To my (admittedly n00bish) eyes these comments seem to indicate that the developers decided to follow the "standard" challenge-response protocol for NTML/Kerberos and not include any way of sending authentication headers up-front.
Setting PreAuthenticate is what you want, which you are doing. The very first request will still do the handshake but for subsequent requests it will automatically send the credentials (based on the URL being used). You can read up on it here: http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.preauthenticate(v=vs.110).aspx.
I would like to grab some content from a website that is made with Drupal.
The challenge here is that i need to login on this site before i can access the page i want to scrape. Is there a way to automate this login process in my C# code, so i can grab the secure content?
To access the secured content, you'll need to store and send cookies with every request to your server, starting with the request that sends your log in info and then saving the session cookie that the server gives you (which is your proof that you are who you say you are).
You can use the System.Windows.Forms.WebBrowser for a less control but out-of-the-box solution that will handle cookies.
My preferred method is to use System.Net.HttpWebRequest to send and receive all web data and then use the HtmlAgilityPack to parse the returned data into a Document Object Model (DOM) which can be easily read from.
The trick to getting System.Net.HttpWebRequest to work is that you must create a long-lived System.Net.CookieContainer that will keep track of your log in info (and other things the server expects you to keep track of). The good news is that the HttpWebRequest will take care of all of this for you if you provide the container.
You need a new HttpWebRequest for each call you make, so you must sets their .CookieContainer to the same object every time. Here is an example:
UNTESTED
using System.Net;
public void TestConnect()
{
CookieContainer cookieJar = new CookieContainer();
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://www.mysite.com/login.htm");
request.CookieContainer = cookieJar;
HttpWebResponse response = (HttpWebResponse) request.GetResponse();
// do page parsing and request setting here
request = (HttpWebRequest)WebRequest.Create("http://www.mysite.com/submit_login.htm");
// add specific page parameters here
request.CookeContainer = cookieJar;
response = (HttpWebResponse) request.GetResponse();
request = (HttpWebRequest)WebRequest.Create("http://www.mysite.com/secured_page.htm");
request.CookeContainer = cookieJar;
// this will now work since you have saved your authentication cookies in 'cookieJar'
response = (HttpWebResponse) request.GetResponse();
}
http://msdn.microsoft.com/en-us/library/system.windows.forms.webbrowser.aspx
HttpWebRequest Class
http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.cookiecontainer.aspx
You'll have to use the Services module to do that. Also check out this link for a bit of explanation.
im trying to build a program that can "login" to site as user to get the html code,
then ill fix the code to make more options to the user:)
i googled it and as i understand i need to send a cookie to identify myself as the user,
i use firefox edit cookies to see which cookies saves at my cmputer and saw only one 'phpsessid' that saves a string represent the session ,
i use wireshark to see how its real going and as i saw when im getting the response page im getting also this line
PHPSESSID=xxxxxxxxxxxxxxxxxxxxxxx; path=/
how i can read it from the response string to be able surf another pages as the 'user'?
tyvm for your help:)
edit:
i got it:
Req.GetResponse().Headers.Get("Set-Cookie");
Before you do the login, create a CookieContainer and assign it to the request:
var request = (HttpWebRequest)WebRequest.Create(loginUrl);
var cookies = new CookieContainer();
request.CookieContainer = cookies;
After you make the request, cookies will contain the cookies that make you logged-in.
When you want to make another request, now as a logged-in user, use cookies again:
var request = (HttpWebRequest)WebRequest.Create(anotherUrl);
request.CookieContainer = cookies;
I'm making a C# windows form application that needs to parse data from an external site that requires me to log in.
First I send the POST data on the login page using a WebRequest. This works correctly as I can see the page source of a page that requires me to log in.
When I try to view the page source code of the data-page however, I'm logged out again as the session gets lost.
Is there any way of storing the PHPSESSID and re-using it in another request?
EDIT:
WebRequest _theRequest;
...
_theRequest = (HttpWebRequest)WebRequest.Create(url);
_theRequest.CookieContainer = new CookieContainer();
The property CookieContainer is not showing up in the intellisense list, anything I'm doing wrong here?
You could assign a CookieContainer and reuse the request object for subsequent requests.