C# application hangs during POST request to python http server - c#

I have a C# app that makes a post request to a simple http server created in Python but my request never "finishes" and doesn't progress past the point of making an asynchronous POST request. This is the call I'm making from my client (C# app):
private void sendPost(HttpClientAdaptor client, MyDataObject myDataObject) {
var payload = JsonConvert.SerializeObject(myDataObject);
var content = new StringContent(payload, Encoding.UTF8, "application/json");
try {
if (client.isDisposed) {
return;
}
var response = client?.PostAsync(ApiEndpoint, content); // this hangs forever
And my http server written in python:
#!/usr/bin/env python3
"""
Very simple HTTP server in python for logging requests
Usage::
./server.py [<port>]
"""
from http.server import BaseHTTPRequestHandler, HTTPServer
import logging
from io import BytesIO
class S(BaseHTTPRequestHandler):
def do_POST(self):
content_length = int(self.headers['Content-Length'])
body = self.rfile.read(content_length)
self.send_response(200)
self.end_headers()
response = BytesIO()
response.write(b'This is POST request. ')
response.write(b'Received: ')
response.write(body)
self.wfile.write(response.getvalue())
def run(server_class=HTTPServer, handler_class=S, port=5000):
logging.basicConfig(level=logging.INFO)
server_address = ('', port)
httpd = server_class(server_address, handler_class)
logging.info('Starting httpd...\n')
try:
httpd.serve_forever()
except KeyboardInterrupt:
pass
httpd.server_close()
logging.info('Stopping httpd...\n')
if __name__ == '__main__':
from sys import argv
if len(argv) == 2:
run(port=int(argv[1]))
else:
run()
I know the request is making it to my server since I used some print statements to print the payload but my client seemingly never acknowledges the 200 response from my server. I've verified the server is running, I'm not mixing up the port, and a GET request works via a browser.
I suspect something's wrong with my python server such that it's not 'finishing' the transaction and therefore my client doesn't get a response.
As an aside: Is there a more simple approach to spin up an http server for my client (windows app written in C#)? I just need a way to return a 200 status.

You are using an async function and are not awaiting it.
Instead of
var response = client?.PostAsync(ApiEndpoint, content);
try
var response = await client?.PostAsync(ApiEndpoint, content);
And change your method signature from
private void sendPost
To
private async Task sendPost

Related

glitch.com project can't be called with c#

I am writing this question in context of glitch.com projects.
so, i made a test project(asp.net .net6 webapi) in glitch.com which returns your ip address. The link to the webpage is https://ror-test.glitch.me/getip . It runs perfectly fine and returns response accurately when called from a browser. Now, I want to access this project from c# client (to be precise unity3d) however i am unable to access it.
My first try was to use httpclient.getstringasync-
Code-
HttpClient client = new HttpClient();
string response = await client.GetStringAsync(url);
System.Console.WriteLine(response);
Output-
Unhandled exception. System.Net.Http.HttpRequestException: Response status code does not indicate success: 403 (Forbidden).
In my second try i used httpclient.getasync
Code-
HttpClient client = new HttpClient();
var response = await client.GetAsync(url);
Output-
Response - https://jpst.it/348Ik
Response.Content - https://jpst.it/348LS
Also just to say that my app is working perfectly fine when i call the project from nodejs it works perfectly-
Code -
var url = "https://ror-test.glitch.me/getip";
var XMLHttpRequest = require("xhr2");
var xhr = new XMLHttpRequest();
console.log("starting");
xhr.open("GET", url);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
console.log(xhr.status);
console.log(xhr.responseText);
}};
xhr.send();
Output -
starting
200
your ip: xx.xx.xxx.xx
In place of xx.xx.xxx.xx my real ip which i have cross checked with https://whatismyipaddress.com/ is coming. so i am sure problem is with c# client.
Plz help me or any other way i can call it from c# client(precisely unity3d).
You need to pass User-Agent request header.
HttpClient client = new HttpClient();
//add user agent
client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "fake");
var response = await client.GetAsync(url);

ASP.NET Core synchronously wait for http request

