
I’m trying to get the scheme and host of the current web request. When I inspect (request-uri req)
I get the following: (struct:url #f #f #f #f #t (#(struct:path/param user ()) #(struct:path/param 45ee5a16a2 ()) #(struct:path/param view ()) #(struct:path/param 1 ())) () #f)
I suspect this is because I’m redirecting using relative URLs. What’s the easiest way to get the scheme and host of the current request? Use the headers?

Hmm.. I see a host
header, but there is no scheme info in the request
struct.

No, it wasn’t due to the relative URL redirect, the info is missing when I use an absolute URL also.

Any pointers @jeapostrophe?

What’s the context - i.e. where is the value in req
coming from?


More specifically: (define (axio-app-init environment route)
(let* ([ instance-id (string->number (vector-ref (current-command-line-arguments) 0)) ]
[ env (get-app-env environment) ]
[ port (+ (app-env-port-base env) instance-id) ]
[ axio-context (axio-init environment) ])
(void
(serve
#:dispatch (seq:make (log:make #:format log:extended-format
#:log-path (format "~a.log" (symbol->string environment)))
(lift:make (λ (request)
(front-controller axio-context request route))))
#:port port))
(do-not-return)))

(require (prefix-in lift: web-server/dispatchers/dispatch-lift)
(prefix-in log: web-server/dispatchers/dispatch-log)
(prefix-in seq: web-server/dispatchers/dispatch-sequencer)
web-server/web-server)

It’s just odd, that the scheme and host is missing. Could it be the first dispatcher that is removing it?

Probably not.

Yeah, that would be quite odd.

I wonder if it’s due to my nginx proxy config.

That’s probably it. I think, I have the same problem with a racket web-server behind nginx.

No, I just checked. I went directly to the Racket web server (bypassing nginx), and the struct fields are still #f
:(

I can workaround with (define scheme-host "<https://foo.bar.com>")
, but that’s hacky

I was about to say, that your first suggestion (looking at the headers) sounds as the right thing. According to https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/ nginx passes extra information in headers:
> By default, NGINX redefines two header fields in proxied requests, “Host” and “Connection”, and eliminates the header fields whose values are empty strings. “Host” is set to the $proxy_host
variable, and “Connection” is set to close
. But … if it doesn’t work when bypassing nginx, I have no clue about what’s going on.

I’ll try and dig into the web server code later. First I need to meet a deadline :)

I don’t think there is anywhere in the HTTP protocol where the scheme is set. In other web frameworks (in other languages) I’ve used either the server passes that knowledge back to the application code (because it got a request via https vs. http) or it needs to be hardwired in.

Maybe if you are reverse proxying or through a loadbalancer there can be X-Forwarded-For
header set.

X-Forwarded-For
only captures the IP. In AWS (maybe others) there is also set X-Forwarded-Proto
and X-Forwarded-Port


Maybe those fields in the URI struct are only set when creating a request vs. receiving one in the web server.

Quite possibly. I figured they were reused because why have more than one URI type in the language.

It’s grabbed by using string->url on the URL component of the HTTP request, which does not include the host or the scheme. You can grab the host from either the Host header (if a direct connection), or X-Forwarded-Host (if reverse proxied). The usual snippet used for proxy headers (on nginx) is something like this: proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
This also sets X-Forwarded-Proto to be the request’s scheme. If connecting directly to the racket server you’d need to compare request-host-port
with which is the http and which is the https port