Full Code of gvolpe/advanced-http4s for AI

master f6bee1e016cb cached
30 files
36.3 KB
10.7k tokens
1 requests
Download .txt
Repository: gvolpe/advanced-http4s
Branch: master
Commit: f6bee1e016cb
Files: 30
Total size: 36.3 KB

Directory structure:
gitextract_r37kbv80/

├── .gitignore
├── README.md
├── build.sbt
├── project/
│   ├── Dependencies.scala
│   └── build.properties
└── src/
    ├── main/
    │   └── scala/
    │       └── com/
    │           └── github/
    │               └── gvolpe/
    │                   ├── fs2/
    │                   │   ├── Counter.scala
    │                   │   ├── Fifo.scala
    │                   │   ├── Once.scala
    │                   │   ├── PubSub.scala
    │                   │   ├── Resources.scala
    │                   │   └── package.scala
    │                   └── http4s/
    │                       ├── StreamUtils.scala
    │                       ├── client/
    │                       │   ├── MultipartClient.scala
    │                       │   └── StreamClient.scala
    │                       └── server/
    │                           ├── Module.scala
    │                           ├── Server.scala
    │                           ├── endpoints/
    │                           │   ├── FileHttpEndpoint.scala
    │                           │   ├── HexNameHttpEndpoint.scala
    │                           │   ├── JsonXmlHttpEndpoint.scala
    │                           │   ├── MultipartHttpEndpoint.scala
    │                           │   ├── TimeoutHttpEndpoint.scala
    │                           │   ├── auth/
    │                           │   │   ├── AuthRepository.scala
    │                           │   │   ├── BasicAuthHttpEndpoint.scala
    │                           │   │   └── GitHubHttpEndpoint.scala
    │                           │   └── package.scala
    │                           └── service/
    │                               ├── FileService.scala
    │                               └── GitHubService.scala
    └── test/
        └── scala/
            └── com/
                └── github/
                    └── gvolpe/
                        └── http4s/
                            ├── IOAssertion.scala
                            └── server/
                                └── endpoints/
                                    ├── HexNameHttpEndpointSpec.scala
                                    └── JsonXmlHttpEndpointSpec.scala

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
ml
*.ipr
*.iws
*.pyc
*.tm.epoch
*.vim
*/project/boot
*/project/build/target
*/project/project.target.config-classes
*-shim.sbt
*~
.#*
.*.swp
.DS_Store
.cache
.cache
.classpath
.codefellow
.ensime*
.eprj
.history
.idea
.manager
.multi-jvm
.project
.scala_dependencies
.scalastyle
.settings
.tags
.tags_sorted_by_file
.target
.worksheet
Makefile
TAGS
lib_managed
logs
project/boot/*
project/plugins/project
src_managed
target
tm*.lck
tm*.log
tm.out
worker*.log
/bin
t
tags
tq:q
TODO


================================================
FILE: README.md
================================================
advanced-http4s
===============

Code samples of advanced features of [Http4s](http://http4s.org/) in combination with some features of [Fs2](https://functional-streams-for-scala.github.io/fs2/) not often seen.

Streaming end to end
--------------------

- **Server**: Streaming responses end to end, from the `FileService` reading all the directories in your `$HOME` directory to the `FileHttpEndpoint`.
- **StreamClient**: Parsing chunks of the response body produced by the server in a streaming fashion way.

> You'll need two sbt sessions. Run the server in one and after the client in the other to try it out.

Middleware Composition
----------------------

- **GZip**: For compressed responses. Client must set the `Accept Encoding` header to `gzip`.
- **AutoSlash**: To make endpoints work with and without the slash `/` at the end.

> Response compression is verified by `HexNameHttpEndpointSpec`. You can also try it out on Postman or similar.

- **Timeout**: Handling response timeouts with the given middleware.

> The `TimeoutHttpEndpoint` generates a response in a random time to demonstrate the use.

- **NonStreamResponse**: Using the `ChunkAggregator` middleware to wrap the streaming `FileHttpEndpoint` and remove the Chunked Transfer Encoding.

> The endpoint `/v1/nonstream/dirs?depth=3` demonstrates the use case.

Media Type negotiation
----------------------

- **XML** and **Json**: Decoding request body with either of these types for the same endpoint.

> The `JsonXmlHttpEndpoint` demonstrates this use case and it's validated in its spec.

Multipart Form Data
-------------------

- **Server**: The `MultipartHttpEndpoint` is responsible for parsing multipart data with the given multipart decoder.
- **MultipartClient**: Example uploading both text and an image.

> Similar to the streaming example, you'll need to run both Server and MultipartClient to see how it works.

*NOTE: Beware of the creation of `rick.jpg` file in your HOME directory!*

Authentication
--------------

- **Basic Auth**: Using the given middleware as demonstrated by the `BasicAuthHttpEndpoint`.
- **OAuth 2**: Using GitHub as demonstrated by the `GitHubHttpEndpoint`.

-----------------------------------------------------------------------------

fs2 examples
============

In the fs2 package you'll find some practical examples of the few things it's possible to build with this powerful streaming library. This might serve as a starting point, your creativity will do the rest.

fs2.async package
------------------

Apart from the use of the three core types `Stream[F, O]`, `Pipe[F, I, O]` and `Sink[F, I]` you'll find examples of use of the following types:

- `Topic[F, A]`
- `Signal[F, A]`
- `Queue[F, A]`
- `Ref[F, A]`
- `Promise[F, A]`
- `Semaphore[F]`

In addition to the use of some other functions useful in Parallel and Concurrent scenarios.



================================================
FILE: build.sbt
================================================
import Dependencies._

lazy val root = (project in file(".")).
  settings(
    inThisBuild(List(
      organization := "com.github.gvolpe",
      scalaVersion := "2.12.4",
      version      := "0.1.0-SNAPSHOT",
      scalacOptions := Seq(
        "-deprecation",
        "-encoding",
        "UTF-8",
        "-feature",
        "-language:existentials",
        "-language:higherKinds",
        "-Ypartial-unification"
      )    
    )),
    name := "Advanced Http4s",
    libraryDependencies ++= Seq(
      Libraries.catsEffect,
      Libraries.monix,
      Libraries.fs2Core,
      Libraries.http4sServer,
      Libraries.http4sClient,
      Libraries.http4sDsl,
      Libraries.http4sCirce,
      Libraries.http4sXml,
      Libraries.circeCore,
      Libraries.circeGeneric,
      Libraries.typesafeConfig,
      Libraries.logback,
      Libraries.scalaTest,
      Libraries.scalaCheck
    ),
    addCompilerPlugin("org.spire-math" % "kind-projector" % "0.9.5" cross CrossVersion.binary)
  )


================================================
FILE: project/Dependencies.scala
================================================
import sbt._

object Dependencies {

  object Versions {
    val CatsEffect  = "0.8"
    val Monix       = "3.0.0-M3"
    val Fs2         = "0.10.2"
    val Http4s      = "0.18.1"
    val Tsec        = "0.0.1-M9"
    val Circe       = "0.9.1"
    val ScalaTest   = "3.0.4"
    val ScalaCheck  = "1.13.4"
    val Logback     = "1.2.1"
    val TypesafeCfg = "1.3.1"
  }

  object Libraries {
    lazy val catsEffect     = "org.typelevel"       %% "cats-effect"                  % Versions.CatsEffect
    lazy val monix          = "io.monix"            %% "monix"                        % Versions.Monix

    lazy val fs2Core        = "co.fs2"              %% "fs2-core"                     % Versions.Fs2
    lazy val fs2IO          = "co.fs2"              %% "fs2-io"                       % Versions.Fs2

    lazy val http4sServer   = "org.http4s"          %% "http4s-blaze-server"          % Versions.Http4s
    lazy val http4sClient   = "org.http4s"          %% "http4s-blaze-client"          % Versions.Http4s
    lazy val http4sDsl      = "org.http4s"          %% "http4s-dsl"                   % Versions.Http4s
    lazy val http4sCirce    = "org.http4s"          %% "http4s-circe"                 % Versions.Http4s
    lazy val http4sXml      = "org.http4s"          %% "http4s-scala-xml"             % Versions.Http4s

    lazy val tsecJwtMac     = "io.github.jmcardon"  %% "tsec-jwt-mac"                 % Versions.Tsec

    lazy val circeCore      = "io.circe"            %% "circe-core"                   % Versions.Circe
    lazy val circeGeneric   = "io.circe"            %% "circe-generic"                % Versions.Circe

    lazy val typesafeConfig = "com.typesafe"        %  "config"                       % Versions.TypesafeCfg
    lazy val logback        = "ch.qos.logback"      %  "logback-classic"              % Versions.Logback

    lazy val scalaTest      = "org.scalatest"       %% "scalatest"                    % Versions.ScalaTest   % Test
    lazy val scalaCheck     = "org.scalacheck"      %% "scalacheck"                   % Versions.ScalaCheck  % Test
  }

}


================================================
FILE: project/build.properties
================================================
sbt.version=1.1.0


================================================
FILE: src/main/scala/com/github/gvolpe/fs2/Counter.scala
================================================
package com.github.gvolpe.fs2

import cats.effect.{Effect, IO}
import fs2.StreamApp.ExitCode
import fs2.async.Ref
import fs2.{Scheduler, Sink, Stream, StreamApp, async}

import scala.concurrent.ExecutionContext.Implicits.global

object CounterApp extends Counter[IO]

/**
  * Concurrent counter that demonstrates the use of [[fs2.async.Ref]].
  *
  * The workers will concurrently run and modify the value of the Ref so this is one
  * possible outcome showing "#worker >> currentCount":
  *
  * #1 >> 0
  * #3 >> 0
  * #2 >> 0
  * #1 >> 2
  * #2 >> 3
  * #3 >> 3
  * */
