-
Notifications
You must be signed in to change notification settings - Fork 788
/
HttpsRedirect.scala
66 lines (59 loc) · 2.44 KB
/
HttpsRedirect.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/*
* Copyright 2014 http4s.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.http4s
package server
package middleware
import cats.Applicative
import cats.Monad
import cats.data.Kleisli
import cats.data.NonEmptyList
import org.http4s.Status.MovedPermanently
import org.http4s.Uri.Authority
import org.http4s.Uri.RegName
import org.http4s.Uri.Scheme
import org.http4s.headers.Host
import org.http4s.headers.Location
import org.http4s.headers.`Content-Type`
import org.http4s.syntax.header._
import org.typelevel.ci._
/** [[Middleware]] to redirect http traffic to https.
* Inspects `X-Forwarded-Proto` header and if it is set to `http`,
* redirects to `Host` with same URL with https schema; otherwise does nothing.
* This middleware is useful when a service is deployed behind a load balancer
* which does not support such redirect feature, e.g. Heroku.
*/
object HttpsRedirect {
private[HttpsRedirect] val logger = Platform.loggerFactory.getLogger
def apply[F[_], G[_]](http: Http[F, G])(implicit F: Applicative[F]): Http[F, G] =
Kleisli { req =>
(req.headers.get(ci"X-Forwarded-Proto"), req.headers.get[Host]) match {
case (Some(NonEmptyList(proto, _)), Some(host))
if Scheme.fromString(proto.value).contains(Scheme.http) =>
logger.debug(s"Redirecting ${req.method} ${req.uri} to https on $host").unsafeRunSync()
val authority = Authority(host = RegName(host.value))
val location = req.uri.copy(scheme = Some(Scheme.https), authority = Some(authority))
val headers = Headers(Location(location), `Content-Type`(MediaType.text.xml))
val response = Response[G](status = MovedPermanently, headers = headers)
F.pure(response)
case _ =>
http(req)
}
}
def httpRoutes[F[_]: Monad](httpRoutes: HttpRoutes[F]): HttpRoutes[F] =
apply(httpRoutes)
def httpApp[F[_]: Applicative](httpApp: HttpApp[F]): HttpApp[F] =
apply(httpApp)
}