Over the past few weeks we've been slowly rolling out an improved version of our Runscope URL gateways. These gateways run in each of the service regions we support, relaying traffic from your apps to the APIs you use and back, capturing a snapshot of the traffic along the way. Runscope Radar tests also send their traffic through the gateways automatically so nearly every request we handle passes through one. These gateways are the workhorses that make our core introspection features possible.
Faithfully transmitting requests and responses has been a top priority from the very beginning. When debugging a problem, you need to minimize the amount of variables introduced by the debugger. As much as possible, we wanted to avoid introducing Heisenbugs so that you can focus on solving your API problems and not debugging the debugger.
Act I: Python
The original version of the gateway was written in Python using Flask and gevent and has served us well for over a year. The vast majority of HTTP requests we carry are relayed transparently to the API and back to the client. But given the flexible nature of HTTP, ambiguity in the HTTP spec and a wide variety of client and server implementations, we would occasionally run into issues where the relay process wasn't 100% faithful to a direct request. This was very rare however and we've done a lot of work to minimize discrepancies.
Act II: Go
As the company has grown and more customers use us, it's become clearer which of the edge cases we encountered were the most common. Even though these issues only arose in only a minute percent of requests, it's still important to us to handle them. We set out to build a new version that would let us relay the requests unmodified, capture data in parallel and asynchronously store the captured data.
Go is a fantastic language for building concurrent, high-performance HTTP handlers so it was an easy decision to use for the next version of the URL gateway. In addition to a nice performance improvement processing and storing the request data (which was already very fast), we were able to fix the following problems along the way.
HTTP Header Case Preserved
A component of the Python system would rewrite header names to be title-cased instead of maintaining the case specified by the client or server. While HTTP header names are case-insensitive according to the spec, many custom server implementations do not adhere to this requirement. The new Runscope URL gateway relays HTTP header names unmodified.
Compressed Data is Maintained
An unfortunate side effect of inspecting HTTP data is needing to decompress it first. With the new gateway, this is no longer the case. The request/response relay happens separately from the processing and storage of the data so data remains compressed all the way through. We decompress for viewing the data asynchronously from the request/response lifecycle.
SNI SSL Support
The combination of Python components we were using (Python 2.7.x, gevent and OpenSSL) did not support SNI SSL certificates, resulting in failed requests and cryptic error messages. You could work around this by disabling certificate validation in your bucket settings, but that's not an ideal setup. In the new gateway, SNI SSL certificates are now fully-supported without disabling certificate validation.
These are the most common issues encountered, but there are some other extremely rare cases that are also solved by minimizing how much we alter the request and response in flight. We're monitoring the gateways closely for any issues that may arise but if you encounter any expected behavior, please get in touch with our support team, preferably with a shared request.
P.S. If these types of problems sound like something you'd like to work on, come join us. We're hiring!