class Counter[F[_] : Effect] extends StreamApp[F] {

  override def stream(args: List[String], requestShutdown: F[Unit]): fs2.Stream[F, ExitCode] =
    Scheduler(corePoolSize = 10).flatMap { implicit S =>
      for {
        ref <- Stream.eval(async.refOf[F, Int](0))
        w1  = new Worker[F](1, ref)
        w2  = new Worker[F](2, ref)
        w3  = new Worker[F](3, ref)
        ec  <- Stream(w1.start, w2.start, w3.start).join(3).drain ++ Stream.emit(ExitCode.Success)
      } yield ec
    }

}

class Worker[F[_]](number: Int, ref: Ref[F, Int])
                  (implicit F: Effect[F]) {

  private val sink: Sink[F, Int] = _.evalMap(n => F.delay(println(s"#$number >> $n")))

  def start: Stream[F, Unit] =
    for {
      _ <- Stream.eval(ref.get) to sink
      _ <- Stream.eval(ref.modify(_ + 1))
      _ <- Stream.eval(ref.get) to sink
    } yield ()

}


================================================
FILE: src/main/scala/com/github/gvolpe/fs2/Fifo.scala
================================================
package com.github.gvolpe.fs2

import cats.effect.{Effect, IO}
import fs2.StreamApp.ExitCode
import fs2.async.mutable.Queue
import fs2.{Scheduler, Stream, StreamApp, async}

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._

object FifoApp extends Fifo[IO]

/**
  * Represents a FIFO (First IN First OUT) system built on top of two [[fs2.async.mutable.Queue]].
  *
  * q1 has a buffer size of 1 while q2 has a buffer size of 100 so you will notice the buffering when
  * pulling elements out of the q2.
  * */
class Fifo[F[_]: Effect] extends StreamApp[F] {

  override def stream(args: List[String], requestShutdown: F[Unit]): fs2.Stream[F, ExitCode] =
    Scheduler(corePoolSize = 4).flatMap { implicit S =>
      for {
        q1 <- Stream.eval(async.boundedQueue[F, Int](1))
        q2 <- Stream.eval(async.boundedQueue[F, Int](100))
        bp = new Buffering[F](q1, q2)
        ec <- S.delay(Stream.emit(ExitCode.Success).covary[F], 5.seconds) concurrently bp.start.drain
      } yield ec
    }

}

class Buffering[F[_]](q1: Queue[F, Int], q2: Queue[F, Int])(implicit F: Effect[F]) {

  def start: Stream[F, Unit] =
    Stream(
      Stream.range(0, 1000).covary[F] to q1.enqueue,
      q1.dequeue to q2.enqueue,
      //.map won't work here as you're trying to map a pure value with a side effect. Use `evalMap` instead.
      q2.dequeue.evalMap(n => F.delay(println(s"Pulling out $n from Queue #2")))
    ).join(3)

}

================================================
FILE: src/main/scala/com/github/gvolpe/fs2/Once.scala
================================================
package com.github.gvolpe.fs2

import cats.effect.{Effect, IO}
import fs2.StreamApp.ExitCode
import fs2.async.Promise
import fs2.{Scheduler, Stream, StreamApp, async}

import scala.concurrent.ExecutionContext.Implicits.global

object OnceApp extends Once[IO]

/**
  * Demonstrate the use of [[fs2.async.Promise]]
  *
  * Two processes will try to complete the promise at the same time but only one will succeed,
  * completing the promise exactly once.
  * The loser one will raise an error when trying to complete a promise already completed,
  * that's why we call `attempt` on the evaluation.
  *
  * Notice that the loser process will remain running in the background and the program will
  * end on completion of all of the inner streams.
  *
  * So it's a "race" in the sense that both processes will try to complete the promise at the
  * same time but conceptually is different from "race". So for example, if you schedule one
  * of the processes to run in 10 seconds from now, then the entire program will finish after
  * 10 seconds and you can know for sure that the process completing the promise is going to
  * be the first one.
  * */
class Once[F[_]: Effect] extends StreamApp[F] {

  override def stream(args: List[String], requestShutdown: F[Unit]): fs2.Stream[F, ExitCode] =
    Scheduler(corePoolSize = 4).flatMap { implicit scheduler =>
      for {
        p <- Stream.eval(async.promise[F, Int])
        e <- new ConcurrentCompletion[F](p).start
      } yield e
    }

}

