Redirect to https with hunchentoot - common-lisp

I've set up a hunchentoot server with ssl. I'd like regular http requests to be redirected to https.
It seems like some combination of hunchentoot:define-easy-handler and hunchentoot:redirect is the way to go, but I can't figure it out.
Here's what I have so far:
(defvar *https-handler*
(make-instance 'hunchentoot:easy-ssl-acceptor
:name 'ssl
:ssl-privatekey-file #P"/path/to/privkey.pem"
:ssl-certificate-file #P"/path/to/cert.pem"
:port 443))
(hunchentoot:start *https-handler*)

Yes, you can add simple http handler with redirect to ssl version:
(defvar *http-handler*
(make-instance 'hunchentoot:easy-acceptor
:name 'http
:port 80))
(hunchentoot:define-easy-handler (redir-to-ssl :uri (lambda (uri) t) :acceptor-names '(http)) ()
(hunchentoot:redirect "/" :protocol :https)) ; where magic happens
...and then start it too:
(hunchentoot:start *http-handler*)
This version redirects simply to index /.

Well, I am using the hunchentoot:*dispatch-table* directly. The way to redirect it independent of the path I discovered was to hunchentoot:redirect unless (hunchentoot:ssl-p) inside the handler. Most of my defuned handlers are wrapped inside a macro for authenthentication. So, I merely had to modify that macro, and then M-x slime-who-macroexpands -> C-c C-k.
(unless (hunchentoot:ssl-p)
(hunchentoot:redirect (hunchentoot:request-uri*)
:protocol :https))

If you need to indiscriminately redirect every HTTP request to HTTPS, using easy-acceptor is unnecessary. I'd suggest to define a specialized acceptor:
(defclass http-to-https-acceptor (hunchentoot:acceptor) ())
(defmethod hunchentoot:acceptor-dispatch-request ((acceptor http-to-https-acceptor) request)
(hunchentoot:redirect (hunchentoot:request-uri request)
:protocol :https))
Then at some point:
(hunchentoot:start (make-instance 'http-to-https-acceptor :port 80))

Related

How to set 'no proxy' in clojure http request like we do in curl?

I want to hit an api (GET request) in clojure inside open shift pod with "no-proxy" but it gives me 404 (resource not found) although I passed correct header (for authorisation inside open shift pod). Same request giving 200 by curl command with noproxy in pod terminal. Please suggest.
Using Clojure API - clj-http.client
Below is Clojure code which is not working (giving 404):
(defn get-toggles []
(let [options {:headers {:x-jwt-payload "valid-value-passed"}}
response (client/get "correct-url" {:proxy-ignore-hosts "*" :proxy-port "*"} options)]))
Below is Curl hit inside pod terminal that is working fine (with noproxy only):curl -i --noproxy "*" "any-correct-url" -H "x-jwt-payload :valid-value"
Note: without "noproxy", we will get 401 thats why we are using it.
Looking at the source code, there seems no way to create a request that specifically uses no proxy at all.
I once ran into the same problem and ended up using Jsoup, a java library for making http requests and parsing HTML. I did not use the HTML parsing function, I sent requests to a Rest Api, but the Api of Jsoup is nice and well usable from Clojure. For example:
(ns something
(import
org.jsoup.Jsoup
java.net.Proxy
java.lang.Thread))
(defn api-call [url]
(->
(Jsoup/connect url)
(.proxy (Proxy/NO_PROXY))
(.ignoreContentType true)
(.get)
(.body)
(.text)))

How to debug a HANDSHAKE_FAILURE when requesting an HTTPS server with ocaml-cohttp Client.post method ? (And why am I getting this error ?)

I wrote the following OCaml code in order to make POST requests to an https server.
open Lwt
open Cohttp
open Cohttp_lwt_unix
try
let headers = Header.init ()
|> fun h -> Header.add h "content-type" "application/json" in
let body = Client.post
~headers
~body:(body_of_credentials nickname secret)
(uri_of_project project)
>>= fun (response, body) ->
let code = response |> Response.status |> Code.code_of_status in
Printf.printf "Response code: %d\n" code;
Printf.printf "Headers: %s\n" (response |> Response.headers |> Header.to_string);
body |> Cohttp_lwt.Body.to_string >|= fun body ->
Printf.printf "Body of length: %d\n" (String.length body);
body
in
let body = Lwt_main.run body in
print_endline ("Received body\n" ^ body)
with
| Tls_lwt.Tls_alert e ->
print_endline (Tls.Packet.alert_type_to_string e);
exit 1
But when executing it with CONDUIT_TLS=native CONDUIT_DEBUG=true COHTTP_DEBUG=true I get the following response :
Selected TLS library: Native
Resolver system: https://my_server/auth/tokens/ ((name https) (port 443) (tls true)) -> (TCP ((V4 xx.xxx.xx.xxx) 443))
HANDSHAKE_FAILURE
I've read all the google results (documentation, ocaml/cohttp/ocaml-tls lists and stack overflow questions) I could find about it, but nothing helps, so I would like to start from scratch here.
How can I get more details about this failure ?
In case it helps, I'm using the following opam configuration:
"lwt" {>= "4.1.0"}
"cohttp" {>= "1.1.1"}
"cohttp-lwt"
"cohttp-lwt-unix" {>= "1.1.1"}
"tls" {>= "0.9.2"}
EDIT:
As suggested by #ivg, I tried with CONDUIT_TLS=openssl but then I get the following error:
Selected TLS library: OpenSSL
Resolver system: https://my_server/auth/tokens/ ((name https) (port 443) (tls true)) -> (TCP ((V4 xx.xxx.xx.xxx) 443))
...: internal error, uncaught exception:
SSL: Method error
EDIT²:
As suggested in the following discussion: github.com/savonet/ocaml-ssl/issues/40 I added an opam pin to ssl-0.5.5: opam pin add ssl 0.5.5 in order to fix this error. Now I am able to post requests to my https server, but not with the pure ocaml implementation of tls.
You're getting this alert because the handshaking process (authentication) had failed. This alert means, that the peer is not authenticated (either the server or the client) and thus a secure connection could not be established.
To debug the issue, I would suggest first to ensure that everything works fine with conventional tools, e.g., openssl, wget, or curl.
If you're sure that your configuration is fine and this is a problem on the ocaml-tls part I would suggest to use the low-level interface of the ocaml-tls library. Apparently, conduit doesn't expose or use any of the tracing capabilities of ocaml-tls, so there is no other choice.
The alert type is projected from two more rich fatal and error types that bear much more information about the nature of the problem, cf. the following code that creates an alert and consider possible input values that lead to the HANDSHAKE_FAILURE alert.
To access the particular error or alert I would suggest you to use the Tracing capabilities of ocaml-tls. There are examples in the source repository that already enable tracing and should provide sufficient information. I hope those examples will fit into your use case.

easy-acceptor ignoring :document-root option; not serving static files

I am trying to serve static files with Hunchentoot, from the www directory inside my project. My acceptor is defined as:
(defvar *acceptor* (make-instance 'easy-acceptor
:port 4242
:document-root (truename "./www/")))
I then start it with:
(start *acceptor*)
The acceptor works, in that I can use define-easy-handler to create a root page:
(define-easy-handler (index :uri "/") ()
(with-html-output-to-string (_)
(:html
(:head
(:title "Hello world")
(:body
(:h1 "Hello world!))))))
... and when I browse to http://localhost:4242/ I see that page.
But no static files are served from my www directory. E.g. if I create www/jquery-3.2.1.min.js and browse to http://localhost:4242/jquery-3.2.1.min.js I receive a 404.
127.0.0.1 - [2017-08-11 08:08:02] "GET /jquery-3.2.1.min.js HTTP/1.1" 404 355 "-" "Mozilla/5.0 (X11; FreeBSD amd64; rv:54.0) Gecko/20100101 Firefox/54.0"
HELLOWORLD> (directory (make-pathname :directory '(:relative "www") :name :wild :type "js"))
(#P"/usr/home/duncan/code/helloworld/www/jquery-3.2.1.min.js")
You must ensure that the directory and the files in it have proper permissions set. The directory needs to have the execute x permission to allow the server program to access the contents of the directory, and files need at least the read r permission.

Endpoint defined with define-easy-handler returns 404

I have defined a simple system in helloworld.asd:
(asdf:defsystem #:helloworld
:description "helloworld"
:author "Duncan Bayne <duncan#bayne.id.au>"
:license "WTFNMF"
:depends-on (#:hunchentoot)
:serial t
:components ((:file "package")
(:file "helloworld")))
... a package definition in package.lisp:
(defpackage #:helloworld
(:use #:cl #:hunchentoot))
... and a corresponding hello world webserver in helloworld.lisp:
(in-package #:helloworld)
(defvar *acceptor* (make-instance 'acceptor :port 4242))
(start *acceptor*)
(define-easy-handler (greet :uri "/hello") ()
"<html><body><h1>Hello World!</h1></body></html>")
In the SLIME REPL I start the web server with:
CL-USER> (load "/usr/home/duncan/code/helloworld/helloworld.asd")
CL-USER> (ql:quickload "helloworld")
If I navigate to http://localhost:4242/hello, I'd expect to see my hello world HTML. Instead I get a 404 error, and the log shows:
127.0.0.1 - [2017-08-10 08:18:19] "GET /hello HTTP/1.1" 404 341 "-" "Mozilla/5.0 (X11; FreeBSD amd64; rv:54.0) Gecko/20100101 Firefox/54.0"
I suspect I'm missing something fairly obvious here; any tips / pointers to documentation would be appreciated. System details are:
Clozure Common Lisp Version 1.11 (FreebsdX8664)
FreeBSD 11.1-RELEASE amd64
Hunchentoot 1.2.37
Mozilla Firefox 54.0.1
SLIME 20170804.1113
You are making an instance of ACCEPTOR instead of EASY-ACCEPTOR (or a subclass). The easy handler is registered but your acceptor is not going to use it. This should work, for example:
(defvar *acceptor* (make-instance 'easy-acceptor :port 4242))
(start *acceptor*)
(define-easy-handler (test :uri "/test") () "Pass")

Sending HTTP POST in Racket

I am trying to send a string via http/post in Racket, this is what I tried so far after reading the Racket HTTP Client Documentation
#lang racket
(require net/http-client)
(define
myUrl "https://something.com")
(http-conn-send!
(http-conn-open
myUrl
#:ssl? #t)
#:version "1.1"
#:method "POST"
#:data "Hello")
But with this I receive the following error:
tcp-connect: connection failed
detail: host not found
address: https://www.w3.org/
port number: 443
step: 1
system error: nodename nor servname provided, or not known; errno=8
I tried it with several different adresses.
I am new to racket and programming in general and unable to figure out what I am missing.
In your example, the hostname is only the www.w3.org portion -- not including the scheme (http or https) nor any path. So for example this does work:
(http-conn-open "www.w3.com"
#:ssl? #t)
To make a post request, you could do this:
#lang racket
(require net/http-client)
(define-values (status headers in)
(http-sendrecv "www.w3.com"
"/"
#:ssl? #t
#:version "1.1"
#:method "POST"
#:data "Hello"))
(displayln status)
(displayln headers)
(displayln (port->string in))
(close-input-port in)
In Racket, a function can return multiple values. http-sendrecv returns three, and the define-values assigns each one to a variable.
net/http-client provides other functions to let you make a connection to a host, make multiple requests on that connection, then close the connection.

Resources