C# HttpClient Host In Header not work with response 301/302 - c#

After using other DNS clients to resolve the IP and manually set the HOST in HEADER, it works fine, but when the server responds with 301/302 to other websites, the HOST in HEADER does not automatically change to the new HOST
public static HttpClient HttpClient { get; } =
new(new HttpClientHandler() {
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
AllowAutoRedirect = true,
MaxAutomaticRedirections = 6,
CheckCertificateRevocationList = false,
ServerCertificateCustomValidationCallback = (_, _, _, _) => true });
HttpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36");
HttpClient.DefaultRequestHeaders.Accept.ParseAdd("text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
HttpClient.DefaultRequestHeaders.AcceptEncoding.ParseAdd("gzip, deflate");
HttpClient.Timeout = TimeSpan.FromMilliseconds(App.Config.HttpClientWait ? App.Config.HttpClientAsyncTimeout : App.Config.HttpClientTimeout);
var url = this.DomainTask.Domain!.Trim().StartsWith("http://") || this.DomainTask.Domain.Trim().StartsWith("https://") ? this.DomainTask.Domain : $"{(this.UseHttps ? "https" : "http")}://{this.DomainTask.Domain}/";
var uri = new Uri(url);
var protocol = url.StartsWith("https://") ? "https" : "http";
var ipUrl = $"{protocol}://{this.DomainTask.Result!.Ip}:{uri.Port}/";
var msg = new HttpRequestMessage(HttpMethod.Get, IPAddress.TryParse(this.DomainTask.Result!.Ip, out _) ? ipUrl : url);
msg.Headers.Host = this.DomainTask.Domain;
if (App.Config.Debug) App.Logger.Debug($"{this.DomainTask.Domain} - ipUrl: {msg.RequestUri}");
var result = HttpClient!.SendAsync(msg).ContinueWith(x => {......});

Related

C# Xamarin file upload to API works using RestSharp but does not work using HttpClient

I'm attempting to upload an image file from the phone camera to a BuddyPress API from my Xamarin app (the API call documentation can be found here - https://developer.buddypress.org/bp-rest-api/reference/attachments/member-avatar/)
I can do this successfully using RestSharp as follows;
public string PostMediaFile(MediaFile data, string path, bool https = false, string authorisationToken = "")
{
var requestMethod = "http://";
if (https)
{
requestMethod = "https://";
}
var serverString = requestMethod + path;
var client = new RestClient(serverString)
{
Timeout = Convert.ToInt32(timeOut)
};
client.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer " + authorisationToken);
request.AddHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36");
request.AddFile("file", data.Path);
request.AddParameter("action", "bp_avatar_upload");
IRestResponse response = client.Execute(request);
return response.Content;
}
However, all my other requests in the application are performed using HttpClient and I'd like to keep it consistent, so I came up with the follow function to replace this;
public async Task<string> PostMediaFile(MediaFile data, string path, bool https = false, string authorisationToken = "")
{
var memoryStream = new MemoryStream();
data.GetStream().CopyTo(memoryStream);
byte[] fileAsBytes = memoryStream.ToArray();
var fileContent = new ByteArrayContent(fileAsBytes);
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
Name = "file",
FileName = Path.GetFileName(data.Path),
};
fileContent.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
var content = new MultipartFormDataContent
{
{ fileContent, "file", Path.GetFileName(data.Path) },
{ new StringContent("action"), "bp_avatar_upload" }
};
var requestMethod = "http://";
if (https)
{
requestMethod = "https://";
}
var clientHandler = new HttpClientHandler()
{
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; }
};
httpClient = new HttpClient(clientHandler);
if (!string.IsNullOrWhiteSpace(authorisationToken))
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authorisationToken);
httpClient.DefaultRequestHeaders.UserAgent.TryParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36");
}
httpClient.Timeout = TimeSpan.FromMilliseconds(timeOut);
var serverString = requestMethod + path;
HttpResponseMessage response = await httpClient.PostAsync(serverString, content);
HttpContent Content = response.Content;
var json = await Content.ReadAsStringAsync();
response.Dispose();
return json;
}
The problem is, obviously it doesn't work, and I don't know why. I just get the following response;
{"code":"bp_rest_attachments_user_avatar_upload_error","message":"Upload failed! Error was: Invalid form submission..","data":{"status":500,"reason":"upload_error"}}
I feel like I'm really close, but not sure where my mistake is.
Ah! it was so simple! I had part of the form data the wrong way around;
{ new StringContent("action"), "bp_avatar_upload" }
should be
{ new StringContent("bp_avatar_upload"), "action" }

How to update the cookies in Httpclient instance which created via HttpClientFactory -C#

