@@ -21,6 +21,8 @@ local req_set_uri = ngx.req.set_uri
2121local req_set_body_data = ngx .req .set_body_data
2222local decode_base64 = ngx .decode_base64
2323local encode_base64 = ngx .encode_base64
24+ local bit = require (" bit" )
25+ local string = string
2426
2527
2628local ALLOW_METHOD_OPTIONS = " OPTIONS"
@@ -87,7 +89,7 @@ function _M.access(conf, ctx)
8789 -- set grpc path
8890 if not (ctx .curr_req_matched and ctx .curr_req_matched [" :ext" ]) then
8991 core .log .error (" routing configuration error, grpc-web plugin only supports " ,
90- " `prefix matching` pattern routing" )
92+ " `prefix matching` pattern routing" )
9193 return 400
9294 end
9395
@@ -130,6 +132,7 @@ function _M.header_filter(conf, ctx)
130132 core .response .set_header (" Access-Control-Allow-Origin" , DEFAULT_CORS_ALLOW_ORIGIN )
131133 end
132134 core .response .set_header (" Content-Type" , ctx .grpc_web_mime )
135+ core .response .set_header (" Access-Control-Expose-Headers" , " grpc-message,grpc-status" )
133136end
134137
135138function _M .body_filter (conf , ctx )
@@ -147,6 +150,50 @@ function _M.body_filter(conf, ctx)
147150 chunk = encode_base64 (chunk )
148151 ngx_arg [1 ] = chunk
149152 end
153+
154+ --[[
155+ upstream_trailer_* available since NGINX version 1.13.10 :
156+ https://nginx.org/en/docs/http/ngx_http_upstream_module.html#var_upstream_trailer_
157+
158+ grpc-web trailer format reference:
159+ envoyproxy/envoy/source/extensions/filters/http/grpc_web/grpc_web_filter.cc
160+
161+ Format for grpc-web trailer
162+ 1 byte: 0x80
163+ 4 bytes: length of the trailer
164+ n bytes: trailer
165+
166+ --]]
167+ local status = ctx .var .upstream_trailer_grpc_status
168+ local message = ctx .var .upstream_trailer_grpc_message
169+ if status ~= " " and status ~= nil then
170+ local status_str = " grpc-status:" .. status
171+ local status_msg = " grpc-message:" .. ( message or " " )
172+ local grpc_web_trailer = status_str .. " \r\n " .. status_msg .. " \r\n "
173+ local len = # grpc_web_trailer
174+
175+ -- 1 byte: 0x80
176+ local trailer_buf = string.char (0x80 )
177+ -- 4 bytes: length of the trailer
178+ trailer_buf = trailer_buf .. string.char (
179+ bit .band (bit .rshift (len , 24 ), 0xff ),
180+ bit .band (bit .rshift (len , 16 ), 0xff ),
181+ bit .band (bit .rshift (len , 8 ), 0xff ),
182+ bit .band (len , 0xff )
183+ )
184+ -- n bytes: trailer
185+ trailer_buf = trailer_buf .. grpc_web_trailer
186+
187+ if ctx .grpc_web_encoding == CONTENT_ENCODING_BINARY then
188+ ngx_arg [1 ] = ngx_arg [1 ] .. trailer_buf
189+ else
190+ ngx_arg [1 ] = ngx_arg [1 ] .. encode_base64 (trailer_buf )
191+ end
192+
193+ -- clear trailer
194+ ctx .var .upstream_trailer_grpc_status = nil
195+ ctx .var .upstream_trailer_grpc_message = nil
196+ end
150197end
151198
152199return _M
0 commit comments