Unable to access HTTP request body content in ASP.NET Core 3.1 exception handler - asp.net-core-3.1

It seems .NET Core 3.1 makes it very difficult to access the raw HTTP request body following an exception
The core of the issue seems to be you can't rewind the stream to the beginning so the whole thing can be read
In the below sample the output is:
Content-Length: 19
Can't rewind body stream. Specified method is not supported.
Body:
If I remove the Seek, there's no exception, but of course the body comes back as an empty string, because it's already been processed
There is some new plumbing based around the Pipeline API involving Request.BodyReader but they suffer from the same problem.
All I need to do, following an exception, is dump the full request to a log file including the body content. Surely this should not be so difficult?
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
Console.WriteLine("Content-Length: " + context.Request.Headers["Content-Length"]);
string tmp;
try
{
context.Request.Body.Seek(0, SeekOrigin.Begin);
}
catch(Exception ex)
{
Console.WriteLine("Can't rewind body stream. " + ex.Message);
}
using (var reader = new StreamReader(context.Request.Body, Encoding.UTF8))
{
tmp = await reader.ReadToEndAsync();
}
Console.WriteLine("Body: " + tmp);
});
});
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapDefaultControllerRoute();
});
}

Please review answer by Stephen Wilkinson
Add the following to your code:
Startup.cs
app.Use((context, next) =>
{
context.Request.EnableBuffering(); // calls EnableRewind() `https://github.com/dotnet/aspnetcore/blob/4ef204e13b88c0734e0e94a1cc4c0ef05f40849e/src/Http/Http/src/Extensions/HttpRequestRewindExtensions.cs#L23`
return next();
});
You should then be able to rewind as per your code above:
string tmp;
try
{
context.Request.Body.Seek(0, SeekOrigin.Begin);
}
catch(Exception ex)
{
Console.WriteLine("Can't rewind body stream. " + ex.Message);
}
using (var reader = new StreamReader(context.Request.Body, Encoding.UTF8))
{
tmp = await reader.ReadToEndAsync();
}

Related

WSDL FaultExceptions not caught

I'm attempting to get FaultExceptions to hit for a WSDL service.
I have attempted the following:
Created a new .Net 7.0 project in Visual studio
Added the WSDL service reference for https://wsaimport.uni-login.dk/wsaimport-v7/ws?WSDL
Called the generated WSDL method hentDataAftalerAsync
Added below code to Program.cs
var binding = new CustomBinding(new TextMessageEncodingBindingElement(MessageVersion.CreateVersion(EnvelopeVersion.Soap12, AddressingVersion.None), Encoding.UTF8), new HttpsTransportBindingElement { MaxReceivedMessageSize = 104857600 });
var ws10Client = new ServiceReference2.WsaImportPortTypeClient(binding, new EndpointAddress("https://wsaimport.uni-login.dk/wsaimport-v7/ws"));
try
{
var res = await ws10Client.hentDataAftalerAsync(new Credentials() {
wsBrugerid = "randomusername",
wsPassword = "randompassword"
});
// this is reached if credentials are correct
foreach (var item in res.hentDataAftalerResponse1.ToList())
{
Console.WriteLine(item);
}
}
catch(FaultException<AuthentificationError> ex)
{
// Neven hit (I expect this exception on wrong credentials)
Console.WriteLine(ex.Message);
}
catch (FaultException ex)
{
// Never hit
Console.WriteLine(ex.Message);
}
catch (ProtocolException ex)
{
// this exception is hit (but no info about the actual soap fault)
}
catch (Exception ex)
{
// Never hit
Console.WriteLine(ex.Message);
}
I have tested the hentDataAftaler operation in SoapUI, and when the credentials are wrong it does generate a SOAP response with a Fault of authentificationError.
So my question is, why is it not working for me in C#?

Unity WebGL POST requests does not work after build the game