By using Httpclient straightly in .Net Console application, I can manage the cookies via below code:
HttpClientHandler handler = new HttpClientHandler();
CookieContainer cookieContainer = handler.CookieContainer;
using (HttpClient client = new HttpClient(handler) { Timeout = TimeSpan.FromMinutes(30) })
{
var urlLogin="http://xxxxxx";
var strContent="key=value"
await client.PostAsync(new Uri(url), new StringContent(strContent, Encoding.UTF8, "application/x-www-form-urlencoded"))
var cookie = cookieContainer.GetCookies(new Uri(url)).Cast<Cookie>().ToList();//get cookies
cookie[0].Path = "/";
cookieContainer.Add(cookie[0]); //update the first cookie path to avoid missing cookies in redirect request (302)
//other action
}
But in .Net core 3.1, I'm using HttpClientFactory to create HttpClient instance, initially I can add cookies with similar code as below when build HttpClient, but the handler is not accessible in the client instance, therefore I don't know how to update the cookie in the instance.
services.AddHttpClient("test1", c =>
{
c.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36");
c.Timeout = TimeSpan.FromHours(1);
}).ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler
{
CookieContainer = new CookieContainer(),
UseDefaultCredentials = true
}) ;
You can switch to HttpClient.SendAsync(HttpRequestMessage). In that case you create HttpRequestMessage instance for each request separately and then set cookie through HttpRequestMessage.Headers. Also you need to keep cookie value itself in some singleton class.
var request = new HttpRequestMessage(HttpMethod.Post, url)
{
Content = new StringContent("content here", Encoding.UTF8, "application/x-www-form-urlencoded")
};
request.Headers.Add("Cookie", "your_value");
await _httpClientFactory.CreateClient().SendAsync(request);
So you set cookie per request and don't need to update it in clients.
Finally it was resolved:
Create a static variable
public class ScopedVars
{
public static HttpClientHandler staticHandler { get; set; } = new
HttpClientHandler();
}
In Setup, register it
services.AddScoped<ScopedVars>();
Add httpclient
services.AddHttpClient("TEST", c => {
c.DefaultRequestHeaders.Add("Accept", "/");
c.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT
10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36"); c.Timeout =
TimeSpan.FromMinutes(5); }).ConfigurePrimaryHttpMessageHandler(_
=>ScopedVars.staticHandler);
In the service layer
private HttpClientHandler _handler; public
TestService(IHttpClientFactory httpClientFactory, ScopedVars
scopedHandler) {
_httpClientFactory = httpClientFactory; _handler = ScopedVars.staticHandler; }
In a specific task
CookieContainer cookieContainer = _handler.CookieContainer; using
(HttpClient client = _httpClientFactory.CreateClient("TEST")) {
//XXXX; }
Since I just run it as QuartzNet task, no need to worry about the multiple request.

Why does Httpclient always return 401 Unauthorized error?

I try to get some data from a web, but always get 401 Unauthorized error. I guess this is caused by my request header setting is wrong.
Could we can set Accept, ContentType by using TryAddWithoutValidation?
How can I set the correct request header in HttpClient?
And I try to set the header like as following:
public static async Task<ResultInfo> Get(string url, string cookie = null)
{
url =$"https://www.zhihu.com/api/v4/questions/46508954/answers?sort_by=default&include=data%5B%2A%5D.is_normal%2Cis_sticky%2Ccollapsed_by%2Csuggest_edit%2Ccomment_count%2Ccan_comment%2Ccontent%2Ceditable_content%2Cvoteup_count%2Creshipment_settings%2Ccomment_permission%2Cmark_infos%2Ccreated_time%2Cupdated_time%2Crelationship.is_authorized%2Cis_author%2Cvoting%2Cis_thanked%2Cis_nothelp%2Cupvoted_followees%3Bdata%5B%2A%5D.author.badge%5B%3F%28type%3Dbest_answerer%29%5D.topics&limit=20&offset=20";
; var result = new ResultInfo()
{
IsSuccessStatusCode = false
};
HttpClientHandler handler = new HttpClientHandler()
{
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
};
using (var client = new HttpClient(handler))
{
try
{
cookie = "_zap=bb597634-86d0-4636-a516-89e76b4b5335; q_c1=b2e3af8dde034209a6948fcd9fd0e70f|1515131469000|1509512040000; d_c0=\"AJBrsuC5AA2PTqu9qOLgytIZlpXzmzgRgcE=|1516167026\"; _xsrf=ec351d2e-f0b2-4b2e-9eff-a3262a9faf10; capsion_ticket=\"2|1:0|10:1516261057|14:capsion_ticket|44:NzU1ZGRjYTA3Y2U3NGVjZWE4MWIzMzllNjA1NjMxZGQ=|b73bb13fe6a623db85478f5eb46ed51906674b89f29e4ef1a067fce6dcd90f54\"; z_c0=\"2|1:0|10:1516261068|4:z_c0|92:Mi4xMzMwVEFBQUFBQUFBa0d1eTRMa0FEU1lBQUFCZ0FsVk56SnhOV3dETFF5VGlla1ZDUllwcUVMRVhMWk4wV1duOVJn|f2fcc1750b966a19dd202ddc2eb42dca9dc9f573421bc3a8675a072e603ca897\"; __utma=51854390.1320208938.1517560249.1517560249.1517560249.1; __utmb=51854390.0.10.1517560249; __utmc=51854390; __utmz=51854390.1517560249.1.1.utmcsr=zhihu.com|utmccn=(referral)|utmcmd=referral|utmcct=/question/38824940; __utmv=51854390.100-1|2=registration_date=20130816=1^3=entry_date=20130816=1";
var replace = cookie.Replace("\"", "");
client.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "*/*");
client.DefaultRequestHeaders.TryAddWithoutValidation("ContentType", "application/x-www-form-urlencoded");
client.DefaultRequestHeaders.TryAddWithoutValidation("UserAgent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Safari/537.36");
client.DefaultRequestHeaders.TryAddWithoutValidation("Cookie", replace);
client.DefaultRequestHeaders.TryAddWithoutValidation("accept-encoding", "gzip/deflate");
//client.DefaultRequestHeaders.TryAddWithoutValidation("accept-language", "en-US,en;q=0.9");
client.DefaultRequestHeaders.TryAddWithoutValidation("AllowAutoRedirect","false");
if (!string.IsNullOrEmpty(url))
{
var data = await client.GetAsync(url);
if (data.IsSuccessStatusCode)
{
var content = await data.Content.ReadAsStringAsync();
result.IsSuccessStatusCode = true;
result.Data = await data.Content.ReadAsStringAsync();
}
else
{
result.Message = data.StatusCode.ToString();
}
}
}
catch (Exception e)
{
result.Message = e.Message;
throw;
}
}
return result;
}

