Tuesday, January 5, 2016

How to fix CORS preflight issues in local grunt app w/ self-signed SSL cert

After many hours of headbanging, I finally succeeded in making xhr requests between my local webapp running via grunt serve and my secure ASP.NET ServiceStack application on a self-signed certificate.

I tried to use the chrome trick of --disable-web-security on a shortcut of chrome, and using the Allow-Control-Allow-Origin: * plugin, but still I was running into errors like

  • Response for preflight has invalid HTTP status code 404
  • The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed.
  • Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:9000' is therefore not allowed access. 
I failed in trying to add CORS support to the server (which was okay-- we only want to bypass it for local testing).

With all these methods failing, I looked at the next logical progression: adding a proxy to the gruntfile to talk to the server. So I got my hands on the latest version of grunt-connect-proxy, followed their instructions for setting up, but I was still receiving issues. My requests would timeout at the proxy with the following error: Proxy error: ECONNRESET.

I finally found some others with the same problem in one of grunt-connect-proxy's github issues. The solution was to downgrade the version of grunt-connect-proxy. Low and behold, it works! My apps are talking to each other!

2. Install grunt-connect-proxy 0.1.10 
npm install grunt-connect-proxy@0.1.10

Here is the snippet of my gruntfile:

"use strict";
var LIVERELOAD_PORT, lrSnippet, mountFolder;
var serveStatic = require('serve-static');


lrSnippet = require("connect-livereload")({

mountFolder = function (connect, dir) {
 return serveStatic(require("path").resolve(dir));

module.exports = function (grunt) {
 var yeomanConfig;
 yeomanConfig = {
  app: "client",
  dist: "dist",
  docs: "documentation"

 var proxySnippet = require('grunt-connect-proxy/lib/utils').proxyRequest;

 try {
  yeomanConfig.app = require("./bower.json").appPath || yeomanConfig.app;
 } catch (_error) {}
  yeoman: yeomanConfig,
  watch: {
   compass: {
    files: ["<%= yeoman.app %>/styles/**/*.{scss,sass}"],
    tasks: ["compass:server"]
   less: {
    files: ["<%= yeoman.app %>/styles-less/**/*.less"],
    tasks: ["less:server"]
   jade: {
    files: ["<%= yeoman.docs %>/jade/*.jade"],
    tasks: ["jade:docs"]
   livereload: {
    options: {
     livereload: LIVERELOAD_PORT
    files: [
                    "<%= yeoman.app %>/index.html",
                    "<%= yeoman.app %>/app/**/*.html",
                    "<%= yeoman.app %>/app/**/*.js",
                    "<%= yeoman.app %>/styles/**/*.scss",
                    "<%= yeoman.app %>/styles-less/**/*.less",
                    "<%= yeoman.app %>/images/**/*.{png,jpg,jpeg,gif,webp,svg}",
                    "<%= yeoman.docs %>/jade/*.jade"
  connect: {
   options: {
    port: 9000,
    hostname: "localhost"
   proxies: [{
     context: '/app', // the context of the data service
     host: 'localhost', // wherever the data service is running
     port: 44300, // the port that the data service is running on
     https: true,
     changeOrigin: true,
     rewrite: {
      '/app': ''
     context: '/payment',
     host: 'localhost',
     port: 17333,
     https: false,
     changeOrigin: true,
     rewrite: {
      '/payment': ''
   livereload: {
    options: {
     middleware: function (connect) {
      return [lrSnippet,
        mountFolder(connect, ".tmp"),
        mountFolder(connect, yeomanConfig.app),