my unity webGL Game doesn't seems sending post request to the server when after build the game, but its working fine in the editor. Is there anything to do additionally than this?
IEnumerator PostRequestY(string url, string jsonToAPI)
{
Debug.Log("--------------Game Data Sending --------------");
var uwr = new UnityWebRequest(url, "POST");
jsonToAPI = jsonToAPI.Replace("'", "\"");
//Debug.Log(jsonToAPI);
byte[] jsonToSend = new System.Text.UTF8Encoding().GetBytes(jsonToAPI);
uwr.uploadHandler = (UploadHandler)new UploadHandlerRaw(jsonToSend);
uwr.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
uwr.SetRequestHeader("Content-Type", CONTENT_TYPE);
uwr.SetRequestHeader("x-api-key", X_API_KEY);
//Send the request then wait here until it returns
yield return uwr.SendWebRequest();
if (uwr.isNetworkError)
{
Debug.Log("Error While Sending Game Data: " + uwr.error);
Debug.Log("URL ::: " + url);
messageText.text = uwr.error;
}
else
{
//Debug.Log("Game Data Received: " + uwr.downloadHandler.text);
string recievedMessage = uwr.downloadHandler.text;
//Debug.Log("Respond for Game Data " + recievedMessage);
messageText.text = uwr.downloadHandler.text +" & "+ accessToken;
}
}
This message appears when the game is played in the editor...
This message appears when the game is played after the build...
Why this is happens?
Any suggestions would be highly appreciated...
Thanks
For the server to accept my request, You should add a header to the API response itself, with the header and its value being Access-Control-Allow-Origin: domainName or Access-Control-Allow-Origin: *
For example:
public class BookApi: MonoBehaviour {
private const string URL = "https://retrofit-backend-demo.herokuapp.com/book";
public void GenerateRequest () {
StartCoroutine (ProcessRequest (URL));
}
private IEnumerator ProcessRequest (string uri) {
using (UnityWebRequest request = UnityWebRequest.Get (uri)) {
request.SetRequestHeader("Access-Control-Allow-Origin", "*");
yield return request.SendWebRequest ();
if (request.isNetworkError) {
Debug.Log (request.error);
} else {
JSONNode books = JSON.Parse(request.downloadHandler.text);
Debug.Log(books["facts"][0]);
}
}
}
}
In Unity WebGL, the underlying web request is done with fetch or XMLHttpRequest, which means you must correctly configure CORS on your server. You are not allowed to set nearly any request header by default.
See https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader for more information.

netty issue when writeAndFlush called from different InboundChannelHandlerAdapter.channelRead

