@@ -2,125 +2,84 @@ package org.ivovk.connect_rpc_scala
2
2
3
3
import cats .Endo
4
4
import cats .data .EitherT
5
- import cats .effect .{ Async , Resource }
5
+ import cats .effect .Async
6
6
import cats .implicits .*
7
- import fs2 .compression .Compression
8
- import io .grpc .*
7
+ import io .grpc .{CallOptions , Channel , ClientInterceptors , Metadata , StatusException , StatusRuntimeException }
9
8
import io .grpc .MethodDescriptor .MethodType
10
9
import io .grpc .stub .MetadataUtils
11
- import org .http4s .*
12
10
import org .http4s .dsl .Http4sDsl
13
- import org .ivovk .connect_rpc_scala .http .*
11
+ import org .http4s .{MediaType , Method , Response }
12
+ import org .ivovk .connect_rpc_scala .Mappings .*
14
13
import org .ivovk .connect_rpc_scala .http .Headers .`X-Test-Case-Name`
15
14
import org .ivovk .connect_rpc_scala .http .MessageCodec .given
16
- import org .ivovk .connect_rpc_scala .http .QueryParams . *
15
+ import org .ivovk .connect_rpc_scala .http .{ MediaTypes , MessageCodec , MessageCodecRegistry , RequestEntity }
17
16
import org .slf4j .{Logger , LoggerFactory }
18
17
import scalapb .grpc .ClientCalls
19
- import scalapb .json4s .JsonFormat
20
18
import scalapb .{GeneratedMessage , GeneratedMessageCompanion , TextFormat }
21
19
22
20
import java .util .concurrent .atomic .AtomicReference
23
- import scala .concurrent .duration .MILLISECONDS
21
+ import scala .concurrent .duration .*
24
22
import scala .util .chaining .*
25
23
26
-
27
- object ConnectRpcHttpRoutes {
28
-
29
- import Mappings .*
24
+ class ConnectHandler [F [_]: Async ](
25
+ codecRegistry : MessageCodecRegistry [F ],
26
+ methodRegistry : MethodRegistry ,
27
+ channel : Channel ,
28
+ httpDsl : Http4sDsl [F ],
29
+ ) {
30
+ import httpDsl .*
30
31
31
32
private val logger : Logger = LoggerFactory .getLogger(getClass)
32
33
33
- def create [F [_] : Async ](
34
- services : Seq [ServerServiceDefinition ],
35
- configuration : Configuration = Configuration .default
36
- ): Resource [F , HttpRoutes [F ]] = {
37
- val dsl = Http4sDsl [F ]
38
- import dsl .*
39
-
40
- val jsonPrinter = configuration.jsonPrinterConfigurator(JsonFormat .printer)
41
-
42
- val codecRegistry = MessageCodecRegistry [F ](
43
- JsonMessageCodec [F ](jsonPrinter),
44
- ProtoMessageCodec [F ],
45
- )
46
-
47
- val methodRegistry = MethodRegistry (services)
48
-
49
- for
50
- ipChannel <- InProcessChannelBridge .create(
51
- services,
52
- configuration.serverBuilderConfigurator,
53
- configuration.channelBuilderConfigurator,
54
- configuration.waitForShutdown,
34
+ def handle (
35
+ httpMethod : Method ,
36
+ contentType : Option [MediaType ],
37
+ entity : RequestEntity [F ],
38
+ grpcMethodName : String ,
39
+ ): F [Response [F ]] = {
40
+ val eitherT = for
41
+ given MessageCodec [F ] <- EitherT .fromOptionM(
42
+ contentType.flatMap(codecRegistry.byContentType).pure[F ],
43
+ UnsupportedMediaType (s " Unsupported content-type ${contentType.show}. " +
44
+ s " Supported content types: ${MediaTypes .allSupported.map(_.show).mkString(" , " )}" )
55
45
)
56
- yield
57
- def handle (
58
- httpMethod : Method ,
59
- contentType : Option [MediaType ],
60
- entity : RequestEntity [F ],
61
- grpcMethod : String ,
62
- ): F [Response [F ]] = {
63
- val eitherT = for
64
- given MessageCodec [F ] <- EitherT .fromOptionM(
65
- contentType.flatMap(codecRegistry.byContentType).pure[F ],
66
- UnsupportedMediaType (s " Unsupported content-type ${contentType.show}. " +
67
- s " Supported content types: ${MediaTypes .allSupported.map(_.show).mkString(" , " )}" )
68
- )
69
-
70
- method <- EitherT .fromOptionM(
71
- methodRegistry.get(grpcMethod).pure[F ],
72
- NotFound (connectrpc.Error (
73
- code = io.grpc.Status .NOT_FOUND .toConnectCode,
74
- message = s " Method not found: $grpcMethod" .some
75
- ))
76
- )
77
46
78
- _ <- EitherT .cond[F ](
79
- // Support GET-requests for all methods until https://github.com/scalapb/ScalaPB/pull/1774 is merged
80
- httpMethod == Method .POST || (httpMethod == Method .GET && method.descriptor.isSafe) || true ,
81
- (),
82
- Forbidden (connectrpc.Error (
83
- code = io.grpc.Status .PERMISSION_DENIED .toConnectCode,
84
- message = s " Only POST-requests are allowed for method: $grpcMethod" .some
85
- ))
86
- ).leftSemiflatMap(identity)
87
-
88
- response <- method.descriptor.getType match
89
- case MethodType .UNARY =>
90
- EitherT .right(handleUnary(dsl, method, entity, ipChannel))
91
- case unsupported =>
92
- EitherT .left(NotImplemented (connectrpc.Error (
93
- code = io.grpc.Status .UNIMPLEMENTED .toConnectCode,
94
- message = s " Unsupported method type: $unsupported" .some
95
- )))
96
- yield response
97
-
98
- eitherT.merge
99
- }
100
-
101
- HttpRoutes .of[F ] {
102
- case req@ Method .GET -> Root / serviceName / methodName :? EncodingQP (contentType) +& MessageQP (message) =>
103
- val grpcMethod = grpcMethodName(serviceName, methodName)
104
- val entity = RequestEntity [F ](message, req.headers)
105
-
106
- handle(Method .GET , contentType.some, entity, grpcMethod)
107
- case req@ Method .POST -> Root / serviceName / methodName =>
108
- val grpcMethod = grpcMethodName(serviceName, methodName)
109
- val contentType = req.contentType.map(_.mediaType)
110
- val entity = RequestEntity [F ](req)
47
+ method <- EitherT .fromOptionM(
48
+ methodRegistry.get(grpcMethodName).pure[F ],
49
+ NotFound (connectrpc.Error (
50
+ code = io.grpc.Status .NOT_FOUND .toConnectCode,
51
+ message = s " Method not found: $grpcMethodName" .some
52
+ ))
53
+ )
111
54
112
- handle(Method .POST , contentType, entity, grpcMethod)
113
- }
55
+ _ <- EitherT .cond[F ](
56
+ // Support GET-requests for all methods until https://github.com/scalapb/ScalaPB/pull/1774 is merged
57
+ httpMethod == Method .POST || (httpMethod == Method .GET && method.descriptor.isSafe) || true ,
58
+ (),
59
+ Forbidden (connectrpc.Error (
60
+ code = io.grpc.Status .PERMISSION_DENIED .toConnectCode,
61
+ message = s " Only POST-requests are allowed for method: $grpcMethodName" .some
62
+ ))
63
+ ).leftSemiflatMap(identity)
64
+
65
+ response <- method.descriptor.getType match
66
+ case MethodType .UNARY =>
67
+ EitherT .right(handleUnary(method, entity, channel))
68
+ case unsupported =>
69
+ EitherT .left(NotImplemented (connectrpc.Error (
70
+ code = io.grpc.Status .UNIMPLEMENTED .toConnectCode,
71
+ message = s " Unsupported method type: $unsupported" .some
72
+ )))
73
+ yield response
74
+
75
+ eitherT.merge
114
76
}
115
77
116
- private def handleUnary [F [_] : Async ](
117
- dsl : Http4sDsl [F ],
78
+ private def handleUnary (
118
79
method : MethodRegistry .Entry ,
119
80
req : RequestEntity [F ],
120
81
channel : Channel
121
82
)(using codec : MessageCodec [F ]): F [Response [F ]] = {
122
- import dsl .*
123
-
124
83
if (logger.isTraceEnabled) {
125
84
// Used in conformance tests
126
85
req.headers.get[`X-Test-Case-Name`] match {
@@ -204,8 +163,8 @@ object ConnectRpcHttpRoutes {
204
163
(messageParts.mkString(" \n " ), details)
205
164
)
206
165
207
- val message = messageWithDetails.map(_._1)
208
- val details = messageWithDetails.map(_._2).getOrElse(Seq .empty)
166
+ // val message = messageWithDetails.map(_._1)
167
+ // val details = messageWithDetails.map(_._2).getOrElse(Seq.empty)
209
168
210
169
val httpStatus = grpcStatus.toHttpStatus
211
170
val connectCode = grpcStatus.toConnectCode
@@ -223,6 +182,4 @@ object ConnectRpcHttpRoutes {
223
182
}
224
183
}
225
184
226
- private inline def grpcMethodName (service : String , method : String ): String = service + " /" + method
227
-
228
185
}
0 commit comments