class ConcurrentCompletion[F[_]](p: Promise[F, Int])(implicit F: Effect[F]) {

  private def attemptPromiseCompletion(n: Int): Stream[F, Unit] =
    Stream.eval(p.complete(n)).attempt.drain

  def start: Stream[F, ExitCode] =
    Stream(
      attemptPromiseCompletion(1),
      attemptPromiseCompletion(2),
      Stream.eval(p.get).evalMap(n => F.delay(println(s"Result: $n")))
    ).join(3).drain ++ Stream.emit(ExitCode.Success)

}


================================================
FILE: src/main/scala/com/github/gvolpe/fs2/PubSub.scala
================================================
package com.github.gvolpe.fs2

import cats.effect.{Effect, IO}
import fs2.StreamApp.ExitCode
import fs2.async.mutable.{Signal, Topic}
import fs2.{Scheduler, Sink, Stream, StreamApp, async}

import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global

object PubSubApp extends PubSub[IO]

/**
  * Single Publisher / Multiple Subscribers application implemented on top of
  * [[fs2.async.mutable.Topic]] and [[fs2.async.mutable.Signal]].
  *
  * The program ends after 15 seconds when the signal interrupts the publishing of more events
  * given that the final streaming merge halts on the end of its left stream (the publisher).
  *
  * - Subscriber #1 should receive 15 events + the initial empty event
  * - Subscriber #2 should receive 10 events
  * - Subscriber #3 should receive 5 events
  * */
class PubSub[F[_]: Effect] extends StreamApp[F] {

  override def stream(args: List[String], requestShutdown: F[Unit]): fs2.Stream[F, ExitCode] =
    Scheduler(corePoolSize = 4).flatMap { implicit S =>
      for {
        topic     <- Stream.eval(async.topic[F, Event](Event("")))
        signal    <- Stream.eval(async.signalOf[F, Boolean](false))
        service   = new EventService[F](topic, signal)
        exitCode  <- Stream(
                      S.delay(Stream.eval(signal.set(true)), 15.seconds),
                      service.startPublisher concurrently service.startSubscribers
                    ).join(2).drain ++ Stream.emit(ExitCode.Success)
      } yield exitCode
    }

}

class EventService[F[_]](eventsTopic: Topic[F, Event],
                         interrupter: Signal[F, Boolean])
                        (implicit F: Effect[F], S: Scheduler) {

  // Publishing events every one second until signaling interruption
  def startPublisher: Stream[F, Unit] =
    S.awakeEvery(1.second).flatMap { _ =>
      val event = Event(System.currentTimeMillis().toString)
      Stream.eval(eventsTopic.publish1(event))
    }.interruptWhen(interrupter)

  // Creating 3 subscribers in a different period of time and join them to run concurrently
  def startSubscribers: Stream[F, Unit] = {
    val s1: Stream[F, Event] = eventsTopic.subscribe(10)
    val s2: Stream[F, Event] = S.delay(eventsTopic.subscribe(10), 5.seconds)
    val s3: Stream[F, Event] = S.delay(eventsTopic.subscribe(10), 10.seconds)

    def sink(subscriberNumber: Int): Sink[F, Event] =
      _.evalMap(e => F.delay(println(s"Subscriber #$subscriberNumber processing event: $e")))

    Stream(s1 to sink(1), s2 to sink(2), s3 to sink(3)).join(3)
  }

}

================================================
FILE: src/main/scala/com/github/gvolpe/fs2/Resources.scala
================================================
package com.github.gvolpe.fs2

import cats.effect.{Effect, IO}
import cats.syntax.functor._
import fs2.StreamApp.ExitCode
import fs2.async.mutable.Semaphore
import fs2.{Scheduler, Stream, StreamApp, async}

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._

object ResourcesApp extends Resources[IO]

/**
  * It demonstrates one of the possible uses of [[fs2.async.mutable.Semaphore]]
  *
  * Three processes are trying to access a shared resource at the same time but only one at
  * a time will be granted access and the next process have to wait until the resource gets
  * available again (availability is one as indicated by the semaphore counter).
  *
  * R1, R2 & R3 will request access of the precious resource concurrently so this could be
  * one possible outcome:
  *
  * R1 >> Availability: 1
  * R2 >> Availability: 1
  * R2 >> Started | Availability: 0
  * R3 >> Availability: 0
  * --------------------------------
  * R1 >> Started | Availability: 0
  * R2 >> Done | Availability: 0
  * --------------------------------
  * R3 >> Started | Availability: 0
  * R1 >> Done | Availability: 0
  * --------------------------------
  * R3 >> Done | Availability: 1
  *
  * This means when R1 and R2 requested the availability it was one and R2 was faster in
  * getting access to the resource so it started processing. R3 was the slowest and saw
  * that there was no availability from the beginning.
  *
  * Once R2 was done R1 started processing immediately showing no availability.
  *
  * Once R1 was done R3 started processing immediately showing no availability.
  *
  * Finally, R3 was done showing an availability of one once again.
  * */
class Resources[F[_]: Effect] extends StreamApp[F] {

  override def stream(args: List[String], requestShutdown: F[Unit]): fs2.Stream[F, ExitCode] =
    Scheduler(corePoolSize = 4).flatMap { implicit scheduler =>
      for {
        s   <- Stream.eval(async.semaphore[F](1))
        r1  = new PreciousResource[F]("R1", s)
        r2  = new PreciousResource[F]("R2", s)
        r3  = new PreciousResource[F]("R3", s)
        ec  <- Stream(r1.use, r2.use, r3.use).join(3).drain ++ Stream.emit(ExitCode.Success)
      } yield ec
    }

}

class PreciousResource[F[_]: Effect](name: String, s: Semaphore[F])
                                    (implicit S: Scheduler) {

  def use: Stream[F, Unit] =
    for {
      _ <- Stream.eval(s.available.map(a => println(s"$name >> Availability: $a")))
      _ <- Stream.eval(s.decrement)
      _ <- Stream.eval(s.available.map(a => println(s"$name >> Started | Availability: $a")))
      _ <- S.sleep(3.seconds)
      _ <- Stream.eval(s.increment)
      _ <- Stream.eval(s.available.map(a => println(s"$name >> Done | Availability: $a")))
    } yield ()

}

================================================
FILE: src/main/scala/com/github/gvolpe/fs2/package.scala
================================================
package com.github.gvolpe

package object fs2 {
  case class Event(value: String) extends AnyVal
}


================================================
FILE: src/main/scala/com/github/gvolpe/http4s/StreamUtils.scala
================================================
package com.github.gvolpe.http4s

import cats.effect.Sync
import fs2.Stream

trait StreamUtils[F[_]] {
  def evalF[A](thunk: => A)(implicit F: Sync[F]): Stream[F, A] = Stream.eval(F.delay(thunk))
  def putStrLn(value: String)(implicit F: Sync[F]): Stream[F, Unit] = evalF(println(value))
  def putStr(value: String)(implicit F: Sync[F]): Stream[F, Unit] = evalF(print(value))
  def env(name: String)(implicit F: Sync[F]): Stream[F, Option[String]] = evalF(sys.env.get(name))
  def error(msg: String): Stream[F, String] = Stream.raiseError[String](new Exception(msg)).covary[F]
}

