-
Notifications
You must be signed in to change notification settings - Fork 342
/
GraphQLRoutesConfiguration.kt
78 lines (71 loc) · 3.28 KB
/
GraphQLRoutesConfiguration.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/*
* Copyright 2023 Expedia, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.expediagroup.graphql.server.spring
import com.expediagroup.graphql.server.spring.execution.SpringGraphQLServer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Import
import org.springframework.http.MediaType
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.bodyValueAndAwait
import org.springframework.web.reactive.function.server.buildAndAwait
import org.springframework.web.reactive.function.server.coRouter
/**
* Default route configuration for GraphQL endpoints.
* Can handle requests over GET or POST as per the following guidelines:
* https://graphql.org/learn/serving-over-http/
*/
@Configuration
@Import(GraphQLSchemaConfiguration::class)
class GraphQLRoutesConfiguration(
private val config: GraphQLConfigurationProperties,
private val graphQLServer: SpringGraphQLServer
) {
@Bean
fun graphQLRoutes() = coRouter {
val isEndpointRequest = POST(config.endpoint) or GET(config.endpoint)
val isNotWebSocketRequest = headers { isWebSocketHeaders(it) }.not()
(isEndpointRequest and isNotWebSocketRequest).invoke { serverRequest ->
try {
val graphQLResponse = graphQLServer.execute(serverRequest)
val acceptMediaType = serverRequest
.headers()
.accept()
.find { it != MediaType.ALL && it.includes(MediaType.APPLICATION_GRAPHQL_RESPONSE) }
?.let { MediaType.APPLICATION_GRAPHQL_RESPONSE }
?: MediaType.APPLICATION_JSON
if (graphQLResponse != null) {
ok().contentType(acceptMediaType).bodyValueAndAwait(graphQLResponse)
} else {
badRequest().buildAndAwait()
}
} catch (e: Exception) {
badRequest().buildAndAwait()
}
}
}
/**
* These headers are defined in the HTTP Protocol upgrade mechanism that identify a web socket request
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism
*/
private fun isWebSocketHeaders(headers: ServerRequest.Headers): Boolean {
val isUpgrade = requestContainsHeader(headers, "Connection", "Upgrade")
val isWebSocket = requestContainsHeader(headers, "Upgrade", "websocket")
return isUpgrade and isWebSocket
}
private fun requestContainsHeader(headers: ServerRequest.Headers, headerName: String, headerValue: String): Boolean =
headers.header(headerName).map { it.lowercase() }.contains(headerValue.lowercase())
}