mirror of https://github.com/grpc/grpc.git
Remove idempotent/cacheable requests (#28922)
* Remove idempotent/cacheable requests * more cleanup * bump core version * fix * fix * fix * review feedback * fixes * fix * remove more * objc * fix * fix * fix * scrub * Modify XdsRbacTests Co-authored-by: Yash Tibrewal <yashkt@google.com>pull/28946/head
parent
b8bbe30452
commit
fe913387da
36 changed files with 39 additions and 1663 deletions
@ -1,92 +0,0 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2019 gRPC authors. |
||||
* |
||||
* 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 |
||||
* |
||||
* http://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. |
||||
* |
||||
*/ |
||||
|
||||
#import <GRPCClient/GRPCInterceptor.h> |
||||
|
||||
NS_ASSUME_NONNULL_BEGIN |
||||
|
||||
@interface RequestCacheEntry : NSObject <NSCopying> |
||||
|
||||
@property(readonly, copy, nullable) NSString *path; |
||||
@property(readonly, copy, nullable) id message; |
||||
|
||||
@end |
||||
|
||||
@interface MutableRequestCacheEntry : RequestCacheEntry |
||||
|
||||
@property(copy, nullable) NSString *path; |
||||
@property(copy, nullable) id<NSObject> message; |
||||
|
||||
@end |
||||
|
||||
@interface ResponseCacheEntry : NSObject <NSCopying> |
||||
|
||||
@property(readonly, copy, nullable) NSDate *deadline; |
||||
|
||||
@property(readonly, copy, nullable) NSDictionary *headers; |
||||
@property(readonly, copy, nullable) id message; |
||||
@property(readonly, copy, nullable) NSDictionary *trailers; |
||||
|
||||
@end |
||||
|
||||
@interface MutableResponseCacheEntry : ResponseCacheEntry |
||||
|
||||
@property(copy, nullable) NSDate *deadline; |
||||
|
||||
@property(copy, nullable) NSDictionary *headers; |
||||
@property(copy, nullable) id message; |
||||
@property(copy, nullable) NSDictionary *trailers; |
||||
|
||||
@end |
||||
|
||||
@interface CacheContext : NSObject <GRPCInterceptorFactory> |
||||
|
||||
- (nullable instancetype)init; |
||||
|
||||
- (nullable ResponseCacheEntry *)getCachedResponseForRequest:(RequestCacheEntry *)request; |
||||
|
||||
- (void)setCachedResponse:(ResponseCacheEntry *)response forRequest:(RequestCacheEntry *)request; |
||||
|
||||
@end |
||||
|
||||
@interface CacheInterceptor : GRPCInterceptor |
||||
|
||||
- (instancetype)init NS_UNAVAILABLE; |
||||
|
||||
+ (instancetype)new NS_UNAVAILABLE; |
||||
|
||||
- (nullable instancetype)initWithInterceptorManager: |
||||
(GRPCInterceptorManager *_Nonnull)intercepterManager |
||||
cacheContext:(CacheContext *_Nonnull)cacheContext |
||||
NS_DESIGNATED_INITIALIZER; |
||||
|
||||
// implementation of GRPCInterceptorInterface
|
||||
- (void)startWithRequestOptions:(GRPCRequestOptions *)requestOptions |
||||
callOptions:(GRPCCallOptions *)callOptions; |
||||
- (void)writeData:(id)data; |
||||
- (void)finish; |
||||
|
||||
// implementation of GRPCResponseHandler
|
||||
- (void)didReceiveInitialMetadata:(nullable NSDictionary *)initialMetadata; |
||||
- (void)didReceiveData:(id)data; |
||||
- (void)didCloseWithTrailingMetadata:(nullable NSDictionary *)trailingMetadata |
||||
error:(nullable NSError *)error; |
||||
|
||||
@end |
||||
|
||||
NS_ASSUME_NONNULL_END |
@ -1,306 +0,0 @@ |
||||
/* |
||||
* |
||||
* Copyright 2019 gRPC authors. |
||||
* |
||||
* 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 |
||||
* |
||||
* http://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. |
||||
* |
||||
*/ |
||||
|
||||
#import "CacheInterceptor.h" |
||||
|
||||
@implementation RequestCacheEntry { |
||||
@protected |
||||
NSString *_path; |
||||
id<NSObject> _message; |
||||
} |
||||
|
||||
@synthesize path = _path; |
||||
@synthesize message = _message; |
||||
|
||||
- (instancetype)initWithPath:(NSString *)path message:(id)message { |
||||
if ((self = [super init])) { |
||||
_path = [path copy]; |
||||
_message = [message copy]; |
||||
} |
||||
return self; |
||||
} |
||||
|
||||
- (id)copyWithZone:(NSZone *)zone { |
||||
return [[RequestCacheEntry allocWithZone:zone] initWithPath:_path message:_message]; |
||||
} |
||||
|
||||
- (BOOL)isEqual:(id)object { |
||||
if ([self class] != [object class]) return NO; |
||||
RequestCacheEntry *rhs = (RequestCacheEntry *)object; |
||||
return ([_path isEqualToString:rhs.path] && [_message isEqual:rhs.message]); |
||||
} |
||||
|
||||
- (NSUInteger)hash { |
||||
return _path.hash ^ _message.hash; |
||||
} |
||||
|
||||
@end |
||||
|
||||
@implementation MutableRequestCacheEntry |
||||
|
||||
@dynamic path; |
||||
@dynamic message; |
||||
|
||||
- (void)setPath:(NSString *)path { |
||||
_path = [path copy]; |
||||
} |
||||
|
||||
- (void)setMessage:(id)message { |
||||
_message = [message copy]; |
||||
} |
||||
|
||||
@end |
||||
|
||||
@implementation ResponseCacheEntry { |
||||
@protected |
||||
NSDate *_deadline; |
||||
NSDictionary *_headers; |
||||
id _message; |
||||
NSDictionary *_trailers; |
||||
} |
||||
|
||||
@synthesize deadline = _deadline; |
||||
@synthesize headers = _headers; |
||||
@synthesize message = _message; |
||||
@synthesize trailers = _trailers; |
||||
|
||||
- (instancetype)initWithDeadline:(NSDate *)deadline |
||||
headers:(NSDictionary *)headers |
||||
message:(id)message |
||||
trailers:(NSDictionary *)trailers { |
||||
if (([super init])) { |
||||
_deadline = [deadline copy]; |
||||
_headers = [[NSDictionary alloc] initWithDictionary:headers copyItems:YES]; |
||||
_message = [message copy]; |
||||
_trailers = [[NSDictionary alloc] initWithDictionary:trailers copyItems:YES]; |
||||
} |
||||
return self; |
||||
} |
||||
|
||||
- (id)copyWithZone:(NSZone *)zone { |
||||
return [[ResponseCacheEntry allocWithZone:zone] initWithDeadline:_deadline |
||||
headers:_headers |
||||
message:_message |
||||
trailers:_trailers]; |
||||
} |
||||
|
||||
@end |
||||
|
||||
@implementation MutableResponseCacheEntry |
||||
|
||||
@dynamic deadline; |
||||
@dynamic headers; |
||||
@dynamic message; |
||||
@dynamic trailers; |
||||
|
||||
- (void)setDeadline:(NSDate *)deadline { |
||||
_deadline = [deadline copy]; |
||||
} |
||||
|
||||
- (void)setHeaders:(NSDictionary *)headers { |
||||
_headers = [[NSDictionary alloc] initWithDictionary:headers copyItems:YES]; |
||||
} |
||||
|
||||
- (void)setMessage:(id)message { |
||||
_message = [message copy]; |
||||
} |
||||
|
||||
- (void)setTrailers:(NSDictionary *)trailers { |
||||
_trailers = [[NSDictionary alloc] initWithDictionary:trailers copyItems:YES]; |
||||
} |
||||
|
||||
@end |
||||
|
||||
@implementation CacheContext { |
||||
NSCache<RequestCacheEntry *, ResponseCacheEntry *> *_cache; |
||||
} |
||||
|
||||
- (instancetype)init { |
||||
if ((self = [super init])) { |
||||
_cache = [[NSCache alloc] init]; |
||||
} |
||||
return self; |
||||
} |
||||
|
||||
- (GRPCInterceptor *)createInterceptorWithManager:(GRPCInterceptorManager *)interceptorManager { |
||||
return [[CacheInterceptor alloc] initWithInterceptorManager:interceptorManager cacheContext:self]; |
||||
} |
||||
|
||||
- (ResponseCacheEntry *)getCachedResponseForRequest:(RequestCacheEntry *)request { |
||||
ResponseCacheEntry *response = nil; |
||||
@synchronized(self) { |
||||
response = [_cache objectForKey:request]; |
||||
if ([response.deadline timeIntervalSinceNow] < 0) { |
||||
[_cache removeObjectForKey:request]; |
||||
response = nil; |
||||
} |
||||
} |
||||
return response; |
||||
} |
||||
|
||||
- (void)setCachedResponse:(ResponseCacheEntry *)response forRequest:(RequestCacheEntry *)request { |
||||
@synchronized(self) { |
||||
[_cache setObject:response forKey:request]; |
||||
} |
||||
} |
||||
|
||||
@end |
||||
|
||||
@implementation CacheInterceptor { |
||||
GRPCInterceptorManager *_manager; |
||||
CacheContext *_context; |
||||
dispatch_queue_t _dispatchQueue; |
||||
|
||||
BOOL _cacheable; |
||||
BOOL _writeMessageSeen; |
||||
BOOL _readMessageSeen; |
||||
GRPCCallOptions *_callOptions; |
||||
GRPCRequestOptions *_requestOptions; |
||||
id _requestMessage; |
||||
MutableRequestCacheEntry *_request; |
||||
MutableResponseCacheEntry *_response; |
||||
} |
||||
|
||||
- (dispatch_queue_t)requestDispatchQueue { |
||||
return _dispatchQueue; |
||||
} |
||||
|
||||
- (dispatch_queue_t)dispatchQueue { |
||||
return _dispatchQueue; |
||||
} |
||||
|
||||
- (instancetype)initWithInterceptorManager:(GRPCInterceptorManager *_Nonnull)intercepterManager |
||||
cacheContext:(CacheContext *_Nonnull)cacheContext { |
||||
if ((self = [super initWithInterceptorManager:intercepterManager |
||||
requestDispatchQueue:dispatch_get_main_queue() |
||||
responseDispatchQueue:dispatch_get_main_queue()])) { |
||||
_manager = intercepterManager; |
||||
_context = cacheContext; |
||||
_dispatchQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); |
||||
|
||||
_cacheable = YES; |
||||
_writeMessageSeen = NO; |
||||
_readMessageSeen = NO; |
||||
_request = nil; |
||||
_response = nil; |
||||
} |
||||
return self; |
||||
} |
||||
|
||||
- (void)startWithRequestOptions:(GRPCRequestOptions *)requestOptions |
||||
callOptions:(GRPCCallOptions *)callOptions { |
||||
if (requestOptions.safety != GRPCCallSafetyCacheableRequest) { |
||||
_cacheable = NO; |
||||
[_manager startNextInterceptorWithRequest:requestOptions callOptions:callOptions]; |
||||
} else { |
||||
_requestOptions = [requestOptions copy]; |
||||
_callOptions = [callOptions copy]; |
||||
} |
||||
} |
||||
|
||||
- (void)writeData:(id)data { |
||||
if (!_cacheable) { |
||||
[_manager writeNextInterceptorWithData:data]; |
||||
} else { |
||||
NSAssert(!_writeMessageSeen, @"CacheInterceptor does not support streaming call"); |
||||
if (_writeMessageSeen) { |
||||
NSLog(@"CacheInterceptor does not support streaming call"); |
||||
} |
||||
_writeMessageSeen = YES; |
||||
_requestMessage = [data copy]; |
||||
} |
||||
} |
||||
|
||||
- (void)finish { |
||||
if (!_cacheable) { |
||||
[_manager finishNextInterceptor]; |
||||
} else { |
||||
_request = [[MutableRequestCacheEntry alloc] init]; |
||||
_request.path = _requestOptions.path; |
||||
_request.message = [_requestMessage copy]; |
||||
_response = [[_context getCachedResponseForRequest:_request] copy]; |
||||
if (!_response) { |
||||
[_manager startNextInterceptorWithRequest:_requestOptions callOptions:_callOptions]; |
||||
[_manager writeNextInterceptorWithData:_requestMessage]; |
||||
[_manager finishNextInterceptor]; |
||||
} else { |
||||
[_manager forwardPreviousInterceptorWithInitialMetadata:_response.headers]; |
||||
[_manager forwardPreviousInterceptorWithData:_response.message]; |
||||
[_manager forwardPreviousInterceptorCloseWithTrailingMetadata:_response.trailers error:nil]; |
||||
[_manager shutDown]; |
||||
} |
||||
} |
||||
} |
||||
|
||||
- (void)didReceiveInitialMetadata:(NSDictionary *)initialMetadata { |
||||
if (_cacheable) { |
||||
NSDate *deadline = nil; |
||||
for (NSString *key in initialMetadata) { |
||||
if ([key.lowercaseString isEqualToString:@"cache-control"]) { |
||||
NSArray *cacheControls = [initialMetadata[key] componentsSeparatedByString:@","]; |
||||
for (NSString *option in cacheControls) { |
||||
NSString *trimmedOption = |
||||
[option stringByTrimmingCharactersInSet:[NSCharacterSet |
||||
characterSetWithCharactersInString:@" "]]; |
||||
if ([trimmedOption.lowercaseString isEqualToString:@"no-cache"] || |
||||
[trimmedOption.lowercaseString isEqualToString:@"no-store"] || |
||||
[trimmedOption.lowercaseString isEqualToString:@"no-transform"]) { |
||||
_cacheable = NO; |
||||
break; |
||||
} else if ([trimmedOption.lowercaseString hasPrefix:@"max-age="]) { |
||||
NSArray<NSString *> *components = [trimmedOption componentsSeparatedByString:@"="]; |
||||
if (components.count == 2) { |
||||
NSUInteger maxAge = components[1].intValue; |
||||
deadline = [NSDate dateWithTimeIntervalSinceNow:maxAge]; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
if (_cacheable) { |
||||
_response = [[MutableResponseCacheEntry alloc] init]; |
||||
_response.headers = [initialMetadata copy]; |
||||
_response.deadline = deadline; |
||||
} |
||||
} |
||||
[_manager forwardPreviousInterceptorWithInitialMetadata:initialMetadata]; |
||||
} |
||||
|
||||
- (void)didReceiveData:(id)data { |
||||
if (_cacheable) { |
||||
NSAssert(!_readMessageSeen, @"CacheInterceptor does not support streaming call"); |
||||
if (_readMessageSeen) { |
||||
NSLog(@"CacheInterceptor does not support streaming call"); |
||||
} |
||||
_readMessageSeen = YES; |
||||
_response.message = [data copy]; |
||||
} |
||||
[_manager forwardPreviousInterceptorWithData:data]; |
||||
} |
||||
|
||||
- (void)didCloseWithTrailingMetadata:(NSDictionary *)trailingMetadata error:(NSError *)error { |
||||
if (error == nil && _cacheable) { |
||||
_response.trailers = [trailingMetadata copy]; |
||||
[_context setCachedResponse:_response forRequest:_request]; |
||||
NSLog(@"Write cache for %@", _request); |
||||
} |
||||
[_manager forwardPreviousInterceptorCloseWithTrailingMetadata:trailingMetadata error:error]; |
||||
[_manager shutDown]; |
||||
} |
||||
|
||||
@end |
@ -1,239 +0,0 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015 gRPC authors. |
||||
* |
||||
* 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 |
||||
* |
||||
* http://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. |
||||
* |
||||
*/ |
||||
|
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
|
||||
#include <grpc/byte_buffer.h> |
||||
#include <grpc/grpc.h> |
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/time.h> |
||||
|
||||
#include "src/core/lib/gpr/string.h" |
||||
#include "test/core/end2end/cq_verifier.h" |
||||
#include "test/core/end2end/end2end_tests.h" |
||||
|
||||
static void* tag(intptr_t t) { return reinterpret_cast<void*>(t); } |
||||
|
||||
static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config, |
||||
const char* test_name, |
||||
grpc_channel_args* client_args, |
||||
grpc_channel_args* server_args) { |
||||
grpc_end2end_test_fixture f; |
||||
gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); |
||||
f = config.create_fixture(client_args, server_args); |
||||
config.init_server(&f, server_args); |
||||
config.init_client(&f, client_args); |
||||
return f; |
||||
} |
||||
|
||||
static gpr_timespec n_seconds_from_now(int n) { |
||||
return grpc_timeout_seconds_to_deadline(n); |
||||
} |
||||
|
||||
static gpr_timespec five_seconds_from_now(void) { |
||||
return n_seconds_from_now(5); |
||||
} |
||||
|
||||
static void drain_cq(grpc_completion_queue* cq) { |
||||
grpc_event ev; |
||||
do { |
||||
ev = grpc_completion_queue_next(cq, five_seconds_from_now(), nullptr); |
||||
} while (ev.type != GRPC_QUEUE_SHUTDOWN); |
||||
} |
||||
|
||||
static void shutdown_server(grpc_end2end_test_fixture* f) { |
||||
if (!f->server) return; |
||||
grpc_server_shutdown_and_notify(f->server, f->shutdown_cq, tag(1000)); |
||||
GPR_ASSERT(grpc_completion_queue_pluck(f->shutdown_cq, tag(1000), |
||||
grpc_timeout_seconds_to_deadline(5), |
||||
nullptr) |
||||
.type == GRPC_OP_COMPLETE); |
||||
grpc_server_destroy(f->server); |
||||
f->server = nullptr; |
||||
} |
||||
|
||||
static void shutdown_client(grpc_end2end_test_fixture* f) { |
||||
if (!f->client) return; |
||||
grpc_channel_destroy(f->client); |
||||
f->client = nullptr; |
||||
} |
||||
|
||||
static void end_test(grpc_end2end_test_fixture* f) { |
||||
shutdown_server(f); |
||||
shutdown_client(f); |
||||
|
||||
grpc_completion_queue_shutdown(f->cq); |
||||
drain_cq(f->cq); |
||||
grpc_completion_queue_destroy(f->cq); |
||||
grpc_completion_queue_destroy(f->shutdown_cq); |
||||
} |
||||
|
||||
static void simple_request_body(grpc_end2end_test_config /*config*/, |
||||
grpc_end2end_test_fixture f) { |
||||
grpc_call* c; |
||||
grpc_call* s; |
||||
cq_verifier* cqv = cq_verifier_create(f.cq); |
||||
grpc_op ops[6]; |
||||
grpc_op* op; |
||||
grpc_metadata_array initial_metadata_recv; |
||||
grpc_metadata_array trailing_metadata_recv; |
||||
grpc_metadata_array request_metadata_recv; |
||||
grpc_call_details call_details; |
||||
grpc_status_code status; |
||||
grpc_call_error error; |
||||
grpc_slice details; |
||||
int was_cancelled = 2; |
||||
char* peer; |
||||
|
||||
gpr_timespec deadline = five_seconds_from_now(); |
||||
c = grpc_channel_create_call(f.client, nullptr, GRPC_PROPAGATE_DEFAULTS, f.cq, |
||||
grpc_slice_from_static_string("/foo"), nullptr, |
||||
deadline, nullptr); |
||||
GPR_ASSERT(c); |
||||
|
||||
peer = grpc_call_get_peer(c); |
||||
GPR_ASSERT(peer != nullptr); |
||||
gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer); |
||||
gpr_free(peer); |
||||
|
||||
grpc_metadata_array_init(&initial_metadata_recv); |
||||
grpc_metadata_array_init(&trailing_metadata_recv); |
||||
grpc_metadata_array_init(&request_metadata_recv); |
||||
grpc_call_details_init(&call_details); |
||||
|
||||
memset(ops, 0, sizeof(ops)); |
||||
op = ops; |
||||
op->op = GRPC_OP_SEND_INITIAL_METADATA; |
||||
op->data.send_initial_metadata.count = 0; |
||||
op->flags = GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST; |
||||
op->reserved = nullptr; |
||||
op++; |
||||
op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; |
||||
op->flags = 0; |
||||
op->reserved = nullptr; |
||||
op++; |
||||
op->op = GRPC_OP_RECV_INITIAL_METADATA; |
||||
op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; |
||||
op->flags = 0; |
||||
op->reserved = nullptr; |
||||
op++; |
||||
op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; |
||||
op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; |
||||
op->data.recv_status_on_client.status = &status; |
||||
op->data.recv_status_on_client.status_details = &details; |
||||
op->flags = 0; |
||||
op->reserved = nullptr; |
||||
op++; |
||||
error = grpc_call_start_batch(c, ops, static_cast<size_t>(op - ops), tag(1), |
||||
nullptr); |
||||
GPR_ASSERT(GRPC_CALL_OK == error); |
||||
|
||||
error = |
||||
grpc_server_request_call(f.server, &s, &call_details, |
||||
&request_metadata_recv, f.cq, f.cq, tag(101)); |
||||
GPR_ASSERT(GRPC_CALL_OK == error); |
||||
CQ_EXPECT_COMPLETION(cqv, tag(101), 1); |
||||
cq_verify(cqv); |
||||
|
||||
peer = grpc_call_get_peer(s); |
||||
GPR_ASSERT(peer != nullptr); |
||||
gpr_log(GPR_DEBUG, "server_peer=%s", peer); |
||||
gpr_free(peer); |
||||
peer = grpc_call_get_peer(c); |
||||
GPR_ASSERT(peer != nullptr); |
||||
gpr_log(GPR_DEBUG, "client_peer=%s", peer); |
||||
gpr_free(peer); |
||||
|
||||
memset(ops, 0, sizeof(ops)); |
||||
op = ops; |
||||
op->op = GRPC_OP_SEND_INITIAL_METADATA; |
||||
op->data.send_initial_metadata.count = 0; |
||||
op->flags = 0; |
||||
op->reserved = nullptr; |
||||
op++; |
||||
op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; |
||||
op->data.send_status_from_server.trailing_metadata_count = 0; |
||||
op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED; |
||||
grpc_slice status_details = grpc_slice_from_static_string("xyz"); |
||||
op->data.send_status_from_server.status_details = &status_details; |
||||
op->flags = 0; |
||||
op->reserved = nullptr; |
||||
op++; |
||||
op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; |
||||
op->data.recv_close_on_server.cancelled = &was_cancelled; |
||||
op->flags = 0; |
||||
op->reserved = nullptr; |
||||
op++; |
||||
error = grpc_call_start_batch(s, ops, static_cast<size_t>(op - ops), tag(102), |
||||
nullptr); |
||||
GPR_ASSERT(GRPC_CALL_OK == error); |
||||
|
||||
CQ_EXPECT_COMPLETION(cqv, tag(102), 1); |
||||
CQ_EXPECT_COMPLETION(cqv, tag(1), 1); |
||||
cq_verify(cqv); |
||||
|
||||
GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED); |
||||
GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); |
||||
GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); |
||||
GPR_ASSERT(GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST == call_details.flags); |
||||
GPR_ASSERT(was_cancelled == 0); |
||||
|
||||
grpc_slice_unref(details); |
||||
grpc_metadata_array_destroy(&initial_metadata_recv); |
||||
grpc_metadata_array_destroy(&trailing_metadata_recv); |
||||
grpc_metadata_array_destroy(&request_metadata_recv); |
||||
grpc_call_details_destroy(&call_details); |
||||
|
||||
grpc_call_unref(c); |
||||
grpc_call_unref(s); |
||||
|
||||
cq_verifier_destroy(cqv); |
||||
} |
||||
|
||||
static void test_invoke_simple_request(grpc_end2end_test_config config) { |
||||
grpc_end2end_test_fixture f; |
||||
|
||||
f = begin_test(config, "test_invoke_simple_request", nullptr, nullptr); |
||||
simple_request_body(config, f); |
||||
end_test(&f); |
||||
config.tear_down_data(&f); |
||||
} |
||||
|
||||
static void test_invoke_10_simple_requests(grpc_end2end_test_config config) { |
||||
int i; |
||||
grpc_end2end_test_fixture f = |
||||
begin_test(config, "test_invoke_10_simple_requests", nullptr, nullptr); |
||||
for (i = 0; i < 10; i++) { |
||||
simple_request_body(config, f); |
||||
gpr_log(GPR_INFO, "Passed simple request %d", i); |
||||
} |
||||
end_test(&f); |
||||
config.tear_down_data(&f); |
||||
} |
||||
|
||||
void idempotent_request(grpc_end2end_test_config config) { |
||||
int i; |
||||
for (i = 0; i < 10; i++) { |
||||
test_invoke_simple_request(config); |
||||
} |
||||
test_invoke_10_simple_requests(config); |
||||
} |
||||
|
||||
void idempotent_request_pre_init(void) {} |
@ -1,274 +0,0 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015 gRPC authors. |
||||
* |
||||
* 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 |
||||
* |
||||
* http://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. |
||||
* |
||||
*/ |
||||
|
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
|
||||
#include <grpc/byte_buffer.h> |
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/time.h> |
||||
|
||||
#include "test/core/end2end/cq_verifier.h" |
||||
#include "test/core/end2end/end2end_tests.h" |
||||
|
||||
enum { TIMEOUT = 200000 }; |
||||
|
||||
static void* tag(intptr_t t) { return reinterpret_cast<void*>(t); } |
||||
|
||||
static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config, |
||||
const char* test_name, |
||||
grpc_channel_args* client_args, |
||||
grpc_channel_args* server_args) { |
||||
grpc_end2end_test_fixture f; |
||||
gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name); |
||||
f = config.create_fixture(client_args, server_args); |
||||
config.init_server(&f, server_args); |
||||
config.init_client(&f, client_args); |
||||
return f; |
||||
} |
||||
|
||||
static gpr_timespec n_seconds_from_now(int n) { |
||||
return grpc_timeout_seconds_to_deadline(n); |
||||
} |
||||
|
||||
static gpr_timespec five_seconds_from_now(void) { |
||||
return n_seconds_from_now(5); |
||||
} |
||||
|
||||
static void drain_cq(grpc_completion_queue* cq) { |
||||
grpc_event ev; |
||||
do { |
||||
ev = grpc_completion_queue_next(cq, five_seconds_from_now(), nullptr); |
||||
} while (ev.type != GRPC_QUEUE_SHUTDOWN); |
||||
} |
||||
|
||||
static void shutdown_server(grpc_end2end_test_fixture* f) { |
||||
if (!f->server) return; |
||||
grpc_server_shutdown_and_notify(f->server, f->shutdown_cq, tag(1000)); |
||||
GPR_ASSERT(grpc_completion_queue_pluck(f->shutdown_cq, tag(1000), |
||||
grpc_timeout_seconds_to_deadline(5), |
||||
nullptr) |
||||
.type == GRPC_OP_COMPLETE); |
||||
grpc_server_destroy(f->server); |
||||
f->server = nullptr; |
||||
} |
||||
|
||||
static void shutdown_client(grpc_end2end_test_fixture* f) { |
||||
if (!f->client) return; |
||||
grpc_channel_destroy(f->client); |
||||
f->client = nullptr; |
||||
} |
||||
|
||||
static void end_test(grpc_end2end_test_fixture* f) { |
||||
shutdown_server(f); |
||||
shutdown_client(f); |
||||
|
||||
grpc_completion_queue_shutdown(f->cq); |
||||
drain_cq(f->cq); |
||||
grpc_completion_queue_destroy(f->cq); |
||||
grpc_completion_queue_destroy(f->shutdown_cq); |
||||
} |
||||
|
||||
/* Request/response with metadata and payload.*/ |
||||
static void test_cacheable_request_response_with_metadata_and_payload( |
||||
grpc_end2end_test_config config) { |
||||
grpc_call* c; |
||||
grpc_call* s; |
||||
grpc_slice request_payload_slice = |
||||
grpc_slice_from_copied_string("hello world"); |
||||
grpc_slice response_payload_slice = |
||||
grpc_slice_from_copied_string("hello you"); |
||||
grpc_byte_buffer* request_payload = |
||||
grpc_raw_byte_buffer_create(&request_payload_slice, 1); |
||||
grpc_byte_buffer* response_payload = |
||||
grpc_raw_byte_buffer_create(&response_payload_slice, 1); |
||||
grpc_metadata meta_c[2] = {{grpc_slice_from_static_string("key1"), |
||||
grpc_slice_from_static_string("val1"), |
||||
{{nullptr, nullptr, nullptr, nullptr}}}, |
||||
{grpc_slice_from_static_string("key2"), |
||||
grpc_slice_from_static_string("val2"), |
||||
{{nullptr, nullptr, nullptr, nullptr}}}}; |
||||
grpc_metadata meta_s[2] = {{grpc_slice_from_static_string("key3"), |
||||
grpc_slice_from_static_string("val3"), |
||||
{{nullptr, nullptr, nullptr, nullptr}}}, |
||||
{grpc_slice_from_static_string("key4"), |
||||
grpc_slice_from_static_string("val4"), |
||||
{{nullptr, nullptr, nullptr, nullptr}}}}; |
||||
grpc_end2end_test_fixture f = begin_test( |
||||
config, "test_cacheable_request_response_with_metadata_and_payload", |
||||
nullptr, nullptr); |
||||
cq_verifier* cqv = cq_verifier_create(f.cq); |
||||
grpc_op ops[6]; |
||||
grpc_op* op; |
||||
grpc_metadata_array initial_metadata_recv; |
||||
grpc_metadata_array trailing_metadata_recv; |
||||
grpc_metadata_array request_metadata_recv; |
||||
grpc_byte_buffer* request_payload_recv = nullptr; |
||||
grpc_byte_buffer* response_payload_recv = nullptr; |
||||
grpc_call_details call_details; |
||||
grpc_status_code status; |
||||
grpc_call_error error; |
||||
grpc_slice details; |
||||
int was_cancelled = 2; |
||||
|
||||
gpr_timespec deadline = five_seconds_from_now(); |
||||
c = grpc_channel_create_call(f.client, nullptr, GRPC_PROPAGATE_DEFAULTS, f.cq, |
||||
grpc_slice_from_static_string("/foo"), nullptr, |
||||
deadline, nullptr); |
||||
GPR_ASSERT(c); |
||||
|
||||
grpc_metadata_array_init(&initial_metadata_recv); |
||||
grpc_metadata_array_init(&trailing_metadata_recv); |
||||
grpc_metadata_array_init(&request_metadata_recv); |
||||
grpc_call_details_init(&call_details); |
||||
|
||||
memset(ops, 0, sizeof(ops)); |
||||
op = ops; |
||||
op->op = GRPC_OP_SEND_INITIAL_METADATA; |
||||
op->data.send_initial_metadata.count = 2; |
||||
op->data.send_initial_metadata.metadata = meta_c; |
||||
op->flags = GRPC_INITIAL_METADATA_CACHEABLE_REQUEST; |
||||
op->reserved = nullptr; |
||||
op++; |
||||
op->op = GRPC_OP_SEND_MESSAGE; |
||||
op->data.send_message.send_message = request_payload; |
||||
op->flags = 0; |
||||
op->reserved = nullptr; |
||||
op++; |
||||
op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; |
||||
op->flags = 0; |
||||
op->reserved = nullptr; |
||||
op++; |
||||
op->op = GRPC_OP_RECV_INITIAL_METADATA; |
||||
op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; |
||||
op->flags = 0; |
||||
op->reserved = nullptr; |
||||
op++; |
||||
op->op = GRPC_OP_RECV_MESSAGE; |
||||
op->data.recv_message.recv_message = &response_payload_recv; |
||||
op->flags = 0; |
||||
op->reserved = nullptr; |
||||
op++; |
||||
op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; |
||||
op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; |
||||
op->data.recv_status_on_client.status = &status; |
||||
op->data.recv_status_on_client.status_details = &details; |
||||
op->flags = 0; |
||||
op->reserved = nullptr; |
||||
op++; |
||||
error = grpc_call_start_batch(c, ops, static_cast<size_t>(op - ops), tag(1), |
||||
nullptr); |
||||
GPR_ASSERT(GRPC_CALL_OK == error); |
||||
|
||||
error = |
||||
grpc_server_request_call(f.server, &s, &call_details, |
||||
&request_metadata_recv, f.cq, f.cq, tag(101)); |
||||
GPR_ASSERT(GRPC_CALL_OK == error); |
||||
CQ_EXPECT_COMPLETION(cqv, tag(101), 1); |
||||
cq_verify(cqv); |
||||
|
||||
memset(ops, 0, sizeof(ops)); |
||||
op = ops; |
||||
op->op = GRPC_OP_SEND_INITIAL_METADATA; |
||||
op->data.send_initial_metadata.count = 2; |
||||
op->data.send_initial_metadata.metadata = meta_s; |
||||
op->flags = 0; |
||||
op->reserved = nullptr; |
||||
op++; |
||||
op->op = GRPC_OP_RECV_MESSAGE; |
||||
op->data.recv_message.recv_message = &request_payload_recv; |
||||
op->flags = 0; |
||||
op->reserved = nullptr; |
||||
op++; |
||||
error = grpc_call_start_batch(s, ops, static_cast<size_t>(op - ops), tag(102), |
||||
nullptr); |
||||
GPR_ASSERT(GRPC_CALL_OK == error); |
||||
|
||||
CQ_EXPECT_COMPLETION(cqv, tag(102), 1); |
||||
cq_verify(cqv); |
||||
|
||||
memset(ops, 0, sizeof(ops)); |
||||
op = ops; |
||||
op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; |
||||
op->data.recv_close_on_server.cancelled = &was_cancelled; |
||||
op->flags = 0; |
||||
op->reserved = nullptr; |
||||
op++; |
||||
op->op = GRPC_OP_SEND_MESSAGE; |
||||
op->data.send_message.send_message = response_payload; |
||||
op->flags = 0; |
||||
op->reserved = nullptr; |
||||
op++; |
||||
op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; |
||||
op->data.send_status_from_server.trailing_metadata_count = 0; |
||||
op->data.send_status_from_server.status = GRPC_STATUS_OK; |
||||
grpc_slice status_details = grpc_slice_from_static_string("xyz"); |
||||
op->data.send_status_from_server.status_details = &status_details; |
||||
op->flags = 0; |
||||
op->reserved = nullptr; |
||||
op++; |
||||
error = grpc_call_start_batch(s, ops, static_cast<size_t>(op - ops), tag(103), |
||||
nullptr); |
||||
GPR_ASSERT(GRPC_CALL_OK == error); |
||||
|
||||
CQ_EXPECT_COMPLETION(cqv, tag(103), 1); |
||||
CQ_EXPECT_COMPLETION(cqv, tag(1), 1); |
||||
cq_verify(cqv); |
||||
|
||||
GPR_ASSERT(status == GRPC_STATUS_OK); |
||||
GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz")); |
||||
GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo")); |
||||
if (config.feature_mask & FEATURE_MASK_SUPPORTS_REQUEST_PROXYING) { |
||||
// Our simple proxy does not support cacheable requests
|
||||
} else { |
||||
GPR_ASSERT(GRPC_INITIAL_METADATA_CACHEABLE_REQUEST & call_details.flags); |
||||
} |
||||
GPR_ASSERT(was_cancelled == 0); |
||||
GPR_ASSERT(byte_buffer_eq_string(request_payload_recv, "hello world")); |
||||
GPR_ASSERT(byte_buffer_eq_string(response_payload_recv, "hello you")); |
||||
GPR_ASSERT(contains_metadata(&request_metadata_recv, "key1", "val1")); |
||||
GPR_ASSERT(contains_metadata(&request_metadata_recv, "key2", "val2")); |
||||
GPR_ASSERT(contains_metadata(&initial_metadata_recv, "key3", "val3")); |
||||
GPR_ASSERT(contains_metadata(&initial_metadata_recv, "key4", "val4")); |
||||
|
||||
grpc_slice_unref(details); |
||||
grpc_metadata_array_destroy(&initial_metadata_recv); |
||||
grpc_metadata_array_destroy(&trailing_metadata_recv); |
||||
grpc_metadata_array_destroy(&request_metadata_recv); |
||||
grpc_call_details_destroy(&call_details); |
||||
|
||||
grpc_call_unref(c); |
||||
grpc_call_unref(s); |
||||
|
||||
cq_verifier_destroy(cqv); |
||||
|
||||
grpc_byte_buffer_destroy(request_payload); |
||||
grpc_byte_buffer_destroy(response_payload); |
||||
grpc_byte_buffer_destroy(request_payload_recv); |
||||
grpc_byte_buffer_destroy(response_payload_recv); |
||||
|
||||
end_test(&f); |
||||
config.tear_down_data(&f); |
||||
} |
||||
|
||||
void simple_cacheable_request(grpc_end2end_test_config config) { |
||||
test_cacheable_request_response_with_metadata_and_payload(config); |
||||
} |
||||
|
||||
void simple_cacheable_request_pre_init(void) {} |
Loading…
Reference in new issue