object StreamUtils {
  implicit def syncInstance[F[_]: Sync]: StreamUtils[F] = new StreamUtils[F] {}
}

================================================
FILE: src/main/scala/com/github/gvolpe/http4s/client/MultipartClient.scala
================================================
package com.github.gvolpe.http4s.client

import java.net.URL

import cats.effect.Effect
import cats.syntax.flatMap._
import cats.syntax.functor._
import com.github.gvolpe.http4s.StreamUtils
import fs2.StreamApp.ExitCode
import fs2.{Scheduler, Stream, StreamApp}
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global
import org.http4s.Method._
import org.http4s.client.blaze.Http1Client
import org.http4s.client.dsl.Http4sClientDsl
import org.http4s.headers.`Content-Type`
import org.http4s.multipart.{Multipart, Part}
import org.http4s.{MediaType, Uri}

object MultipartClient extends MultipartHttpClient[Task]

class MultipartHttpClient[F[_]](implicit F: Effect[F], S: StreamUtils[F]) extends StreamApp with Http4sClientDsl[F] {

  private val image: F[URL] = F.delay(getClass.getResource("/rick.jpg"))

  private def multipart(url: URL) = Multipart[F](
    Vector(
      Part.formData("name", "gvolpe"),
      Part.fileData("rick", url, `Content-Type`(MediaType.`image/png`))
    )
  )

  private val request =
    for {
      body <- image.map(multipart)
      req  <- POST(Uri.uri("http://localhost:8080/v1/multipart"), body)
    } yield req.replaceAllHeaders(body.headers)

  override def stream(args: List[String], requestShutdown: F[Unit]): Stream[F, ExitCode] = {
    Scheduler(corePoolSize = 2).flatMap { implicit scheduler =>
      for {
        client <- Http1Client.stream[F]()
        req    <- Stream.eval(request)
        value  <- Stream.eval(client.expect[String](req))
        _      <- S.evalF(println(value))
      } yield ()
    }.drain
  }

}


================================================
FILE: src/main/scala/com/github/gvolpe/http4s/client/StreamClient.scala
================================================
package com.github.gvolpe.http4s.client

import cats.effect.Effect
import com.github.gvolpe.http4s.StreamUtils
import fs2.StreamApp.ExitCode
import fs2.{Stream, StreamApp}
import io.circe.Json
import jawn.Facade
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global
import org.http4s.client.blaze.Http1Client
import org.http4s.{Request, Uri}

object StreamClient extends HttpClient[Task]

class HttpClient[F[_]](implicit F: Effect[F], S: StreamUtils[F]) extends StreamApp {

  implicit val jsonFacade: Facade[Json] = io.circe.jawn.CirceSupportParser.facade

  override def stream(args: List[String], requestShutdown: F[Unit]): Stream[F, ExitCode] = {
    Http1Client.stream[F]().flatMap { client =>
      val request = Request[F](uri = Uri.uri("http://localhost:8080/v1/dirs?depth=3"))
      for {
        response <- client.streaming(request)(_.body.chunks.through(fs2.text.utf8DecodeC))
        _        <- S.putStr(response)
      } yield ()
    }.drain
  }

}


================================================
FILE: src/main/scala/com/github/gvolpe/http4s/server/Module.scala
================================================
package com.github.gvolpe.http4s.server

import cats.effect.Effect
import cats.syntax.semigroupk._ // For <+>
import com.github.gvolpe.http4s.server.endpoints._
import com.github.gvolpe.http4s.server.endpoints.auth.{BasicAuthHttpEndpoint, GitHubHttpEndpoint}
import com.github.gvolpe.http4s.server.service.{FileService, GitHubService}
import fs2.Scheduler
import org.http4s.HttpService
import org.http4s.client.Client
import org.http4s.server.HttpMiddleware
import org.http4s.server.middleware.{AutoSlash, ChunkAggregator, GZip, Timeout}

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._

class Module[F[_]](client: Client[F])(implicit F: Effect[F], S: Scheduler) {

  private val fileService = new FileService[F]

  private val gitHubService = new GitHubService[F](client)

  def middleware: HttpMiddleware[F] = {
    {(service: HttpService[F]) => GZip(service)(F)} compose
      { service => AutoSlash(service)(F) }
  }

  val fileHttpEndpoint: HttpService[F] =
    new FileHttpEndpoint[F](fileService).service

  val nonStreamFileHttpEndpoint = ChunkAggregator(fileHttpEndpoint)

  private val hexNameHttpEndpoint: HttpService[F] =
    new HexNameHttpEndpoint[F].service

  private val compressedEndpoints: HttpService[F] =
    middleware(hexNameHttpEndpoint)

  private val timeoutHttpEndpoint: HttpService[F] =
    new TimeoutHttpEndpoint[F].service

  private val timeoutEndpoints: HttpService[F] =
    Timeout(1.second)(timeoutHttpEndpoint)

  private val mediaHttpEndpoint: HttpService[F] =
    new JsonXmlHttpEndpoint[F].service

  private val multipartHttpEndpoint: HttpService[F] =
    new MultipartHttpEndpoint[F](fileService).service

  private val gitHubHttpEndpoint: HttpService[F] =
    new GitHubHttpEndpoint[F](gitHubService).service

  val basicAuthHttpEndpoint: HttpService[F] =
    new BasicAuthHttpEndpoint[F].service

  // NOTE: If you mix services wrapped in `AuthMiddleware[F, ?]` the entire namespace will be protected.
  // You'll get 401 (Unauthorized) instead of 404 (Not found). Mount it separately as done in Server.
  val httpServices: HttpService[F] = (
    compressedEndpoints <+> timeoutEndpoints
    <+> mediaHttpEndpoint <+> multipartHttpEndpoint
    <+> gitHubHttpEndpoint
  )

}


================================================
FILE: src/main/scala/com/github/gvolpe/http4s/server/Server.scala
================================================
package com.github.gvolpe.http4s.server

import cats.effect.Effect
import fs2.StreamApp.ExitCode
import fs2.{Scheduler, Stream, StreamApp}
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global
import org.http4s.client.blaze.Http1Client
import org.http4s.server.blaze.BlazeBuilder

object Server extends HttpServer[Task]

class HttpServer[F[_]](implicit F: Effect[F]) extends StreamApp[F] {

  override def stream(args: List[String], requestShutdown: F[Unit]): Stream[F, ExitCode] =
    Scheduler(corePoolSize = 2).flatMap { implicit scheduler =>
      for {
        client   <- Http1Client.stream[F]()
        ctx      <- Stream(new Module[F](client))
        exitCode <- BlazeBuilder[F]
                      .bindHttp(8080, "0.0.0.0")
                      .mountService(ctx.fileHttpEndpoint, s"/${endpoints.ApiVersion}")
                      .mountService(ctx.nonStreamFileHttpEndpoint, s"/${endpoints.ApiVersion}/nonstream")
                      .mountService(ctx.httpServices)
                      .mountService(ctx.basicAuthHttpEndpoint, s"/${endpoints.ApiVersion}/protected")
                      .serve
      } yield exitCode
    }

}