I've got an issue, for which I am unable to post full code (sorry), due to security reasons. The gist of my issue is that I have a ServerBootstrap, created as follows:
bossGroup = new NioEventLoopGroup();
workerGroup = new NioEventLoopGroup();
final ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addFirst("idleStateHandler", new IdleStateHandler(0, 0, 3000));
//Adds the MQTT encoder and decoder
ch.pipeline().addLast("decoder", new MyMessageDecoder());
ch.pipeline().addLast("encoder", new MyMessageEncoder());
ch.pipeline().addLast(createMyHandler());
}
}).option(ChannelOption.SO_BACKLOG, 128).option(ChannelOption.SO_REUSEADDR, true)
.option(ChannelOption.TCP_NODELAY, true)
.childOption(ChannelOption.SO_KEEPALIVE, true);
// Bind and start to accept incoming connections.
channelFuture = b.bind(listenAddress, listenPort);
With createMyHandlerMethod() that basically returns an extended implementation of ChannelInboundHandlerAdapter
I also have a "client" listener, that listens for incoming connection requests, and is loaded as follows:
final String host = getHost();
final int port = getPort();
nioEventLoopGroup = new NioEventLoopGroup();
bootStrap = new Bootstrap();
bootStrap.group(nioEventLoopGroup);
bootStrap.channel(NioSocketChannel.class);
bootStrap.option(ChannelOption.SO_KEEPALIVE, true);
bootStrap.handler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addFirst("idleStateHandler", new IdleStateHandler(0, 0, getKeepAliveInterval()));
ch.pipeline().addAfter("idleStateHandler", "idleEventHandler", new MoquetteIdleTimeoutHandler());
ch.pipeline().addLast("decoder", new MyMessageDecoder());
ch.pipeline().addLast("encoder", new MyMessageEncoder());
ch.pipeline().addLast(MyClientHandler.this);
}
})
.option(ChannelOption.SO_REUSEADDR, true)
.option(ChannelOption.TCP_NODELAY, true);
// Start the client.
try {
channelFuture = bootStrap.connect(host, port).sync();
} catch (InterruptedException e) {
throw new MyException(“Exception”, e);
}
Where MyClientHandler is again a subclassed instance of ChannelInboundHandlerAdapter. Everything works fine, I get messages coming in from the "server" adapter, i process them, and send them back on the same context. And vice-versa for the "client" handler.
The problem happens when I have to (for some messages) proxy them from the server or client handler to other connection. Again, I am very sorry for not being able to post much code, but the gist of it is that I'm calling from:
serverHandler.channelRead(ChannelHandlerContext ctx, Object msg) {
if (msg instanceof myProxyingMessage) {
if (ctx.channel().isActive()) {
ctx.channel().writeAndFlush(someOtherMessage);
**getClientHandler().writeAndFlush(myProxyingMessage);**
}
}
}
Now here's the problem: the bolded (client) writeAndFlush - never actually writes the message bytes, it doesn't throw any errors. The ChannelFuture returns all false (success, cancelled, done). And if I sync on it, eventually it times out for other reasons (connection timeout set within my code).
I know I haven't posted all of my code, but I'm hoping that someone has some tips and/or pointers for how to isolate the problem of WHY it is not writing to the client context. I'm not a Netty expert by any stretch, and most of this code was written by someone else. They are both subclassing ChannelInboundHandlerAdapter
Feel free to ask any questions if you have any.
*****EDIT*********
I tried to proxy the request back to a DIFFERENT context/channel (ie, the client channel) using the following test code:
public void proxyPubRec(int messageId) throws MQTTException {
logger.log(logLevel, "proxying PUBREC to context: " + debugContext());
PubRecMessage pubRecMessage = new PubRecMessage();
pubRecMessage.setMessageID(messageId);
pubRecMessage.setRemainingLength(2);
logger.log(logLevel, "pipeline writable flag: " + ctx.pipeline().channel().isWritable());
MyMQTTEncoder encoder = new MyMQTTEncoder();
ByteBuf buff = null;
try {
buff = encoder.encode(pubRecMessage);
ctx.channel().writeAndFlush(buff);
} catch (Throwable t) {
logger.log(Level.SEVERE, "unable to encode PUBREC");
} finally {
if (buff != null) {
buff.release();
}
}
}
public class MyMQTTEncoder extends MQTTEncoder {
public ByteBuf encode(AbstractMessage msg) {
PooledByteBufAllocator allocator = new PooledByteBufAllocator();
ByteBuf buf = allocator.buffer();
try {
super.encode(ctx, msg, buf);
} catch (Throwable t) {
logger.log(Level.SEVERE, "unable to encode PUBREC, " + t.getMessage());
}
return buf;
}
}
But the above at line: ctx.channel().writeAndFlush(buff) is NOT writing to the other channel - any tips/tricks on debugging this sort of issue?
someOtherMessage has to be ByteBuf.
So, take this :
serverHandler.channelRead(ChannelHandlerContext ctx, Object msg) {
if (msg instanceof myProxyingMessage) {
if (ctx.channel().isActive()) {
ctx.channel().writeAndFlush(someOtherMessage);
**getClientHandler().writeAndFlush(myProxyingMessage);**
}
}
}
... and replace it with this :
serverHandler.channelRead(ChannelHandlerContext ctx, Object msg) {
if (msg instanceof myProxyingMessage) {
if (ctx.channel().isActive()) {
ctx.channel().writeAndFlush(ByteBuf);
**getClientHandler().writeAndFlush(myProxyingMessage);**
}
}
}
Actually, this turned out to be a threading issue. One of my threads was blocked/waiting while other threads were writing to the context and because of this, the writes were buffered and not sent, even with a flush. Problem solved!
Essentially, I put the first message code in an Runnable/Executor thread, which allowed it to run separately so that the second write/response was able to write to the context. There are still potentially some issues with this (in terms of message ordering), but this is not on topic for the original question. Thanks for all your help!

