How to place HttpWebRequest Host header first? - c#

I'm using standard HttpWebRequest to get html page:
using System;
using System.IO;
using System.Net;
namespace TestConsole.Classes {
class RequestHeadersOrder {
public void Test() {
var req = (HttpWebRequest)WebRequest.Create("https://www.google.com");
req.Proxy = new WebProxy("localhost", 8888); // for debug in Fiddler proxy (https://www.telerik.com/fiddler)
req.Host = "www.google.com";
req.UserAgent = "Robot-tester";
req.Accept = "*/*";
req.Headers.Add(HttpRequestHeader.AcceptLanguage, "en-US");
req.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip, deflate");
req.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
string html;
using (var resp = req.GetResponse())
using (var respStream = resp.GetResponseStream())
using (var ms = new MemoryStream()) {
respStream.CopyTo(ms);
html = System.Text.Encoding.UTF8.GetString(ms.ToArray());
}
Console.WriteLine(html);
}
}
}
This produces request headers (raw format):
GET https://www.google.com/ HTTP/1.1
User-Agent: Robot-tester
Accept: */*
Accept-Language: en-US
Accept-Encoding: gzip, deflate
Host: www.google.com
Connection: Keep-Alive
But most browsers use different headers order: "Host" header first, then "User agent", then other headers. So, I need this:
GET https://www.google.com/ HTTP/1.1
Host: www.google.com
User-Agent: Robot-tester
Accept: */*
Accept-Language: en-US
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Please advice me how to place "Host" header first. This is important for some web sites.

Related

What is the equivalent HttpClient JSON POST to this RestClient JSON POST?

Given:
Uri location = ...; // Remote 3rd party HTTP Rest API
string body = "SOME JSON";
The following RestClient code generates HTTP traffic that is accepted by the server.
var client = new RestClient(location);
var request = new RestRequest(Method.POST);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "application/json; charset=utf-8");
request.AddParameter("application/json; charset=utf-8", body,
ParameterType.RequestBody);
var restResponse = client.Execute(request);
However, the HttpClient code below must be generating different HTTP traffic (indicated by the server rejecting the request).
using (var client = new HttpClient())
{
var request = new HttpRequestMessage();
request.Method = HttpMethod.Post;
request.RequestUri = location;
var bodyContent = new StringContent(body, Encoding.UTF8, "application/json");
request.Content = bodyContent;
request.Headers.Add("cache-control", "no-cache");
client.Timeout = TimeSpan.FromMinutes(5.0);
var response = await client.SendAsync(request);
}
Why is the HttpClient code serializing differently?
A simple way to find the precise differences is to run Fiddler or another debugging proxy and check the raw request. Here's what I got with HttpClient:
POST http://example.com/ HTTP/1.1
cache-control: no-cache
Content-Type: application/json; charset=utf-8
Host: example.com
Content-Length: 4
Expect: 100-continue
Connection: Keep-Alive
test
And with RestSharp:
POST http://example.com/ HTTP/1.1
cache-control: no-cache
Content-Type: application/json; charset=utf-8
Accept: application/json, text/json, text/x-json, text/javascript, application/xml, text/xml
User-Agent: RestSharp/106.6.9.0
Host: example.com
Content-Length: 4
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
test
Your results may differ depending on your system configuration, versions etc., so you should try it yourself to make sure.

cURL call in c# bad request

