Windows 10 MessageWebsocket adding cookie - c#

I'm writing a Windows 10 app in which I'm trying to connect a websocket to the IRCCloud api.
The IRCCloud api requires the following request headers:
GET / HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
User-Agent: [REDACTED]
Cookie: session=[REDACTED]
Sec-WebSocket-Key: [REDACTED]==
Host: api.irccloud.com
Origin: https://api.irccloud.com
Sec-WebSocket-Version: 13
I'm using the following code to connect the websocket to the api:
MessageWebSocket Socket = new MessageWebSocket();
Socket.SetRequestHeader("Origin", "https://api.irccloud.com");
Socket.SetRequestHeader("Cookie", "session=" + info.Session);
Socket.MessageReceived += Socket_MessageReceived;
await Socket.ConnectAsync(new Uri("wss://" + Host + Path));
However, when I connect I get an authentication error due to the Websocket adding a cookie all by itself with the name __cfduid. IRCClouds API is very specific in that the session cookie should be the ONLY cookie that's in the request header. The session key is retrieved with a Windows.Web.Http.HttpClient.
I verified all my other parameters with the IRCCloud devs, who are saying they are correct, and they also pointed me to the cookie. So my question is, how do I remove the cookie? There's no public method for it in the MessageWebSocket class that seems to work for it.

The WebSockets API uses the same underlying stack as Windows.Web.Http namespace, so, you will need to remove the cookies manually using something like this:
var filter = new HttpBaseProtocolFilter();
var cookieManager = filter.CookieManager;
var uri = new Uri("http://example.com/foo/bar");
foreach (var cookie in cookieManager.GetCookies(uri))
{
cookieManager.DeleteCookie(cookie);
}

Related

.NET HttpClient do not persist authentication between reqeusts to IIS when using NTLM Negotiate