HttpWebRequest maintenance and http web errors causing it to return "HRESULT E_FAIL" and "server not found"

I am iterating through a large list of objects (1503) and calling a save method on a ServiceProxy I have written. The service proxy uses the new networking stack in Silverlight 4 to call BeginGetRequestStream to start the process of asynchronously sending my objects to an azure REST service I have written for saving off the objects. The Http method I am using is POST. I know HttpWebClient is smart enough to reuse the Http connection so I am not concurrently opening 1503 connections to the server. Saving works fine and all 1503 objects are saved very quickly. However, when I try to save the same objects again, I expect to recieve an HttpStatus code of forbidden because the objects already exist and that is the code I set my azure web service to return. On small groups of objects, it works as expected. However, when I try saving the entire list of 1503 objects, I receive only 455 correct responses and 1048 errors such as "server not found" and
System.Exception ---> System.Exception:Error HRESULT E_FAIL has been returned from a call to a COM component.
at
System.Net.Browser.ClientHttpWebRequest.InternalEndGetResponse(IAsyncResult asyncResult)...
I wonder if there is some sort of book keeping or maintenance I am supposed to be performing on my HttpWebClient instances that I am neglecting and that is what is causing the http errors to throw exceptions but the new saves to work perfectly. Here is my code for handling the error cases:
private static void SendAncestorResponseCallback(IAsyncResult result)
{
var info = (SendAncestorInfo)result.AsyncState;
try
{
var response = info.Request.EndGetResponse(result);
info.Response = response;
}
catch ( Exception ex)
{
info.Error = ex;
}
info.MainThreadContext.Post(SendAncestorMainThreadCallback, info);
}
private static void SendAncestorMainThreadCallback(object state)
{
var info = (SendAncestorInfo)state;
IAncestor origAncestor = info.Content;
HttpWebResponse response = null;
if (info.Error != null)
{
if ((info.Error as WebException) == null)
{
info.Callback(false, origAncestor, null, info.Error);
return;
}
else //get response from WebException
{
response = (HttpWebResponse)(info.Error as WebException).Response;
}
}
else //get response from info.Response
{
response = info.Response as HttpWebResponse;
}
if (response.StatusCode == HttpStatusCode.Created || response.StatusCode == HttpStatusCode.Forbidden)
{
var stream = response.GetResponseStream();
using (var reader = new StreamReader(stream))
{
IAncestor retAncestor = XMLSerializerHelper.DeserializeObject<Ancestor>(reader.ReadToEnd());
info.Callback(response.StatusCode == HttpStatusCode.Created, origAncestor, retAncestor, null);
}
}
else info.Callback(false, origAncestor, null, info.Error);
}
considering how the web service is written I should only expect http status codes of created or forbidden and like I said with small groups this is the case. The fact that I only start getting the errors mentioned earlier makes me feel like I am doing something wrong with the HttpWebRequest objects etc. Any assistance would be greatly appreciated. Thanks.
--update here is the code that generates the HttpWebRequest:
foreach (IAncestor ancestor in ancestors)
{
AncestorViewModel ancestorVM = new AncestorViewModel(ancestor);
ancestorVM.Status = SaveStatus.Undefined;
ParsedAncestors.Add(ancestorVM);
_service.CreateAncestor(UserSrc, ancestor, (success, origAncestor, retAncestor, exception) =>
{
AncestorViewModel result = ParsedAncestors.First(a => a.Model.IdNo == origAncestor.IdNo);
if (exception == null)//web response was either Created or Forbidden
{
if (success)//Ancestor successfully created
{
savedAncestors++;
SuccessMessage = string.Format("{0} Saved\n", savedAncestors);
result.Status = SaveStatus.Saved;
}
else //Ancestor already existed
{
conflictAncestors.Add(origAncestor, retAncestor);
ConflictMessage = string.Format("{0} Conflicts\n", conflictAncestors.Count);
result.Status = SaveStatus.Conflicted;
}
}
else //Show exception recieved from remote web service
{
//if (exception as WebException != null)
//{
// //if exception is WebException get status code and description
// HttpWebResponse rs = (HttpWebResponse)(exception as WebException).Response;
// Message += string.Format("WebServer returned status code {0}: '{1}'\n", (int)rs.StatusCode, rs.StatusDescription);
//}
errors.Add(origAncestor, exception);
ErrorMessage = string.Format("{0} Errors\n", errors.Count);
result.Status = SaveStatus.Error;
}
});
}
public void CreateAncestor(string userSrc, IAncestor ancestor, Action<bool, IAncestor, IAncestor, Exception> callback)
{
WebRequest.RegisterPrefix("http://", WebRequestCreator.ClientHttp);
var request = (HttpWebRequest)WebRequest.Create(
new Uri(string.Format("{0}/{1}/{2}", rootUri, AncestorsRestPoint, userSrc)));
request.Method = "POST";
request.ContentType = "application/xml";
var info = new SendAncestorInfo
{
Request = request,
Callback = callback,
Content = ancestor,
MainThreadContext = SynchronizationContext.Current
};
request.BeginGetRequestStream(SendAncestorRequestCallback, info);
}

