aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/lib
diff options
context:
space:
mode:
authorGravatar Mark D. Roth <roth@google.com>2016-08-04 11:19:26 -0700
committerGravatar Mark D. Roth <roth@google.com>2016-08-04 11:19:26 -0700
commit32387b3a34918ae0cc16348e001c08f1d60ed121 (patch)
tree97e544e4e9363a39081c2a8e530d8d50a344ab42 /src/core/lib
parent34befcaa20699eb9fe3e26be1ec6fcaa57d333ad (diff)
parenta3e7bd85c064da9a1011fd31209e5af6a7ff8f38 (diff)
Merge branch 'handshaker_api' into http_connect
Diffstat (limited to 'src/core/lib')
-rw-r--r--src/core/lib/channel/handshaker.c24
-rw-r--r--src/core/lib/channel/handshaker.h9
-rw-r--r--src/core/lib/http/httpcli_security_connector.c8
-rw-r--r--src/core/lib/json/json_reader.c14
-rw-r--r--src/core/lib/security/transport/handshake.c9
-rw-r--r--src/core/lib/security/transport/handshake.h7
-rw-r--r--src/core/lib/security/transport/security_connector.c59
-rw-r--r--src/core/lib/security/transport/security_connector.h13
-rw-r--r--src/core/lib/transport/timeout_encoding.c188
-rw-r--r--src/core/lib/transport/timeout_encoding.h47
10 files changed, 324 insertions, 54 deletions
diff --git a/src/core/lib/channel/handshaker.c b/src/core/lib/channel/handshaker.c
index 6c3ca198b7..c0979f5e80 100644
--- a/src/core/lib/channel/handshaker.c
+++ b/src/core/lib/channel/handshaker.c
@@ -62,11 +62,13 @@ void grpc_handshaker_do_handshake(grpc_exec_ctx* exec_ctx,
grpc_handshaker* handshaker,
grpc_endpoint* endpoint,
grpc_channel_args* args,
+ gpr_slice_buffer* read_buffer,
gpr_timespec deadline,
grpc_tcp_server_acceptor* acceptor,
grpc_handshaker_done_cb cb, void* user_data) {
handshaker->vtable->do_handshake(exec_ctx, handshaker, endpoint, args,
- deadline, acceptor, cb, user_data);
+ read_buffer, deadline, acceptor, cb,
+ user_data);
}
//
@@ -143,7 +145,8 @@ void grpc_handshake_manager_shutdown(grpc_exec_ctx* exec_ctx,
// handshakers together.
static void call_next_handshaker(grpc_exec_ctx* exec_ctx,
grpc_endpoint* endpoint,
- grpc_channel_args* args, void* user_data,
+ grpc_channel_args* args,
+ gpr_slice_buffer* read_buffer, void* user_data,
grpc_error* error) {
grpc_handshake_manager* mgr = user_data;
GPR_ASSERT(mgr->state != NULL);
@@ -151,8 +154,8 @@ static void call_next_handshaker(grpc_exec_ctx* exec_ctx,
// If we got an error, skip all remaining handshakers and invoke the
// caller-supplied callback immediately.
if (error != GRPC_ERROR_NONE) {
- mgr->state->final_cb(exec_ctx, endpoint, args, mgr->state->final_user_data,
- error);
+ mgr->state->final_cb(exec_ctx, endpoint, args, read_buffer,
+ mgr->state->final_user_data, error);
return;
}
grpc_handshaker_done_cb cb = call_next_handshaker;
@@ -163,9 +166,9 @@ static void call_next_handshaker(grpc_exec_ctx* exec_ctx,
user_data = mgr->state->final_user_data;
}
// Invoke handshaker.
- grpc_handshaker_do_handshake(exec_ctx, mgr->handshakers[mgr->state->index],
- endpoint, args, mgr->state->deadline,
- mgr->state->acceptor, cb, user_data);
+ grpc_handshaker_do_handshake(
+ exec_ctx, mgr->handshakers[mgr->state->index], endpoint, args,
+ read_buffer, mgr->state->deadline, mgr->state->acceptor, cb, user_data);
++mgr->state->index;
// If this is the last handshaker, clean up state.
if (mgr->state->index == mgr->count) {
@@ -180,10 +183,12 @@ void grpc_handshake_manager_do_handshake(
gpr_timespec deadline, grpc_tcp_server_acceptor* acceptor,
grpc_handshaker_done_cb cb, void* user_data) {
grpc_channel_args* args_copy = grpc_channel_args_copy(args);
+ gpr_slice_buffer* read_buffer = malloc(sizeof(*read_buffer));
+ gpr_slice_buffer_init(read_buffer);
if (mgr->count == 0) {
// No handshakers registered, so we just immediately call the done
// callback with the passed-in endpoint.
- cb(exec_ctx, endpoint, args_copy, user_data, GRPC_ERROR_NONE);
+ cb(exec_ctx, endpoint, args_copy, read_buffer, user_data, GRPC_ERROR_NONE);
} else {
GPR_ASSERT(mgr->state == NULL);
mgr->state = gpr_malloc(sizeof(struct grpc_handshaker_state));
@@ -192,6 +197,7 @@ void grpc_handshake_manager_do_handshake(
mgr->state->acceptor = acceptor;
mgr->state->final_cb = cb;
mgr->state->final_user_data = user_data;
- call_next_handshaker(exec_ctx, endpoint, args_copy, mgr, GRPC_ERROR_NONE);
+ call_next_handshaker(exec_ctx, endpoint, args_copy, read_buffer, mgr,
+ GRPC_ERROR_NONE);
}
}
diff --git a/src/core/lib/channel/handshaker.h b/src/core/lib/channel/handshaker.h
index dfc469c417..b276f6028c 100644
--- a/src/core/lib/channel/handshaker.h
+++ b/src/core/lib/channel/handshaker.h
@@ -36,6 +36,7 @@
#include <grpc/impl/codegen/grpc_types.h>
#include <grpc/impl/codegen/time.h>
+#include <grpc/support/slice_buffer.h>
#include "src/core/lib/iomgr/closure.h"
#include "src/core/lib/iomgr/endpoint.h"
@@ -56,10 +57,11 @@
typedef struct grpc_handshaker grpc_handshaker;
/// Callback type invoked when a handshaker is done.
-/// Takes ownership of \a args.
+/// Takes ownership of \a args and \a read_buffer.
typedef void (*grpc_handshaker_done_cb)(grpc_exec_ctx* exec_ctx,
grpc_endpoint* endpoint,
grpc_channel_args* args,
+ gpr_slice_buffer* read_buffer,
void* user_data, grpc_error* error);
struct grpc_handshaker_vtable {
@@ -72,10 +74,12 @@ struct grpc_handshaker_vtable {
/// Performs handshaking. When finished, calls \a cb with \a user_data.
/// Takes ownership of \a args.
+ /// Takes ownership of \a read_buffer, which contains leftover bytes read
+ /// from the endpoint by the previous handshaker.
/// \a acceptor will be NULL for client-side handshakers.
void (*do_handshake)(grpc_exec_ctx* exec_ctx, grpc_handshaker* handshaker,
grpc_endpoint* endpoint, grpc_channel_args* args,
- gpr_timespec deadline,
+ gpr_slice_buffer* read_buffer, gpr_timespec deadline,
grpc_tcp_server_acceptor* acceptor,
grpc_handshaker_done_cb cb, void* user_data);
};
@@ -101,6 +105,7 @@ void grpc_handshaker_do_handshake(grpc_exec_ctx* exec_ctx,
grpc_handshaker* handshaker,
grpc_endpoint* endpoint,
grpc_channel_args* args,
+ gpr_slice_buffer* read_buffer,
gpr_timespec deadline,
grpc_tcp_server_acceptor* acceptor,
grpc_handshaker_done_cb cb, void* user_data);
diff --git a/src/core/lib/http/httpcli_security_connector.c b/src/core/lib/http/httpcli_security_connector.c
index a57d93bb7b..0006e809a6 100644
--- a/src/core/lib/http/httpcli_security_connector.c
+++ b/src/core/lib/http/httpcli_security_connector.c
@@ -61,6 +61,7 @@ static void httpcli_ssl_destroy(grpc_security_connector *sc) {
static void httpcli_ssl_do_handshake(grpc_exec_ctx *exec_ctx,
grpc_channel_security_connector *sc,
grpc_endpoint *nonsecure_endpoint,
+ gpr_slice_buffer *read_buffer,
gpr_timespec deadline,
grpc_security_handshake_done_cb cb,
void *user_data) {
@@ -69,6 +70,7 @@ static void httpcli_ssl_do_handshake(grpc_exec_ctx *exec_ctx,
tsi_result result = TSI_OK;
tsi_handshaker *handshaker;
if (c->handshaker_factory == NULL) {
+ gpr_free(read_buffer);
cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL, NULL);
return;
}
@@ -77,10 +79,12 @@ static void httpcli_ssl_do_handshake(grpc_exec_ctx *exec_ctx,
if (result != TSI_OK) {
gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.",
tsi_result_to_string(result));
+ gpr_free(read_buffer);
cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL, NULL);
} else {
grpc_do_security_handshake(exec_ctx, handshaker, &sc->base, true,
- nonsecure_endpoint, deadline, cb, user_data);
+ nonsecure_endpoint, read_buffer, deadline, cb,
+ user_data);
}
}
@@ -183,7 +187,7 @@ static void ssl_handshake(grpc_exec_ctx *exec_ctx, void *arg,
pem_root_certs, pem_root_certs_size, host, &sc) ==
GRPC_SECURITY_OK);
grpc_channel_security_connector_do_handshake(
- exec_ctx, sc, tcp, deadline, on_secure_transport_setup_done, c);
+ exec_ctx, sc, tcp, NULL, deadline, on_secure_transport_setup_done, c);
GRPC_SECURITY_CONNECTOR_UNREF(&sc->base, "httpcli");
}
diff --git a/src/core/lib/json/json_reader.c b/src/core/lib/json/json_reader.c
index bc04bccc65..5b42ca53ff 100644
--- a/src/core/lib/json/json_reader.c
+++ b/src/core/lib/json/json_reader.c
@@ -1,6 +1,6 @@
/*
*
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -171,8 +171,9 @@ grpc_json_reader_status grpc_json_reader_run(grpc_json_reader *reader) {
switch (reader->state) {
case GRPC_JSON_STATE_OBJECT_KEY_STRING:
case GRPC_JSON_STATE_VALUE_STRING:
- if (reader->unicode_high_surrogate != 0)
+ if (reader->unicode_high_surrogate != 0) {
return GRPC_JSON_PARSE_ERROR;
+ }
json_reader_string_add_char(reader, c);
break;
@@ -289,8 +290,9 @@ grpc_json_reader_status grpc_json_reader_run(grpc_json_reader *reader) {
break;
case GRPC_JSON_STATE_OBJECT_KEY_STRING:
- if (reader->unicode_high_surrogate != 0)
+ if (reader->unicode_high_surrogate != 0) {
return GRPC_JSON_PARSE_ERROR;
+ }
if (c == '"') {
reader->state = GRPC_JSON_STATE_OBJECT_KEY_END;
json_reader_set_key(reader);
@@ -302,8 +304,9 @@ grpc_json_reader_status grpc_json_reader_run(grpc_json_reader *reader) {
break;
case GRPC_JSON_STATE_VALUE_STRING:
- if (reader->unicode_high_surrogate != 0)
+ if (reader->unicode_high_surrogate != 0) {
return GRPC_JSON_PARSE_ERROR;
+ }
if (c == '"') {
reader->state = GRPC_JSON_STATE_VALUE_END;
json_reader_set_string(reader);
@@ -383,8 +386,9 @@ grpc_json_reader_status grpc_json_reader_run(grpc_json_reader *reader) {
} else {
reader->state = GRPC_JSON_STATE_VALUE_STRING;
}
- if (reader->unicode_high_surrogate && c != 'u')
+ if (reader->unicode_high_surrogate && c != 'u') {
return GRPC_JSON_PARSE_ERROR;
+ }
switch (c) {
case '"':
case '/':
diff --git a/src/core/lib/security/transport/handshake.c b/src/core/lib/security/transport/handshake.c
index 540a17283d..fbeec312b6 100644
--- a/src/core/lib/security/transport/handshake.c
+++ b/src/core/lib/security/transport/handshake.c
@@ -325,8 +325,9 @@ static void on_timeout(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
void grpc_do_security_handshake(
grpc_exec_ctx *exec_ctx, tsi_handshaker *handshaker,
grpc_security_connector *connector, bool is_client_side,
- grpc_endpoint *nonsecure_endpoint, gpr_timespec deadline,
- grpc_security_handshake_done_cb cb, void *user_data) {
+ grpc_endpoint *nonsecure_endpoint, gpr_slice_buffer *read_buffer,
+ gpr_timespec deadline, grpc_security_handshake_done_cb cb,
+ void *user_data) {
grpc_security_connector_handshake_list *handshake_node;
grpc_security_handshake *h = gpr_malloc(sizeof(grpc_security_handshake));
memset(h, 0, sizeof(grpc_security_handshake));
@@ -346,6 +347,10 @@ void grpc_do_security_handshake(
gpr_slice_buffer_init(&h->left_overs);
gpr_slice_buffer_init(&h->outgoing);
gpr_slice_buffer_init(&h->incoming);
+ if (read_buffer != NULL) {
+ gpr_slice_buffer_move_into(read_buffer, &h->incoming);
+ gpr_free(read_buffer);
+ }
if (!is_client_side) {
grpc_server_security_connector *server_connector =
(grpc_server_security_connector *)connector;
diff --git a/src/core/lib/security/transport/handshake.h b/src/core/lib/security/transport/handshake.h
index c0906dd6af..53092f5421 100644
--- a/src/core/lib/security/transport/handshake.h
+++ b/src/core/lib/security/transport/handshake.h
@@ -37,12 +37,13 @@
#include "src/core/lib/iomgr/endpoint.h"
#include "src/core/lib/security/transport/security_connector.h"
-/* Calls the callback upon completion. Takes owership of handshaker. */
+/* Calls the callback upon completion. Takes owership of handshaker and
+ * read_buffer. */
void grpc_do_security_handshake(
grpc_exec_ctx *exec_ctx, tsi_handshaker *handshaker,
grpc_security_connector *connector, bool is_client_side,
- grpc_endpoint *nonsecure_endpoint, gpr_timespec deadline,
- grpc_security_handshake_done_cb cb, void *user_data);
+ grpc_endpoint *nonsecure_endpoint, gpr_slice_buffer *read_buffer,
+ gpr_timespec deadline, grpc_security_handshake_done_cb cb, void *user_data);
void grpc_security_handshake_shutdown(grpc_exec_ctx *exec_ctx, void *handshake);
diff --git a/src/core/lib/security/transport/security_connector.c b/src/core/lib/security/transport/security_connector.c
index f0ee6770e5..0eca46eb52 100644
--- a/src/core/lib/security/transport/security_connector.c
+++ b/src/core/lib/security/transport/security_connector.c
@@ -127,25 +127,29 @@ void grpc_server_security_connector_shutdown(
void grpc_channel_security_connector_do_handshake(
grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc,
- grpc_endpoint *nonsecure_endpoint, gpr_timespec deadline,
- grpc_security_handshake_done_cb cb, void *user_data) {
+ grpc_endpoint *nonsecure_endpoint, gpr_slice_buffer *read_buffer,
+ gpr_timespec deadline, grpc_security_handshake_done_cb cb,
+ void *user_data) {
if (sc == NULL || nonsecure_endpoint == NULL) {
+ gpr_free(read_buffer);
cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL, NULL);
} else {
- sc->do_handshake(exec_ctx, sc, nonsecure_endpoint, deadline, cb, user_data);
+ sc->do_handshake(exec_ctx, sc, nonsecure_endpoint, read_buffer, deadline,
+ cb, user_data);
}
}
void grpc_server_security_connector_do_handshake(
grpc_exec_ctx *exec_ctx, grpc_server_security_connector *sc,
grpc_tcp_server_acceptor *acceptor, grpc_endpoint *nonsecure_endpoint,
- gpr_timespec deadline, grpc_security_handshake_done_cb cb,
- void *user_data) {
+ gpr_slice_buffer *read_buffer, gpr_timespec deadline,
+ grpc_security_handshake_done_cb cb, void *user_data) {
if (sc == NULL || nonsecure_endpoint == NULL) {
+ gpr_free(read_buffer);
cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL, NULL);
} else {
- sc->do_handshake(exec_ctx, sc, acceptor, nonsecure_endpoint, deadline, cb,
- user_data);
+ sc->do_handshake(exec_ctx, sc, acceptor, nonsecure_endpoint, read_buffer,
+ deadline, cb, user_data);
}
}
@@ -312,23 +316,23 @@ static void fake_channel_check_call_host(grpc_exec_ctx *exec_ctx,
static void fake_channel_do_handshake(grpc_exec_ctx *exec_ctx,
grpc_channel_security_connector *sc,
grpc_endpoint *nonsecure_endpoint,
+ gpr_slice_buffer *read_buffer,
gpr_timespec deadline,
grpc_security_handshake_done_cb cb,
void *user_data) {
grpc_do_security_handshake(exec_ctx, tsi_create_fake_handshaker(1), &sc->base,
- true, nonsecure_endpoint, deadline, cb, user_data);
+ true, nonsecure_endpoint, read_buffer, deadline,
+ cb, user_data);
}
-static void fake_server_do_handshake(grpc_exec_ctx *exec_ctx,
- grpc_server_security_connector *sc,
- grpc_tcp_server_acceptor *acceptor,
- grpc_endpoint *nonsecure_endpoint,
- gpr_timespec deadline,
- grpc_security_handshake_done_cb cb,
- void *user_data) {
+static void fake_server_do_handshake(
+ grpc_exec_ctx *exec_ctx, grpc_server_security_connector *sc,
+ grpc_tcp_server_acceptor *acceptor, grpc_endpoint *nonsecure_endpoint,
+ gpr_slice_buffer *read_buffer, gpr_timespec deadline,
+ grpc_security_handshake_done_cb cb, void *user_data) {
grpc_do_security_handshake(exec_ctx, tsi_create_fake_handshaker(0), &sc->base,
- false, nonsecure_endpoint, deadline, cb,
- user_data);
+ false, nonsecure_endpoint, read_buffer, deadline,
+ cb, user_data);
}
static grpc_security_connector_vtable fake_channel_vtable = {
@@ -418,6 +422,7 @@ static grpc_security_status ssl_create_handshaker(
static void ssl_channel_do_handshake(grpc_exec_ctx *exec_ctx,
grpc_channel_security_connector *sc,
grpc_endpoint *nonsecure_endpoint,
+ gpr_slice_buffer *read_buffer,
gpr_timespec deadline,
grpc_security_handshake_done_cb cb,
void *user_data) {
@@ -430,30 +435,32 @@ static void ssl_channel_do_handshake(grpc_exec_ctx *exec_ctx,
: c->target_name,
&handshaker);
if (status != GRPC_SECURITY_OK) {
+ gpr_free(read_buffer);
cb(exec_ctx, user_data, status, NULL, NULL);
} else {
grpc_do_security_handshake(exec_ctx, handshaker, &sc->base, true,
- nonsecure_endpoint, deadline, cb, user_data);
+ nonsecure_endpoint, read_buffer, deadline, cb,
+ user_data);
}
}
-static void ssl_server_do_handshake(grpc_exec_ctx *exec_ctx,
- grpc_server_security_connector *sc,
- grpc_tcp_server_acceptor *acceptor,
- grpc_endpoint *nonsecure_endpoint,
- gpr_timespec deadline,
- grpc_security_handshake_done_cb cb,
- void *user_data) {
+static void ssl_server_do_handshake(
+ grpc_exec_ctx *exec_ctx, grpc_server_security_connector *sc,
+ grpc_tcp_server_acceptor *acceptor, grpc_endpoint *nonsecure_endpoint,
+ gpr_slice_buffer *read_buffer, gpr_timespec deadline,
+ grpc_security_handshake_done_cb cb, void *user_data) {
grpc_ssl_server_security_connector *c =
(grpc_ssl_server_security_connector *)sc;
tsi_handshaker *handshaker;
grpc_security_status status =
ssl_create_handshaker(c->handshaker_factory, false, NULL, &handshaker);
if (status != GRPC_SECURITY_OK) {
+ gpr_free(read_buffer);
cb(exec_ctx, user_data, status, NULL, NULL);
} else {
grpc_do_security_handshake(exec_ctx, handshaker, &sc->base, false,
- nonsecure_endpoint, deadline, cb, user_data);
+ nonsecure_endpoint, read_buffer, deadline, cb,
+ user_data);
}
}
diff --git a/src/core/lib/security/transport/security_connector.h b/src/core/lib/security/transport/security_connector.h
index c2ddf5ee1e..0b5b44bf1a 100644
--- a/src/core/lib/security/transport/security_connector.h
+++ b/src/core/lib/security/transport/security_connector.h
@@ -143,7 +143,8 @@ struct grpc_channel_security_connector {
grpc_security_call_host_check_cb cb, void *user_data);
void (*do_handshake)(grpc_exec_ctx *exec_ctx,
grpc_channel_security_connector *sc,
- grpc_endpoint *nonsecure_endpoint, gpr_timespec deadline,
+ grpc_endpoint *nonsecure_endpoint,
+ gpr_slice_buffer *read_buffer, gpr_timespec deadline,
grpc_security_handshake_done_cb cb, void *user_data);
};
@@ -156,8 +157,8 @@ void grpc_channel_security_connector_check_call_host(
/* Handshake. */
void grpc_channel_security_connector_do_handshake(
grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *connector,
- grpc_endpoint *nonsecure_endpoint, gpr_timespec deadline,
- grpc_security_handshake_done_cb cb, void *user_data);
+ grpc_endpoint *nonsecure_endpoint, gpr_slice_buffer *read_buffer,
+ gpr_timespec deadline, grpc_security_handshake_done_cb cb, void *user_data);
/* --- server_security_connector object. ---
@@ -174,14 +175,16 @@ struct grpc_server_security_connector {
void (*do_handshake)(grpc_exec_ctx *exec_ctx,
grpc_server_security_connector *sc,
grpc_tcp_server_acceptor *acceptor,
- grpc_endpoint *nonsecure_endpoint, gpr_timespec deadline,
+ grpc_endpoint *nonsecure_endpoint,
+ gpr_slice_buffer *read_buffer, gpr_timespec deadline,
grpc_security_handshake_done_cb cb, void *user_data);
};
void grpc_server_security_connector_do_handshake(
grpc_exec_ctx *exec_ctx, grpc_server_security_connector *sc,
grpc_tcp_server_acceptor *acceptor, grpc_endpoint *nonsecure_endpoint,
- gpr_timespec deadline, grpc_security_handshake_done_cb cb, void *user_data);
+ gpr_slice_buffer *read_buffer, gpr_timespec deadline,
+ grpc_security_handshake_done_cb cb, void *user_data);
void grpc_server_security_connector_shutdown(
grpc_exec_ctx *exec_ctx, grpc_server_security_connector *connector);
diff --git a/src/core/lib/transport/timeout_encoding.c b/src/core/lib/transport/timeout_encoding.c
new file mode 100644
index 0000000000..b58ebbd0a8
--- /dev/null
+++ b/src/core/lib/transport/timeout_encoding.c
@@ -0,0 +1,188 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/lib/transport/timeout_encoding.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/port_platform.h>
+#include "src/core/lib/support/string.h"
+
+static int64_t round_up(int64_t x, int64_t divisor) {
+ return (x / divisor + (x % divisor != 0)) * divisor;
+}
+
+/* round an integer up to the next value with three significant figures */
+static int64_t round_up_to_three_sig_figs(int64_t x) {
+ if (x < 1000) return x;
+ if (x < 10000) return round_up(x, 10);
+ if (x < 100000) return round_up(x, 100);
+ if (x < 1000000) return round_up(x, 1000);
+ if (x < 10000000) return round_up(x, 10000);
+ if (x < 100000000) return round_up(x, 100000);
+ if (x < 1000000000) return round_up(x, 1000000);
+ return round_up(x, 10000000);
+}
+
+/* encode our minimum viable timeout value */
+static void enc_tiny(char *buffer) { memcpy(buffer, "1n", 3); }
+
+static void enc_ext(char *buffer, int64_t value, char ext) {
+ int n = int64_ttoa(value, buffer);
+ buffer[n] = ext;
+ buffer[n + 1] = 0;
+}
+
+static void enc_seconds(char *buffer, int64_t sec) {
+ if (sec % 3600 == 0) {
+ enc_ext(buffer, sec / 3600, 'H');
+ } else if (sec % 60 == 0) {
+ enc_ext(buffer, sec / 60, 'M');
+ } else {
+ enc_ext(buffer, sec, 'S');
+ }
+}
+
+static void enc_nanos(char *buffer, int64_t x) {
+ x = round_up_to_three_sig_figs(x);
+ if (x < 100000) {
+ if (x % 1000 == 0) {
+ enc_ext(buffer, x / 1000, 'u');
+ } else {
+ enc_ext(buffer, x, 'n');
+ }
+ } else if (x < 100000000) {
+ if (x % 1000000 == 0) {
+ enc_ext(buffer, x / 1000000, 'm');
+ } else {
+ enc_ext(buffer, x / 1000, 'u');
+ }
+ } else if (x < 1000000000) {
+ enc_ext(buffer, x / 1000000, 'm');
+ } else {
+ /* note that this is only ever called with times of less than one second,
+ so if we reach here the time must have been rounded up to a whole second
+ (and no more) */
+ memcpy(buffer, "1S", 3);
+ }
+}
+
+static void enc_micros(char *buffer, int64_t x) {
+ x = round_up_to_three_sig_figs(x);
+ if (x < 100000) {
+ if (x % 1000 == 0) {
+ enc_ext(buffer, x / 1000, 'm');
+ } else {
+ enc_ext(buffer, x, 'u');
+ }
+ } else if (x < 100000000) {
+ if (x % 1000000 == 0) {
+ enc_ext(buffer, x / 1000000, 'S');
+ } else {
+ enc_ext(buffer, x / 1000, 'm');
+ }
+ } else {
+ enc_ext(buffer, x / 1000000, 'S');
+ }
+}
+
+void grpc_http2_encode_timeout(gpr_timespec timeout, char *buffer) {
+ if (timeout.tv_sec < 0) {
+ enc_tiny(buffer);
+ } else if (timeout.tv_sec == 0) {
+ enc_nanos(buffer, timeout.tv_nsec);
+ } else if (timeout.tv_sec < 1000 && timeout.tv_nsec != 0) {
+ enc_micros(buffer,
+ (int64_t)(timeout.tv_sec * 1000000) +
+ (timeout.tv_nsec / 1000 + (timeout.tv_nsec % 1000 != 0)));
+ } else {
+ enc_seconds(buffer, timeout.tv_sec + (timeout.tv_nsec != 0));
+ }
+}
+
+static int is_all_whitespace(const char *p) {
+ while (*p == ' ') p++;
+ return *p == 0;
+}
+
+int grpc_http2_decode_timeout(const char *buffer, gpr_timespec *timeout) {
+ int32_t x = 0;
+ const uint8_t *p = (const uint8_t *)buffer;
+ int have_digit = 0;
+ /* skip whitespace */
+ for (; *p == ' '; p++)
+ ;
+ /* decode numeric part */
+ for (; *p >= '0' && *p <= '9'; p++) {
+ int32_t digit = (int32_t)(*p - (uint8_t)'0');
+ have_digit = 1;
+ /* spec allows max. 8 digits, but we allow values up to 1,000,000,000 */
+ if (x >= (100 * 1000 * 1000)) {
+ if (x != (100 * 1000 * 1000) || digit != 0) {
+ *timeout = gpr_inf_future(GPR_TIMESPAN);
+ return 1;
+ }
+ }
+ x = x * 10 + digit;
+ }
+ if (!have_digit) return 0;
+ /* skip whitespace */
+ for (; *p == ' '; p++)
+ ;
+ /* decode unit specifier */
+ switch (*p) {
+ case 'n':
+ *timeout = gpr_time_from_nanos(x, GPR_TIMESPAN);
+ break;
+ case 'u':
+ *timeout = gpr_time_from_micros(x, GPR_TIMESPAN);
+ break;
+ case 'm':
+ *timeout = gpr_time_from_millis(x, GPR_TIMESPAN);
+ break;
+ case 'S':
+ *timeout = gpr_time_from_seconds(x, GPR_TIMESPAN);
+ break;
+ case 'M':
+ *timeout = gpr_time_from_minutes(x, GPR_TIMESPAN);
+ break;
+ case 'H':
+ *timeout = gpr_time_from_hours(x, GPR_TIMESPAN);
+ break;
+ default:
+ return 0;
+ }
+ p++;
+ return is_all_whitespace((const char *)p);
+}
diff --git a/src/core/lib/transport/timeout_encoding.h b/src/core/lib/transport/timeout_encoding.h
new file mode 100644
index 0000000000..92f02f6ecd
--- /dev/null
+++ b/src/core/lib/transport/timeout_encoding.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_TRANSPORT_TIMEOUT_ENCODING_H
+#define GRPC_CORE_LIB_TRANSPORT_TIMEOUT_ENCODING_H
+
+#include <grpc/support/time.h>
+#include "src/core/lib/support/string.h"
+
+#define GRPC_HTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE (GPR_LTOA_MIN_BUFSIZE + 1)
+
+/* Encode/decode timeouts to the GRPC over HTTP/2 format;
+ encoding may round up arbitrarily */
+void grpc_http2_encode_timeout(gpr_timespec timeout, char *buffer);
+int grpc_http2_decode_timeout(const char *buffer, gpr_timespec *timeout);
+
+#endif /* GRPC_CORE_LIB_TRANSPORT_TIMEOUT_ENCODING_H */