I have an external endpoint which I call to get some Json response.
This endpoint will initiate a session to a POS device, so the device will show the request details and ask the customer to enter his credit card to complete the payment, then when the customer finishes; the POS will call the endpoint and it will return the result back to my application.
The problem here is that I need the operation to complete as described in this scenario (synchronously).
When I do the call to this endpoint from postman; it waits a lot of time (until the POS receives the request and customer do his entries then returns the results back to endpoint and endpoint returns the results back to Postman) ... this is all works fine.
The problem is when I do this from an ASP.NET Core app, the request is not waited for endpoint and the response is returned with null directly.
I need something to wait for it.
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("x-API-Key", "ApiKey");
client.DefaultRequestHeaders.Add("Connection", "keep-alive");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var postTask = client.PostAsJsonAsync(new Uri("terminalEndpoint here"), dto);//dto is the request payload
postTask.Wait();
var result = postTask.Result;
if (result.IsSuccessStatusCode)
{
//Should hang after this line to wait for POS
var terminalPaymentResponseDto = result.Content.ReadAsAsync<InitiateTerminalPaymentResponseDto>().Result;
//Should hit this line after customer finishes with POS device
return terminalPaymentResponseDto;
}
}
First of all, there's no need to block. In fact, in an ASP.NET Core application you should avoid blocking as much as possible. Use async and await instead. This allows ASP.NET Core to use the freed threadpool thread for other work.
Second, HttpClient is thread-safe and meant to be reused. Creating a new one every time in a using block leaks sockets. You could use a static instance but a better solution is to use IHttpClientFactory as Make HTTP requests using IHttpClientFactory in ASP.NET Core shows, to both reuse and recycle HttpClient instances automatically.
Finally, there's no reason to add these headers on every call. The Content-Type is set by PostAsJsonAsync anyway. I also suspect the API key doesn't change when calling the same server either.
In your Startup.cs or Program.cs you can use AddHttpClient to configure the API Key :
builder.Services.AddHttpClient(client=>{
client.DefaultRequestHeaders.Add("x-API-Key", "ApiKey");
});
After that you can inject IHttpClientFactory into your controllers or pages and call it asynchronously in asynchronous actions or handlers :
public class MyController:ControllerBase
{
private readonly IHttpClientFactory _httpClientFactory;
public MyController:ControllerBase(IHttpClientFactory httpClientFactory) =>
_httpClientFactory = httpClientFactory;
public async Task<InitiateTerminalPaymentResponseDto> PostAsync(MyDTO dto)
{
var client=_httpClientFactory.CreateClient();
var uri=new Uri("terminalEndpoint here");
var result = client.PostAsJsonAsync(uri, dto);payload
if (result.IsSuccessStatusCode)
{
//Should hang after this line to wait for POS
var paymentDto= await result.Content.ReadAsAsync<InitiateTerminalPaymentResponseDto>();
//Should hit this line after customer finishes with POS device
return paymentDto;
}
else {
//Do whatever is needed in case of error
}
}
}
Using HttpClientFactory allows adding retry strategies using Polly eg, to recover from a temporary network disconnection.
Why not use the await like below? And make sure to change the function to async
var postTask = await client.PostAsJsonAsync(new Uri("terminalEndpoint here"), dto);

C# HTTP Server - Respond post request without processing post data

I am currently working on a local http server written in C#. At this point I am not yet processing post data, but only distinguish between get and post request. In both cases, of course, a 200 should be answered. While testing the server, I noticed that if I send an empty post request, it is answered by the server with a 200 and an html page just like a get request without any problems. However, if there are images attached to the post request, as in my example, the connection to the server fails immediately.
I handle a client connection as follows. I know it's not ideal to store the received bytes in a string, but for testing purposes I haven't seen any problems with it.
private void HandleClient(TcpClient client)
{
Byte[] bytes;
String requestData = "";
NetworkStream ns = client.GetStream();
if (client.ReceiveBufferSize > 0)
{
bytes = new byte[client.ReceiveBufferSize];
ns.Read(bytes, 0, client.ReceiveBufferSize);
requestData = Encoding.ASCII.GetString(bytes);
}
// Get Request out of message
Request request = Request.GetRequest(requestData);
// Create Response
Response response = Response.From(request);
response.Post(client.GetStream());
}
And here is the method I use to determine what type of request it is.
public static Request GetRequest(String request)
{
//return if request is null
if(String.IsNullOrEmpty(request))
{
return null;
}
//Split Request to get tokens - split by spaces
String[] tokens = request.Split(' ');
String type = tokens[0];
String url = tokens[1];
String host = tokens[4];
return new Request(type, url, host);
}
Surely it must be possible to read only the headers from a get as well as post request and then still give a 200 response. Is there a rule of behavior for an http server on how it should handle post-request data?
The answer to my question was quite simple in the end. The input stream of a request must be read completely before the server can respond to the request. In my case it was so, that I only read the header of the request to know if it is a Post or Get request, therefore the server could not respond to the request in case of an attached image, because the input stream was not read completely.

SQL Server service brokers waitfor c# httpclient long open http