linking my applet to a server dirctory to recieve or save a file from there?

I' m looking for a code to save the files created in a applet normally text files i want to save them on a server directory how can i do so.
Here is an example of how to send a String. In fact any Object can be sent this method so long as it's serializable and the same version of the Object exists on both the applet and the servlet.
To send from the applet
public void sendSomeString(String someString) {
ObjectOutputStream request = null;
try {
URL servletURL = new URL(getCodeBase().getProtocol(),
getCodeBase().getHost(),
getCodeBase().getPort(),
"/servletName");
// open the connection
URLConnection con = servletURL.openConnection();
con.setDoOutput(true);
con.setUseCaches(false);
con.setRequestProperty("Content-Type", "application/octet-stream");
// send the data
request =
new ObjectOutputStream(
new BufferedOutputStream(con.getOutputStream()));
request.writeObject(someString);
request.flush();
// performs the connection
new ObjectInputStream(new BufferedInputStream(con.getInputStream()));
} catch (Exception e) {
System.err.println("" + e);
} finally {
if (request != null) {
try {
request.close();
} catch (Exception e) {
System.err.println("" + e);
};
}
}
}
To retrieve on the server side
#Override
public void doPost(HttpServletRequest request, HttpServletResponse response) {
try {
// get the input stream
ObjectInputStream inputStream = new ObjectInputStream(
new BufferedInputStream(request.getInputStream()));
String someString = (String)inputStream.readObject();
ObjectOutputStream oos = new ObjectOutputStream(
new BufferedOutputStream(response.getOutputStream()));
oos.flush();
// handle someString....
} catch (SocketException e) {
// ignored, occurs when connection is terminated
} catch (IOException e) {
// ignored, occurs when connection is terminated
} catch (Exception e) {
log.error("Exception", e);
}
}
No one is going to hand you this on a plate. You have to write code in your applet to make a socket connection back to your server and send the data. One way to approach this is to push the data via HTTP, and use a library such as commons-httpclient. That requires your server to handle the appropriate HTTP verb.
There are many other options, and the right one will depend on the fine details of the problem you are trying to solve.