16
16
17
17
package org .http4s .armeria .server
18
18
19
+ import java .io .{File , InputStream }
20
+ import java .net .InetSocketAddress
21
+ import java .security .PrivateKey
22
+ import java .security .cert .X509Certificate
23
+ import java .util .function .{Function => JFunction }
24
+ import javax .net .ssl .KeyManagerFactory
25
+
26
+ import cats .Monad
19
27
import cats .effect .{Async , Resource }
20
- import cats .syntax .applicative ._
21
- import cats .syntax .flatMap ._
22
- import cats .syntax .functor ._
28
+ import cats .effect .std .Dispatcher
29
+ import cats .syntax .all ._
23
30
import com .linecorp .armeria .common .util .Version
24
- import com .linecorp .armeria .common .{HttpRequest , HttpResponse , SessionProtocol , TlsKeyPair }
31
+ import com .linecorp .armeria .common .{
32
+ ContentTooLargeException ,
33
+ HttpRequest ,
34
+ HttpResponse ,
35
+ SessionProtocol ,
36
+ TlsKeyPair
37
+ }
25
38
import com .linecorp .armeria .server .{
26
39
HttpService ,
27
40
HttpServiceWithRoutes ,
@@ -33,18 +46,10 @@ import com.linecorp.armeria.server.{
33
46
import io .micrometer .core .instrument .MeterRegistry
34
47
import io .netty .channel .ChannelOption
35
48
import io .netty .handler .ssl .SslContextBuilder
36
-
37
- import java .io .{File , InputStream }
38
- import java .net .InetSocketAddress
39
- import java .security .PrivateKey
40
- import java .security .cert .X509Certificate
41
- import java .util .function .{Function => JFunction }
42
- import cats .effect .std .Dispatcher
43
49
import com .comcast .ip4s
44
-
45
- import javax .net .ssl .KeyManagerFactory
46
50
import org .http4s .armeria .server .ArmeriaServerBuilder .AddServices
47
- import org .http4s .{BuildInfo , HttpApp , HttpRoutes }
51
+ import org .http4s .headers .{Connection , `Content-Length` }
52
+ import org .http4s .{BuildInfo , Headers , HttpApp , HttpRoutes , Request , Response , Status }
48
53
import org .http4s .server .{
49
54
DefaultServiceErrorHandler ,
50
55
Server ,
@@ -169,7 +174,9 @@ sealed class ArmeriaServerBuilder[F[_]] private (
169
174
def withHttpApp (prefix : String , service : HttpApp [F ]): Self =
170
175
copy(addServices = (ab, dispatcher) =>
171
176
addServices(ab, dispatcher).map(
172
- _.serviceUnder(prefix, ArmeriaHttp4sHandler (prefix, service, dispatcher))))
177
+ _.serviceUnder(
178
+ prefix,
179
+ ArmeriaHttp4sHandler (prefix, service, serviceErrorHandler, dispatcher))))
173
180
174
181
/** Decorates all HTTP services with the specified [[DecoratingFunction ]]. */
175
182
def withDecorator (decorator : DecoratingFunction ): Self =
@@ -205,6 +212,14 @@ sealed class ArmeriaServerBuilder[F[_]] private (
205
212
def withIdleTimeout (idleTimeout : FiniteDuration ): Self =
206
213
atBuild(_.idleTimeoutMillis(idleTimeout.toMillis))
207
214
215
+ /** Sets the maximum allowed length of the content decoded at the session layer.
216
+ *
217
+ * @param limit
218
+ * the maximum allowed length. {@code 0} disables the length limit.
219
+ */
220
+ def withMaxRequestLength (limit : Long ): Self =
221
+ atBuild(_.maxRequestLength(limit))
222
+
208
223
/** Sets the timeout of a request.
209
224
*
210
225
* @param requestTimeout
@@ -359,7 +374,29 @@ object ArmeriaServerBuilder {
359
374
new ArmeriaServerBuilder (
360
375
(armeriaBuilder, _) => armeriaBuilder.pure,
361
376
socketAddress = defaults.IPv4SocketAddress .toInetSocketAddress,
362
- serviceErrorHandler = DefaultServiceErrorHandler ,
377
+ serviceErrorHandler = defaultServiceErrorHandler[ F ] ,
363
378
banner = defaults.Banner
364
379
)
380
+
381
+ /** Incorporates the default service error handling from Http4s'
382
+ * [[org.http4s.server.DefaultServiceErrorHandler DefaultServiceErrorHandler ]] and adds handling
383
+ * for some errors propagated from the Armeria side.
384
+ */
385
+ def defaultServiceErrorHandler [F [_]](implicit
386
+ F : Monad [F ],
387
+ LF : LoggerFactory [F ]): Request [F ] => PartialFunction [Throwable , F [Response [F ]]] = {
388
+ val contentLengthErrorHandler : Request [F ] => PartialFunction [Throwable , F [Response [F ]]] =
389
+ req => { case _ : ContentTooLargeException =>
390
+ Response [F ](
391
+ Status .PayloadTooLarge ,
392
+ req.httpVersion,
393
+ Headers (
394
+ Connection .close,
395
+ `Content-Length`.zero
396
+ )
397
+ ).pure[F ]
398
+ }
399
+
400
+ req => contentLengthErrorHandler(req).orElse(DefaultServiceErrorHandler (LF , F )(req))
401
+ }
365
402
}
0 commit comments