IIS site configured to use windows authentication with default options. The client is written in C# and uses single HttpClient instance to perform requests. Requests success, but every request triggers 401 Challenge:
Traffic captured with Wireshark. We performed loud test, and noticed, that with anonymous authentication client performs 5000 reqeusts per second, but with windows authentication - 800. So, looks like wireshark does not impact to authentication, performance slowdown indicates, that 401 Challenge also occurs without wireshark.
Wirehshark log: https://drive.google.com/file/d/1vDNZMjiKPDisFLq6ZDhASQZJJKuN2cpj/view?usp=sharing
Code of client is here:
var httpClientHandler = new HttpClientHandler();
httpClientHandler.UseDefaultCredentials = true;
var httpClient = new HttpClient(httpClientHandler);
while (working)
{
var response = await httpClient.GetAsync(textBoxAddress.Text + "/api/v1/cards/" + cardId);
var content = await response.Content.ReadAsStringAsync();
}
IIS site settings:
How to make HttpClient to persist authentication between requests, to prevent waste negotiate handshakes on every request?
UPD: Code of client: https://github.com/PFight/httpclientauthtest
Steps to reproduce:
Create folder with simple file index.html, create application 'testsite' in IIS for this folder. Enable anonymous authentication.
Run client (https://github.com/PFight/httpclientauthtest/blob/main/TestDv5/bin/Debug/TestDv5.exe), press start button - see count of requests per second. Press stop.
Disable anonymous atuhentication, enable windows authentication.
Press start button in client, see count of reqeusts per second.
On my computer I see ~1000 requests per second on anonymous, and ~180 on windows. Wireshark shows 401 challenges on every request for windows authentication. keep-alive header enabled in IIS.
IIS version: 10.0.18362.1 (windows 10)
Version of System.Net.Http.dll loaded to process: 4.8.3752.0
Firstly I tried to save the Authorization header for re-use it with every new request.
using System.Net.Http.Headers;
Requester requester = new Requester();
await requester.MakeRequest("http://localhost/test.txt");
await Task.Delay(100);
await requester.MakeRequest("http://localhost/test.txt");
class Requester
{
private readonly HttpClientHandler _httpClientHandler;
private readonly HttpClient _httpClient;
private AuthenticationHeaderValue _auth = null;
public Requester()
{
_httpClientHandler = new HttpClientHandler();
_httpClientHandler.UseDefaultCredentials = true;
_httpClient = new HttpClient(_httpClientHandler);
_httpClient.DefaultRequestHeaders.Add("User-Agent", Guid.NewGuid().ToString("D"));
}
public async Task<string> MakeRequest(string url)
{
HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Get, url);
message.Headers.Authorization = _auth;
HttpResponseMessage resp = await _httpClient.SendAsync(message);
_auth = resp.RequestMessage?.Headers?.Authorization;
resp.EnsureSuccessStatusCode();
string responseText = await resp.Content.ReadAsStringAsync();
return responseText;
}
}
But it didn't work. Every time there was http code 401 asking for authentication despite of Authorization header.
The IIS logs is listed below.
2021-12-23 15:07:47 ::1 GET /test.txt - 80 - ::1 c75eeab7-a0ea-4ebd-91a8-21f5cd59c10f - 401 2 5 127
2021-12-23 15:07:47 ::1 GET /test.txt - 80 MicrosoftAccount\account#domain.com ::1 c75eeab7-a0ea-4ebd-91a8-21f5cd59c10f - 200 0 0 4
2021-12-23 15:07:47 ::1 GET /test.txt - 80 - ::1 c75eeab7-a0ea-4ebd-91a8-21f5cd59c10f - 401 1 2148074248 0
2021-12-23 15:07:47 ::1 GET /test.txt - 80 MicrosoftAccount\account#domain.com ::1 c75eeab7-a0ea-4ebd-91a8-21f5cd59c10f - 200 0 0 0
IIS's Failed Requests Tracing reports the following when receiving re-used Authentication Header:
Property
Value
ModuleName
WindowsAuthenticationModule
Notification
AUTHENTICATE_REQUEST
HttpStatus
401
HttpReason
Unauthorized
HttpSubStatus
1
ErrorCode
The token supplied to the function is invalid (0x80090308)
I've made a research and I can say that this is not possible without alive connection.
Every time the connection is closed there will be new handshake.
According this and this answers NTLM authenticates a connection, so you need to keep your connection open.
NTLM over http is using HTTP persistent connection or http keep-alive.
A single connection is created and then kept open for the rest of the session.
If using the same authenticated connection, it is not necessary to send the authentication headers anymore.
This is also the reason why NTLM doesn't work with certain proxy servers that don't support keep-alive connections.
UPDATE:
I found the key point using your example.
First: You must enable keep-alive at your IIS
Second: You must set authPersistSingleRequest flag to false. Setting this flag to True specifies that authentication persists only for a single request on a connection. IIS resets the authentication at the end of each request, and forces re-authentication on the next request of the session. The default value is False.
Third: You can force the HttpClient to send keep-alive headers:
httpClient.DefaultRequestHeaders.Add("Connection", "keep-alive");
httpClient.DefaultRequestHeaders.Add("Keep-Alive", "600");
Using this three key points I have achieved only one NTLM handshake during connection lifetime.
Also it's important which version of .NET \ .NET Framework do you use.
Because HttpClient hides different realizations dependent on framework version.
Framework
Realization of HttpClient
.Net Framework
Wrapper around WebRequest
.Net Core < 2.1
Native handlers (WinHttpHandler / CurlHandler)
.Net Core >= 2.1
SocketsHttpHandler
I tried it on .NET 6 and it works great, but it didn't work on .Net Framework as I can see, so here is the question: which platform do you use?
UPDATE 2:
Found the solution for .Net Framework.
CredentialCache myCache = new CredentialCache();
WebRequestHandler handler = new WebRequestHandler()
{
UseDefaultCredentials = true,
AllowAutoRedirect = true,
UnsafeAuthenticatedConnectionSharing = true,
Credentials = myCache,
};
var httpClient = new HttpClient(handler);
httpClient.DefaultRequestHeaders.Add("Connection", "keep-alive");
httpClient.DefaultRequestHeaders.Add("Keep-Alive", "600");
var from = DateTime.Now;
var countPerSecond = 0;
working = true;
while (working)
{
var response = await httpClient.GetAsync(textBoxAddress.Text);
var content = await response.Content.ReadAsStringAsync();
countPerSecond++;
if ((DateTime.Now - from).TotalSeconds >= 1)
{
this.labelRPS.Text = countPerSecond.ToString();
countPerSecond = 0;
from = DateTime.Now;
}
Application.DoEvents();
}
The key point is to use WebRequestHandler with UnsafeAuthenticatedConnectionSharing enabled option and use credential cache.
If this property is set to true, the connection used to retrieve the response remains open after the authentication has been performed. In this case, other requests that have this property set to true may use the connection without re-authenticating. In other words, if a connection has been authenticated for user A, user B may reuse A's connection; user B's request is fulfilled based on the credentials of user A.
Caution
Because it is possible for an application to use the connection without being authenticated, you need to be sure that there is no administrative vulnerability in your system when setting this property to true. If your application sends requests for multiple users (impersonates multiple user accounts) and relies on authentication to protect resources, do not set this property to true unless you use connection groups as described below.
Big thanks to this article for solution.