================================================
FILE: src/main/scala/com/github/gvolpe/http4s/server/endpoints/FileHttpEndpoint.scala
================================================
package com.github.gvolpe.http4s.server.endpoints

import cats.Monad
import com.github.gvolpe.http4s.server.service.FileService
import org.http4s._
import org.http4s.dsl.Http4sDsl

class FileHttpEndpoint[F[_] : Monad](fileService: FileService[F]) extends Http4sDsl[F] {

  object DepthQueryParamMatcher extends OptionalQueryParamDecoderMatcher[Int]("depth")

  val service: HttpService[F] = HttpService {
    case GET -> Root / "dirs" :? DepthQueryParamMatcher(depth) =>
      Ok(fileService.homeDirectories(depth))
  }

}

================================================
FILE: src/main/scala/com/github/gvolpe/http4s/server/endpoints/HexNameHttpEndpoint.scala
================================================
package com.github.gvolpe.http4s.server.endpoints

import cats.Monad
import org.http4s._
import org.http4s.dsl.Http4sDsl

class HexNameHttpEndpoint[F[_]: Monad] extends Http4sDsl[F] {

  object NameQueryParamMatcher extends QueryParamDecoderMatcher[String]("name")

  val service: HttpService[F] = HttpService {
    case GET -> Root / ApiVersion / "hex" :? NameQueryParamMatcher(name) =>
      Ok(name.getBytes("UTF-8").map("%02x".format(_)).mkString)
  }

}


================================================
FILE: src/main/scala/com/github/gvolpe/http4s/server/endpoints/JsonXmlHttpEndpoint.scala
================================================
package com.github.gvolpe.http4s.server.endpoints

import cats.effect.Effect
import cats.syntax.flatMap._
import io.circe.generic.auto._
import org.http4s._
import org.http4s.circe._
import org.http4s.dsl.Http4sDsl

// Docs: http://http4s.org/v0.18/entity/
class JsonXmlHttpEndpoint[F[_]: Effect] extends Http4sDsl[F] {

  implicit def jsonXmlDecoder: EntityDecoder[F, Person] = jsonOf[F, Person] orElse personXmlDecoder[F]

  val service: HttpService[F] = HttpService {
    case GET -> Root / ApiVersion / "media" =>
      Ok("Send either json or xml via POST method. Eg: \n{\n  \"name\": \"gvolpe\",\n  \"age\": 30\n}\n or \n <person>\n  <name>gvolpe</name>\n  <age>30</age>\n</person>")

    case req @ POST -> Root / ApiVersion / "media" =>
      req.as[Person].flatMap { person =>
        Ok(s"Successfully decoded person: ${person.name}")
      }
  }

}


================================================
FILE: src/main/scala/com/github/gvolpe/http4s/server/endpoints/MultipartHttpEndpoint.scala
================================================
package com.github.gvolpe.http4s.server.endpoints

import cats.effect.Sync
import cats.implicits._
import com.github.gvolpe.http4s.server.service.FileService
import org.http4s._
import org.http4s.dsl.Http4sDsl
import org.http4s.multipart.Part

class MultipartHttpEndpoint[F[_]](fileService: FileService[F])
                                 (implicit F: Sync[F]) extends Http4sDsl[F] {

  val service: HttpService[F] = HttpService {
    case GET -> Root / ApiVersion / "multipart" =>
      Ok("Send a file (image, sound, etc) via POST Method")

    case req @ POST -> Root / ApiVersion / "multipart" =>
      req.decodeWith(multipart[F], strict = true) { response =>
        def filterFileTypes(part: Part[F]): Boolean = {
          part.headers.exists(_.value.contains("filename"))
        }

        val stream = response.parts.filter(filterFileTypes).traverse(fileService.store)

        Ok(stream.map(_ => s"Multipart file parsed successfully > ${response.parts}"))
      }
  }

}


================================================
FILE: src/main/scala/com/github/gvolpe/http4s/server/endpoints/TimeoutHttpEndpoint.scala
================================================
package com.github.gvolpe.http4s.server.endpoints

import java.util.concurrent.TimeUnit

import cats.effect.Async
import fs2.Scheduler
import org.http4s._
import org.http4s.dsl.Http4sDsl

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.FiniteDuration
import scala.util.Random

class TimeoutHttpEndpoint[F[_]](implicit F: Async[F], S: Scheduler) extends Http4sDsl[F] {

  val service: HttpService[F] = HttpService {
    case GET -> Root / ApiVersion / "timeout" =>
      val randomDuration = FiniteDuration(Random.nextInt(3) * 1000L, TimeUnit.MILLISECONDS)
      S.effect.delay(Ok("delayed response"), randomDuration)
  }

}


================================================
FILE: src/main/scala/com/github/gvolpe/http4s/server/endpoints/auth/AuthRepository.scala
================================================
package com.github.gvolpe.http4s.server.endpoints.auth

import cats.effect.Sync
import org.http4s.BasicCredentials

trait AuthRepository[F[_], A] {
  def persist(entity: A): F[Unit]
  def find(entity: A): F[Option[A]]
}

object AuthRepository {

  implicit def authUserRepo[F[_]](implicit F: Sync[F]): AuthRepository[F, BasicCredentials] =
    new AuthRepository[F, BasicCredentials] {
      private val storage = scala.collection.mutable.Set[BasicCredentials](
        BasicCredentials("gvolpe", "123456")
      )
      override def persist(entity: BasicCredentials): F[Unit] = F.delay(storage.add(entity))
      override def find(entity: BasicCredentials): F[Option[BasicCredentials]] = F.delay(storage.find(_ == entity))
    }

}


================================================
FILE: src/main/scala/com/github/gvolpe/http4s/server/endpoints/auth/BasicAuthHttpEndpoint.scala
================================================
package com.github.gvolpe.http4s.server.endpoints.auth

import cats.effect.Sync
import com.github.gvolpe.http4s.server.endpoints.ApiVersion
import org.http4s._
import org.http4s.dsl.Http4sDsl
import org.http4s.server.AuthMiddleware
import org.http4s.server.middleware.authentication.BasicAuth

// Use this header --> Authorization: Basic Z3ZvbHBlOjEyMzQ1Ng==
class BasicAuthHttpEndpoint[F[_]](implicit F: Sync[F], R: AuthRepository[F, BasicCredentials]) extends Http4sDsl[F] {

  private val authedService: AuthedService[BasicCredentials, F] = AuthedService {
    case GET -> Root as user =>
      Ok(s"Access Granted: ${user.username}")
  }

  private val authMiddleware: AuthMiddleware[F, BasicCredentials] =
    BasicAuth[F, BasicCredentials]("Protected Realm", R.find)

  val service: HttpService[F] = authMiddleware(authedService)

}


================================================
FILE: src/main/scala/com/github/gvolpe/http4s/server/endpoints/auth/GitHubHttpEndpoint.scala
================================================
package com.github.gvolpe.http4s.server.endpoints.auth

