<commit_before>package org.http4s

import scala.concurrent.Future
import play.api.libs.iteratee.Iteratee

/**
 * @author <NAME>
 *         Created on 11/5/13
 */

object PushSupport {

  // TODO: choose the right ec
  import concurrent.ExecutionContext.Implicits.global

  // An implicit conversion class
  implicit class PushSupportResponder(responder: Responder) extends AnyRef {
    def push(url: String, cascade: Boolean = true): Responder = {
      val newPushResouces = responder.attributes.get(pushLocationKey)
          .map(_ :+ PushLocation(url, cascade))
          .getOrElse(Vector(PushLocation(url,cascade)))

      Responder(responder.prelude, responder.body,
        responder.attributes.put(PushSupport.pushLocationKey, newPushResouces))
    }
  }

  private def locToRequest(push: PushLocation, prelude: RequestPrelude): RequestPrelude =
    RequestPrelude(pathInfo = push.location, headers = prelude.headers)

  private def collectResponder(r: Vector[PushLocation], req: RequestPrelude, route: Route): Future[Vector[PushResponder]] = 
    r.foldLeft(Future.successful(Vector.empty[PushResponder])){ (facc, v) =>
      if (v.cascade) facc.flatMap { accumulated => // Need to gather the sub resources
        route(locToRequest(v, req)).run
          .flatMap { responder =>             // Inside the future result of this pushed resource
            responder.attributes.get(pushLocationKey)
            .map { pushed =>
              collectResponder(pushed, req, route)
                .map(accumulated ++ _ :+ PushResponder(v.location, responder))
            }.getOrElse(Future.successful(accumulated:+PushResponder(v.location, responder)))
          }
      } else {
        route(locToRequest(v, req))
          .run
          .flatMap( resp => facc.map(_ :+ PushResponder(v.location, resp)))
      }
    }
  

  /** Transform the route such that requests will gather pushed resources
   *
   * @param route Route to transform
   * @return      Transformed route
   */
  def apply(route: Route): Route = {
    def gather(req: RequestPrelude, i: Iteratee[Chunk, Responder]): Iteratee[Chunk, Responder] = i map { resp =>
      resp.attributes.get(pushLocationKey).map { fresource =>
        val collected: Future[Vector[PushResponder]] = collectResponder(fresource, req, route)
        Responder(resp.prelude, resp.body, resp.attributes.put(pushRespondersKey, collected))
      }.getOrElse(resp)
    }

    new Route {
      def apply(v1: RequestPrelude): Iteratee[Chunk, Responder] = gather(v1, route(v1))
      def isDefinedAt(x: RequestPrelude): Boolean = route.isDefinedAt(x)
    }<commit_msg>Made push a bit safer.<commit_after>package org.http4s

import scala.concurrent.Future
import play.api.libs.iteratee.Iteratee
import com.typesafe.scalalogging.slf4j.Logging

/**
 * @author <NAME>
 *         Created on 11/5/13
 */

object PushSupport extends Logging {

  // TODO: choose the right ec
  import concurrent.ExecutionContext.Implicits.global

  // An implicit conversion class
  implicit class PushSupportResponder(responder: Responder) extends AnyRef {
    def push(url: String, cascade: Boolean = true): Responder = {
      val newPushResouces = responder.attributes.get(pushLocationKey)
          .map(_ :+ PushLocation(url, cascade))
          .getOrElse(Vector(PushLocation(url,cascade)))

      Responder(responder.prelude, responder.body,
        responder.attributes.put(PushSupport.pushLocationKey, newPushResouces))
    }
  }

  private def handleException(ex: Throwable) {
    logger.error("Push resource route failure", ex)
  }

  private def locToRequest(push: PushLocation, prelude: RequestPrelude): RequestPrelude =
    RequestPrelude(pathInfo = push.location, headers = prelude.headers)

  private def collectResponder(r: Vector[PushLocation], req: RequestPrelude, route: Route): Future[Vector[PushResponder]] = 
    r.foldLeft(Future.successful(Vector.empty[PushResponder])){ (facc, v) =>
      val newReq = locToRequest(v, req)
      if (v.cascade) facc.flatMap { accumulated => // Need to gather the sub resources
        try route(newReq).run
          .flatMap { responder =>                  // Inside the future result of this pushed resource
            responder.attributes.get(pushLocationKey)
            .map { pushed =>
              collectResponder(pushed, req, route)
                .map(accumulated ++ _ :+ PushResponder(v.location, responder))
            }.getOrElse(Future.successful(accumulated:+PushResponder(v.location, responder)))
          }
        catch { case t: Throwable => handleException(t); facc }
      } else {
        try route(newReq)   // Need to make sure to catch exceptions
          .run
          .flatMap( resp => facc.map(_ :+ PushResponder(v.location, resp)))
        catch { case t: Throwable => handleException(t); facc }
      }
    }
  

  /** Transform the route such that requests will gather pushed resources
   *
   * @param route Route to transform
   * @return      Transformed route
   */
  def apply(route: Route): Route = {
    def gather(req: RequestPrelude, i: Iteratee[Chunk, Responder]): Iteratee[Chunk, Responder] = i map { resp =>
      resp.attributes.get(pushLocationKey).map { fresource =>
        val collected: Future[Vector[PushResponder]] = collectResponder(fresource, req, route)
        Responder(resp.prelude, resp.body, resp.attributes.put(pushRespondersKey, collected))
      }.getOrElse(resp)
    }

    new Route {
      def apply(v1: RequestPrelude): Iteratee[Chunk, Responder] = gather(v1, route(v1))
      def isDefinedAt(x: RequestPrelude): Boolean = route.isDefinedAt(x)
    }
{'fraction_non_alphanumeric': 0.11055634807417974, 'fraction_numerical': 0.0033880171184022824, 'mean_word_length': 3.201498127340824, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '247041', 'n_tokens_mistral': 1753, 'n_tokens_neox': 1649, 'n_words': 386}