/src/grpc-swift/Sources/GRPC/GRPCServerRequestRoutingHandler.swift
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2019, gRPC Authors All rights reserved. |
3 | | * |
4 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | * you may not use this file except in compliance with the License. |
6 | | * You may obtain a copy of the License at |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | import Logging |
17 | | import NIOCore |
18 | | import NIOHPACK |
19 | | import NIOHTTP1 |
20 | | import NIOHTTP2 |
21 | | import SwiftProtobuf |
22 | | |
23 | | /// Provides ``GRPCServerHandlerProtocol`` objects for the methods on a particular service name. |
24 | | /// |
25 | | /// Implemented by the generated code. |
26 | | public protocol CallHandlerProvider: AnyObject { |
27 | | /// The name of the service this object is providing methods for, including the package path. |
28 | | /// |
29 | | /// - Example: "io.grpc.Echo.EchoService" |
30 | | var serviceName: Substring { get } |
31 | | |
32 | | /// Returns a call handler for the method with the given name, if this service provider implements |
33 | | /// the given method. Returns `nil` if the method is not handled by this provider. |
34 | | /// - Parameters: |
35 | | /// - name: The name of the method to handle. |
36 | | /// - context: An opaque context providing components to construct the handler with. |
37 | | func handle(method name: Substring, context: CallHandlerContext) -> GRPCServerHandlerProtocol? |
38 | | } |
39 | | |
40 | | // This is public because it will be passed into generated code, all members are `internal` because |
41 | | // the context will get passed from generated code back into gRPC library code and all members should |
42 | | // be considered an implementation detail to the user. |
43 | | public struct CallHandlerContext { |
44 | | @usableFromInline |
45 | | internal var errorDelegate: ServerErrorDelegate? |
46 | | @usableFromInline |
47 | | internal var logger: Logger |
48 | | @usableFromInline |
49 | | internal var encoding: ServerMessageEncoding |
50 | | @usableFromInline |
51 | | internal var eventLoop: EventLoop |
52 | | @usableFromInline |
53 | | internal var path: String |
54 | | @usableFromInline |
55 | | internal var remoteAddress: SocketAddress? |
56 | | @usableFromInline |
57 | | internal var responseWriter: GRPCServerResponseWriter |
58 | | @usableFromInline |
59 | | internal var allocator: ByteBufferAllocator |
60 | | @usableFromInline |
61 | | internal var closeFuture: EventLoopFuture<Void> |
62 | | } |
63 | | |
64 | | /// A call URI split into components. |
65 | | struct CallPath { |
66 | | /// The name of the service to call. |
67 | | var service: String.UTF8View.SubSequence |
68 | | /// The name of the method to call. |
69 | | var method: String.UTF8View.SubSequence |
70 | | |
71 | | /// Character used to split the path into components. |
72 | 106k | private let pathSplitDelimiter = UInt8(ascii: "/") |
73 | | |
74 | | /// Split a path into service and method. |
75 | | /// Split is done in UTF8 as this turns out to be approximately 10x faster than a simple split. |
76 | | /// URI format: "/package.Servicename/MethodName" |
77 | 106k | init?(requestURI: String) { |
78 | 106k | var utf8View = requestURI.utf8[...] |
79 | 106k | // Check and remove the split character at the beginning. |
80 | 106k | guard let prefix = utf8View.trimPrefix(to: self.pathSplitDelimiter), prefix.isEmpty else { |
81 | 2.17k | return nil |
82 | 104k | } |
83 | 104k | guard let service = utf8View.trimPrefix(to: pathSplitDelimiter) else { |
84 | 761 | return nil |
85 | 103k | } |
86 | 103k | guard let method = utf8View.trimPrefix(to: pathSplitDelimiter) else { |
87 | 2.17k | return nil |
88 | 101k | } |
89 | 101k | |
90 | 101k | self.service = service |
91 | 101k | self.method = method |
92 | 101k | } |
93 | | } |
94 | | |
95 | | extension Collection where Self == Self.SubSequence, Self.Element: Equatable { |
96 | | /// Trims out the prefix up to `separator`, and returns it. |
97 | | /// Sets self to the subsequence after the separator, and returns the subsequence before the separator. |
98 | | /// If self is empty returns `nil` |
99 | | /// - parameters: |
100 | | /// - separator : The Element between the head which is returned and the rest which is left in self. |
101 | | /// - returns: SubSequence containing everything between the beginning and the first occurrence of |
102 | | /// `separator`. If `separator` is not found this will be the entire Collection. If the collection is empty |
103 | | /// returns `nil` |
104 | 314k | mutating func trimPrefix(to separator: Element) -> SubSequence? { |
105 | 314k | guard !self.isEmpty else { |
106 | 2.93k | return nil |
107 | 311k | } |
108 | 311k | if let separatorIndex = self.firstIndex(of: separator) { |
109 | 206k | let indexAfterSeparator = self.index(after: separatorIndex) |
110 | 206k | defer { self = self[indexAfterSeparator...] } |
111 | 206k | return self[..<separatorIndex] |
112 | 206k | } else { |
113 | 104k | defer { self = self[self.endIndex...] } |
114 | 104k | return self[...] |
115 | 104k | } |
116 | 311k | } |
117 | | } |