import cats.effect.Sync
import cats.syntax.flatMap._
import cats.syntax.functor._
import com.github.gvolpe.http4s.server.endpoints.ApiVersion
import com.github.gvolpe.http4s.server.service.GitHubService
import org.http4s._
import org.http4s.dsl.Http4sDsl

class GitHubHttpEndpoint[F[_]](gitHubService: GitHubService[F])
                              (implicit F: Sync[F]) extends Http4sDsl[F] {

  object CodeQuery extends QueryParamDecoderMatcher[String]("code")
  object StateQuery extends QueryParamDecoderMatcher[String]("state")

  val service: HttpService[F] = HttpService {
    case GET -> Root / ApiVersion / "github" =>
      Ok(gitHubService.authorize)

    // OAuth2 Callback URI
    case GET -> Root / ApiVersion / "login" / "github" :? CodeQuery(code) :? StateQuery(state) =>
      Ok(gitHubService.accessToken(code, state).flatMap(gitHubService.userData))
        .map(_.putHeaders(Header("Content-Type", "application/json")))
  }

}


================================================
FILE: src/main/scala/com/github/gvolpe/http4s/server/endpoints/package.scala
================================================
package com.github.gvolpe.http4s.server

import cats.effect.Sync
import org.http4s.EntityDecoder

import scala.xml._

package object endpoints {
  val ApiVersion = "v1"

  case class Person(name: String, age: Int)

  /**
    * XML Example for Person:
    *
    * <person>
    *   <name>gvolpe</name>
    *   <age>30</age>
    * </person>
    * */
  object Person {
    def fromXml(elem: Elem): Person = {
      val name = (elem \\ "name").text
      val age  = (elem \\ "age").text
      Person(name, age.toInt)
    }
  }

  def personXmlDecoder[F[_]: Sync]: EntityDecoder[F, Person] =
    org.http4s.scalaxml.xml[F].map(Person.fromXml)

}


================================================
FILE: src/main/scala/com/github/gvolpe/http4s/server/service/FileService.scala
================================================
package com.github.gvolpe.http4s.server.service

import java.io.File
import java.nio.file.Paths

import cats.effect.Effect
import com.github.gvolpe.http4s.StreamUtils
import fs2.Stream
import org.http4s.multipart.Part

class FileService[F[_]](implicit F: Effect[F], S: StreamUtils[F]) {

  def homeDirectories(depth: Option[Int]): Stream[F, String] =
    S.env("HOME").flatMap { maybePath =>
      val ifEmpty = S.error("HOME environment variable not found!")
      maybePath.fold(ifEmpty)(directories(_, depth.getOrElse(1)))
    }

  def directories(path: String, depth: Int): Stream[F, String] = {

    def dir(f: File, d: Int): Stream[F, File] = {
      val dirs = Stream.emits(f.listFiles().toSeq).filter(_.isDirectory).covary[F]

      if (d <= 0) Stream.empty
      else if (d == 1) dirs
      else dirs ++ dirs.flatMap(x => dir(x, d - 1))
    }

    S.evalF(new File(path)).flatMap { file =>
      dir(file, depth)
        .map(_.getName)
        .filter(!_.startsWith("."))
        .intersperse("\n")
    }
  }

  def store(part: Part[F]): Stream[F, Unit] =
    for {
      home      <- S.evalF(sys.env.getOrElse("HOME", "/tmp"))
      filename  <- S.evalF(part.filename.getOrElse("sample"))
      path      <- S.evalF(Paths.get(s"$home/$filename"))
      _         <- part.body to fs2.io.file.writeAll(path)
    } yield ()

}


================================================
FILE: src/main/scala/com/github/gvolpe/http4s/server/service/GitHubService.scala
================================================
package com.github.gvolpe.http4s.server.service

import cats.effect.Sync
import cats.syntax.functor._
import com.github.gvolpe.http4s.server.endpoints.ApiVersion
import fs2.Stream
import io.circe.generic.auto._
import org.http4s.circe._
import org.http4s.client.Client
import org.http4s.client.dsl.Http4sClientDsl
import org.http4s.{Header, Request, Uri}

// See: https://developer.github.com/apps/building-oauth-apps/authorization-options-for-oauth-apps/#web-application-flow
class GitHubService[F[_]: Sync](client: Client[F]) extends Http4sClientDsl[F] {

  // NEVER make this data public! This is just a demo!
  private val ClientId     = "959ea01cd3065cad274a"
  private val ClientSecret = "53901db46451977e6331432faa2616ba24bc2550"

  private val RedirectUri  = s"http://localhost:8080/$ApiVersion/login/github"

  case class AccessTokenResponse(access_token: String)

  val authorize: Stream[F, Byte] = {
    val uri = Uri.uri("https://github.com")
      .withPath("/login/oauth/authorize")
      .withQueryParam("client_id", ClientId)
      .withQueryParam("redirect_uri", RedirectUri)
      .withQueryParam("scopes", "public_repo")
      .withQueryParam("state", "test_api")

    client.streaming[Byte](Request[F](uri = uri))(_.body)
  }

  def accessToken(code: String, state: String): F[String] = {
    val uri = Uri.uri("https://github.com")
      .withPath("/login/oauth/access_token")
      .withQueryParam("client_id", ClientId)
      .withQueryParam("client_secret", ClientSecret)
      .withQueryParam("code", code)
      .withQueryParam("redirect_uri", RedirectUri)
      .withQueryParam("state", state)

    client.expect[AccessTokenResponse](Request[F](uri = uri))(jsonOf[F, AccessTokenResponse])
      .map(_.access_token)
  }

  def userData(accessToken: String): F[String] = {
    val request = Request[F](uri = Uri.uri("https://api.github.com/user"))
      .putHeaders(Header("Authorization", s"token $accessToken"))

    client.expect[String](request)
  }

}


================================================
FILE: src/test/scala/com/github/gvolpe/http4s/IOAssertion.scala
================================================
package com.github.gvolpe.http4s

import cats.effect.IO

object IOAssertion {
  def apply[A](ioa: IO[A]): A = ioa.unsafeRunSync()
}


================================================
FILE: src/test/scala/com/github/gvolpe/http4s/server/endpoints/HexNameHttpEndpointSpec.scala
================================================
package com.github.gvolpe.http4s.server.endpoints

import cats.effect.IO
import com.github.gvolpe.http4s.IOAssertion
import org.http4s.server.middleware.GZip
import org.http4s.{Header, HttpService, Query, Request, Uri}
import org.scalatest.FunSuite

// Docs: http://http4s.org/v0.18/gzip/
class HexNameHttpEndpointSpec extends FunSuite {

  private val httpService: HttpService[IO] = GZip(new HexNameHttpEndpoint[IO].service)

  private val CompressedLength  = 74
  private val NormalLength      = 88

  private val request = Request[IO](uri =
    Uri(
      path = s"/$ApiVersion/hex",
      query = Query("name" -> Some("Scala is a really cool programming language!"))
    )
  )

