Browse Source

Merge branch 'master' of github.com:grpc/grpc-swift into optional-completion-blocks

Daniel Alm 7 years ago
parent
commit
8b34c26255

+ 12 - 12
Plugin/Sources/protoc-gen-swiftgrpc/filters.swift

@@ -28,7 +28,7 @@ extension String {
   }
 
   var uppercasedFirst: String {
-    var out = characters
+    var out = Substring(self)
     if let first = out.popFirst() {
       return String(first).uppercased() + String(out)
     } else {
@@ -58,7 +58,7 @@ func protoMessageName(_ descriptor: SwiftProtobufPluginLibrary.Descriptor) -> St
   return namer.fullName(message: descriptor)
 }
 
-func pathName(filter _: String, arguments: [Any?]) throws -> String {
+func pathName(arguments: [Any?]) throws -> String {
   if arguments.count != 3 {
     throw invalidArgumentCount(filter: "path", expected: 3)
   }
@@ -116,31 +116,31 @@ class GRPCFilterExtension: Extension {
     super.init()
     // initialize template engine and add custom filters
     let ext = self
-    ext.registerFilter("call") { (_: Any?, arguments: [Any?]) in
+    ext.registerFilter("call") { (_, arguments: [Any?]) in
       return try packageServiceMethodName(filter: "call", arguments: arguments) + "Call"
     }
-    ext.registerFilter("session") { (_: Any?, arguments: [Any?]) in
+    ext.registerFilter("session") { (_, arguments: [Any?]) in
       return try packageServiceMethodName(filter: "session", arguments: arguments) + "Session"
     }
-    ext.registerFilter("path") { (_: Any?, arguments: [Any?]) in
-      return try pathName(filter: "path", arguments: arguments)
+    ext.registerFilter("path") { (_, arguments: [Any?]) in
+      return try pathName(arguments: arguments)
     }
-    ext.registerFilter("provider") { (_: Any?, arguments: [Any?]) in
+    ext.registerFilter("provider") { (_, arguments: [Any?]) in
       return try packageServiceName(filter: "provider", arguments: arguments) + "Provider"
     }
-    ext.registerFilter("clienterror") { (_: Any?, arguments: [Any?]) in
+    ext.registerFilter("clienterror") { (_, arguments: [Any?]) in
       return try packageServiceName(filter: "clienterror", arguments: arguments) + "ClientError"
     }
-    ext.registerFilter("serviceclass") { (_: Any?, arguments: [Any?]) in
+    ext.registerFilter("serviceclass") { (_, arguments: [Any?]) in
       return try packageServiceName(filter: "serviceclass", arguments: arguments) + "Service"
     }
-    ext.registerFilter("servererror") { (_: Any?, arguments: [Any?]) in
+    ext.registerFilter("servererror") { (_, arguments: [Any?]) in
       return try packageServiceName(filter: "servererror", arguments: arguments) + "ServerError"
     }
-    ext.registerFilter("server") { (_: Any?, arguments: [Any?]) in
+    ext.registerFilter("server") { (_, arguments: [Any?]) in
       return try packageServiceName(filter: "server", arguments: arguments) + "Server"
     }
-    ext.registerFilter("service") { (_: Any?, arguments: [Any?]) in
+    ext.registerFilter("service") { (_, arguments: [Any?]) in
       return try packageServiceName(filter: "server", arguments: arguments)
     }
     ext.registerFilter("input") { (value: Any?) in

+ 24 - 24
Sources/CgRPC/shim/metadata.c

@@ -13,6 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+#include <grpc/support/alloc.h>
+
 #include "internal.h"
 #include "cgrpc.h"
 
@@ -20,13 +23,14 @@
 #include "string.h"
 
 cgrpc_metadata_array *cgrpc_metadata_array_create() {
-  cgrpc_metadata_array *metadata = (cgrpc_metadata_array *) malloc(sizeof(cgrpc_metadata_array));
-  memset(metadata, 0, sizeof(cgrpc_metadata_array));
+  cgrpc_metadata_array *metadata = (cgrpc_metadata_array *) gpr_malloc(sizeof(cgrpc_metadata_array));
+  grpc_metadata_array_init(metadata);
   return metadata;
 }
 
 void cgrpc_metadata_array_destroy(cgrpc_metadata_array *array) {
   grpc_metadata_array_destroy(array);
+  gpr_free(array);
 }
 
 size_t cgrpc_metadata_array_get_count(cgrpc_metadata_array *array) {
@@ -49,19 +53,8 @@ char *cgrpc_metadata_array_copy_value_at_index(cgrpc_metadata_array *array, size
   return str;
 }
 
-size_t cgrpc_metadata_array_get_value_length_at_index(cgrpc_metadata_array *array, size_t index) {
-  return GRPC_SLICE_LENGTH(array->metadata[index].value);
-  /*
-  int length = GRPC_SLICE_LENGTH(array->metadata[index].value);
-  char *str = (char *) malloc(length + 1);
-  memcpy(str, GRPC_SLICE_START_PTR(array->metadata[index].value), length);
-  str[length] = 0;
-  return str;
-*/
-}
-
 void cgrpc_metadata_array_move_metadata(cgrpc_metadata_array *destination,
-                                       cgrpc_metadata_array *source) {
+                                        cgrpc_metadata_array *source) {
   destination->count = source->count;
   destination->capacity = source->capacity;
   destination->metadata = source->metadata;
@@ -72,15 +65,22 @@ void cgrpc_metadata_array_move_metadata(cgrpc_metadata_array *destination,
 }
 
 void cgrpc_metadata_array_append_metadata(cgrpc_metadata_array *metadata, const char *key, const char *value) {
-  if (!metadata->count) {
-    metadata->metadata = (grpc_metadata *) malloc(10 * sizeof(grpc_metadata));
-    metadata->count = 0;
-    metadata->capacity = 10;
-  }
-  if (metadata->count < metadata->capacity) {
-    size_t i = metadata->count;
-    metadata->metadata[i].key = grpc_slice_from_copied_string(key);
-    metadata->metadata[i].value = grpc_slice_from_copied_string(value);
-    metadata->count++;
+  if (metadata->count >= metadata->capacity) {
+    size_t new_capacity = 2 * metadata->capacity;
+    if (new_capacity < 10) {
+      new_capacity = 10;
+    }
+    
+    if (metadata->metadata != NULL) {
+      metadata->metadata = gpr_realloc(metadata->metadata, new_capacity * sizeof(grpc_metadata));
+    } else {
+      metadata->metadata = gpr_malloc(new_capacity * sizeof(grpc_metadata));
+    }
+    metadata->capacity = new_capacity;
   }
+
+  size_t i = metadata->count;
+  metadata->metadata[i].key = grpc_slice_from_copied_string(key);
+  metadata->metadata[i].value = grpc_slice_from_copied_string(value);
+  metadata->count++;
 }

+ 4 - 4
Sources/gRPC/Call.swift

@@ -218,7 +218,7 @@ public class Call {
         throw CallError.invalidMessage
       }
       operations = [
-        .sendInitialMetadata(metadata.copy() as! Metadata),
+        .sendInitialMetadata(metadata.copy()),
         .receiveInitialMetadata,
         .receiveStatusOnClient,
         .sendMessage(ByteBuffer(data: message)),
@@ -230,14 +230,14 @@ public class Call {
         throw CallError.invalidMessage
       }
       operations = [
-        .sendInitialMetadata(metadata.copy() as! Metadata),
+        .sendInitialMetadata(metadata.copy()),
         .receiveInitialMetadata,
         .sendMessage(ByteBuffer(data: message)),
         .sendCloseFromClient
       ]
     case .clientStreaming, .bidiStreaming:
       operations = [
-        .sendInitialMetadata(metadata.copy() as! Metadata),
+        .sendInitialMetadata(metadata.copy()),
         .receiveInitialMetadata
       ]
     }
@@ -298,7 +298,7 @@ public class Call {
 
   // Receive a message over a streaming connection.
   /// - Throws: `CallError` if fails to call.
-  public func receiveMessage(callback: @escaping ((Data?) throws -> Void)) throws {
+  public func receiveMessage(callback: @escaping (Data?) throws -> Void) throws {
     try perform(OperationGroup(call: self, operations: [.receiveMessage]) { operationGroup in
       if operationGroup.success {
         if let messageBuffer = operationGroup.receivedMessage() {

+ 25 - 24
Sources/gRPC/Handler.swift

@@ -21,13 +21,13 @@ import Foundation // for String.Encoding
 /// A gRPC request handler
 public class Handler {
   /// Pointer to underlying C representation
-  private var underlyingHandler: UnsafeMutableRawPointer
+  fileprivate let underlyingHandler: UnsafeMutableRawPointer
 
   /// Completion queue for handler response operations
-  var completionQueue: CompletionQueue
+  let completionQueue: CompletionQueue
 
   /// Metadata received with the request
-  public var requestMetadata: Metadata
+  public let requestMetadata: Metadata
 
   /// A Call object that can be used to respond to the request
   lazy var call: Call = {
@@ -37,33 +37,26 @@ public class Handler {
   }()
 
   /// The host name sent with the request
-  public lazy var host: String = {
-    if let string = cgrpc_handler_copy_host(self.underlyingHandler) {
-      defer {
-        cgrpc_free_copied_string(string)
-      }
-      return String(cString: string, encoding: .utf8)!
-    } else {
-      return ""
-    }
+  public lazy var host: String? = {
+    // We actually know that this method will never return nil,
+    // so we can forcibly unwrap the result. (Also below.)
+    let string = cgrpc_handler_copy_host(self.underlyingHandler)!
+    defer { cgrpc_free_copied_string(string) }
+    return String(cString: string, encoding: .utf8)
   }()
 
   /// The method name sent with the request
-  public lazy var method: String = {
-    if let string = cgrpc_handler_copy_method(self.underlyingHandler) {
-      defer {
-        cgrpc_free_copied_string(string)
-      }
-      return String(cString: string, encoding: .utf8)!
-    } else {
-      return ""
-    }
+  public lazy var method: String? = {
+    let string = cgrpc_handler_copy_method(self.underlyingHandler)!
+    defer { cgrpc_free_copied_string(string) }
+    return String(cString: string, encoding: .utf8)
   }()
 
   /// The caller address associated with the request
-  public lazy var caller: String = {
-    String(cString: cgrpc_handler_call_peer(self.underlyingHandler),
-           encoding: .utf8)!
+  public lazy var caller: String? = {
+    let string = cgrpc_handler_call_peer(self.underlyingHandler)!
+    defer { cgrpc_free_copied_string(string) }
+    return String(cString: string, encoding: .utf8)
   }()
 
   /// Initializes a Handler
@@ -235,3 +228,11 @@ public class Handler {
     try call.perform(operations)
   }
 }
+
+extension Handler: Hashable {
+  public var hashValue: Int { return underlyingHandler.hashValue }
+  
+  public static func ==(A: Handler, B: Handler) -> Bool {
+    return A === B
+  }
+}

+ 30 - 55
Sources/gRPC/Metadata.swift

@@ -18,18 +18,8 @@
 #endif
 import Foundation // for String.Encoding
 
-/// An item of metadata
-private struct MetadataPair {
-  var key: String
-  var value: String
-  init(key: String, value: String) {
-    self.key = key
-    self.value = value
-  }
-}
-
 /// Metadata sent with gRPC messages
-public class Metadata: CustomStringConvertible, NSCopying {
+public class Metadata: CustomStringConvertible {
   /// Pointer to underlying C representation
   var underlyingArray: UnsafeMutableRawPointer
 
@@ -41,23 +31,10 @@ public class Metadata: CustomStringConvertible, NSCopying {
     underlyingArray = cgrpc_metadata_array_create()
   }
 
-  public init(_ pairs: [[String: String]]) {
-    underlyingArray = cgrpc_metadata_array_create()
-    for pair in pairs {
-      for key in pair.keys {
-        if let value = pair[key] {
-          add(key: key, value: value)
-        }
-      }
-    }
-  }
-
   public init(_ pairs: [String: String]) {
     underlyingArray = cgrpc_metadata_array_create()
-    for key in pairs.keys {
-      if let value = pairs[key] {
-        add(key: key, value: value)
-      }
+    for (key, value) in pairs {
+      add(key: key, value: value)
     }
   }
 
@@ -68,49 +45,47 @@ public class Metadata: CustomStringConvertible, NSCopying {
   public func count() -> Int {
     return cgrpc_metadata_array_get_count(underlyingArray)
   }
-
-  public func key(_ index: Int) -> String {
-    if let string = cgrpc_metadata_array_copy_key_at_index(underlyingArray, index) {
-      defer {
-        cgrpc_free_copied_string(string)
-      }
-      if let key = String(cString: string, encoding: String.Encoding.utf8) {
-        return key
-      }
-    }
-    return "<binary-metadata-key>"
+  
+  // Returns `nil` for non-UTF8 metadata key strings.
+  public func key(_ index: Int) -> String? {
+    // We actually know that this method will never return nil,
+    // so we can forcibly unwrap the result. (Also below.)
+    let keyData = cgrpc_metadata_array_copy_key_at_index(underlyingArray, index)!
+    defer { cgrpc_free_copied_string(keyData) }
+    return String(cString: keyData, encoding: String.Encoding.utf8)
   }
-
-  public func value(_ index: Int) -> String {
-    if let string = cgrpc_metadata_array_copy_value_at_index(underlyingArray, index) {
-      defer {
-        cgrpc_free_copied_string(string)
-      }
-      if let value = String(cString: string, encoding: String.Encoding.utf8) {
-        return value
-      }
-    }
-    return "<binary-metadata-value>"
+  
+  // Returns `nil` for non-UTF8 metadata value strings.
+  public func value(_ index: Int) -> String? {
+    // We actually know that this method will never return nil,
+    // so we can forcibly unwrap the result. (Also below.)
+    let valueData = cgrpc_metadata_array_copy_value_at_index(underlyingArray, index)!
+    defer { cgrpc_free_copied_string(valueData) }
+    return String(cString: valueData, encoding: String.Encoding.utf8)
   }
-
+  
   public func add(key: String, value: String) {
     cgrpc_metadata_array_append_metadata(underlyingArray, key, value)
   }
-
+  
   public var description: String {
     var result = ""
     for i in 0..<count() {
       let key = self.key(i)
       let value = self.value(i)
-      result += key + ":" + value + "\n"
+      result += (key ?? "(nil)") + ":" + (value ?? "(nil)") + "\n"
     }
     return result
   }
-
-  public func copy(with _: NSZone? = nil) -> Any {
+  
+  public func copy() -> Metadata {
     let copy = Metadata()
-    for i in 0..<count() {
-      copy.add(key: key(i), value: value(i))
+    for index in 0..<count() {
+      let keyData = cgrpc_metadata_array_copy_key_at_index(underlyingArray, index)!
+      defer { cgrpc_free_copied_string(keyData) }
+      let valueData = cgrpc_metadata_array_copy_value_at_index(underlyingArray, index)!
+      defer { cgrpc_free_copied_string(valueData) }
+      cgrpc_metadata_array_append_metadata(copy.underlyingArray, keyData, valueData)
     }
     return copy
   }

+ 4 - 8
Sources/gRPC/OperationGroup.swift

@@ -164,14 +164,10 @@ class OperationGroup {
     for (i, operation) in operations.enumerated() {
       switch operation {
       case .receiveStatusOnClient:
-        if let string = cgrpc_observer_recv_status_on_client_copy_status_details(underlyingObservers[i]) {
-          defer {
-            cgrpc_free_copied_string(string)
-          }
-          return String(cString: string, encoding: String.Encoding.utf8)!
-        } else {
-          return nil
-        }
+        // We actually know that this method will never return nil, so we can forcibly the result. (Also below.)
+        let string = cgrpc_observer_recv_status_on_client_copy_status_details(underlyingObservers[i])!
+        defer { cgrpc_free_copied_string(string) }
+        return String(cString: string, encoding: String.Encoding.utf8)
       default:
         continue
       }

+ 5 - 7
Sources/gRPC/Server.swift

@@ -22,16 +22,16 @@ import Foundation
 /// gRPC Server
 public class Server {
   /// Pointer to underlying C representation
-  private var underlyingServer: UnsafeMutableRawPointer
+  private let underlyingServer: UnsafeMutableRawPointer
 
   /// Completion queue used for server operations
-  var completionQueue: CompletionQueue
+  let completionQueue: CompletionQueue
 
   /// Active handlers
-  private var handlers: NSMutableSet
+  private var handlers = Set<Handler>()
 
   /// Mutex for synchronizing access to handlers
-  private var handlersMutex: Mutex = Mutex()
+  private let handlersMutex: Mutex = Mutex()
 
   /// Optional callback when server stops serving
   public var onCompletion: (() -> Void)?
@@ -43,7 +43,6 @@ public class Server {
     underlyingServer = cgrpc_server_create(address)
     completionQueue = CompletionQueue(underlyingCompletionQueue: cgrpc_server_get_completion_queue(underlyingServer))
     completionQueue.name = "Server " + address
-    handlers = NSMutableSet()
   }
 
   /// Initializes a secure Server
@@ -55,7 +54,6 @@ public class Server {
     underlyingServer = cgrpc_server_create_secure(address, key, certs)
     completionQueue = CompletionQueue(underlyingCompletionQueue: cgrpc_server_get_completion_queue(underlyingServer))
     completionQueue.name = "Server " + address
-    handlers = NSMutableSet()
   }
 
   deinit {
@@ -81,7 +79,7 @@ public class Server {
               if event.success != 0 {
                 // hold onto the handler while it runs
                 self.handlersMutex.synchronize {
-                  self.handlers.add(handler)
+                  self.handlers.insert(handler)
                 }
                 // this will start the completion queue on a new thread
                 handler.completionQueue.runToCompletion(callbackQueue: dispatchQueue) {

+ 4 - 4
Sources/gRPC/gRPC.swift

@@ -31,15 +31,15 @@ public func shutdown() {
 /// Returns version of underlying gRPC library
 ///
 /// Returns: gRPC version string
-public func version() -> String {
-  return String(cString: grpc_version_string(), encoding: String.Encoding.utf8)!
+public func version() -> String? {
+  return String(cString: grpc_version_string(), encoding: String.Encoding.utf8)
 }
 
 /// Returns name associated with gRPC version
 ///
 /// Returns: gRPC version name
-public func gStandsFor() -> String {
-  return String(cString: grpc_g_stands_for(), encoding: String.Encoding.utf8)!
+public func gStandsFor() -> String? {
+  return String(cString: grpc_g_stands_for(), encoding: String.Encoding.utf8)
 }
 
 /// Status codes for gRPC operations (replicated from status_code_enum.h)

+ 25 - 10
Tests/gRPCTests/GRPCTests.swift

@@ -53,9 +53,21 @@ let initialServerMetadata =
   ]
 let trailingServerMetadata =
   [
+    // We have more than ten entries here to ensure that even large metadata entries work
+    // and aren't limited by e.g. a fixed-size entry buffer.
     "0": "zero",
     "1": "one",
-    "2": "two"
+    "2": "two",
+    "3": "three",
+    "4": "four",
+    "5": "five",
+    "6": "six",
+    "7": "seven",
+    "8": "eight",
+    "9": "nine",
+    "10": "ten",
+    "11": "eleven",
+    "12": "twelve"
   ]
 let steps = 10
 let hello = "/hello"
@@ -68,7 +80,7 @@ func runTest(useSSL: Bool) {
   let serverRunningSemaphore = DispatchSemaphore(value: 0)
 
   // create the server
-  var server: gRPC.Server!
+  let server: gRPC.Server
   if useSSL {
     let certificateURL = URL(fileURLWithPath: "Tests/ssl.crt")
     let keyURL = URL(fileURLWithPath: "Tests/ssl.key")
@@ -109,18 +121,21 @@ func runTest(useSSL: Bool) {
   _ = serverRunningSemaphore.wait(timeout: DispatchTime.distantFuture)
 }
 
-func verify_metadata(_ metadata: Metadata, expected: [String: String]) {
+func verify_metadata(_ metadata: Metadata, expected: [String: String], file: StaticString = #file, line: UInt = #line) {
   XCTAssertGreaterThanOrEqual(metadata.count(), expected.count)
+  var allPresentKeys = Set<String>()
   for i in 0..<metadata.count() {
-    if expected[metadata.key(i)] != nil {
-      XCTAssertEqual(metadata.value(i), expected[metadata.key(i)])
-    }
+    guard let expectedValue = expected[metadata.key(i)!]
+      else { continue }
+    allPresentKeys.insert(metadata.key(i)!)
+    XCTAssertEqual(metadata.value(i), expectedValue, file: file, line: line)
   }
+  XCTAssertEqual(allPresentKeys.sorted(), expected.keys.sorted(), file: file, line: line)
 }
 
 func runClient(useSSL: Bool) throws {
   let message = clientText.data(using: .utf8)
-  var channel: gRPC.Channel!
+  let channel: gRPC.Channel
 
   if useSSL {
     let certificateURL = URL(fileURLWithPath: "Tests/ssl.crt")
@@ -136,7 +151,7 @@ func runClient(useSSL: Bool) throws {
   }
 
   channel.host = host
-  for i in 0..<steps {
+  for _ in 0..<steps {
     let sem = DispatchSemaphore(value: 0)
     let method = hello
     let call = channel.makeCall(method)
@@ -147,8 +162,8 @@ func runClient(useSSL: Bool) throws {
       XCTAssertEqual(response.statusCode, statusCode)
       XCTAssertEqual(response.statusMessage, statusMessage)
       // verify the message from the server
-      let resultData = response.resultData
-      let messageString = String(data: resultData!, encoding: .utf8)
+      let resultData = response.resultData!
+      let messageString = String(data: resultData, encoding: .utf8)
       XCTAssertEqual(messageString, serverText)
       // verify the initial metadata from the server
       let initialMetadata = response.initialMetadata!