grpc-web call receives CORS error when using nginx proxy
See original GitHub issueI am testing the helloworld demo using nginx as proxy. But received a CORS error in chrome console. Here is the console error:
Access to XMLHttpRequest at 'http://localhost/helloworld.Greeter/SayHello' from origin 'http://localhost:8080' has been blocked by
CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I am a little confused by this error. Should I configure Access control setting in grpc-web client side or nginx proxy side? I tried putting code below to my nignx.conf file’s server.location field. But the error still shows.
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Transfer-Encoding,Custom-Header-1,X-Accept-Content-Transfer-Encoding,X-Accept-Response-Streaming,X-User-Agent,X-Grpc-Web';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Transfer-Encoding,Custom-Header-1,X-Accept-Content-Transfer-Encoding,X-Accept-Response-Streaming,X-User-Agent,X-Grpc-Web';
add_header 'Access-Control-Expose-Headers' 'Content-Transfer-Encoding';
}
Then I tried putting an extra meta to the client call:
client.sayHello(request, {'Access-Control-Allow-Origin': '*'}, (err, response) => {
console.log('response:', response.getMessage());
});
The console output error is still there.
It seems that no matter where I set the CORS, the client can only send a OPTIONS request to the server and I can’t see the Access-Control-Allow-Origin header that I previous set. Here is the chrome debug panel shows:
And the client.js code:
client.js
const {HelloRequest, RepeatHelloRequest, HelloReply} = require('./protos/helloworld_pb.js');
const {GreeterClient} = require('./protos/helloworld_grpc_web_pb.js');
var client = new GreeterClient('http://' + window.location.hostname + ':80',
null, null);
// simple unary call
function helloworld () {
var request = new HelloRequest();
request.setName('World');
client.sayHello(request, {'Access-Control-Allow-Origin': '*'}, (err, response) => {
console.log('response:', response.getMessage());
});
}
export {helloworld}
I am using Vue call the helloworld
method in the App.vue
file:
App.vue
<template>
<div id="app">
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld'
const {helloworld} = require('./grpc/client')
export default {
name: 'App',
components: {
HelloWorld
},
mounted() {
helloworld()
},
methods: {
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
Here is my full nginx.conf file looks:
nginx.conf
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
#include mime.types;
#default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
#include servers/*;
server {
listen 80 http2;
server_name localhost;
access_log logs/access.log main;
location ~ \.(html|js|css|ico)$ {
root /usr/local/var/www;
}
location ~ \.*(helloworld) {
grpc_pass grpc://localhost:50051;
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Transfer-Encoding,Custom-Header-1,X-Accept-Content-Transfer-Encoding,X-Accept-Response-Streaming,X-User-Agent,X-Grpc-Web';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Transfer-Encoding,Custom-Header-1,X-Accept-Content-Transfer-Encoding,X-Accept-Response-Streaming,X-User-Agent,X-Grpc-Web';
add_header 'Access-Control-Expose-Headers' 'Content-Transfer-Encoding';
}
}
}
}
The grpc server side is the same as the helloworld demo:
server.js
var PROTO_PATH = __dirname + '/protos/helloworld.proto';
var grpc = require('grpc');
var _ = require('lodash');
var async = require('async');
var protoLoader = require('@grpc/proto-loader');
var packageDefinition = protoLoader.loadSync(
PROTO_PATH,
{keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});
var protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
var helloworld = protoDescriptor.helloworld;
/**
* @param {!Object} call
* @param {function():?} callback
*/
function doSayHello(call, callback) {
console.log('do say hello', call.request.name)
callback(null, {message: 'Hello! '+ call.request.name});
}
/**
* @param {!Object} call
*/
function doSayRepeatHello(call) {
var senders = [];
function sender(name) {
return (callback) => {
call.write({
message: 'Hey! ' + name
});
_.delay(callback, 500); // in ms
};
}
for (var i = 0; i < call.request.count; i++) {
senders[i] = sender(call.request.name + i);
}
async.series(senders, () => {
call.end();
});
}
/**
* @param {!Object} call
* @param {function():?} callback
*/
function doSayHelloAfterDelay(call, callback) {
function dummy() {
return (cb) => {
_.delay(cb, 5000);
};
}
async.series([dummy()], () => {
callback(null, {
message: 'Hello! '+call.request.name
});
});
}
/**
* @return {!Object} gRPC server
*/
function getServer() {
var server = new grpc.Server();
server.addService(helloworld.Greeter.service, {
sayHello: doSayHello,
sayRepeatHello: doSayRepeatHello,
sayHelloAfterDelay: doSayHelloAfterDelay
});
return server;
}
if (require.main === module) {
var server = getServer();
server.bind('localhost:50051', grpc.ServerCredentials.createInsecure());
console.log('start server...')
server.start();
}
exports.getServer = getServer;
Issue Analytics
- State:
- Created 5 years ago
- Comments:7
It appears that the main thing is this line
listen 80 http2;
. If I removehttp2
, it works. You may have to have a separate port to listen to http2 traffic likeThis doesn’t work for windows I suppose? Is there an option to just compile the module, which I can use on a windows machine?