  test("Compressed Response") {
    IOAssertion {
      val gzipHeader  = Header("Accept-Encoding", "gzip")
      val gzipRequest = request.putHeaders(gzipHeader)

      httpService(gzipRequest).value.flatMap { maybe =>
        maybe.fold(IO[Unit](fail("Empty response"))) { response =>
          response.as[String].map(r => assert(r.length == CompressedLength))
        }
      }
    }
  }

  test("Uncompressed Response") {
    IOAssertion {
      httpService(request).value.flatMap { maybe =>
        maybe.fold(IO[Unit](fail("Empty response"))) { response =>
          response.as[String].map(r => assert(r.length == NormalLength))
        }
      }
    }
  }

}


================================================
FILE: src/test/scala/com/github/gvolpe/http4s/server/endpoints/JsonXmlHttpEndpointSpec.scala
================================================
package com.github.gvolpe.http4s.server.endpoints

import cats.effect.IO
import com.github.gvolpe.http4s.IOAssertion
import org.http4s.{Header, HttpService, Method, Request, Status, Uri}
import org.scalatest.FunSuite

class JsonXmlHttpEndpointSpec extends FunSuite {

  private val httpService: HttpService[IO] = new JsonXmlHttpEndpoint[IO].service

  private val jsonPerson =
    """
      |{
      |  "name": "gvolpe",
      |  "age": 30
      |}
    """.stripMargin

  private val xmlPerson =
    """
      |<person>
      |  <name>gvolpe</name>
      |  <age>30</age>
      |</person>
    """.stripMargin

  private val request = Request[IO](method = Method.POST, uri = Uri(path = s"/$ApiVersion/media"))

  test("json is decoded") {
    IOAssertion {
      val bodyRequest = request.withBody[String](jsonPerson)

      bodyRequest.flatMap { req =>
        httpService(req.putHeaders(Header("Content-Type", "application/json"))).value.map { maybe =>
          maybe.fold(fail("Empty response")) { response =>
            assert(response.status == Status.Ok)
          }
        }
      }
    }
  }

  test("xml is decoded") {
    IOAssertion {
      val bodyRequest = request.withBody[String](xmlPerson)

      bodyRequest.flatMap { req =>
        httpService(req.putHeaders(Header("Content-Type", "application/xml"))).value.map { maybe =>
          maybe.fold(fail("Empty response")) { response =>
            assert(response.status == Status.Ok)
          }
        }
      }
    }
  }

  test("decoding fails, no Content Type") {
    IOAssertion {
      val bodyRequest = request.withBody[String](jsonPerson)

      bodyRequest.flatMap { req =>
        httpService(req).value.attempt.map {
          case Left(e)  => assert(e.getMessage == "Malformed message body: Invalid XML")
          case Right(_) => fail("Got a response when a failure was expected")
        }
      }

      // Using `req.decode` gives you a response, using `req.as` throws an exception
      // https://gitter.im/http4s/http4s?at=5a964662758c233504cc0fec
//      bodyRequest.flatMap { req =>
//        httpService(req).value.map { maybe =>
//          maybe.fold(fail("Empty response")) { response =>
//            assert(response.status == Status.BadRequest)
//          }
//        }
//      }
    }
  }

}
Download .txt
gitextract_r37kbv80/

├── .gitignore
├── README.md
├── build.sbt
├── project/
│   ├── Dependencies.scala
│   └── build.properties
└── src/
    ├── main/
    │   └── scala/
    │       └── com/
    │           └── github/
    │               └── gvolpe/
    │                   ├── fs2/
    │                   │   ├── Counter.scala
    │                   │   ├── Fifo.scala
    │                   │   ├── Once.scala
    │                   │   ├── PubSub.scala
    │                   │   ├── Resources.scala
    │                   │   └── package.scala
    │                   └── http4s/
    │                       ├── StreamUtils.scala
    │                       ├── client/
    │                       │   ├── MultipartClient.scala
    │                       │   └── StreamClient.scala
    │                       └── server/
    │                           ├── Module.scala
    │                           ├── Server.scala
    │                           ├── endpoints/
    │                           │   ├── FileHttpEndpoint.scala
    │                           │   ├── HexNameHttpEndpoint.scala
    │                           │   ├── JsonXmlHttpEndpoint.scala
    │                           │   ├── MultipartHttpEndpoint.scala
    │                           │   ├── TimeoutHttpEndpoint.scala
    │                           │   ├── auth/
    │                           │   │   ├── AuthRepository.scala
    │                           │   │   ├── BasicAuthHttpEndpoint.scala
    │                           │   │   └── GitHubHttpEndpoint.scala
    │                           │   └── package.scala
    │                           └── service/
    │                               ├── FileService.scala
    │                               └── GitHubService.scala
    └── test/
        └── scala/
            └── com/
                └── github/
                    └── gvolpe/
                        └── http4s/
                            ├── IOAssertion.scala
                            └── server/
                                └── endpoints/
                                    ├── HexNameHttpEndpointSpec.scala
                                    └── JsonXmlHttpEndpointSpec.scala