I'm trying to do the following cURL call in a c# .net environment
curl -XPOST -d 'Metadata/Type = "sas"' http://bms.org/bcknd/republish
The C# code is as follows:
var requestContent = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("Metadata/Type", "\"sas\""), });
HttpResponseMessage response = await client.PostAsync("http://bms.org/bcknd/republish", requestContent);
HttpContent responseContent = response.Content;
using (var reader = new StreamReader(await responseContent.ReadAsStreamAsync()))
{
Console.WriteLine(await reader.ReadToEndAsync());
}
I'm getting a 400 Bad request and when I print it out. Maybe it has something to do with the -XPOST and -d parameter from the curl call?
EDIT:
Here's the http request from curl:
POST http://bms.org/bcknd/republish HTTP/1.1
Host: bms.org/bcknd
User-Agent: curl/7.48.0
Accept: */*
Content-Length: 43
Content-Type: application/x-www-form-urlencoded
Metadata/Type = "sas"
Here's the http request from my code:
POST http://bms.org/bcknd/republish HTTP/1.1
Accept: */*
User-Agent: curl/7.48.0
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Host: bms.org/bcknd
Content-Length: 43
Connection: Keep-Alive
Metadata/Type = "sas"
Short Version
Post the data as StringContent without url encoding and check the response status before trying to read the response body. Make sure the call completes before the application exits, otherwise the call will be cancelled when the application exits. That means, use async Task in Main, not async void :
class Program
{
static async Task Main(string[] args)
{
var client=new HttpClient();
var data = new StringContent("Metadata/Type=\"sas\"",Encoding.UTF8,"application/x-www-form-urlencoded");
var response = await client.PostAsync("http://www.google.com/bcknd/republish", data);
if(response.IsSuccessStatusCode)
{
var responseContent = response.Content;
var body=await response.Content.ReadAsStringAsync();
Console.WriteLine(body);
}
else
{
Console.WriteLine($"Oops! {response.StatusCode} - {response.ReasonPhrase}");
}
}
}
Explanation
In cases like this it's very important to know what's actually being sent. To do that, one can use a debugging proxy like Fiddler or Charles.
Curl with -d sends unencoded data. This call :
curl -XPOST -d 'Metadata/Type = "sas"' http://bms.org/bcknd/republish
will send :
POST http://www.google.com/bcknd/republish HTTP/1.1
Host: www.google.com
User-Agent: curl/7.55.1
Accept: */*
Connection: Keep-Alive
Content-Length: 21
Content-Type: application/x-www-form-urlencoded
Metadata/Type = "sas"
/ and " would have been replaced with other characters if URL encoding was applied. Note also the User-Agent and Accept headers
If --data-urlencode is used, the value will be URL encoded :
POST http://www.google.com/bcknd/republish HTTP/1.1
Host: www.google.com
User-Agent: curl/7.55.1
Accept: */*
Connection: Keep-Alive
Content-Length: 27
Content-Type: application/x-www-form-urlencoded
Metadata/Type =%20%22sas%22
This code on the other hand :
static async Task Main(string[] args)
{
var client=new HttpClient();
var data = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("Metadata/Type", "\"sas\""), });
var response = await client.PostAsync("http://www.google.com/bcknd/republish", data);
var responseContent = response.Content;
var body=await response.Content.ReadAsStringAsync();
Console.WriteLine(body);
}
Will send :
POST http://www.google.com/bcknd/republish HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 25
Host: www.google.com
Metadata%2FType=%22sas%22
To get the original payload, one can use StringContent with hand-coded content:
var data = new StringContent("Metadata/Type= \"sas\"",Encoding.UTF8,"application/x-www-form-urlencoded");
The request is :
POST http://www.google.com/bcknd/republish HTTP/1.1
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Content-Length: 19
Host: www.google.com
Metadata/Type= "sas"
If you want to send the User-Agent and Accept headers, you can add them to each individual message or as default request headers :
var client=new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("curl","7.55.1"));
These will add :
Accept: */*
User-Agent: curl/7.55.1
to the request
You can call remote URL as following using HttpClient
using (var httpClient = new HttpClient())
{
using (var request = new HttpRequestMessage(new HttpMethod("POST"), "http://bms.org/bcknd/republish"))
{
request.Content = new StringContent("Metadata/Type = \"sas\"", Encoding.UTF8, "application/x-www-form-urlencoded");
var response = await httpClient.SendAsync(request);
}
}
Here I have just added reference code, by using that you can create your own. I checked your curl request and it seems issue it self.

Posting to a form, can't get the address right

I have intercepted an HTTP POST as follows
Header
Key Value
Request POST /east-berkshire/local/quick_search HTTP/1.1
Accept text/html, application/xhtml+xml, */*
Referer https://www.netmums.com/east-berkshire/local/index/childcare/nannies-au-pairs
Accept-Language en-GB
User-Agent Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Content-Type application/x-www-form-urlencoded
Accept-Encoding gzip, deflate
Host www.netmums.com
Content-Length 107
DNT 1
Connection Keep-Alive
Cache-Control no-cache
Cookie AMCV_44326DF2572396FB7F000101%40AdobeOrg=817868104%7CMCMID%7C34574735755395522184062187835447062918%7CMCAAMLH-1486721296%7C6%7CMCAAMB-1486721296%7CNRX38WO0n5BH8Th-nqAG_A%7CMCOPTOUT-1486123696s%7CNONE; _ga=GA1.2.258060262.1486116497; _gat=1; _lp4_u=dZXxbBpqGf; __qca=P0-238174588-1486116496764; _tynt_crtg=; aam_uuid=34158303305859258534090346121149142657; __gads=ID=b3ba42a045f2be6a:T=1486116505:S=ALNI_MZHsVecqphdMO7SI-l4IEGrCyFpsg; AMCVS_44326DF2572396FB7F000101%40AdobeOrg=1; ABTastySession=LiwioHashMRASN%3Anull%5E%7C%5ELiwioUTMC%3A1; ABTasty=ABTastyUTMB%3A1%5E%7C%5ELiwioTracking%3A17020310101198682%5E%7C%5EsegmentationTracking%3A17020310101198682%5E%7C%5ELiwioUTMA%3A0.1.1486116611618.0.1486116611618.2; firstvisit=1; Cake=3qdc1afjmdvq0fg9kdunu2okn4; NetmumsLocation=east-berkshire; OX_plg=swf|sl|shk|pm
Body
_method=POST&data%5BListing%5D%5Blisting_category_id%5D=2&data%5BListing%5D%5Blisting_subcategory_id%5D=211
I have written the following C# code to try simulate this
var request = WebRequest.Create("https://www.netmums.com/east-berkshire/local/quick_search") as HttpWebRequest;
if (request == null) throw new HttpRequestException("Could not create web request");
request.Method = "post";
request.ContentType = "application/x-www-form-urlencoded";
var bs = Encoding.ASCII.GetBytes("[Listing][listing_category_id]=2&[Listing][listing_subcategory_id]=211");
using (var reqStream = request.GetRequestStream())
reqStream.Write(bs, 0, bs.Length);
string result;
using (var response = request.GetResponse())
{
var stream = response.GetResponseStream();
if (stream == null) throw new HttpRequestException("No data returned");
var sr = new StreamReader(stream);
result = sr.ReadToEnd();
sr.Close();
}
However when I execute it, on the GetResponse() call I get the error
The remote server returned an error: (404) Not Found.
What am I doing wrong?

