How do I enable CORS in Giraffe? - asp.net-web-api

I am unable to successfully perform a Post operation using the Giraffe framework on the server with an Elm client sending the request.
I receive the following message when attempting to test an http request:
info: Microsoft.AspNetCore.Hosting.Internal.WebHost1
Request starting HTTP/1.1 OPTIONS http://localhost:5000/register 0
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request
starting HTTP/1.1 OPTIONS http://localhost:5000/register 0 dbug:
Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware1
OPTIONS requests are not supported
The service implementation is the following:
let private registrationHandler =
fun(context: HttpContext) ->
async {
let! data = context.BindJson<RegistrationRequest>()
match register data with
| Success profile -> return! json profile context
| Failure -> return! (setStatusCode 400 >=> json "registration failed") context
}
I then attempted the following and observed the same result:
let private registrationHandler =
fun(context: HttpContext) ->
async {
return! text "hello world" context
}
Appendix:
POST >=>
choose [
route "/register" >=> registrationHandler
]
The source file can be found here.
Elm and CORS
WebAPI enable Cors
Here's a Giraffe sample that shows the code for supporting Cors.

Add package: Microsoft.AspNetCore.Cors
In .fs file add:
open Microsoft.AspNetCore.Cors
Add UseCors e.g.:
let configureApp (app : IApplicationBuilder) =
app.UseGiraffeErrorHandler errorHandler
app.UseStaticFiles() |> ignore
app.UseAuthentication() |> ignore
app.UseCors(Action<_>(fun (b: Infrastructure.CorsPolicyBuilder) -> b.AllowAnyHeader() |> ignore; b.AllowAnyMethod() |> ignore)) |> ignore
app.UseGiraffe webApp
In services add cors:
let configureServices (services : IServiceCollection) =
let sp = services.BuildServiceProvider()
let env = sp.GetService<IHostingEnvironment>()
let viewsFolderPath = Path.Combine(env.ContentRootPath, "Views")
services
.AddCors()
.AddAuthentication(authScheme)
.AddCookie(cookieAuth)
|> ignore

Related

KTOR Client and Spring Switchuser

I'm trying to implement a client for spring-security's SwitchUserFilter (server-side). As client I'm using KTOR (with OKHttp inside).
SwitchUserFilter requires me to log in, then drop the Authorization header and use the Cookie tinstead. If I send the Authorization header together with the Cookie header, spring's SecurityContext coming from SwitchUserFilter will be overwritten with my admin user again.
Is there something I can configure in KTOR, so that the [Authorization] header is removed, once I have switched the user?
KTOR has to be setup with two things:
SwitchUserFilter will send a redirect (HTTP 302) that we need to ignore. For this a HttpResponseValidator needs to be configured.
Auth needs to be removed similar to the comment from #Delta_George
HttpClient(OkHttp) {
HttpResponseValidator {
// for 302 don't react - so we can switch user successfully. If we follow, this doesn't work anymore.
validateResponse { response ->
val statusCode = response.status.value
val originCall = response.call
if (statusCode < 300 || originCall.attributes.contains(ValidateMark)) {
return#validateResponse
}
val exceptionCall = originCall.save().apply {
attributes.put(ValidateMark, Unit)
}
val excResp = exceptionCall.response
val excRespTxt = excResp.readText()
when (statusCode) {
302 -> {} // do nothing on "Found" statuscode
in 300..399 -> throw RedirectResponseException(excResp, excRespTxt)
in 400..499 -> throw ClientRequestException(excResp, excRespTxt)
in 500..599 -> throw ServerResponseException(excResp, excRespTxt)
else -> throw ResponseException(excResp, excRespTxt)
}
}
}
... // other configurations
}
and impersonate(...):
suspend fun impersonate(impersonateWithUser: PersonEntity): Impersonation<PersonEntity> {
return runCatching {
val toImpersonate = impersonateWithUser.login.replace(Regex("^\\+"), "%2B")
client.get<HttpResponse>("$BASE_URL/login/impersonate?username=${toImpersonate}") // with baseauth again
}.map {
when (it.status) {
HttpStatusCode.Found -> {
client.feature(Auth)!!.providers.removeAll { true }
Impersonation.ok(impersonateWithUser)
}
else -> Impersonation.failure(impersonateWithUser, it)
}
}.getOrElse {
Log.e(TAG, "impersonate: ", it)
Impersonation.communicationError(impersonateWithUser, it)
}
}
to end the impersonation you call the respective endpoint given in SwitchUserFilter on the serverside.

