diff --git a/README.md b/README.md index 3b4fb18..5d420a1 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,6 @@ [![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FGraphQLSwift%2Fgraphql-vapor%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/GraphQLSwift/graphql-vapor) [![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FGraphQLSwift%2Fgraphql-vapor%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/GraphQLSwift/graphql-vapor) -> ***WARNING***: This package is in v0.x beta. It's API is still evolving and is subject to breaking changes in minor version bumps. - A Swift library for integrating [GraphQL](https://github.com/GraphQLSwift/GraphQL) with [Vapor](https://github.com/vapor/vapor), enabling you to easily expose GraphQL APIs in your Vapor applications. ## Features diff --git a/Sources/GraphQLVapor/GraphQLConfig.swift b/Sources/GraphQLVapor/GraphQLConfig.swift index 89bfcb8..92049f7 100644 --- a/Sources/GraphQLVapor/GraphQLConfig.swift +++ b/Sources/GraphQLVapor/GraphQLConfig.swift @@ -8,6 +8,7 @@ public struct GraphQLConfig< >: Sendable { let allowGet: Bool let allowMissingAcceptHeader: Bool + let maxBodySize: ByteCount? let ide: IDE let subscriptionProtocols: Set let websocket: WebSocket @@ -17,6 +18,7 @@ public struct GraphQLConfig< /// - Parameters: /// - allowGet: Whether to allow GraphQL queries via `GET` requests. /// - allowMissingAcceptHeader: Whether to allow clients to omit "Accept" headers and default to `application/graphql-response+json` encoded responses. + /// - maxBodySize: The maximum size of GraphQL requests in bytes. If not provided, this uses the default [`app.routes.defaultMaxBodySize`](https://docs.vapor.codes/basics/routing/#body-streaming) /// - ide: The IDE to expose /// - subscriptionProtocols: Protocols used to support GraphQL subscription requests /// - websocket: WebSocket-specific configuration @@ -24,6 +26,7 @@ public struct GraphQLConfig< public init( allowGet: Bool = true, allowMissingAcceptHeader: Bool = false, + maxBodySize: ByteCount? = nil, ide: IDE = .graphiql, subscriptionProtocols: Set = [], websocket: WebSocket = .init( @@ -34,12 +37,14 @@ public struct GraphQLConfig< ) { self.allowGet = allowGet self.allowMissingAcceptHeader = allowMissingAcceptHeader + self.maxBodySize = maxBodySize self.additionalValidationRules = additionalValidationRules self.ide = ide self.subscriptionProtocols = subscriptionProtocols self.websocket = websocket } + /// An embeddable GraphQL IDE public struct IDE: Sendable, Equatable { /// GraphiQL: https://github.com/graphql/graphiql public static var graphiql: Self { @@ -58,6 +63,7 @@ public struct GraphQLConfig< } } + /// A GraphQL subscription implementation public struct SubscriptionProtocol: Sendable, Hashable { /// Expose GraphQL subscriptions over WebSockets public static var websocket: Self { @@ -70,6 +76,7 @@ public struct GraphQLConfig< } } + /// WebSocket configuration public struct WebSocket: Sendable { let onWebSocketInit: @Sendable (WebSocketInit, Request) async throws -> WebSocketInitResult diff --git a/Sources/GraphQLVapor/HTTP/GraphQLHandler+HTTP.swift b/Sources/GraphQLVapor/HTTP/GraphQLHandler+HTTP.swift index c276465..0dc758a 100644 --- a/Sources/GraphQLVapor/HTTP/GraphQLHandler+HTTP.swift +++ b/Sources/GraphQLVapor/HTTP/GraphQLHandler+HTTP.swift @@ -86,24 +86,16 @@ extension GraphQLHandler { let response = Response() - let configuredMediaTypes: Set = [.jsonGraphQL, .json] + let configuredMediaTypes: [HTTPMediaType] = [.jsonGraphQL, .json] // Try to respond with the best matching media type, in order var selectedMediaType: HTTPMediaType? = nil - for mediaType in headers.accept.mediaTypes { - if configuredMediaTypes.contains(mediaType) { - selectedMediaType = mediaType - break - } - } - - // If no exact matches, look for any matching wildcards - if selectedMediaType == nil { - let acceptableMediaSet = HTTPMediaTypeSet(mediaTypes: headers.accept.mediaTypes) - for mediaType in configuredMediaTypes { - if acceptableMediaSet.contains(mediaType) { - selectedMediaType = mediaType - break + headerLoop: for mediaType in headers.accept.mediaTypes { + for configuredMediaType in configuredMediaTypes { + // Note that `==` handles wildcards. + if mediaType == configuredMediaType { + selectedMediaType = configuredMediaType + break headerLoop } } } diff --git a/Sources/GraphQLVapor/RoutesBuilder+graphql.swift b/Sources/GraphQLVapor/RoutesBuilder+graphql.swift index 673857e..c71e969 100644 --- a/Sources/GraphQLVapor/RoutesBuilder+graphql.swift +++ b/Sources/GraphQLVapor/RoutesBuilder+graphql.swift @@ -63,7 +63,7 @@ public extension RoutesBuilder { } return try await handler.handleGet(request: request) } - post(path) { request in + on(.POST, path, body: .collect(maxSize: config.maxBodySize)) { request in try await handler.handlePost(request: request) } } diff --git a/Sources/GraphQLVapor/WebSocket/GraphQLHandler+handleWebSocket.swift b/Sources/GraphQLVapor/WebSocket/GraphQLHandler+handleWebSocket.swift index edbdcff..ce0cc7b 100644 --- a/Sources/GraphQLVapor/WebSocket/GraphQLHandler+handleWebSocket.swift +++ b/Sources/GraphQLVapor/WebSocket/GraphQLHandler+handleWebSocket.swift @@ -11,7 +11,7 @@ extension GraphQLHandler { let subProtocol = try negotiateSubProtocol(request: request) let response = Response(status: .switchingProtocols) response.upgrader = WebSocketUpgrader( - maxFrameSize: .default, + maxFrameSize: .init(integerLiteral: (config.maxBodySize ?? request.application.routes.defaultMaxBodySize).value), shouldUpgrade: { request.eventLoop.makeFutureWithTask { ["Sec-WebSocket-Protocol": subProtocol.rawValue]