(partial) HTTP Request isn't recognized by C# HTTP Server

In my visual studio 13 console application, I receive following byte stream on a TCP-Socket(receiving from a embedded device):
POST /setup HTTP/1.1
Content-Length: 6
Content-Type: application/setup+tlv8
TLV8-data
Although it seem to be a valid http request, none of my following attempts have successful been recognize it as a http request: (on regular HTTP Requests they work perfectly)
.NET HttpListener class (does not even inform me that any request has been invoked)
Grapevine (same thing, with any routes given on POST or GET) https://github.com/scottoffen/Grapevine
Alchemy (OnConnect method has been invoked, but in the according UserContext i was just seeing request path: / . Similar to this reported issue: https://github.com/Olivine-Labs/Alchemy-Websockets/issues/70
So far, I'm actually only interested in the requested path from the POST or GET as well as the attached content (tlv formatted) from the body.
Am I wrong in configuring? such as: I need to tell the proper content-type ?
Is there any way to get rid of writing a own simple text parser ?
Code sample in case of grapevine:
private void init()
{
s = new PairServer();
s.Host = "172.28.22.78";
s.Port = "52025";
s.Start();
}
providing following server class:
public class PairServer : RestServer
{
[RestRoute(Method = HttpMethod.POST, PathInfo = #"^/setup")]
[RestRoute(Method = HttpMethod.GET, PathInfo = #"^/setup")]
public void PairSetup(HttpListenerContext context)
{
// will not reach here
}
[RestRoute(Method = HttpMethod.POST)]
public void AnyRoute(HttpListenerContext context)
{
// Not even here
}
Although it seem to be a valid http request
No, that's not a valid HTTP request. A valid HTTP request, as the specification states, must include a Host request header:
A client MUST include a Host header field in all HTTP/1.1 request
messages . If the requested URI does not include an Internet host name
for the service being requested, then the Host header field MUST be
given with an empty value. An HTTP/1.1 proxy MUST ensure that any
request message it forwards does contain an appropriate Host header
field that identifies the service being requested by the proxy. All
Internet-based HTTP/1.1 servers MUST respond with a 400 (Bad Request)
status code to any HTTP/1.1 request message which lacks a Host header
field.
So make sure that your client is following the specification:
POST /setup HTTP/1.1
Host: example.com
Content-Length: 6
Content-Type: application/setup+tlv8
TLV8-data

Using DotNetOpenAuth for NetSuite SuiteSignOn (Outbound Single Sign-on)

I am trying to figure out how to use DotNetOpenAuth (DNOA) to interface to NetSuite's SuiteSignOn. I have a java example I am trying to duplicate the function of, but I am new to OAuth. Here is what I have to work with:
This is the high level of what NetSuite wants to happen:
User logs in to NetSuite, initiating a NetSuite session.
User clicks on one of the following in the NetSuite user interface:
o A subtab that provides SuiteSignOn access
o A page displaying a portlet that provides SuiteSignOn access
o A link for a Suitelet that provides SuiteSignOn access
o An action button that results in the execution of a user event script that provides SuiteSignOn access
NetSuite generates a token, and sends this token to the external application as the value for the oauth_token URL parameter. This outbound HTTP call also includes a dcand an env URL parameter. These values can be mapped to the URL to be used for NetSuite access (see Mappings of dc and env URL Parameter Values). If any data fields were previously defined as required context for the connection, NetSuite sends values for these fields at the same time.
The external application sends back to NetSuite the token, the consumer key, and its shared secret, along with other information such as the timestamp and nonce, in order to verify the user. The consumer key is a unique identifier for the application provider, generated by NetSuite when the application provider sets up a SuiteSignOn connection. The shared secret is a password defined by the application provider during this setup.
NetSuite responds to the verification, sending any user identification information that was previously defined as necessary for the connection, in XML format. This information may include standard fields like email address or name, or custom fields.
The external application sends the HTML for the landing page, and the page displays. Or, if there is a problem, an error is returned instead.
NetSuite HTTP Outbound Call (got this figured out).
When a user accesses a SuiteSignOn connection point, NetSuite issues an outbound call to start the handshake. The following is an example of this call:
GET /SSO/demoApp.php?oauth_token=01046c1211661d6c6b415040422f0daf09310e3ea4ba&dc=001&env=PRODUCTION HTTP/1.1
Host: externalsystem.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:19.0) Gecko/20100101 Firefox/19.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
External Application HTTP Verify Call (trying to prepare this with DotNetOpenAuth).
Upon receipt of the NetSuite HTTP outbound call, the external application needs to issue an HTTP verify call. The following is an example of this call:
GET /app/common/integration/ssoapplistener.nl HTTP/1.0
Host: system.netsuite.com
Authorization: OAuth oauth_token="01046c1211661d6c6b415040422f0daf09310e3ea4ba", oauth_consumer_key="3moWE2ukbW4lohz7", oauth_signature_method="PLAINTEXT", oauth_signature="foobar1%26", oauth_timestamp="1364997730", oauth_nonce="392380036"
NetSuite HTTP Verify Call Response (I can code this).
Upon receipt of the verify call from the external application, NetSuite sends a response. The following is an example of this response:
HTTP/1.1 200 OK
Date: Tue, 16 Apr 2013 13:30:41 GMT
Server: Apache/2.2.17
Set-Cookie: lastUser=1326288_79_3; expires=Tuesday, 23-Apr-2013 13:30:42 GMT; path=/
Set-Cookie: NS_VER=2013.1.0; domain=system.netsuite.com; path=/
X-Powered-By: Servlet/2.5 JSP/2.1
P3P: CP="CAO PSAa OUR BUS PUR"
Vary: User-Agent
Connection: close
Content-Type: text/html; charset=utf-8
<?xml version="1.0" encoding="UTF-8"?>
<outboundSso>
<entityInfo>
<ENTITYLASTNAME>Smith</ENTITYLASTNAME>
<ENTITYINTERNALID>79</ENTITYINTERNALID>
<ENTITYACCOUNT>1326288</ENTITYACCOUNT>
<ENTITYFIRSTNAME>John</ENTITYFIRSTNAME>
<ENTITYEMAIL>jsmith#netsuite.com</ENTITYEMAIL>
</entityInfo>
</outboundSso>
The excerpts of a Java example using OAuth 1.0a that I'm trying to port to .net/DotNetOpenAuth:
import net.oauth.OAuth;
import net.oauth.OAuthAccessor;
import net.oauth.OAuthConsumer;
import net.oauth.OAuthMessage;
import net.oauth.client.OAuthClient;
import net.oauth.http.HttpMessage;
<<snip>>
OAuthConsumer consumer = new OAuthConsumer(null, CONSUMER_KEY, SHARED_SECRET, null);
consumer.setProperty(OAuth.OAUTH_SIGNATURE_METHOD, "PLAINTEXT");
OAuthAccessor oauthAccessor = new OAuthAccessor(consumer);
//Get the token from NetSuite
oauthAccessor.accessToken = request.getParameter("oauth_token");
<<snip>>
OAuthMessage rqt = null;
rqt = oauthAccessor.newRequestMessage("POST", ssoVerifyUrl, null);
HttpMessage message =
rqt.toHttpRequest(OAuthClient.ParameterStyle.AUTHORIZATION_HEADER);
verifyConnection.setRequestProperty("Authorization",
message.getHeader("Authorization"));
Being new to OAuth and DotNetOpenAuth, I'm fumbling around.
What is the proper replacement for OAuthConsumer in DNOA in this situation? WebConsumer? DesktopConsumer?
Assuming I need such a consumer, how much of the ServiceProviderDescription do I need to provide? I only have one endpoint (/app/common/integration/ssoapplistener.nl), I'm not sure if that is a Request, Access, or other type of endpoint.
What is the proper replacement for OAuthAccessor in DNOA?
Thanks for any assistance,
Bo.
Ok, after a lot of digging and experimenting, I got DotNetOpenAuth to work with NetSuite's SuiteSignOn. It may not be perfect, but it does work!
I got my tokenmanager from this post:
https://developer.yahoo.com/forum/Fantasy-Sports-API/Authenticating-with-NET-using-DotNetOpenAuth/1279209867000-4eee22f1-25fd-3589-9115-1a835add3212
using DotNetOpenAuth.OAuth;
using DotNetOpenAuth.OAuth.ChannelElements;
using DotNetOpenAuth.OAuth.Messages;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.Extensions.OAuth;
// In my Page_Load method, I receive the GET request from NetSuite:
public partial class sso_page : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// This is what the NetSuite SuiteSignOn ConnectionPoint sends:
// GET /administratorportal/SSO/sso_page.aspx?oauth_token=08046c1c166a7a6c47471857502d364b0d59415418156f15db22f76dcfe648&dc=001&env=SANDBOX
// see the NetSuite SuiteSignOn doc about dc & env processing to build endpoints
ServiceProviderDescription provider = GetServiceDescription();
// Set up OAuth with our keys and stuff
string token = Request.Params["oauth_token"];
string consumerKey = "yourconsumerkey"; // this has to match what is defined on our NetSuite account - ConnectionPoint to CRMLink
string sharedSecret = "yoursharedsecret"; // this has to match what is defined on our NetSuite account - ConnectionPoint to CRMLink - Careful - NO funny chars like '!'
// I got this InMemoryTokenManager from another DotNetOpenAuth post in SO
InMemoryTokenManager _tokenManager = new InMemoryTokenManager(consumerKey, sharedSecret);
AuthorizationApprovedResponse authApprovedResponse = new AuthorizationApprovedResponse();
authApprovedResponse.RequestToken = token;
_tokenManager.StoreOpenIdAuthorizedRequestToken(consumerKey, authApprovedResponse);
WebConsumer consumer = new WebConsumer(provider, _tokenManager);
// this is the SSO address in netsuite to use. Should be production or sandbox, based on the values of dc and env
string uri = "https://system.sandbox.netsuite.com/app/common/integration/ssoapplistener.nl";
MessageReceivingEndpoint endpoint = new MessageReceivingEndpoint(uri, methods);
WebRequest verifyRequest = consumer.PrepareAuthorizedRequest(endpoint, token );
HttpWebResponse responseData = verifyRequest.GetResponse() as HttpWebResponse;
XDocument responseXml;
responseXml = XDocument.Load(responseData.GetResponseStream());
// process the SSO values that come back from NetSuite in the XML They should look something
// like the following:
/* XML response should look like this:
<?xml version="1.0" encoding="UTF-8"?>
<outboundSso>
<entityInfo>
<ENTITYINTERNALID>987654</ENTITYINTERNALID>
<ENTITYNAME>Fred</ENTITYNAME>
<ENTITYEMAIL>fred#yourcompany.com</ENTITYEMAIL>
</entityInfo>
</outboundSso>
*/
// If that data looks good, you can mark the user as logged in, and redirect to whatever
// page (like SSOLandingPage.aspx) you want, which will be shown inside a frame on the NetSuite page.
Response.Redirect("~/SSOLandingPage.aspx", false);
// If that data looks bad, invalid user/login? Then you could respond with an error or redirect to a login.aspx page or something.
There is some other error handling and different returns depending on what happens, but the above is the basics of receiving an SSO login from NetSuite SuiteSignOn.
This was a hardcoded ServiceProviderDescription I used. You need to read the NetSuite SuiteSignOn doc to understand how to dynamically build these endpoints based on values of dc and env, I did not do that here yet.
// I'm not completely sure why I need all these endpoints below, and since I provide an endpoint as such:
// MessageReceivingEndpoint endpoint = new MessageReceivingEndpoint(uri, methods );
// these don't seem like I need them. But I need a ServiceProviderDescription to create a consumer, so...
private ServiceProviderDescription GetServiceDescription()
{
return new ServiceProviderDescription
{
AccessTokenEndpoint = new MessageReceivingEndpoint("https://system.sandbox.netsuite.com/app/common/integration/ssoapplistener.nl", HttpDeliveryMethods.GetRequest),
RequestTokenEndpoint = new MessageReceivingEndpoint("https://system.sandbox.netsuite.com/app/common/integration/ssoapplistener.nl", HttpDeliveryMethods.GetRequest),
UserAuthorizationEndpoint = new MessageReceivingEndpoint("https://system.sandbox.netsuite.com/app/common/integration/ssoapplistener.nl", HttpDeliveryMethods.GetRequest),
ProtocolVersion = ProtocolVersion.V10a,
TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new PlaintextSigningBindingElement() }
};
}

JsonServiceClient not using cookie placed in cookiecontainer

I've posted a couple of questions previously to get where I am now:
Reconnecting to Servicestack session in an asp.net MVC4 application
and
nullreference exception when adding session cookie to ServiceStack
Quick background on the app: This is an asp.net MVC application accessing data on a remote servicestack installation.
At this point I am successfully authenticating with SS, saving the session key in a cookie, and inserting that cookie into the CookieContainer of a new JsonServiceClient instance.
However when I try to grab some data via the new JsonServiceClient instance:
CallList = client.Get(new ServiceCallRequest()).Result;
The remote ServiceStack instance seems to be redirecting me to the default ASP login area (/Auth/login or something similar). This redirect in itself isn't a problem, but it does seem to indicate that the client isn't using the SS session already established on the remote machine.
This is the code that is actually inserting the cookie into the client cookie container and calling for a list of objects:
public List<ServiceCallModel> LoadList()
{
try
{
var cookie = HttpContext.Request.Cookies.Get(SessionFeature.PermanentSessionId);
var client = new JsonServiceClient([api address]);
cookie.Domain = ".domain.com";
var cookie1 = new Cookie(SessionFeature.PermanentSessionId, cookie.Value);
cookie1.Domain = ".domain.com";
client.CookieContainer.Add(cookie1);
List<ServiceCallModel> CallList = new List<ServiceCallModel>();
CallList = client.Get(new ServiceCallRequest()).Result;
return CallList;
}
catch (Exception ex)
{
return new List<ServiceCallModel>();
}
}
I can verify that this remote resource works with a monotouch Android application using the C# client. The only difference of course is that the Android client is persistent; the one in question here isn't.
The above example always returns a WebServiceException ("Not Found") (Which I assume is actually a 401/403 that has been redirected annoyingly by ASP).
Does this seem reasonable, or am I missing/misunderstanding some functionality of the JsonServiceClient/ServiceStack?
Thanks much
Update
Using Fiddler, I can confirm that the cookie is being stored in the browser and sent back to the MVC application in the request header:
GET / HTTP/1.1
Host: [web app address]
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko)
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Cookie: ASP.NET_SessionId=3457C511FECBECCD3C055C21;.MONOAUTH=PzE6iBuLIbv1evgACfpUwpC1D7opCANroPDQN/mXHNvGAgkjqq04Tdd8EnnGTL7y3lWYWY4+GWaXGDT0Fm7+eJxRdpy LJMaUQ6BiYmb3dxRi1B3f/qkmPMbFIoC7vC9M; ss-pid=lzJ+o9vG/7F3YZ9JNN2F
At this point I am trying to find out of that same ss-pid value is then making it back to the API server in a request header.
Update 2
Using tcpdump I was able to see that the ss-pid value is in fact making it all the way back to the API server (The "remote servicestack instance"). So now I think I need to troubleshoot that, not the client. Any thoughts?
Snippet of tcpdump output:
0x0090: 6e65 740d 0a43 6f6f 6b69 653a 2073 732d net..Cookie:.ss-
0x00a0: 7069 643d 6c7a 4a2b 6f39 7647 2f37 4633 pid=lzJ+o9vG/7F3
0x00b0: 595a 394a 4e4e 3246 0d0a 4163 6365 7074 YZ9JNN2F..Accept
I know that the ss-pid values are different in each part of this post. They were obtained at different times
Update 3
I've also changed the LogFormat in the vhost config file to spit out the value for the cookie called "ss-pid" (At the end of the log entry).
The resulting logs on the ServiceStack remote API server looks like this:
172.16.0.17 - - [08/Oct/2013:12:26:52 -0400] "GET /sc/0 HTTP/1.1" 500 3082 "-" "-" "HFMtFpPQkpE0Br6/fEFg"
172.16.0.17 - - [08/Oct/2013:12:27:06 -0400] "GET /sc/0 HTTP/1.1" 302 394 "-" "-" "HFMtFpPQkpE0Br6/fEFg"
172.16.0.17 - - [08/Oct/2013:12:27:07 -0400] "GET /login.aspx?ReturnUrl=%2fsc%2f0 HTTP/1.1" 404 451 "-" "-" "HFMtFpPQkpE0Br6/fEFg"
This "500" status on the first request sticks out. I will be investigating this now.
Update 4
The 500 status seems to be a case of Microsoft.Web.Infrastructure.dll being included in the bin directory. Deleted that to resolve the 500 response, but this did not fix the overall problem.
ServiceStack places two Session cookies in the Request, 'ss-id' and 'ss-pid'. This line in your code...
var cookie = HttpContext.Request.Cookies.Get(SessionFeature.PermanentSessionId);
will grab the 'ss-pid' cookie. But, you probably want the grab the 'ss-id' cookie which is the cookie for your current authenticated session.
var cookie = HttpContext.Request.Cookies.Get(SessionFeature.SessionId);
Take a look here for more information on ServiceStack Sessions.

How to simulate browser HTTP POST request and capture result in C#

Lets say we have a web page with a search input form, which submits data to server via HTTP GET. So that's mean server receive search data through query strings. User can see the URL and can also initialize this request by himself (via URL + Query strings).
We all know that. Here is the question.
What if this web page submits data to the server via HTTP POST? How can user initialize this request by himself?
Well I know how to capture HTTP POST (that's why network sniffers are for), but how can I simulate this HTTP POST request by myself in a C# code?
You could take a look at the WebClient class. It allows you to post data to an arbitrary url:
using (var client = new WebClient())
{
var dataToPost = Encoding.Default.GetBytes("param1=value1&param2=value2");
var result = client.UploadData("http://example.com", "POST", dataToPost);
// do something with the result
}
Will generate the following request:
POST / HTTP/1.1
Host: example.com
Content-Length: 27
Expect: 100-continue
Connection: Keep-Alive
param1=value1&param2=value2

Categories