HttpClient is ignoring the `www.` part of the Request URI

I'm trying to send a GET request with HttpClient:
public static HttpClient Client { get
{
var handler = new HttpClientHandler() { AutomaticDecompression = System.Net.DecompressionMethods.Deflate | System.Net.DecompressionMethods.GZip };
var http = new HttpClient(handler) { BaseAddress = new Uri("https://www.example.com/") };
http.DefaultRequestHeaders.Accept.Clear();
http.DefaultRequestHeaders.Accept.TryParseAdd("*/*");
http.DefaultRequestHeaders.AcceptLanguage.TryParseAdd("en-US;q=0.6,en;q=0.4");
http.DefaultRequestHeaders.AcceptEncoding.TryParseAdd("gzip, deflate, br");
http.DefaultRequestHeaders.Host = "example.com";
http.DefaultRequestHeaders.Add("X-Requested-With", "XMLHttpRequest");
http.DefaultRequestHeaders.Add("Connection", "keep-alive");
http.DefaultRequestHeaders.Add("Keep-Alive", "600");
http.DefaultRequestHeaders.UserAgent.TryParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36");
ServicePointManager
.ServerCertificateValidationCallback +=
(sender, cert, chain, sslPolicyErrors) => true;
return http;
}
...
using (var client = HttpHelper.Client)
using (var res = await client.GetAsync("api/stuff/filter?order=" + order + "&page=" + page))
if (res.IsSuccessStatusCode)
{
var json = await res.Content.ReadAsStringAsync();
var response = JsonConvert.DeserializeObject<AnimeResponse>(json);
return response;
}
Unfortunately this sends a request without the www. part and results in 301 status code as well as many, many failed redirects to the same address (without the www. part).
How can I fix this?
Edit: When I re-send the request with Fiddler it also returns 301, but the n it redirects correctly to wwww.. The HttpClient doesn't, and just redirects to the same www-less URI.

C# HttpClient wont add Cookies from CookieContainer to request

Cookies from CookieContainer are not added to Get or Post requests. Other headers work without a problem. What is the correct way to add cookies? I have done this before without a problem but I can't locate the error here.
var cookieContainer = new CookieContainer();
var handler = new HttpClientHandler();
handler.AllowAutoRedirect = true;
handler.UseCookies = true;
handler.CookieContainer = cookieContainer;
var baseAddress = new Uri("https://www.example.se");
cookieContainer.Add(baseAddress, new Cookie("Testing", "Test"));
//This did not work either
//cookieContainer.Add(baseAddress, new Cookie("Testing", "Test", "/"));
using (var client = new HttpClient(new LoggingHandler(handler)))
{
client.BaseAddress = baseAddress;
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36");
client.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
client.DefaultRequestHeaders.Add("Accept-Language", "sv-SE,sv;q=0.8,en-US;q=0.6,en;q=0.4");
var getResponse = client.GetAsync("/test").Result;
string responseString = getResponse.Content.ReadAsStringAsync().Result;
}
LoggingHandler:
https://stackoverflow.com/a/18925296/3850405
For some reason cookies aren't in the HttpRequestMessage headers when using CookieContainer. If I checked my CookieContainer object they were there though.
var cookieList = new List<Cookie>();
foreach (Cookie cookie in cookieContainer.GetCookies(baseAddress))
{
cookieList.Add(cookie);
}

Categories