Log in to site programmatically and redirect browser to signed in state

I want to sign in to a site when a link is clicked and then redirect the browser there with a signed in session. Im having some troubles and here is what Ive tried:
First I get the session cookies from the login site:
CookieContainer cookies= new CookieContainer();
HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create("http://someuri.com");
myHttpWebRequest.CookieContainer = cookies;
HttpWebResponse myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
myHttpWebResponse.Close();
Then I post to the sign in page to get signed in:
HttpWebRequest getRequest = (HttpWebRequest)WebRequest.Create("http://signInURL.com");
getRequest.CookieContainer = cookies;
getRequest.Method = WebRequestMethods.Http.Post;
getRequest.UserAgent = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.121 Safari/535.2";
getRequest.AllowWriteStreamBuffering = true;
getRequest.ProtocolVersion = HttpVersion.Version11;
getRequest.AllowAutoRedirect = true;
getRequest.ContentType = "application/x-www-form-urlencoded";
byte[] byteArray = Encoding.ASCII.GetBytes(PostParameterStringWithSignInInfo);
getRequest.ContentLength = byteArray.Length;
Stream newStream = getRequest.GetRequestStream();
newStream.Write(byteArray, 0, byteArray.Length);
newStream.Close();
HttpWebResponse getResponse = (HttpWebResponse)getRequest.GetResponse();
Then I figured I need to set the cookies to the client:
CookieCollection cooki = getRequest.CookieContainer.GetCookies(new Uri("http://someUri.com"));
for(int i = 0; i < cooki.Count; i++)
{
Cookie c = cooki[i];
Response.Cookies.Add(new HttpCookie(c.Name, c.Value));
}
And then redirect to where you end up being signed in:
Response.Redirect("http://URLwhenBeingSignedIn.com");
This doesnt work. When redirected Im still logged out.
Tried to do this with Fiddler and succeeded to sign in and get redirected:
Get the session cookies:
GET / HTTP/1.1
Content-type: application/x-www-form-urlencoded
Host: someuri.com
Post to the sign in page to get signed in:
POST /signIn HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Referer: http://someuri.com
Accept-Language: en-GB,en;q=0.7,tr;q=0.3
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Content-Length: 90
DNT: 1
Host: signInURL.com
Pragma: no-cache
Cookie: JSESSIONID=fromBefore; Cookie2=fromBefore
PostParameterStringWithSignInInfo
Perhaps there's an easier way than the one I thought of now that you can see the fiddler requests that works, if so I'm happy to see it.