How to set netty log level on selected spring boot Webclient instances

We have 2 different instances of spring boot Webclients. We want one of them to log all requests/responses and the other to log nothing. Settiing:
logging.level.reactor.netty.http.client=debug
in the application.properties file causes both instances to log requests/responses. Is there a way to programmatically set the log level on one of the instances to log debug levels and the other to not?
We create the first webclient like this:
WebClient.create();
For this one we don't want debug logging.
The second one, for which we want logging, we create like this:
return WebClient.builder()
.clientConnector(createWebClientWithTimeout())
// Add base url to all requests (callers only need to add the path and query params)
.baseUrl(baseUrl)
// Increase the buffer size from 256K to 1M
.codecs(configurer -> configurer
.defaultCodecs()
.maxInMemorySize(1024 * 1024))
// Filter to add bearer token to auth header before sending request
.filter((request, next) -> getToken(custId).map(setAuthHeader(request)).flatMap(next::exchange))
// Filter to send the request, and try again if it has an auth error
.filter((request, next) -> next.exchange(request).flatMap(clientResponse -> {
:::
return Mono.just(clientResponse);
})).build();
and
private ClientHttpConnector createWebClientWithTimeout() {
// create reactor netty HTTP client
HttpClient httpClient = HttpClient.newConnection()
.wiretap("reactor.netty.http.client.HttpClient",
LogLevel.DEBUG, AdvancedByteBufFormat.TEXTUAL)
.compress(true)
.tcpConfiguration(tcpClient -> {
tcpClient = tcpClient.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, linkedinConnectTimeoutInSeconds * 1000)
.doOnConnected(conn -> conn
.addHandlerLast(new ReadTimeoutHandler(linkedinReadTimeoutInSeconds, TimeUnit.SECONDS))
.addHandlerLast(new WriteTimeoutHandler(linkedinWriteTimeoutInSeconds, TimeUnit.SECONDS)));
return tcpClient;
});
// create a client http connector using above http client
return new ReactorClientHttpConnector(httpClient);
}
If we don't set logging.level.reactor.netty.http.client=debug, neither of these webclients log netty debug statements. If we set the debug parameter, both log the requests, the second one also logs request/response headers and bodies.

How to make chained api calls in spring webflux/webclient

I am creating a aggregator service which needs to call an api and based on response of make second call and so on. So i am calling chain of apis each dependent on response of previous one.
Example Scenario:
request 1 {"Date1" : "2019-03-03"}
response 1 {"price" : 10}
request 2 {"Date1" : "2019-03-03", "price":10}
response 2 {"recommendedPrice" : 8}
request 2 {"Date1" : "2019-03-03", "price":10, "recommendedPrice":8}
and so on
I have created this
public Mono<Result> getFinalRes(Request req){
res = client.post()
.uri(url)
.body(BodyInserters.fromObject(request))
.exchange()
.flatMap(res -> res.bodyToMono(PriceTuple.class))
return res.subscribe(res -> getApi2(res,request ))
}
Or
public Mono<Result> getFinalRes(Request req){
res = client.post()
.uri(url)
.body(BodyInserters.fromObject(request))
.exchange()
.flatMap(res -> getApi2(res,request ))
}
getApi2() is again doing similar kind of thing. Am i right to do chaining in such a way or there is some better way of doing this kind of request chaining.

How to catch okhttp3 WebSocket network activity using okhttp3.Interceptor?

