From 6e5d2dcef30ac6866d8375367ea63ea9da391aa0 Mon Sep 17 00:00:00 2001 From: Feng Li Date: Tue, 21 Nov 2017 14:55:48 -0800 Subject: [PATCH] Supports base64 encoded gRPC-Web with content-type: application/grpc-web-text and content-type: application/grpc-web-text+proto. --- .../gateway/codec/grpc_web_text_decoder.cc | 25 +++++++++ .../gateway/codec/grpc_web_text_decoder.h | 26 +++++++++ .../gateway/codec/grpc_web_text_encoder.cc | 26 +++++++++ .../gateway/codec/grpc_web_text_encoder.h | 28 ++++++++++ .../gateway/frontend/nginx_http_frontend.cc | 3 ++ net/grpc/gateway/runtime/constants.h | 4 ++ net/grpc/gateway/runtime/runtime.cc | 54 ++++++++++++++++--- 7 files changed, 160 insertions(+), 6 deletions(-) create mode 100644 net/grpc/gateway/codec/grpc_web_text_decoder.cc create mode 100644 net/grpc/gateway/codec/grpc_web_text_decoder.h create mode 100644 net/grpc/gateway/codec/grpc_web_text_encoder.cc create mode 100644 net/grpc/gateway/codec/grpc_web_text_encoder.h diff --git a/net/grpc/gateway/codec/grpc_web_text_decoder.cc b/net/grpc/gateway/codec/grpc_web_text_decoder.cc new file mode 100644 index 0000000..9fa1521 --- /dev/null +++ b/net/grpc/gateway/codec/grpc_web_text_decoder.cc @@ -0,0 +1,25 @@ +#include "net/grpc/gateway/codec/grpc_web_text_decoder.h" + +namespace grpc { +namespace gateway { + +GrpcWebTextDecoder::GrpcWebTextDecoder() {} + +GrpcWebTextDecoder::~GrpcWebTextDecoder() {} + +Status GrpcWebTextDecoder::Decode() { + std::vector buffer; + if (!base64_.Decode(*inputs(), &buffer)) { + return Status(StatusCode::INVALID_ARGUMENT, "Invalid base64 inputs."); + } + + inputs()->clear(); + for (Slice& s : buffer) { + Append(s); + } + + return GrpcWebDecoder::Decode(); +} + +} // namespace gateway +} // namespace grpc diff --git a/net/grpc/gateway/codec/grpc_web_text_decoder.h b/net/grpc/gateway/codec/grpc_web_text_decoder.h new file mode 100644 index 0000000..58bd59f --- /dev/null +++ b/net/grpc/gateway/codec/grpc_web_text_decoder.h @@ -0,0 +1,26 @@ +#ifndef NET_GRPC_GATEWAY_CODEC_GRPC_WEB_TEXT_DECODER_H_ +#define NET_GRPC_GATEWAY_CODEC_GRPC_WEB_TEXT_DECODER_H_ + +#include "net/grpc/gateway/codec/base64.h" +#include "net/grpc/gateway/codec/grpc_web_decoder.h" + +namespace grpc { +namespace gateway { + +class GrpcWebTextDecoder : public GrpcWebDecoder { + public: + GrpcWebTextDecoder(); + ~GrpcWebTextDecoder() override; + GrpcWebTextDecoder(const GrpcWebTextDecoder&) = delete; + GrpcWebTextDecoder& operator=(const GrpcWebTextDecoder&) = delete; + + Status Decode() override; + + private: + Base64 base64_; +}; + +} // namespace gateway +} // namespace grpc + +#endif // NET_GRPC_GATEWAY_CODEC_GRPC_WEB_TEXT_DECODER_H_ diff --git a/net/grpc/gateway/codec/grpc_web_text_encoder.cc b/net/grpc/gateway/codec/grpc_web_text_encoder.cc new file mode 100644 index 0000000..3d592f2 --- /dev/null +++ b/net/grpc/gateway/codec/grpc_web_text_encoder.cc @@ -0,0 +1,26 @@ +#include "net/grpc/gateway/codec/grpc_web_text_encoder.h" + +namespace grpc { +namespace gateway { + +GrpcWebTextEncoder::GrpcWebTextEncoder() {} + +GrpcWebTextEncoder::~GrpcWebTextEncoder() {} + +void GrpcWebTextEncoder::Encode(grpc::ByteBuffer* input, + std::vector* result) { + std::vector buffer; + GrpcWebEncoder::Encode(input, &buffer); + base64_.Encode(buffer, result); +} + +void GrpcWebTextEncoder::EncodeStatus(const grpc::Status& status, + const Trailers* trailers, + std::vector* result) { + std::vector buffer; + GrpcWebEncoder::EncodeStatus(status, trailers, &buffer); + base64_.Encode(buffer, result); +} + +} // namespace gateway +} // namespace grpc diff --git a/net/grpc/gateway/codec/grpc_web_text_encoder.h b/net/grpc/gateway/codec/grpc_web_text_encoder.h new file mode 100644 index 0000000..b2f555c --- /dev/null +++ b/net/grpc/gateway/codec/grpc_web_text_encoder.h @@ -0,0 +1,28 @@ +#ifndef NET_GRPC_GATEWAY_CODEC_GRPC_WEB_TEXT_ENCODER_H_ +#define NET_GRPC_GATEWAY_CODEC_GRPC_WEB_TEXT_ENCODER_H_ + +#include "net/grpc/gateway/codec/base64.h" +#include "net/grpc/gateway/codec/grpc_web_encoder.h" + +namespace grpc { +namespace gateway { + +class GrpcWebTextEncoder : public GrpcWebEncoder { + public: + GrpcWebTextEncoder(); + ~GrpcWebTextEncoder() override; + GrpcWebTextEncoder(const GrpcWebTextEncoder&) = delete; + GrpcWebTextEncoder& operator=(const GrpcWebTextEncoder&) = delete; + + void Encode(grpc::ByteBuffer* input, std::vector* result) override; + void EncodeStatus(const grpc::Status& status, const Trailers* trailers, + std::vector* result) override; + + private: + Base64 base64_; +}; + +} // namespace gateway +} // namespace grpc + +#endif // NET_GRPC_GATEWAY_CODEC_GRPC_WEB_TEXT_ENCODER_H_ diff --git a/net/grpc/gateway/frontend/nginx_http_frontend.cc b/net/grpc/gateway/frontend/nginx_http_frontend.cc index 06dfe28..3cee228 100644 --- a/net/grpc/gateway/frontend/nginx_http_frontend.cc +++ b/net/grpc/gateway/frontend/nginx_http_frontend.cc @@ -512,6 +512,9 @@ void NginxHttpFrontend::SendResponseHeadersToClient(Response *response) { case GRPC_WEB: AddHTTPHeader(http_request_, kContentType, kContentTypeGrpcWeb); break; + case GRPC_WEB_TEXT: + AddHTTPHeader(http_request_, kContentType, kContentTypeGrpcWebText); + break; case JSON_STREAM_BODY: AddHTTPHeader(http_request_, kContentType, kContentTypeJson); break; diff --git a/net/grpc/gateway/runtime/constants.h b/net/grpc/gateway/runtime/constants.h index b23aa88..d64faf7 100644 --- a/net/grpc/gateway/runtime/constants.h +++ b/net/grpc/gateway/runtime/constants.h @@ -78,6 +78,9 @@ const char kContentLength[] = "content-length"; // The metadata name of content-transfer-encoding. const char kContentTransferEncoding[] = "content-transfer-encoding"; +// The metadata name of accept. +const char kAccept[] = "accept"; + // The metadata value of content-transfer-encoding for base64. const char kContentTransferEncoding_Base64[] = "base64"; const size_t kContentTransferEncoding_Base64_Length = @@ -103,6 +106,7 @@ enum Protocol { UNKNOWN = 0, GRPC, GRPC_WEB, + GRPC_WEB_TEXT, JSON_STREAM_BODY, PROTO_STREAM_BODY, B64_PROTO_STREAM_BODY, diff --git a/net/grpc/gateway/runtime/runtime.cc b/net/grpc/gateway/runtime/runtime.cc index e843b1d..70d57dc 100644 --- a/net/grpc/gateway/runtime/runtime.cc +++ b/net/grpc/gateway/runtime/runtime.cc @@ -14,6 +14,8 @@ #include "net/grpc/gateway/codec/grpc_encoder.h" #include "net/grpc/gateway/codec/grpc_web_decoder.h" #include "net/grpc/gateway/codec/grpc_web_encoder.h" +#include "net/grpc/gateway/codec/grpc_web_text_decoder.h" +#include "net/grpc/gateway/codec/grpc_web_text_encoder.h" #include "net/grpc/gateway/codec/json_decoder.h" #include "net/grpc/gateway/codec/json_encoder.h" #include "net/grpc/gateway/codec/proto_decoder.h" @@ -62,6 +64,19 @@ bool IsResponseB64(ngx_http_request_t* http_request) { } return false; } + +bool IsResponseGrpcWebText(ngx_http_request_t* http_request) { + string_ref value = + GetHTTPHeader(&http_request->headers_in.headers.part, kAccept); + if ((kContentTypeGrpcWebTextLength == value.size() && + strncasecmp(kContentTypeGrpcWebText, value.data(), value.size()) == 0) || + (kContentTypeGrpcWebTextProtoLength == value.size() && + strncasecmp(kContentTypeGrpcWebTextProto, value.data(), value.size()) == + 0)) { + return true; + } + return false; +} } // namespace Runtime::Runtime() { @@ -116,6 +131,8 @@ std::unique_ptr Runtime::CreateEncoder( return std::unique_ptr(new GrpcEncoder()); case GRPC_WEB: return std::unique_ptr(new GrpcWebEncoder()); + case GRPC_WEB_TEXT: + return std::unique_ptr(new GrpcWebTextEncoder()); case JSON_STREAM_BODY: return std::unique_ptr(new JsonEncoder()); case PROTO_STREAM_BODY: @@ -151,6 +168,8 @@ std::unique_ptr Runtime::CreateDecoder( } case GRPC_WEB: return std::unique_ptr(new GrpcWebDecoder()); + case GRPC_WEB_TEXT: + return std::unique_ptr(new GrpcWebTextDecoder()); case JSON_STREAM_BODY: return std::unique_ptr(new JsonDecoder()); case PROTO_STREAM_BODY: @@ -208,11 +227,22 @@ Protocol Runtime::DetectRequestProtocol(ngx_http_request_t* http_request) { 0) { return GRPC; } - if (content_type_length == kContentTypeGrpcWebLength && - strncasecmp(kContentTypeGrpcWeb, content_type, - kContentTypeGrpcWebLength) == 0) { + if ((content_type_length == kContentTypeGrpcWebLength && + strncasecmp(kContentTypeGrpcWeb, content_type, + kContentTypeGrpcWebLength) == 0) || + (content_type_length == kContentTypeGrpcWebProtoLength && + strncasecmp(kContentTypeGrpcWebProto, content_type, + kContentTypeGrpcWebProtoLength) == 0)) { return GRPC_WEB; } + if ((content_type_length == kContentTypeGrpcWebTextLength && + strncasecmp(kContentTypeGrpcWebText, content_type, + kContentTypeGrpcWebTextLength) == 0) || + (content_type_length == kContentTypeGrpcWebTextProtoLength && + strncasecmp(kContentTypeGrpcWebTextProto, content_type, + kContentTypeGrpcWebTextProtoLength) == 0)) { + return GRPC_WEB_TEXT; + } return UNKNOWN; } @@ -252,9 +282,21 @@ Protocol Runtime::DetectResponseProtocol(ngx_http_request_t* http_request) { 0) { return GRPC; } - if (content_type_length == kContentTypeGrpcWebLength && - strncasecmp(kContentTypeGrpcWeb, content_type, - kContentTypeGrpcWebLength) == 0) { + if ((content_type_length == kContentTypeGrpcWebLength && + strncasecmp(kContentTypeGrpcWeb, content_type, + kContentTypeGrpcWebLength) == 0) || + (content_type_length == kContentTypeGrpcWebProtoLength && + strncasecmp(kContentTypeGrpcWebProto, content_type, + kContentTypeGrpcWebProtoLength) == 0) || + (content_type_length == kContentTypeGrpcWebTextLength && + strncasecmp(kContentTypeGrpcWebText, content_type, + kContentTypeGrpcWebTextLength) == 0) || + (content_type_length == kContentTypeGrpcWebTextProtoLength && + strncasecmp(kContentTypeGrpcWebTextProto, content_type, + kContentTypeGrpcWebTextProtoLength) == 0)) { + if (IsResponseGrpcWebText(http_request)) { + return GRPC_WEB_TEXT; + } return GRPC_WEB; } return UNKNOWN;