Skip to content

Commit 3e332f8

Browse files
committed
Improve robustness of metadata string handling, fix top-level tests.
1 parent 18624e8 commit 3e332f8

File tree

5 files changed

+121
-89
lines changed

5 files changed

+121
-89
lines changed

Sources/CgRPC/include/CgRPC.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,9 @@ void cgrpc_operations_add_operation(cgrpc_operations *call, cgrpc_observer *obse
165165
cgrpc_metadata_array *cgrpc_metadata_array_create();
166166
void cgrpc_metadata_array_destroy(cgrpc_metadata_array *array);
167167
size_t cgrpc_metadata_array_get_count(cgrpc_metadata_array *array);
168-
const char *cgrpc_metadata_array_get_key_at_index(cgrpc_metadata_array *array, size_t index);
169-
const char *cgrpc_metadata_array_get_value_at_index(cgrpc_metadata_array *array, size_t index);
168+
const char *cgrpc_metadata_array_copy_key_at_index(cgrpc_metadata_array *array, size_t index);
169+
const char *cgrpc_metadata_array_copy_value_at_index(cgrpc_metadata_array *array, size_t index);
170+
void cgrpc_metadata_free_copied_string(const char *string);
170171
void cgrpc_metadata_array_move_metadata(cgrpc_metadata_array *dest, cgrpc_metadata_array *src);
171172
void cgrpc_metadata_array_append_metadata(cgrpc_metadata_array *metadata, const char *key, const char *value);
172173

Sources/CgRPC/shim/cgrpc.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,9 @@ void cgrpc_operations_add_operation(cgrpc_operations *call, cgrpc_observer *obse
165165
cgrpc_metadata_array *cgrpc_metadata_array_create();
166166
void cgrpc_metadata_array_destroy(cgrpc_metadata_array *array);
167167
size_t cgrpc_metadata_array_get_count(cgrpc_metadata_array *array);
168-
const char *cgrpc_metadata_array_get_key_at_index(cgrpc_metadata_array *array, size_t index);
169-
const char *cgrpc_metadata_array_get_value_at_index(cgrpc_metadata_array *array, size_t index);
168+
const char *cgrpc_metadata_array_copy_key_at_index(cgrpc_metadata_array *array, size_t index);
169+
const char *cgrpc_metadata_array_copy_value_at_index(cgrpc_metadata_array *array, size_t index);
170+
void cgrpc_metadata_free_copied_string(const char *string);
170171
void cgrpc_metadata_array_move_metadata(cgrpc_metadata_array *dest, cgrpc_metadata_array *src);
171172
void cgrpc_metadata_array_append_metadata(cgrpc_metadata_array *metadata, const char *key, const char *value);
172173

Sources/CgRPC/shim/metadata.c

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,35 @@ size_t cgrpc_metadata_array_get_count(cgrpc_metadata_array *array) {
3333
return array->count;
3434
}
3535

36-
const char *cgrpc_metadata_array_get_key_at_index(cgrpc_metadata_array *array, size_t index) {
37-
return (const char *) GRPC_SLICE_START_PTR(array->metadata[index].key);
36+
const char *cgrpc_metadata_array_copy_key_at_index(cgrpc_metadata_array *array, size_t index) {
37+
int length = GRPC_SLICE_LENGTH(array->metadata[index].key);
38+
char *str = (char *) malloc(length + 1);
39+
memcpy(str, GRPC_SLICE_START_PTR(array->metadata[index].key), length);
40+
str[length] = 0;
41+
return str;
3842
}
3943

40-
const char *cgrpc_metadata_array_get_value_at_index(cgrpc_metadata_array *array, size_t index) {
41-
return (const char *) GRPC_SLICE_START_PTR(array->metadata[index].value);
44+
const char *cgrpc_metadata_array_copy_value_at_index(cgrpc_metadata_array *array, size_t index) {
45+
int length = GRPC_SLICE_LENGTH(array->metadata[index].value);
46+
char *str = (char *) malloc(length + 1);
47+
memcpy(str, GRPC_SLICE_START_PTR(array->metadata[index].value), length);
48+
str[length] = 0;
49+
return str;
50+
}
51+
52+
void cgrpc_metadata_free_copied_string(const char *string) {
53+
free(string);
54+
}
55+
56+
int cgrpc_metadata_array_get_value_length_at_index(cgrpc_metadata_array *array, size_t index) {
57+
return GRPC_SLICE_LENGTH(array->metadata[index].value);
58+
/*
59+
int length = GRPC_SLICE_LENGTH(array->metadata[index].value);
60+
char *str = (char *) malloc(length + 1);
61+
memcpy(str, GRPC_SLICE_START_PTR(array->metadata[index].value), length);
62+
str[length] = 0;
63+
return str;
64+
*/
4265
}
4366

4467
void cgrpc_metadata_array_move_metadata(cgrpc_metadata_array *destination,

Sources/gRPC/Metadata.swift

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -71,21 +71,27 @@ public class Metadata : CustomStringConvertible, NSCopying {
7171
}
7272

7373
public func key(_ index: Int) -> (String) {
74-
if let key = String(cString:cgrpc_metadata_array_get_key_at_index(underlyingArray, index),
75-
encoding:String.Encoding.utf8) {
76-
return key
77-
} else {
78-
return "<binary-metadata-key>"
74+
if let string = cgrpc_metadata_array_copy_key_at_index(underlyingArray, index) {
75+
defer {
76+
cgrpc_metadata_free_copied_string(string)
77+
}
78+
if let key = String(cString:string, encoding:String.Encoding.utf8) {
79+
return key
80+
}
7981
}
82+
return "<binary-metadata-key>"
8083
}
8184

8285
public func value(_ index: Int) -> (String) {
83-
if let value = String(cString:cgrpc_metadata_array_get_value_at_index(underlyingArray, index),
84-
encoding:String.Encoding.utf8) {
85-
return value
86-
} else {
87-
return "<binary-metadata-value>"
86+
if let string = cgrpc_metadata_array_copy_value_at_index(underlyingArray, index) {
87+
defer {
88+
cgrpc_metadata_free_copied_string(string)
89+
}
90+
if let value = String(cString:string, encoding:String.Encoding.utf8) {
91+
return value
92+
}
8893
}
94+
return "<binary-metadata-value>"
8995
}
9096

9197
public func add(key:String, value:String) {

Tests/gRPCTests/GRPCTests.swift

Lines changed: 72 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,106 @@
1-
/*
2-
* Copyright 2017, 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 XCTest
17-
import Foundation
18-
import Dispatch
19-
@testable import gRPC
1+
/*
2+
* Copyright 2017, 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 XCTest
17+
import Foundation
18+
import Dispatch
19+
@testable import gRPC
2020

21-
func Log(_ message : String) {
21+
func Log(_ message : String) {
2222
FileHandle.standardError.write((message + "\n").data(using:.utf8)!)
23-
}
23+
}
2424

25-
class gRPCTests: XCTestCase {
25+
class gRPCTests: XCTestCase {
2626

2727
func testBasicSanity() {
2828
gRPC.initialize()
29-
let latch = CountDownLatch(2)
29+
30+
let sem = DispatchSemaphore(value: 0)
31+
32+
// start the server
3033
DispatchQueue.global().async() {
3134
do {
32-
try server()
35+
try runServer()
3336
} catch (let error) {
3437
XCTFail("server error \(error)")
3538
}
36-
latch.signal()
39+
sem.signal() // when the server exits, the test is finished
3740
}
38-
DispatchQueue.global().async() {
39-
do {
40-
try client()
41-
} catch (let error) {
42-
XCTFail("client error \(error)")
43-
}
44-
latch.signal()
41+
42+
// run the client
43+
do {
44+
try runClient()
45+
} catch (let error) {
46+
XCTFail("client error \(error)")
4547
}
46-
latch.wait()
48+
49+
// stop the server
50+
server.stop()
51+
52+
// wait until the server has shut down
53+
_ = sem.wait(timeout: DispatchTime.distantFuture)
4754
}
48-
}
55+
}
4956

50-
extension gRPCTests {
57+
extension gRPCTests {
5158
static var allTests : [(String, (gRPCTests) -> () throws -> Void)] {
5259
return [
5360
("testBasicSanity", testBasicSanity),
5461
]
5562
}
56-
}
63+
}
5764

58-
let address = "localhost:8999"
59-
let host = "foo.test.google.fr"
60-
let clientText = "hello, server!"
61-
let serverText = "hello, client!"
62-
let initialClientMetadata =
65+
let address = "localhost:8998"
66+
let host = "foo.test.google.fr"
67+
let clientText = "hello, server!"
68+
let serverText = "hello, client!"
69+
let initialClientMetadata =
6370
["x": "xylophone",
6471
"y": "yu",
6572
"z": "zither"]
66-
let initialServerMetadata =
73+
let initialServerMetadata =
6774
["a": "Apple",
6875
"b": "Banana",
6976
"c": "Cherry"]
70-
let trailingServerMetadata =
77+
let trailingServerMetadata =
7178
["0": "zero",
7279
"1": "one",
7380
"2": "two"]
74-
let steps = 30
75-
let hello = "/hello"
76-
let goodbye = "/goodbye"
77-
let statusCode = 0
78-
let statusMessage = "OK"
81+
let steps = 1
82+
let hello = "/hello"
83+
let statusCode = 0
84+
let statusMessage = "OK"
7985

80-
func verify_metadata(_ metadata: Metadata, expected: [String:String]) {
86+
let server = gRPC.Server(address:address)
87+
88+
func verify_metadata(_ metadata: Metadata, expected: [String:String]) {
8189
XCTAssertGreaterThanOrEqual(metadata.count(), expected.count)
8290
for i in 0..<metadata.count() {
8391
if expected[metadata.key(i)] != nil {
8492
XCTAssertEqual(metadata.value(i), expected[metadata.key(i)])
8593
}
8694
}
87-
}
95+
}
8896

89-
func client() throws {
97+
func runClient() throws {
9098
let message = clientText.data(using: .utf8)
9199
let channel = gRPC.Channel(address:address)
92100
channel.host = host
93101
for i in 0..<steps {
94-
let latch = CountDownLatch(1)
95-
let method = (i < steps-1) ? hello : goodbye
102+
let sem = DispatchSemaphore(value: 0)
103+
let method = hello
96104
let call = channel.makeCall(method)
97105
let metadata = Metadata(initialClientMetadata)
98106
try call.start(.unary, metadata:metadata, message:message) {
@@ -111,27 +119,23 @@ func client() throws {
111119
let trailingMetadata = response.trailingMetadata!
112120
verify_metadata(trailingMetadata, expected: trailingServerMetadata)
113121
// report completion
114-
latch.signal()
122+
sem.signal()
115123
}
116124
// wait for the call to complete
117-
latch.wait()
125+
_ = sem.wait(timeout: DispatchTime.distantFuture)
126+
print("finished client step \(i)")
118127
}
119-
usleep(500) // temporarily delay calls to the channel destructor
120-
}
128+
}
121129

122-
func server() throws {
123-
let server = gRPC.Server(address:address)
130+
func runServer() throws {
124131
var requestCount = 0
125-
let latch = CountDownLatch(1)
132+
let sem = DispatchSemaphore(value: 0)
126133
server.run() {(requestHandler) in
127134
do {
135+
print("handling request \(requestHandler.method)")
128136
requestCount += 1
129137
XCTAssertEqual(requestHandler.host, host)
130-
if (requestCount < steps) {
131138
XCTAssertEqual(requestHandler.method, hello)
132-
} else {
133-
XCTAssertEqual(requestHandler.method, goodbye)
134-
}
135139
let initialMetadata = requestHandler.requestMetadata
136140
verify_metadata(initialMetadata, expected: initialClientMetadata)
137141
let initialMetadataToSend = Metadata(initialServerMetadata)
@@ -140,9 +144,6 @@ func server() throws {
140144
let messageString = String(data: messageData!, encoding: .utf8)
141145
XCTAssertEqual(messageString, clientText)
142146
}
143-
if requestHandler.method == goodbye {
144-
server.stop()
145-
}
146147
let replyMessage = serverText
147148
let trailingMetadataToSend = Metadata(trailingServerMetadata)
148149
try requestHandler.sendResponse(message:replyMessage.data(using: .utf8)!,
@@ -154,9 +155,9 @@ func server() throws {
154155
}
155156
}
156157
server.onCompletion() {
157-
// exit the server thread
158-
latch.signal()
158+
// return from runServer()
159+
sem.signal()
159160
}
160161
// wait for the server to exit
161-
latch.wait()
162-
}
162+
_ = sem.wait(timeout: DispatchTime.distantFuture)
163+
}

0 commit comments

Comments
 (0)