question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

grpc-web call receives CORS error when using nginx proxy

See original GitHub issue

I 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.

image

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:

image

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:closed
  • Created 5 years ago
  • Comments:7

github_iconTop GitHub Comments

1reaction
stanley-cheungcommented, Nov 19, 2018

It appears that the main thing is this line listen 80 http2;. If I remove http2, it works. You may have to have a separate port to listen to http2 traffic like

listen 8080;
listen 8443 http2;
0reactions
Edregolcommented, Nov 22, 2018

This doesn’t work for windows I suppose? Is there an option to just compile the module, which I can use on a windows machine?

Read more comments on GitHub >

github_iconTop Results From Across the Web

NGINX Proxy for CORS - Perficient Blogs
It occurs between the browser and a server (usually some sort of API endpoint). The browser sends some information via HTTP Access-Control- ...
Read more >
Unable to call gRPC service from Angular client - Stack Overflow
I believe your gRPC web application is not configured with the proper CORS to authorize communications from the website where you are ...
Read more >
Ingress Configuration - Declarative GitOps CD for Kubernetes
The above rule terminates TLS at the Argo CD API server, which detects the protocol being used, and responds appropriately. Note that the...
Read more >
grpcwebproxy - Go Packages
gRPC Web Proxy. This is a small reverse proxy that can front existing gRPC servers and expose their functionality using gRPC-Web protocol, ...
Read more >
Seamless Cloud-Native Apps with gRPC-Web and Istio
when I call a backend service from grpcurl I get something in the istio gateway logs like the following: I did not set...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found