Working on a web project spread in more domains brings you to Cross-Origin Resource Sharing. Browsers use it to if it code from one origin can call HTTP methods placed elsewhere. Responses must contain specific headers, like Access-Control-Allow-Origin, which must match requesting Origin of the site issuing the call. More information about CORS can be found at html5rocks.

CORS look like simple thing to develop. Just add a few HTTP headers for regular methods. Then you must handle OPTIONS, then you must know all the headers you’re sending, figure out how will your application behave if Origin matches and if not. Therefor it is a good idea to use existing library doing so. For golang applications there is github.com/rs/cors. net/http compatible middleware, which wraps all handlers and apply CORS rules based on configuration. Usage is very simple, one just create instance of cors.Cors, http.ServeMux and register handlers. The question is then - how to test the CORS as a part of testing routine?.

Handler and HandlerFunc

HTTP handling is done in simple function with two mandatory arguments.

func(w http.ResponseWriter, r *http.Request)

where http.ResponseWritter is code providing methods writing HTTP reply and http.Request provides data from HTTP request. Functions are represented as interface http.Handler. It has only one method

type Handler interface {
        ServeHTTP(ResponseWriter, *Request)
}

Beauty of this system is simplicity and the fact ResponseWritter is interface enables unit testing via httptest.NewRecorder.

req := httptest.NewRequest("GET", "http://example.com/foo", nil)
w := httptest.NewRecorder()
handler(w, req)

Handlers all the way down

The main trick is here

func Middleware(h http.Handler) http.Handler

http.ServeMux is new type we have not talked before. It registers more handling functions and dispatch them based on request URL. And because it implements http.Handler interface, it is easy to join it with Middleware function and make them working together. The integration is simply done by calling ServeHTTP method. This is exactly how cors.Cors is integrated to your application, by the way.

CORS and testing

As we have discussed before, golang supports HTTP handlers testing via httptest.NewRecorder. This implements http.ResponseWritter interface, so it is easy to run handler function. The main trick is to get CORS enabled http.Handler in the test and call it.

// main.go
cors = cors.Default()           // initialize global variable cors.Cors
mux = http.NewServeMux()        // initialize new serve mux

// main_test.go
r := http.NewRequest(....)
w := httptest.NewRecorder()
handler := cors.Handler(mux)    // THE TRICK!
handler.ServeHTTP(w, r)

An example

Here is the full working example integrating CORS to simple http server. ServeMux and cors.Cors are now wrapped in Srv structure, so we can access the right handler in the test.

First, the proof, that CORS works well

$ curl -v -H Origin:http://example.com http://localhost:8000
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8000 (#0)
> GET / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.62.0
> Accept: */*
> Origin:http://example.com
> 
< HTTP/1.1 200 OK
**< Access-Control-Allow-Origin: *
< Vary: Origin**
< Date: Tue, 18 Dec 2018 14:16:56 GMT
< Content-Length: 25
< Content-Type: text/html; charset=utf-8
< 
<html>Hello world</html>
* Connection #0 to host localhost left intact

And the source code + test.

Major takeways

  • It is usually better to use battle tested code even for simple looking problem
  • net/http is simple yet powerful library providing great abstractions to build on top of
  • Testing is important and there should be always the way to write testable code
  • Working example of unit test for CORS integartion in golang application

Logo by samthor@Flickr: [https://www.flickr.com/photos/samthor/5994939587]