I am working on a system that uses HTTPClient to get a HTTP route like /api/GUID and holds the connection open until another system sends a message to SQL Server to notify that the work is completed. Once the message is received the connection ends and my client continues on with its data it was requesting.
When I test this with curl or postman I get the intended results.
I get intermittent issues with c# httpclient. Sometimes I get my data immediately. Other times httpclient will stay open and after its timeout expires the message is shown in my test app despite throwing a task cancellation exception. Other times nothing is returned and a timeout occurs OR I only receive an empty string from my data. This very well could be related to our service broker implementation but i want to rule out everything about HTTPclient. I have tried using threads/tasks/ lots of async await using.Result and I am getting the same results so far. It is highly likely related to our servicebroker waitfor but any insight would be great. The issue tends to be connections that come in rapidly from a single source tend to have this pattern.
public string GetSynchronous(string url)
{
var tokenSource = new CancellationTokenSource();
var cancellationToken = tokenSource.Token;
return _httpClient.GetAsync(url, cancellationToken).Result.Content.ReadAsStringAsync().Result;
}
public void PostAsync(string url, JObject jsonBody)
{
var content = new StringContent(jsonBody.ToString(), Encoding.UTF8, "application/json");
var tokenSource = new CancellationTokenSource();
var cancellationToken = tokenSource.Token;
var result = _httpClient.PostAsync(url, content, cancellationToken).Result;
}
private static readonly HttpClient _httpClient = new HttpClient {
Timeout = TimeSpan.FromSeconds(60),
DefaultRequestHeaders =
{
Accept = { new MediaTypeWithQualityHeaderValue("application/json")},
ConnectionClose = true
},
};
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hit enter to start");
Console.ReadLine();
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
ServicePointManager.DefaultConnectionLimit = 100;
var tests = new Tests();
tests.CallSynchronizer();
tests.CallSynchronizerPostFirst();
Console.WriteLine("Hit enter to end");
Console.ReadLine();
}
}
Service Broker is designed for long exchanges (hours, days, weeks). Waiting for a response in the HTTP requests is definitely an anti-pattern. And more fundamentally, if concurrent requests are using this patter, there is nothing preventing them from each receiving the response intended for the other request.
You HTTP requests should post the work to-do (ie. issue a SEND) and return immediately. The response, when it comes, should activate a stored procedure and this should update some state, write the result. From HTTP you could then poll for the result and return current status immediately. Your app should work with service broker responses that return after a week same way as if the response returned in 10ms.
Ultimately this ended up being related to how we are load balancing our application and sqldependency.start queue name being set. This is a service that is setup in an anti-pattern way to migrate old code off of a really unreliable set of services.

JSON body gets corrupted when sending to Web API endpoint on Azure

I am sending a huge number (30 000 calls one by one) of JSONs over HTTP to Web API endpoint hosted on Azure. While most of them are processed just fine, about 1%-2% of JSONs received by WebAPI is simply corrupted and cannot be deserialized.
The code that sends messages looks following:
private async Task Send(string messageBody)
{
try
{
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, _configurationService.ServiceUrl);
requestMessage.Content = new StringContent(messageBody, Encoding.UTF8);
requestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
HttpResponseMessage responseMessage = await _httpClient.SendAsync(requestMessage);
... // do rest of the stuff - check http codes, handle exceptions, etc
}
The messageBody string is a json array of 100 items serialized with Json.net. Sample:
[{"ContentSnippet":"Body142370","ContentImageUrl":null,"ContentNotFullySupported":false,"Title":"142370","ContentType":9,"ContentId":"98fdb109-6548-40b9-900b-6914f16a6481","ActionType":14,"ActionDate":"2016-08-31T07:42:45.3161067Z","ActionByUserFirstName":"W","ActionByUserLastName":"J","ActionByUserId":"92caa514-7168-4085-bc17-85ef2fd2a682","UserId":"96e243eb-97be-4d36-9881-74e761def5aa","CommunityId":"10d44cf3-44ea-451e-8bfb-5275ba2f28a5","NotificationType":0}, {...next item in array...}]
The backend code I am using for diagnosing it looks following:
[HttpPost]
[Route("api/feed")]
public HttpResponseMessage Insert(IEnumerable<FeedItem> feedItems)
{
Stream s = Request.Content.ReadAsStreamAsync().Result;
s.Seek(0, System.IO.SeekOrigin.Begin);
using (StreamReader reader = new StreamReader(s))
{
string reqestBody = reader.ReadToEnd();
}
What I get on backend side (in requestBody variable) is:
[""ContentSnippet"":""Body142370"",""ContentImageUrl"":null,""ContentNotFullySupported"":false,""Title"":""142370"",""ContentType"":9,""ContentId"":""98fdb109-6548-40b9-900b-6914f16a6481"",""ActionType"":14,""ActionDate"":""2016-08-31T07:42:45.e����3�� v��tionByUse��E<stName""��K��UCctionByUserLast��0���d�v���r�P�gByUserId"":""92UŇ���U�sH/ia�y��""L)?8:�ԙN�JY�:,���X�1;�%�x!; `��,�981-74
Interestingly - it happens only for 1% - 2% of calls. More interestingly - when I try to simply resend the same JSON, everything works fine. So its a phantom issue (i dont see any patterns indicating when) - but still it happens.
I tried changing HttpClient to RestSharp but the result were event worse.
What is going on here?

Categories