Skip to content

Commit

Permalink
Add proxy functionality. Re-enable local conf.
Browse files Browse the repository at this point in the history
  • Loading branch information
rpiaggio committed Dec 19, 2023
1 parent 0cb8679 commit 6a739fd
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 23 deletions.
2 changes: 2 additions & 0 deletions app/navigate-server/src/main/resources/conf/app.conf
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ web-server {
insecure-port = 7071
# External url used for redirects
external-base-url = "navigate.hi.gemini.edu"
# Uri to forward requests made to /proxy to.
proxy-base-uri = "https://localhost:8080"
}

# Configuration of the navigate engine
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package navigate.model.config

import cats.Eq
import org.http4s.Uri

import java.nio.file.Path

Expand Down Expand Up @@ -43,7 +44,8 @@ case class WebServerConfiguration(
host: String,
port: Int,
insecurePort: Int,
externalBaseUrl: String
externalBaseUrl: String,
proxyBaseUri: Uri
) {
// FIXME Pureconfig can't load this anymore
val tls: Option[TLSConfig] = None
Expand Down
2 changes: 2 additions & 0 deletions modules/web/server/src/main/resources/app.conf
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ web-server {
insecure-port = 7071
# External url used for redirects
external-base-url = "localhost"
# Uri to forward requests made to /proxy to.
proxy-base-uri = "https://localhost:8080"
}

# Configuration of the navigate engine
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (c) 2016-2023 Association of Universities for Research in Astronomy, Inc. (AURA)
// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause

package navigate.web.server.http4s

import cats.data.OptionT
import cats.effect.Async
import cats.effect.kernel.Resource
import cats.syntax.all.*
import fs2.io.net.Network
import org.http4s.HttpRoutes
import org.http4s.Uri
import org.http4s.ember.client.EmberClientBuilder
import org.http4s.headers._

object ProxyBuilder:
def buildService[F[_]: Async: Network](
baseUri: Uri,
localPath: Uri.Path
): Resource[F, HttpRoutes[F]] =
val remoteBaseHost: String = baseUri.host.map(_.toString).orEmpty
val localPathElements: Int = localPath.segments.length

EmberClientBuilder
.default[F]
.build
.map(
_.toHttpApp
.mapK(OptionT.liftK) // Turns HttpApp into HttpRoutes
.local: req =>
req
.withUri(
// Drop the local path (eg: "/proxy")
baseUri.resolve(req.uri.withPath(req.uri.path.splitAt(localPathElements)._2))
)
.putHeaders(Host(remoteBaseHost))
)
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package navigate.web.server.http4s
import cats.Parallel
import cats.effect.*
import cats.effect.std.Dispatcher
import cats.effect.syntax.all.*
import cats.syntax.all.*
import ch.qos.logback.classic.spi.ILoggingEvent
import ch.qos.logback.core.Appender
Expand All @@ -14,6 +15,7 @@ import fs2.Stream
import fs2.compression.Compression
import fs2.concurrent.Topic
import fs2.io.file.Files
import fs2.io.net.Network
import natchez.Trace.Implicits.noop
import navigate.model.NavigateEvent
import navigate.model.config.*
Expand All @@ -30,6 +32,7 @@ import navigate.web.server.config.*
import navigate.web.server.logging.SubscriptionAppender
import navigate.web.server.security.AuthenticationService
import org.http4s.HttpRoutes
import org.http4s.Uri
import org.http4s.blaze.server.BlazeServerBuilder
import org.http4s.client.Client
import org.http4s.ember.client.EmberClientBuilder
Expand All @@ -53,15 +56,22 @@ import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManagerFactory
import scala.concurrent.duration.*

object WebServerLauncher extends IOApp with LogInitialization {
object WebServerLauncher extends IOApp with LogInitialization:
private val ProxyRoute: Uri.Path = Uri.Path.empty / "db"

private given Logger[IO] = Slf4jLogger.getLoggerFromName[IO]("navigate")

// Attempt to get the configuration file relative to the base dir
def configurationFile[F[_]: Sync]: F[FilePath] =
baseDir[F].map(_.resolve("conf").resolve("app.conf"))

def config[F[_]: Sync]: F[ConfigObjectSource] =
configurationFile.map(ConfigSource.file)
val defaultConfig = ConfigSource.resources("app.conf").pure[F]
val fileConfig = configurationFile.map(ConfigSource.file)

// ConfigSource, first attempt the file or default to the classpath/resources file
(fileConfig, defaultConfig).mapN: (file, default) =>
file.optional.withFallback(default.optional)

/** Configures the Authentication service */
def authService[F[_]: Sync: Logger](
Expand Down Expand Up @@ -103,39 +113,46 @@ object WebServerLauncher extends IOApp with LogInitialization {
}

/** Resource that yields the running web server */
def webServer[F[_]: Logger: Async: Dns: Files: Compression](
def webServer[F[_]: Logger: Async: Dns: Files: Compression: Network](
conf: NavigateConfiguration,
as: AuthenticationService[F],
outputs: Topic[F, NavigateEvent],
logTopic: Topic[F, ILoggingEvent],
se: NavigateEngine[F],
clientsDb: ClientsSetDb[F]
): Resource[F, Server] = {

val ssl: F[Option[SSLContext]] = conf.webServer.tls.map(makeContext[F]).sequence

def build(all: WebSocketBuilder2[F] => HttpRoutes[F]): Resource[F, Server] = Resource.eval {
val builder =
BlazeServerBuilder[F]
.bindHttp(conf.webServer.port, conf.webServer.host)
.withHttpWebSocketApp(wsb => all(wsb).orNotFound)
ssl.map(_.fold(builder)(builder.withSslContext)).map(_.resource)
}.flatten

def router(wsBuilder: WebSocketBuilder2[F]) = Router[F](
"/" -> new StaticRoutes().service,
"/navigate" -> new GraphQlRoutes(se, logTopic).service(wsBuilder)
// val proxyService: HttpRoutes[F] =

def router(wsBuilder: WebSocketBuilder2[F], proxyService: HttpRoutes[F]) = Router[F](
"/" -> new StaticRoutes().service,
"/navigate" -> new GraphQlRoutes(se, logTopic).service(wsBuilder),
ProxyRoute.toString -> proxyService
)

val pingRouter = Router[F](
"/ping" -> new PingRoutes(as).service
)

def loggedRoutes(wsBuilder: WebSocketBuilder2[F]) =
pingRouter <+> Http4sLogger.httpRoutes(logHeaders = false, logBody = false)(router(wsBuilder))

build(loggedRoutes)

def loggedRoutes(wsBuilder: WebSocketBuilder2[F], proxyService: HttpRoutes[F]) =
pingRouter <+>
Http4sLogger.httpRoutes(logHeaders = false, logBody = false)(
router(wsBuilder, proxyService)
)

def builder(proxyService: HttpRoutes[F]) =
BlazeServerBuilder[F]
.bindHttp(conf.webServer.port, conf.webServer.host)
.withHttpWebSocketApp(wsb => loggedRoutes(wsb, proxyService).orNotFound)

for
proxyService <- ProxyBuilder.buildService[F](conf.webServer.proxyBaseUri, ProxyRoute)
server <- ssl
.map(_.fold(builder(proxyService))(builder(proxyService).withSslContext).resource)
.toResource
.flatten
yield server
}

def redirectWebServer[F[_]: Async](conf: WebServerConfiguration): Resource[F, Server] = {
Expand Down Expand Up @@ -284,5 +301,3 @@ object WebServerLauncher extends IOApp with LogInitialization {
if (oc.isSuccess) IO.unit
else IO(Console.println(s"Exit code $oc")) // scalastyle:off console.io
}

}

0 comments on commit 6a739fd

Please sign in to comment.