require HTTP/1.1 for chunked responses
parent
1ae01f9368
commit
92ef2b9ae9
|
@ -282,9 +282,11 @@ object Api extends LilaController {
|
|||
} map toApiResult
|
||||
}
|
||||
|
||||
def gameStream = Action(parse.tolerantText) { req =>
|
||||
val userIds = req.body.split(',').take(300).toSet map lila.user.User.normalize
|
||||
Ok.chunked(Env.game.stream.startedByUserIds(userIds))
|
||||
def gameStream = Action.async(parse.tolerantText) { req =>
|
||||
RequireHttp11(req) {
|
||||
val userIds = req.body.split(',').take(300).toSet map lila.user.User.normalize
|
||||
Ok.chunked(Env.game.stream.startedByUserIds(userIds)).fuccess
|
||||
}
|
||||
}
|
||||
|
||||
def activity(name: String) = ApiRequest { implicit ctx =>
|
||||
|
|
|
@ -49,14 +49,16 @@ object Export extends LilaController {
|
|||
|
||||
def png(id: String) = Open { implicit ctx =>
|
||||
OnlyHumansAndFacebookOrTwitter {
|
||||
PngRateLimitGlobal("-", msg = s"${HTTPRequest.lastRemoteAddress(ctx.req).value} ${~HTTPRequest.userAgent(ctx.req)}") {
|
||||
lila.mon.export.png.game()
|
||||
OptionFuResult(GameRepo game id) { game =>
|
||||
env.pngExport fromGame game map { stream =>
|
||||
Ok.chunked(stream).withHeaders(
|
||||
CONTENT_TYPE -> "image/png",
|
||||
CACHE_CONTROL -> "max-age=7200"
|
||||
)
|
||||
RequireHttp11 {
|
||||
PngRateLimitGlobal("-", msg = s"${HTTPRequest.lastRemoteAddress(ctx.req).value} ${~HTTPRequest.userAgent(ctx.req)}") {
|
||||
lila.mon.export.png.game()
|
||||
OptionFuResult(GameRepo game id) { game =>
|
||||
env.pngExport fromGame game map { stream =>
|
||||
Ok.chunked(stream).withHeaders(
|
||||
CONTENT_TYPE -> "image/png",
|
||||
CACHE_CONTROL -> "max-age=7200"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -65,21 +67,23 @@ object Export extends LilaController {
|
|||
|
||||
def puzzlePng(id: Int) = Open { implicit ctx =>
|
||||
OnlyHumansAndFacebookOrTwitter {
|
||||
PngRateLimitGlobal("-", msg = HTTPRequest.lastRemoteAddress(ctx.req).value) {
|
||||
lila.mon.export.png.puzzle()
|
||||
OptionFuResult(Env.puzzle.api.puzzle find id) { puzzle =>
|
||||
env.pngExport(
|
||||
fen = chess.format.FEN(puzzle.fenAfterInitialMove | puzzle.fen),
|
||||
lastMove = puzzle.initialMove.uci.some,
|
||||
check = none,
|
||||
orientation = puzzle.color.some,
|
||||
logHint = s"puzzle $id"
|
||||
) map { stream =>
|
||||
Ok.chunked(stream).withHeaders(
|
||||
CONTENT_TYPE -> "image/png",
|
||||
CACHE_CONTROL -> "max-age=7200"
|
||||
)
|
||||
}
|
||||
RequireHttp11 {
|
||||
PngRateLimitGlobal("-", msg = HTTPRequest.lastRemoteAddress(ctx.req).value) {
|
||||
lila.mon.export.png.puzzle()
|
||||
OptionFuResult(Env.puzzle.api.puzzle find id) { puzzle =>
|
||||
env.pngExport(
|
||||
fen = chess.format.FEN(puzzle.fenAfterInitialMove | puzzle.fen),
|
||||
lastMove = puzzle.initialMove.uci.some,
|
||||
check = none,
|
||||
orientation = puzzle.color.some,
|
||||
logHint = s"puzzle $id"
|
||||
) map { stream =>
|
||||
Ok.chunked(stream).withHeaders(
|
||||
CONTENT_TYPE -> "image/png",
|
||||
CACHE_CONTROL -> "max-age=7200"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package controllers
|
|||
import org.joda.time.DateTime
|
||||
import org.joda.time.format.DateTimeFormat
|
||||
import scala.concurrent.duration._
|
||||
import play.api.mvc.RequestHeader
|
||||
|
||||
import lila.app._
|
||||
import lila.game.{ GameRepo, Game => GameModel }
|
||||
|
@ -36,7 +37,7 @@ object Game extends LilaController {
|
|||
err => Env.security.forms.anyCaptcha map { captcha =>
|
||||
BadRequest(html.game.export(err, captcha))
|
||||
},
|
||||
_ => fuccess(streamGamesPgn(me, since = none, until = none))
|
||||
_ => streamGamesPgn(req, me, since = none, until = none)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -44,7 +45,7 @@ object Game extends LilaController {
|
|||
val since = getLong("since", req) map { ts => new DateTime(ts) }
|
||||
val until = getLong("until", req) map { ts => new DateTime(ts) }
|
||||
val max = getInt("max", req)
|
||||
fuccess(streamGamesPgn(me, since, until, max))
|
||||
streamGamesPgn(req, me, since, until, max)
|
||||
}
|
||||
|
||||
private val ExportRateLimitPerUser = new lila.memo.RateLimit[lila.user.User.ID](
|
||||
|
@ -54,13 +55,15 @@ object Game extends LilaController {
|
|||
key = "game_export.user"
|
||||
)
|
||||
|
||||
private def streamGamesPgn(user: lila.user.User, since: Option[DateTime], until: Option[DateTime], max: Option[Int] = None) =
|
||||
ExportRateLimitPerUser(user.id, cost = 1) {
|
||||
val date = (DateTimeFormat forPattern "yyyy-MM-dd") print new DateTime
|
||||
Ok.chunked(Env.api.pgnDump.exportUserGames(user.id, since, until, max | Int.MaxValue)).withHeaders(
|
||||
CONTENT_TYPE -> pgnContentType,
|
||||
CONTENT_DISPOSITION -> ("attachment; filename=" + s"lichess_${user.username}_$date.pgn")
|
||||
)
|
||||
private def streamGamesPgn(req: RequestHeader, user: lila.user.User, since: Option[DateTime], until: Option[DateTime], max: Option[Int] = None) =
|
||||
RequireHttp11(req) {
|
||||
ExportRateLimitPerUser(user.id, cost = 1) {
|
||||
val date = (DateTimeFormat forPattern "yyyy-MM-dd") print new DateTime
|
||||
Ok.chunked(Env.api.pgnDump.exportUserGames(user.id, since, until, max | Int.MaxValue)).withHeaders(
|
||||
CONTENT_TYPE -> pgnContentType,
|
||||
CONTENT_DISPOSITION -> ("attachment; filename=" + s"lichess_${user.username}_$date.pgn")
|
||||
)
|
||||
} fuccess
|
||||
}
|
||||
|
||||
private[controllers] def preloadUsers(game: GameModel): Funit =
|
||||
|
|
|
@ -54,7 +54,9 @@ object Irwin extends LilaController {
|
|||
|
||||
def eventStream = Open { implicit ctx =>
|
||||
ModExternalBot {
|
||||
Ok.chunked(Env.irwin.stream.enumerator).fuccess
|
||||
RequireHttp11 {
|
||||
Ok.chunked(Env.irwin.stream.enumerator).fuccess
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -410,6 +410,12 @@ private[controllers] trait LilaController
|
|||
else if (HTTPRequest isBot ctx.req) fuccess(NotFound)
|
||||
else result
|
||||
|
||||
protected def RequireHttp11(result: => Fu[Result])(implicit ctx: lila.api.Context): Fu[Result] =
|
||||
RequireHttp11(ctx.req)(result)
|
||||
protected def RequireHttp11(req: RequestHeader)(result: => Fu[Result]): Fu[Result] =
|
||||
if (HTTPRequest isHttp10 req) BadRequest("Requires HTTP 1.1").fuccess
|
||||
else result
|
||||
|
||||
private val jsonGlobalErrorRenamer = {
|
||||
import play.api.libs.json._
|
||||
__.json update (
|
||||
|
|
|
@ -81,23 +81,25 @@ object Search extends LilaController {
|
|||
|
||||
def export = OpenBody { implicit ctx =>
|
||||
NotForBots {
|
||||
implicit def req = ctx.body
|
||||
searchForm.bindFromRequest.fold(
|
||||
failure => Env.game.cached.nbTotal map { nbGames =>
|
||||
Ok(html.search.index(failure, none, nbGames))
|
||||
},
|
||||
data => data.nonEmptyQuery ?? { query =>
|
||||
env.api.ids(query, 5000) map { ids =>
|
||||
import org.joda.time.DateTime
|
||||
import org.joda.time.format.DateTimeFormat
|
||||
val date = (DateTimeFormat forPattern "yyyy-MM-dd") print DateTime.now
|
||||
Ok.chunked(Env.api.pgnDump exportGamesFromIds ids).withHeaders(
|
||||
CONTENT_TYPE -> pgnContentType,
|
||||
CONTENT_DISPOSITION -> ("attachment; filename=" + s"lichess_search_$date.pgn")
|
||||
)
|
||||
RequireHttp11 {
|
||||
implicit def req = ctx.body
|
||||
searchForm.bindFromRequest.fold(
|
||||
failure => Env.game.cached.nbTotal map { nbGames =>
|
||||
Ok(html.search.index(failure, none, nbGames))
|
||||
},
|
||||
data => data.nonEmptyQuery ?? { query =>
|
||||
env.api.ids(query, 5000) map { ids =>
|
||||
import org.joda.time.DateTime
|
||||
import org.joda.time.format.DateTimeFormat
|
||||
val date = (DateTimeFormat forPattern "yyyy-MM-dd") print DateTime.now
|
||||
Ok.chunked(Env.api.pgnDump exportGamesFromIds ids).withHeaders(
|
||||
CONTENT_TYPE -> pgnContentType,
|
||||
CONTENT_DISPOSITION -> ("attachment; filename=" + s"lichess_search_$date.pgn")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,15 +70,17 @@ object Tv extends LilaController {
|
|||
}
|
||||
}
|
||||
|
||||
def feed = Action.async {
|
||||
import makeTimeout.short
|
||||
import akka.pattern.ask
|
||||
import lila.round.TvBroadcast
|
||||
import play.api.libs.EventSource
|
||||
Env.round.tvBroadcast ? TvBroadcast.GetEnumerator mapTo
|
||||
manifest[TvBroadcast.EnumeratorType] map { enum =>
|
||||
Ok.chunked(enum &> EventSource()).as("text/event-stream")
|
||||
}
|
||||
def feed = Action.async { req =>
|
||||
RequireHttp11(req) {
|
||||
import makeTimeout.short
|
||||
import akka.pattern.ask
|
||||
import lila.round.TvBroadcast
|
||||
import play.api.libs.EventSource
|
||||
Env.round.tvBroadcast ? TvBroadcast.GetEnumerator mapTo
|
||||
manifest[TvBroadcast.EnumeratorType] map { enum =>
|
||||
Ok.chunked(enum &> EventSource()).as("text/event-stream")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def embed = Action { req =>
|
||||
|
|
|
@ -71,4 +71,6 @@ object HTTPRequest {
|
|||
def printClient(req: RequestHeader) = s"${lastRemoteAddress(req)} origin:${~origin(req)} referer:${~referer(req)} ua:${~userAgent(req)}"
|
||||
|
||||
def isOAuth(req: RequestHeader) = req.headers.toMap.contains(HeaderNames.AUTHORIZATION)
|
||||
|
||||
def isHttp10(req: RequestHeader) = req.version == "HTTP/1.0"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue