HttpContext.Request.Body is disposed when accessing via ApplicationInsights ITelemetryInitializer - azure-application-insights

I'm trying to log POST, PUT, PATCH json bodies to app insights using ITelemetryInitializer. Every time a post comes in though it seems that my body stream is already disposed of some how. I'm assuming there is something in the request pipeline that I'm not registering/doing properly.
public class RequestBodyLogger : ITelemetryInitializer
{
readonly IHttpContextAccessor httpContextAccessor;
public RequestBodyLogger(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
public void Initialize(ITelemetry telemetry)
{
try
{
if (telemetry is RequestTelemetry requestTelemetry)
{
if ((httpContextAccessor.HttpContext.Request.Method == HttpMethods.Post ||
httpContextAccessor.HttpContext.Request.Method == HttpMethods.Put ||
httpContextAccessor.HttpContext.Request.Method == HttpMethods.Patch) &&
httpContextAccessor.HttpContext.Request.Body.CanRead)
{
const string jsonBody = "JsonBody";
if (requestTelemetry.Properties.ContainsKey(jsonBody))
{
return;
}
//Allows re-usage of the stream
httpContextAccessor.HttpContext.Request.EnableRewind();
var stream = new StreamReader(httpContextAccessor.HttpContext.Request.Body);
var body = stream.ReadToEnd(); <<Blows here object disposed (stream)
//Reset the stream so data is not lost
httpContextAccessor.HttpContext.Request.Body.Position = 0;
requestTelemetry.Properties.Add(jsonBody, body);
}
}
}
catch (Exception e)
{
}
}
}
Configure services method...
public void ConfigureServices(IServiceCollection services)
{
if (_env.IsDevelopment())
{
services.AddSwaggerGen(c =>
{
c.SwaggerDoc(Constants.ApiVersion, new Info { Title = Constants.ApiName, Version = Constants.ApiVersion });
c.AddSecurityDefinition("Bearer", new ApiKeyScheme { In = "header", Description = "Please enter Bearer Token", Name = "Authorization", Type = "apiKey" });
c.AddSecurityRequirement(new Dictionary<string, IEnumerable<string>> { { "Bearer", Enumerable.Empty<string>() } });
c.IncludeXmlComments($"{AppDomain.CurrentDomain.BaseDirectory}\\TradeJournal.Api.xml");
});
}
services.AddAuthentication(options =>
{
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.Authority = Configuration["IdentityAuthority"];
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
IssuerSigningKeyResolver = (s, securityToken, identifier, parameters) =>
{
var json = new WebClient().DownloadString(parameters.ValidIssuer + "/.well-known/jwks.json");
var keys = JsonConvert.DeserializeObject<JsonWebKeySet>(json).Keys;
return (IEnumerable<SecurityKey>)keys;
},
ValidIssuer = Configuration["IdentityAuthority"],
ValidateIssuerSigningKey = true,
ValidateIssuer = true,
ValidateLifetime = true,
ValidateAudience = false
};
});
services
.AddCors(c =>
{
c.AddPolicy("AllowOrigin", options => options.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod().AllowCredentials());
});
services.AddMvc(opts =>
{
opts.Filters.Add(typeof(ModelStateValidationFilter));
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.AddJsonOptions(opt =>
{
opt.SerializerSettings.DateFormatString = "yyyy-MM-ddTHH:mm:ssZ";
});
services.AddSingleton<ITelemetryInitializer, RequestBodyLogger>();
services.AddTransient<ExceptionToHttpResponseMiddleware>();
services.AddTransient<MaintenanceMiddleware>();
services.AddRouting(opts =>
{
opts.LowercaseUrls = true;
opts.LowercaseQueryStrings = true;
});
BootstrapLayers(services);
}
```
Configure method...
public void Configure(IApplicationBuilder app)
{
if (_env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"/swagger/{Constants.ApiVersion}/swagger.json", Constants.ApiName);
});
}
else
{
//The default HSTS value is 30 days.You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseAuthentication();
app.UseCors(options => options.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader().AllowCredentials());
app.UseMiddleware<ExceptionToHttpResponseMiddleware>();
app.UseMiddleware<MaintenanceMiddleware>();
app.UseHttpsRedirection();
app.UseMvc();
}
**Why is the context disposed by the time AI calls this telemetry initializer?**

This is also discussed in github: https://github.com/microsoft/ApplicationInsights-aspnetcore/issues/940#issuecomment-513297006
Since by the time TelemetryInitializers are run, the request body is disposed, your best bet is to read and populate the body into RequestTelemetry when the body is still available. If its a controller or middleware - then retrieve RequestTelemetry there, add body to it. The following is sample code to be written in controller/middleware where you can retrieve request body.
RequestTelemetry reqTelemetry = httpContext?.Features.Get<RequestTelemetry>();
reqTelemetry.Properties.Add("body","body contents").

Related

Webkit2 with gjs - get response headers

I'm experimenting with gjs and webkit2, how can i get the http headers of a request made with load_uri
i have the following code
const Gtk = imports.gi.Gtk, WebKit=imports.gi.WebKit2, contentManager=new WebKit.UserContentManager,
view = WebKit.WebView.new_with_user_content_manager(contentManager);
Gtk.init(null);
let win = new Gtk.Window(), Response=new WebKit.URIResponse();
contentManager.add_script (new WebKit.UserScript("alert ('test');",0,1,null,null));
view.load_uri('https://www.gnome.org');
win.add(view);
win.set_title("test");
win.set_icon_from_file("/games/aptdaemon-resolve.png");
win.connect('destroy', () => { Gtk.main_quit(); });
win.set_size_request(640, 480);
win.show_all();
view.connect("load-changed",function (instance,state)
{
if (state == 3)
{
log ("URL"+Response.get_uri());
view.run_javascript ("alert (document.body.innerHTML)",null,null);
}
});
Gtk.main();
for example Response.get_uri returns an empty string, how to access response headers, and how to exchange messages between scripts injected with view.run_javascript and gjs. i want the body html be sent to gjs-?
got it
const Gtk = imports.gi.Gtk;
const WebKit=imports.gi.WebKit2;
Gtk.init(null);
const win = new Gtk.Window(), contentManager=new WebKit.UserContentManager, view = WebKit.WebView.new_with_user_content_manager(contentManager);
let response_STR;
contentManager.connect("script-message-received::pipe", function (instance, message)
{
message=message.get_js_value().to_string ();
log (message);
});
contentManager.register_script_message_handler("pipe");
view.load_uri('https://www.gnome.org');
win.add(view);
win.set_title("test");
win.connect('destroy', () => { Gtk.main_quit(); });
win.set_size_request(640, 480);
win.show_all();
view.connect("load-changed",function (instance,status)
{
let headers, response_STR="";
if (status == 3)
{
/* WebKitView.get_main_resource -> returns WebResource
WebResource.get_response -> returns URIResponse
URIResponse.get_http_headers -> returns Soup.MessageHeaders */
headers=view.get_main_resource().get_response().get_http_headers();
response_STR="";
headers.foreach ((name, value) => { response_STR+=name+": "+value+"\n"});
view.run_javascript('window.webkit.messageHandlers.pipe.postMessage(document.body.innerHTML);', null, null);
log (response_STR);
}
});
Gtk.main();

No service for type 'IdentityServer4.Configuration.IdentityServerOptions' has been registered. - Mocking HttpContext.SignInAsync

I'm trying to cover External auth with Unit Tests.
Mocked all dependencies including SignInAsync(). But execution fails with the message "No service for type 'IdentityServer4.Configuration.IdentityServerOptions' has been registered." when it hits SignInAsync().
Stack trace here
Test method.
public async Task CallBack_User_Is_Defined_In_AAD_ReturnChallenge()
{
var ticket = new AuthenticationTicket(TestUsers.Principal, "Test");
var result = AuthenticateResult.Success(ticket);
result.Properties?.Items.Add("returnUrl", "sign-callback");
result.Properties?.Items.Add("scheme", "aad");
authenticationService.Setup(c => c.AuthenticateAsync(It.IsAny<HttpContext>(), IdentityServerConstants.ExternalCookieAuthenticationScheme)).Returns(Task.FromResult(result)); ;
var serviceProviderMock = new Mock<IServiceProvider>();
serviceProviderMock
.Setup(_ => _.GetService(typeof(IAuthenticationService)))
.Returns(authenticationService.Object);
serviceProviderMock
.Setup(_ => _.GetService(typeof(IdentityOptions)))
.Returns(authenticationService.Object);
var identityServiceUserResponse = new IdentityServiceUserResponse
{
Email = TestUsers.Principal.FindFirstValue(ClaimTypes.Email),
CompanyID = "1",
Login = "john"
};
identityServiceAuthService.Setup(c => c.GetUserByExternalEmailAsync(It.IsAny<string>()))
.Returns(Task.FromResult(identityServiceUserResponse));
var authRequest = new AuthorizationRequest
{
Client = new Client { ClientId = "client" },
ValidatedResources = resourceValidationResult,
Parameters = { { "returnUrl", "signin-callback" } },
};
interactionService.Setup(i => i.GetAuthorizationContextAsync(It.IsAny<string>()))
.Returns(Task.FromResult(authRequest));
authenticationService
.Setup(_ => _.SignInAsync(It.IsAny<HttpContext>(), It.IsAny<string>(), It.IsAny<ClaimsPrincipal>(), It.IsAny<AuthenticationProperties>()))
.Returns(Task.FromResult((object)null));
controller = new ExternalController(configuration.Object, interactionService.Object,
identityServiceAuthService.Object, clientStore.Object, events.Object, logger.Object);
controller.ControllerContext = controller.CreateControllerContext(TestUsers.Principal);
controller.ControllerContext.HttpContext.RequestServices = serviceProviderMock.Object;
await controller.Callback();
}

Not being able to send message into chatroom using npm ws package

I'm using npm ws package for websocket. I'm not being able send message into chatroom. any idea how to do it. Below is my code with sendMessage() and broadcast()
const sendMessage = (room_name, message, socket) => {
// rooms[room_name].message = JSON.stringify(message);
const obj = rooms[room_name];
for(i=0;i<obj.length;i++){
var temp = obj[i];
for(var innerObject in temp){
var wsClientID = temp[innerObject];
if(socket!==wsClientID){
wsClientID.send(JSON.stringify({
'message':message,
}));
}
}
}
// rooms[room_name].message = message;
socket.send(JSON.stringify(message));
// rooms[room_name].message = message;
}
socket.on("message", async (data) => {
broadcast(data)}
function broadcast(data) {
var count = 0;
for (const client of server.clients) {
if (client.readyState === socket.OPEN) {
count++;
client.send(data.toString())
}
}
}

Error UseHealthChecksUI Unexpected character encountered

I'm trying to implement the ASP.NET Core 2.2 health check feature. Setting up the health check itself isn't the problem, but I also want to be able to use the UI feature in other project to monitoring all my apis. Right now I get the exception message
Unexpected character encountered while parsing value: <.
What I'm doing bad?
API Project:
var healthCheckOptions = new HealthCheckOptions
{
Predicate = _ => true,
ResponseWriter = async (c, r) =>
{
c.Response.ContentType = MediaTypeNames.Application.Json;
var result = JsonConvert.SerializeObject(
new
{
Checks = r.Entries.Select(e =>
new
{
Description = e.Key,
Status = e.Value.Status.ToString(),
ResponseTime = e.Value.Duration.TotalMilliseconds
}),
TotalResponseTime = r.TotalDuration.TotalMilliseconds
});
await c.Response.WriteAsync(result);
}
};
app.UseHealthChecks("/live", new HealthCheckOptions
{
Predicate = _ => true
});
app.UseHealthChecks("/hc", healthCheckOptions);
app.UseHealthChecksUI(options => options.UIPath = "/healtcheck");
// Registers required services for health checks
services
.AddHealthChecks()
.AddCheck("self", () => HealthCheckResult.Healthy())
.AddCheck("ComunContext Database", new SqlServerHealthCheck(configuration["ConnectionStrings:ComunContext"]));
Web project:
services.AddHealthChecksUI();
app.UseHealthChecksUI(config =>
{
config.UIPath = "/healthcheck";
});
appsettings.json
{
"HealthChecks-UI": {
"HealthChecks": [
{
"Name": "Local",
"Uri": "http://localhost:27365/hc"
}
],
"EvaluationTimeOnSeconds": 10,
"MinimumSecondsBetweenFailureNotifications": 60
}
}
Try adding a ResponseWriter:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseHealthChecks("/healthchecks", new HealthCheckOptions
{
ResponseWriter = async (context, report) =>
{
context.Response.ContentType = "application/json; charset=utf-8";
var bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(report));
await context.Response.Body.WriteAsync(bytes);
}
});
app.UseHealthChecksUI();
}
After a few days struggling with this parser error, I've figured out that there are 2 problems:
1 - If you have an Exception, Health UI tries to convert Exception object field, resulting on error;
2 - If you try to pass your own anonymous object, Health UI fails to convert Entries collection, because it need to be an specific anonymous Dictionary.
Try this:
var healthCheckOptions = new HealthCheckOptions
{
Predicate = _ => true,
ResponseWriter = async (c, r) =>
{
c.Response.ContentType = MediaTypeNames.Application.Json;
var result = JsonConvert.SerializeObject(
new
{
Checks = r.Entries.ToDictionary(
e => e.Key,
e =>
new
{
Description = e.Key,
Status = e.Value.Status.ToString(),
ResponseTime = e.Value.Duration.TotalMilliseconds
}),
TotalResponseTime = r.TotalDuration.TotalMilliseconds
});
await c.Response.WriteAsync(result);
}
};

upload image using file transfer ionic 3 not work on iOS

upload image using file transfer in ionic 3 works fine on android,
but give me error on iOS when try it in simulator ..
* this is the error:
My Ionic Code:
chooseImageFromGallery()
{
this.type="0"
const options: CameraOptions = {
quality: 60,
destinationType: this.camera.DestinationType.FILE_URI,
encodingType: this.camera.EncodingType.JPEG,
mediaType: this.camera.MediaType.PICTURE,
saveToPhotoAlbum:true,
sourceType:0
}
this.camera.getPicture(options)
.then((imageData) => {
if (this.platform.is('ios'))
{
this.base64Image = imageData;
}
else
{
this.base64Image = imageData;
}
this.uploadimage(); // this function to upload img to server
},
(err) => {
}).then((path)=>{
})
}
uploadimage(){
this.photoSrc="";
this.translate.get("uploading Image...").subscribe(
value => {
this.sucesss=false
const fileTransfer: FileTransferObject = this.transfer.create();
let options: FileUploadOptions = {
fileKey: "file",
fileName:'test',
chunkedMode:false,
mimeType:"image/jpeg",
headers:{
Connection:"close"
},
httpMethod: "POST",
}
//------------ android ------------//
this.base64Image =this.base64Image
//------------ ios ------------//
//this.base64Image =this.base64Image.substring(28)
fileTransfer.upload(this.base64Image,encodeURI('mydomain/api/Product/upload'), options)
.then((data:any) => {
alert("upload success ")
}, (err) => {
this.translate.get( "error in upload Data").subscribe(
value => {
this.service.presentToast(value,2000)
}
)
})
})
}
using asp.net api2 .. My server Code :
[HttpPost]
[Route("upload")]
[AllowAnonymous]
public HttpResponseMessage uploadImage()
{
var request = HttpContext.Current.Request;
if (Request.Content.IsMimeMultipartContent())
{
foreach (string file in request.Files)
{
var postedFile = request.Files[file];
if (postedFile != null && postedFile.ContentLength > 0)
{
string root = HttpContext.Current.Server.MapPath("~/ServerImg");
root = root + "/" + postedFile.FileName;
postedFile.SaveAs(root);
//Save post to DB
return Request.CreateResponse(HttpStatusCode.Found, new
{
error = false,
status = "created",
path = root
});
}
else
{
return Request.CreateResponse(HttpStatusCode.NotFound, new
{
error = true
});
}
// var title = request.Params["title"];
}
// }
return null;
}
else
{
return Request.CreateResponse(HttpStatusCode.Forbidden, new
{
error = true
});
}
}
I spend more than 4 days.. but nothing is work for me ..
And this code works fine on Android but not iOS I don't know what's the wrong, I tried real iPhone and Xcode simulator and not worked
always upload error {"code":3... "http_status":500,..
Can anyone Help me please...

Resources