I have an okhttp3 (3.9.1) WebSocket instance and would like to view all it's network requests and responses. I tried to add some okhttp3.Interceptor instances to OkHttpClient instance before creating WebSocket on it but had no luck in viewing network activity. Here's sample code which demonstrates what I've tried to do:
package sample
import okhttp3.*
import java.io.IOException
import java.lang.Thread.sleep
fun main(args: Array<String>) {
val listener = object : WebSocketListener() {
override fun onMessage(webSocket: WebSocket?, text: String?) {
println("Got server message: $text")
}
}
val dummyInterceptor = Interceptor { chain ->
val request = chain.request()
val response = chain.proceed(request)
println("Dummy interceptor fired!\n\nRequest: ${request.headers()}\nResponse: ${response.headers()}")
return#Interceptor response
}
val dummyNetworkInterceptor = Interceptor { chain ->
val request = chain.request()
val response = chain.proceed(request)
println("Dummy network interceptor fired!\n\nRequest: ${request.headers()}\nResponse: ${response.headers()}")
return#Interceptor response
}
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(dummyInterceptor)
.addNetworkInterceptor(dummyNetworkInterceptor)
.build()
val request = Request.Builder().url("ws://echo.websocket.org").build()
val webSocket = okHttpClient.newWebSocket(request, listener)
webSocket.send("Hello1!")
webSocket.send("Hello2!")
webSocket.send("Hello3!")
sleep(2000) //Just for this sample to ensure all WS requests done
println("\n\n\tSome network activity\n\n")
okHttpClient.newCall(Request.Builder().get().url("http://echo.websocket.org").build()).enqueue(object : Callback {
override fun onFailure(call: Call?, exc: IOException?) {
println("OnFailure: ${exc?.message}")
}
override fun onResponse(call: Call?, response: Response?) {
println("OnResponse: ${response?.headers()}")
}
})
}
I tried to dive into okhttp3 source code and didn't find any reason why any of my interceptors doesn't fire on WS requests but works perfectly for any OkHttpClient request.
Is it a bug in okhttp3 or am I doing something wrong or it's just not possible to monitor WS requests using okhttp3.Interceptor?
WebSocket calls made with OkHttp don't use the interceptor chains that HTTP calls do, therefore you can't monitor them through interceptors.
I've faced this issue before myself, and so I looked at the source code and found the following then:
The regular HTTP calls go through the getResponseWithInterceptorChain() method in the RealCall class, which quite clearly starts the chained call of interceptors for each request.
The okhttp3.internal.ws package that includes the implementation of the WebSocket handling contains no code related to interceptors.
And really, interceptors catching WebSocket requests wouldn't really make sense in the first place. The Request that you can obtain in an interceptor represents an HTTP request, which WebSocket messages are not.
It isn't possible at this point, there's a feature request open for OkHttp but it isn't getting much traction: https://github.com/square/okhttp/issues/4192

FinderSync Extension HTTP request to failing

Currently I am writing FinderSync Extension for my App using Swift language. My Extension require to do the REST call to the Server which is running on local host at port number 40322. Based on the response I will create the context menu Items. For same reason I wrote the following code in "FinderSync.swift"
file as
let config = URLSessionConfiguration.default
// Session Configuration
let session = URLSession(configuration: config)
// Load configuration into Session
let request_url = URL(string: "http://127.0.0.1:40322/api/get_rclick_settings_and_check_target")!
let task = session.dataTask(with: request_url, completionHandler: {
(data, response, error) in
if error != nil {
print("TAKS ERROR: \(error!.localizedDescription)")
}
else {
do {
if let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any]
{
NSLog("TASK RESPONSE: \(json)")
}
} catch {
NSLog("error in JSONSerialization")
}
}
})
task.resume()
But The code giving Error as
"nw_socket_connect connectx failed: [1] Operation not permitted"
But the same code is running of playground after importing XCPlayground and adding line as "XCPSetExecutionShouldContinueIndefinitely(continueIndefinitely: true)"
My Question is do we need to add any elements in "info.plist" of application or FinderSync Extension to allow the extension to do the REST call or is there any other way to solve this problem?
Have you set the extension's Capabilities tab to allow network connections?
Your app extension uses a different .entitlements file than the main app. Make sure you additionally add any capabilities the extension will require there.
<key>com.apple.security.network.client</key>
<true/>

Resources