Condensed preview — 30 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (41K chars).
[
  {
    "path": ".gitignore",
    "chars": 481,
    "preview": "ml\n*.ipr\n*.iws\n*.pyc\n*.tm.epoch\n*.vim\n*/project/boot\n*/project/build/target\n*/project/project.target.config-classes\n*-sh"
  },
  {
    "path": "README.md",
    "chars": 2863,
    "preview": "advanced-http4s\n===============\n\nCode samples of advanced features of [Http4s](http://http4s.org/) in combination with s"
  },
  {
    "path": "build.sbt",
    "chars": 998,
    "preview": "import Dependencies._\n\nlazy val root = (project in file(\".\")).\n  settings(\n    inThisBuild(List(\n      organization := \""
  },
  {
    "path": "project/Dependencies.scala",
    "chars": 2090,
    "preview": "import sbt._\n\nobject Dependencies {\n\n  object Versions {\n    val CatsEffect  = \"0.8\"\n    val Monix       = \"3.0.0-M3\"\n  "
  },
  {
    "path": "project/build.properties",
    "chars": 18,
    "preview": "sbt.version=1.1.0\n"
  },
  {
    "path": "src/main/scala/com/github/gvolpe/fs2/Counter.scala",
    "chars": 1437,
    "preview": "package com.github.gvolpe.fs2\n\nimport cats.effect.{Effect, IO}\nimport fs2.StreamApp.ExitCode\nimport fs2.async.Ref\nimport"
  },
  {
    "path": "src/main/scala/com/github/gvolpe/fs2/Fifo.scala",
    "chars": 1468,
    "preview": "package com.github.gvolpe.fs2\n\nimport cats.effect.{Effect, IO}\nimport fs2.StreamApp.ExitCode\nimport fs2.async.mutable.Qu"
  },
  {
    "path": "src/main/scala/com/github/gvolpe/fs2/Once.scala",
    "chars": 1930,
    "preview": "package com.github.gvolpe.fs2\n\nimport cats.effect.{Effect, IO}\nimport fs2.StreamApp.ExitCode\nimport fs2.async.Promise\nim"
  },
  {
    "path": "src/main/scala/com/github/gvolpe/fs2/PubSub.scala",
    "chars": 2564,
    "preview": "package com.github.gvolpe.fs2\n\nimport cats.effect.{Effect, IO}\nimport fs2.StreamApp.ExitCode\nimport fs2.async.mutable.{S"
  },
  {
    "path": "src/main/scala/com/github/gvolpe/fs2/Resources.scala",
    "chars": 2795,
    "preview": "package com.github.gvolpe.fs2\n\nimport cats.effect.{Effect, IO}\nimport cats.syntax.functor._\nimport fs2.StreamApp.ExitCod"
  },
  {
    "path": "src/main/scala/com/github/gvolpe/fs2/package.scala",
    "chars": 99,
    "preview": "package com.github.gvolpe\n\npackage object fs2 {\n  case class Event(value: String) extends AnyVal\n}\n"
  },
  {
    "path": "src/main/scala/com/github/gvolpe/http4s/StreamUtils.scala",
    "chars": 682,
    "preview": "package com.github.gvolpe.http4s\n\nimport cats.effect.Sync\nimport fs2.Stream\n\ntrait StreamUtils[F[_]] {\n  def evalF[A](th"
  },
  {
    "path": "src/main/scala/com/github/gvolpe/http4s/client/MultipartClient.scala",
    "chars": 1584,
    "preview": "package com.github.gvolpe.http4s.client\n\nimport java.net.URL\n\nimport cats.effect.Effect\nimport cats.syntax.flatMap._\nimp"
  },
  {
    "path": "src/main/scala/com/github/gvolpe/http4s/client/StreamClient.scala",
    "chars": 983,
    "preview": "package com.github.gvolpe.http4s.client\n\nimport cats.effect.Effect\nimport com.github.gvolpe.http4s.StreamUtils\nimport fs"
  },
  {
    "path": "src/main/scala/com/github/gvolpe/http4s/server/Module.scala",
    "chars": 2262,
    "preview": "package com.github.gvolpe.http4s.server\n\nimport cats.effect.Effect\nimport cats.syntax.semigroupk._ // For <+>\nimport com"
  },
  {
    "path": "src/main/scala/com/github/gvolpe/http4s/server/Server.scala",
    "chars": 1166,
    "preview": "package com.github.gvolpe.http4s.server\n\nimport cats.effect.Effect\nimport fs2.StreamApp.ExitCode\nimport fs2.{Scheduler, "
  },
  {
    "path": "src/main/scala/com/github/gvolpe/http4s/server/endpoints/FileHttpEndpoint.scala",
    "chars": 522,
    "preview": "package com.github.gvolpe.http4s.server.endpoints\n\nimport cats.Monad\nimport com.github.gvolpe.http4s.server.service.File"
  },
  {
    "path": "src/main/scala/com/github/gvolpe/http4s/server/endpoints/HexNameHttpEndpoint.scala",
    "chars": 459,
    "preview": "package com.github.gvolpe.http4s.server.endpoints\n\nimport cats.Monad\nimport org.http4s._\nimport org.http4s.dsl.Http4sDsl"
  },
  {
    "path": "src/main/scala/com/github/gvolpe/http4s/server/endpoints/JsonXmlHttpEndpoint.scala",
    "chars": 860,
    "preview": "package com.github.gvolpe.http4s.server.endpoints\n\nimport cats.effect.Effect\nimport cats.syntax.flatMap._\nimport io.circ"
  },
  {
    "path": "src/main/scala/com/github/gvolpe/http4s/server/endpoints/MultipartHttpEndpoint.scala",
    "chars": 984,
    "preview": "package com.github.gvolpe.http4s.server.endpoints\n\nimport cats.effect.Sync\nimport cats.implicits._\nimport com.github.gvo"
  },
  {
    "path": "src/main/scala/com/github/gvolpe/http4s/server/endpoints/TimeoutHttpEndpoint.scala",
    "chars": 667,
    "preview": "package com.github.gvolpe.http4s.server.endpoints\n\nimport java.util.concurrent.TimeUnit\n\nimport cats.effect.Async\nimport"
  },
  {
    "path": "src/main/scala/com/github/gvolpe/http4s/server/endpoints/auth/AuthRepository.scala",
    "chars": 733,
    "preview": "package com.github.gvolpe.http4s.server.endpoints.auth\n\nimport cats.effect.Sync\nimport org.http4s.BasicCredentials\n\ntrai"
  },
  {
    "path": "src/main/scala/com/github/gvolpe/http4s/server/endpoints/auth/BasicAuthHttpEndpoint.scala",
    "chars": 839,
    "preview": "package com.github.gvolpe.http4s.server.endpoints.auth\n\nimport cats.effect.Sync\nimport com.github.gvolpe.http4s.server.e"
  },
  {
    "path": "src/main/scala/com/github/gvolpe/http4s/server/endpoints/auth/GitHubHttpEndpoint.scala",
    "chars": 1004,
    "preview": "package com.github.gvolpe.http4s.server.endpoints.auth\n\nimport cats.effect.Sync\nimport cats.syntax.flatMap._\nimport cats"
  },
  {
    "path": "src/main/scala/com/github/gvolpe/http4s/server/endpoints/package.scala",
    "chars": 640,
    "preview": "package com.github.gvolpe.http4s.server\n\nimport cats.effect.Sync\nimport org.http4s.EntityDecoder\n\nimport scala.xml._\n\npa"
  },
  {
    "path": "src/main/scala/com/github/gvolpe/http4s/server/service/FileService.scala",
    "chars": 1335,
    "preview": "package com.github.gvolpe.http4s.server.service\n\nimport java.io.File\nimport java.nio.file.Paths\n\nimport cats.effect.Effe"
  },
  {
    "path": "src/main/scala/com/github/gvolpe/http4s/server/service/GitHubService.scala",
    "chars": 1983,
    "preview": "package com.github.gvolpe.http4s.server.service\n\nimport cats.effect.Sync\nimport cats.syntax.functor._\nimport com.github."
  },
  {
    "path": "src/test/scala/com/github/gvolpe/http4s/IOAssertion.scala",
    "chars": 132,
    "preview": "package com.github.gvolpe.http4s\n\nimport cats.effect.IO\n\nobject IOAssertion {\n  def apply[A](ioa: IO[A]): A = ioa.unsafe"
  },
  {
    "path": "src/test/scala/com/github/gvolpe/http4s/server/endpoints/HexNameHttpEndpointSpec.scala",
    "chars": 1350,
    "preview": "package com.github.gvolpe.http4s.server.endpoints\n\nimport cats.effect.IO\nimport com.github.gvolpe.http4s.IOAssertion\nimp"
  },
  {
    "path": "src/test/scala/com/github/gvolpe/http4s/server/endpoints/JsonXmlHttpEndpointSpec.scala",
    "chars": 2291,
    "preview": "package com.github.gvolpe.http4s.server.endpoints\n\nimport cats.effect.IO\nimport com.github.gvolpe.http4s.IOAssertion\nimp"
  }
]

About this extraction

This page contains the full source code of the gvolpe/advanced-http4s GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 30 files (36.3 KB), approximately 10.7k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!