How do I run code before receiving http request in ktor? - ktor

First of all, ktor is a awesome product. Is there a way in ktor to be able to run code before a request or a certain set of http requests are processed in the server. I want to be able to check for a certain http header in the request and that the header matches a certain value. If the http header does not exist or the http header value does not match a configured value in the server I want to be able to return a 403 or another http status code.

You can either use custom plugins API:
val plugin = createApplicationPlugin("plugin") {
onCall { call ->
if (call.request.headers["Custom-Header"] == null) {
call.respond(HttpStatusCode.Forbidden)
}
}
}
fun main() {
embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
install(plugin)
// ...
}.start(wait = true)
}
Or intercept the ApplicationCallPipeline:
embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
intercept(ApplicationCallPipeline.Plugins) {
if (call.request.headers["Custom-Header"] == null) {
call.respond(HttpStatusCode.Forbidden)
finish()
}
}
// ...
}.start(wait = true)

Related

How to get the value of another function synchronously

I am using the ktor websocket module
When I send data to the client, how do I get the data back from the client after this send?
val result = serverSession.send(json)
// result
Just like this
It is actually the Unit type
But I want to get the String
There are great examples on official site of Ktor.
If you are server-side, check this link (https://ktor.io/docs/websocket.html#handle-single-session) and the below example.
webSocket("/echo") {
send("Please enter your name")
for (frame in incoming) {
when (frame) {
is Frame.Text -> {
val receivedText = frame.readText()
if (receivedText.equals("bye", ignoreCase = true)) {
close(CloseReason(CloseReason.Codes.NORMAL, "Client said BYE"))
} else {
send(Frame.Text("Hi, $receivedText!"))
}
}
}
}
}
If you are client-side, check this link (https://ktor.io/docs/websocket-client.html#example) and the below example.
client.webSocket(method = HttpMethod.Get, host = "127.0.0.1", port = 8080, path = "/echo") {
while(true) {
val othersMessage = incoming.receive() as? Frame.Text
println(othersMessage?.readText())
val myMessage = Scanner(System.`in`).next()
if(myMessage != null) {
send(myMessage)
}
}
}

How do I force https redirect on static files in ASP.NET Core 2.1?

I have a ASP.NET Core app that has HTTPS redirection enabled. The problem is that HTTPS redirection doesn´t work on static files, so the front-end is in a different port than the back-end when I enter the site with http and this causes CORS to block all requests made to the back-end. I tried allowing CORS to all requests (for testing, of course) but no matter what I did, the requests failed. Only visiting the front end with https worked.How do I force https redirect on static files with ASP.NET Core?
Based on my expeirence, you must add this code to the startup.cs file
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpsRedirection(options =>
{
options.HttpsPort = 443;
});
You have to love Microsoft documentation. 6 paragraphs below the incomplete example they tell you about this requirement:
If no port is set:
Requests aren't redirected.
You can set port using the following techniques:
The port can be configured by setting the:
ASPNETCORE_HTTPS_PORT environment variable.
http_port host configuration key (for example, via hostsettings.json or a command
line argument).
HttpsRedirectionOptions.HttpsPort. See the preceding
This worked for me.
app.UseStaticFiles(new StaticFileOptions()
{
OnPrepareResponse = (context) =>
{
var request = context.Context.Request;
var response = context.Context.Response;
UrlRewriteUtils.RedirectIfHttp(request, response);
}
});
And here is the Utility method.
public class UrlRewriteUtils
{
public static void RedirectIfHttp(HttpRequest request, HttpResponse response)
{
string reqProtocol;
if (request.Headers.ContainsKey("X-Forwarded-Proto"))
reqProtocol = request.Headers["X-Forwarded-Proto"][0];
else if (request.IsLocal())
reqProtocol = "https";
else
reqProtocol = request.IsHttps ? "https" : "http";
if (reqProtocol.ToLower() != "https")
{
var newUrl = new StringBuilder()
.Append("https://").Append(request.Host)
.Append(request.PathBase).Append(request.Path)
.Append(request.QueryString);
response.Redirect(newUrl.ToString(), true);
}
}
}

How can I support an HTTP Proxy using Spring 5 WebClient?

I am using Spring 5 WebClient. I want to know if it is possible to configure it to use an HTTP Proxy, or if there is a way of changing it's default configuration to do so.
This is something that the underlying client library should support.
When using Reactor Netty, you can do something like:
HttpClient httpClient = HttpClient.create()
.tcpConfiguration(tcpClient ->
tcpClient.proxy(proxy -> proxy.type(ProxyProvider.Proxy.HTTP).host("myproxyhost")));
ReactorClientHttpConnector connector = new ReactorClientHttpConnector(httpClient);
WebClient client = WebClient.builder().clientConnector(connector).build();
" tcpConfiguration" is deprecated.
So used this part of code instead.
HttpClient httpClient =
HttpClient.create()
.proxy(proxy -> proxy.type(ProxyProvider.Proxy.HTTP)
.host(sasConfig.getProxyHost())
.port(Integer.parseInt(sasConfig.getProxyPort())));
ReactorClientHttpConnector connector = new ReactorClientHttpConnector(httpClient);
WebClient webClient = WebClient.builder().clientConnector(connector).build();
Sharing recent experience here
Step 1 : Define proxy environment variables
-Dhttp.proxyHost=<proxyHost>
-Dhttp.proxyPort=8080
-Dhttps.proxyHost=<proxyHost>
-Dhttps.proxyPort=8080
-Dhttps.nonProxyHosts=localhost
Configuration of proxy on webClient
#Configuration
public class WebClientConfiguration {
#Bean
public WebClient webClient() {
return WebClient.builder() //
.defaultHeader(ACCEPT, APPLICATION_JSON_VALUE) //
.clientConnector(new ReactorClientHttpConnector(httpClient())) //
.build();
}
private HttpClient httpClient() {
return HttpClient //
.create() //
.proxyWithSystemProperties();
}
}
Set the spring cloud proxy properties (In the application start)
static {
String nonProxyHosts = System.getProperty("http.nonProxyHosts");
if (nonProxyHosts != null) {
String regexProxyList = nonProxyHosts.replaceAll("\\.", "\\\\.").replaceAll("\\/", "\\\\/").replaceAll("\\*", ".\\*");
System.setProperty("spring.cloud.gateway.httpclient.proxy.non-proxy-hosts-pattern", regexProxyList);
}
String proxyHost = System.getProperty("https.proxyHost");
String proxyPort = System.getProperty("https.proxyPort");
if (proxyHost != null && proxyPort != null) {
System.setProperty("spring.cloud.gateway.httpclient.proxy.host", proxyHost);
System.setProperty("spring.cloud.gateway.httpclient.proxy.port", proxyPort);
}
}

Apache HttpClient - Default protocol

I am using Apache HttpClient to send a POST requests. How can I determine which PROTOCOL my Apache HttpClient instance is using for sending "https://" requests. I use following code block to send my POST requests.
public void sendPostURL(String url, HashMap<String, String>params, String user, String pass) {
HttpClient client = new HttpClient();
String urlContent = "";
PostMethod method = new PostMethod("https://...");
// Prepare connection information
client.getParams().setParameter("http.useragent", "MyApp");
if ( (user != null) &&(pass != null) ) {
client.getParams().setAuthenticationPreemptive(true);
client.getState().setCredentials(AuthScope.ANY, (new UsernamePasswordCredentials(user, pass)));
}
// Prepare parameters
for (Map.Entry<String, String> entry : params.entrySet()) {
method.addParameter(entry.getKey(), ((entry.getValue() != null) ? entry.getValue().toString() : ""));
}
try{
// HTTP execution
int returnCode = client.executeMethod(method);
} catch (Exception e) {
// Exception
e.printStackTrace();
} finally {
method.releaseConnection();
}
}
Please guide me on how can I get the PROTOCOL that HttpClient is using to send the request. Also how can I override the PROTOCOL used. Hoping for a solution. Thanks in advance.
The protocol is HTTPS, is it not ?

Unable to tunnel through proxy. Proxy returns "HTTP/1.1 407" via https

I try to connect to a server via https that requires authentication.Moreover, I have an http proxy in the middle that also requires authentication. I use ProxyAuthSecurityHandler to authenticate with the proxy and BasicAuthSecurityHandler to authenticate with the server.
Receiving java.io.IOException: Unable to tunnel through proxy.
Proxy returns "HTTP/1.1 407 Proxy Auth Required"
at sun.net.www.protocol.http.HttpURLConnection.doTunneling(HttpURLConnection.java:1525)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect (AbstractDelegateHttpsURLConnection.java:164)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:133)
at org.apache.wink.client.internal.handlers.HttpURLConnectionHandler.processRequest(HttpURLConnectionHandler.java:97)
I noticed that the implementation of ProxyAuthSecurityHandler is expecting response code 407 however, during debug we never get to the second part due to the IOException thrown.
Code snap:
ClientConfig configuration = new ClientConfig();
configuration.connectTimeout(timeout);
MyBasicAuthenticationSecurityHandler basicAuthProps = new MyBasicAuthenticationSecurityHandler();
basicAuthProps.setUserName(user);
basicAuthProps.setPassword(password);
configuration.handlers(basicAuthProps);
if ("true".equals(System.getProperty("setProxy"))) {
configuration.proxyHost(proxyHost);
if ((proxyPort != null) && !proxyPort.equals("")) {
configuration.proxyPort(Integer.parseInt(proxyPort));
}
MyProxyAuthSecurityHandler proxyAuthSecHandler =
new MyProxyAuthSecurityHandler();
proxyAuthSecHandler.setUserName(proxyUser);
proxyAuthSecHandler.setPassword(proxyPass);
configuration.handlers(proxyAuthSecHandler);
}
restClient = new RestClient(configuration);
// create the createResourceWithSessionCookies instance to interact with
Resource resource = getResource(loginUrl);
// Request body is empty
ClientResponse response = resource.post(null);
Tried using wink client versions 1.1.2 and also 1.2.1. the issue repeats in both.
What I found out is that when trying to pass through a proxy using https url we first send CONNECT and only then try to send the request. The proxy server cannot read any headrs we attach to the request, cause it doesn't have the key to decrypt the traffic.
This means that the CONNECT should already have the user/pass to the proxy to pass this stage.
here is a code snap I used - that works for me:
import sun.misc.BASE64Encoder;
import java.io.*;
import java.net.*;
public class ProxyPass {
public ProxyPass(String proxyHost, int proxyPort, final String userid, final String password, String url) {
try {
/* Create a HttpURLConnection Object and set the properties */
URL u = new URL(url);
Proxy proxy =
new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));
HttpURLConnection uc = (HttpURLConnection)u.openConnection(proxy);
Authenticator.setDefault(new Authenticator() {
#Override
protected PasswordAuthentication getPasswordAuthentication() {
if (getRequestorType().equals(RequestorType.PROXY)) {
return new PasswordAuthentication(userid, password.toCharArray());
}
return super.getPasswordAuthentication();
}
});
uc.connect();
/* Print the content of the url to the console. */
showContent(uc);
} catch (IOException e) {
e.printStackTrace();
}
}
private void showContent(HttpURLConnection uc) throws IOException {
InputStream i = uc.getInputStream();
char c;
InputStreamReader isr = new InputStreamReader(i);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}
public static void main(String[] args) {
String proxyhost = "proxy host";
int proxyport = port;
String proxylogin = "proxy username";
String proxypass = "proxy password";
String url = "https://....";
new ProxyPass(proxyhost, proxyport, proxylogin, proxypass, url);
}
}
if you are using wink - like I do, you need to set the proxy in the ClientConfig and before passing it to the RestClient set the default authenticator.
ClientConfig configuration = new ClientConfig();
configuration.connectTimeout(timeout);
BasicAuthenticationSecurityHandler basicAuthProps = new BasicAuthenticationSecurityHandler();
basicAuthProps.setUserName(user);
basicAuthProps.setPassword(password);
configuration.handlers(basicAuthProps);
if (proxySet()) {
configuration.proxyHost(proxyHost);
if ((proxyPort != null) && !proxyPort.equals("")) {
configuration.proxyPort(Integer.parseInt(proxyPort));
}
Authenticator.setDefault(new Authenticator() {
#Override
protected PasswordAuthentication getPasswordAuthentication() {
if (getRequestorType().equals(RequestorType.PROXY)) {
return new PasswordAuthentication(proxyUser), proxyPass.toCharArray());
}
return super.getPasswordAuthentication();
}
});
}
restClient = new RestClient(configuration);
Resource resource = getResource(loginUrl);
// Request body is empty
ClientResponse response = resource.post(null);
if (response.getStatusCode() != Response.Status.OK.getStatusCode()) {
throw new RestClientException("Authentication failed for user " + user);
}
If Ilana Platonov's answer doesn't work, try editing the variables :
jdk.http.auth.tunneling.disabledSchemes
jdk.http.auth.proxying.disabledSchemes