C# HTTP POST with Boundary

I need a little help setting up a HTTP Post in C#. I appreciate any assistance I receive in advance.
Using Fiddler here is my RAW POST:
POST http://www.domain.com/tester.aspx HTTP/1.1
User-Agent: Tegan
Content-Type: multipart/form-data; boundary=myboundary
Host: www.domain.com
Content-Length: 1538
Expect: 100-continue
<some-xml>
<customer>
<user-id>george</user-id>
<first-name>George</first-name>
<last-name>Jones</last-name>
</customer>
</some-xml>
My requirements are a little tricky. They require a multi-part post with a boundary. I'm not familiar with setting up a boundary. If any one can assist I would appreciate it.
Here are my requirements:
POST http://www.domain.com/tester.aspx HTTP/1.0(CRLF)
User-Agent: myprogramname(CRLF)
Content-Type: multipart/form-data; boundary=myboundary(CRLF)
Content-Length: nnn(CRLF)
(CRLF)
(CRLF)
--myboundary(CRLF)
Content-Disposition: form-data; name=”xmlrequest”(CRLF)
Content-Type: text/xml(CRLF)
(CRLF)
(XML request message)(CRLF)
(CRLF)
--myboundary--(CRLF)
So I think this is what the POST should look like but I need some help with my C#.
POST http://www.domain.com/tester.aspx HTTP/1.1
User-Agent: Tegan
Content-Type: multipart/form-data; boundary=myboundary
Content-Length: 1538
--myboundary
Content-Disposition: form-data; name="xmlrequest"
Content-Type: text/xml
<some-xml>
<customer>
<user-id>george</user-id>
<first-name>George</first-name>
<last-name>Jones</last-name>
</customer>
</some-xml>
(CRLF)
--myboundary--
Here is the C# code I'm using to create the WebRequest.
HttpWebRequest request = null;
Uri uri = new Uri("http://domain.com/tester.aspx");
request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = "POST";
request.UserAgent = "NPPD";
request.ContentType = "multipart/form-data; boundary=myboundary";
request.ContentLength = postData.Length;
using (Stream writeStream = request.GetRequestStream())
{
writeStream.Write(postData, 0, postData.Length);
}
string result = string.Empty;
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (Stream responseStream = response.GetResponseStream())
{
using (StreamReader readStream = new StreamReader(responseStream, Encoding.UTF8))
{
result = readStream.ReadToEnd();
}
}
}
return result;
I blogged about this and presented a sample method that could be used to send multipart/form-data requests. Checkout here: http://www.bratched.com/en/home/dotnet/69-uploading-multiple-files-with-c.html

Categories

Resources