498 lines
35 KiB
HTML
498 lines
35 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en-us">
|
||
<head>
|
||
<link rel="preload" href="/lib/font-awesome/webfonts/fa-brands-400.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
||
<link rel="preload" href="/lib/font-awesome/webfonts/fa-regular-400.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
||
<link rel="preload" href="/lib/font-awesome/webfonts/fa-solid-900.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
||
<link rel="preload" href="/lib/JetBrainsMono/web/woff2/JetBrainsMono-Regular.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
||
<script type="text/javascript" src="https://latest.cactus.chat/cactus.js"></script>
|
||
<link rel="stylesheet" href="https://latest.cactus.chat/style.css" type="text/css">
|
||
<meta charset="utf-8">
|
||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||
<title> Writing your own Reverse Proxy server using Golang | Basit's Blog</title>
|
||
<link rel = 'canonical' href = 'https://blog.rjbasitali.com/posts/writing-your-own-reverse-proxy-using-golang/'>
|
||
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<meta name="robots" content="all,follow">
|
||
<meta name="googlebot" content="index,follow,snippet,archive">
|
||
<meta property="og:title" content="Writing your own Reverse Proxy server using Golang" />
|
||
<meta property="og:description" content="Writing a Reverse Proxy server in Go is a matter of single digit lines of code due to its rock solid standard library and its low level network plumbing capabilities. Recently I came across some use cases where I needed to write my own Reverse Proxy server and of course Go was the pragmatic choice.
|
||
Let us first start with defining Reverse Proxy and its common uses.
|
||
Reverse Proxy A Reverse Proxy is an intermediary server that sits between multiple clients and servers, and directs client requests to appropriate backend server." />
|
||
<meta property="og:type" content="article" />
|
||
<meta property="og:url" content="https://blog.rjbasitali.com/posts/writing-your-own-reverse-proxy-using-golang/" /><meta property="article:section" content="posts" />
|
||
<meta property="article:published_time" content="2021-10-12T22:05:04+05:00" />
|
||
<meta property="article:modified_time" content="2021-10-12T22:05:04+05:00" />
|
||
|
||
|
||
<meta name="twitter:card" content="summary"/>
|
||
<meta name="twitter:title" content="Writing your own Reverse Proxy server using Golang"/>
|
||
<meta name="twitter:description" content="Writing a Reverse Proxy server in Go is a matter of single digit lines of code due to its rock solid standard library and its low level network plumbing capabilities. Recently I came across some use cases where I needed to write my own Reverse Proxy server and of course Go was the pragmatic choice.
|
||
Let us first start with defining Reverse Proxy and its common uses.
|
||
Reverse Proxy A Reverse Proxy is an intermediary server that sits between multiple clients and servers, and directs client requests to appropriate backend server."/>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<link rel="stylesheet" href="https://blog.rjbasitali.com/css/styles.94f653e9e151e28067a7c5dbbc4600cbd5a3c721e79faaf971e523c40f3b249b8e4f20bb57810dfffa8d559ca5c140fd56eb4cd9c0853113ad08e66afdb08bdd.css" integrity="sha512-lPZT6eFR4oBnp8XbvEYAy9WjxyHnn6r5ceUjxA87JJuOTyC7V4EN//qNVZylwUD9VutM2cCFMROtCOZq/bCL3Q==">
|
||
|
||
|
||
|
||
|
||
<!--[if lt IE 9]>
|
||
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
|
||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||
<![endif]-->
|
||
|
||
|
||
|
||
<link rel="icon" type="image/png" href="https://blog.rjbasitali.com/images/favicon.ico" />
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-N33SDFP4VQ"></script>
|
||
<script>
|
||
window.dataLayer = window.dataLayer || [];
|
||
function gtag(){dataLayer.push(arguments);}
|
||
gtag('js', new Date());
|
||
|
||
gtag('config', 'G-N33SDFP4VQ');
|
||
</script>
|
||
|
||
|
||
</head>
|
||
|
||
<body class="max-width mx-auto px3 ltr">
|
||
<div class="content index py4">
|
||
|
||
<div id="header-post">
|
||
<a id="menu-icon" href="#"><i class="fas fa-bars fa-lg"></i></a>
|
||
<a id="menu-icon-tablet" href="#"><i class="fas fa-bars fa-lg"></i></a>
|
||
<a id="top-icon-tablet" href="#" onclick="$('html, body').animate({ scrollTop: 0 }, 'fast');" style="display:none;" aria-label="Top of Page"><i class="fas fa-chevron-up fa-lg"></i></a>
|
||
<span id="menu">
|
||
<span id="nav">
|
||
<ul>
|
||
|
||
<li><a href="/">Home</a></li>
|
||
|
||
<li><a href="/tags">Tags</a></li>
|
||
|
||
<li><a href="/about">About</a></li>
|
||
|
||
</ul>
|
||
</span>
|
||
<br/>
|
||
<span id="actions">
|
||
<ul>
|
||
|
||
<li>
|
||
<a class="icon" href=" https://blog.rjbasitali.com/posts/writing-a-golang-middleware-for-your-http-handler/" aria-label="Previous">
|
||
<i class="fas fa-chevron-left" aria-hidden="true" onmouseover="$('#i-prev').toggle();" onmouseout="$('#i-prev').toggle();"></i>
|
||
</a>
|
||
</li>
|
||
|
||
|
||
<li>
|
||
<a class="icon" href="#" onclick="$('html, body').animate({ scrollTop: 0 }, 'fast');" aria-label="Top of Page">
|
||
<i class="fas fa-chevron-up" aria-hidden="true" onmouseover="$('#i-top').toggle();" onmouseout="$('#i-top').toggle();"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="#" aria-label="Share">
|
||
<i class="fas fa-share-alt" aria-hidden="true" onmouseover="$('#i-share').toggle();" onmouseout="$('#i-share').toggle();" onclick="$('#share').toggle();return false;"></i>
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
<span id="i-prev" class="info" style="display:none;">Previous post</span>
|
||
<span id="i-next" class="info" style="display:none;">Next post</span>
|
||
<span id="i-top" class="info" style="display:none;">Back to top</span>
|
||
<span id="i-share" class="info" style="display:none;">Share post</span>
|
||
</span>
|
||
<br/>
|
||
<div id="share" style="display: none">
|
||
|
||
<ul>
|
||
|
||
|
||
|
||
|
||
|
||
<li>
|
||
<a class="icon" href="http://www.facebook.com/sharer.php?u=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-your-own-reverse-proxy-using-golang%2f" aria-label="Facebook">
|
||
<i class="fab fa-facebook " aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="https://twitter.com/share?url=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-your-own-reverse-proxy-using-golang%2f&text=Writing%20your%20own%20Reverse%20Proxy%20server%20using%20Golang" aria-label="Twitter">
|
||
<i class="fab fa-twitter " aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="http://www.linkedin.com/shareArticle?url=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-your-own-reverse-proxy-using-golang%2f&title=Writing%20your%20own%20Reverse%20Proxy%20server%20using%20Golang" aria-label="Linkedin">
|
||
<i class="fab fa-linkedin " aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="https://pinterest.com/pin/create/bookmarklet/?url=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-your-own-reverse-proxy-using-golang%2f&is_video=false&description=Writing%20your%20own%20Reverse%20Proxy%20server%20using%20Golang" aria-label="Pinterest">
|
||
<i class="fab fa-pinterest " aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="mailto:?subject=Writing%20your%20own%20Reverse%20Proxy%20server%20using%20Golang&body=Check out this article: https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-your-own-reverse-proxy-using-golang%2f" aria-label="Email">
|
||
<i class="fas fa-envelope " aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="https://getpocket.com/save?url=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-your-own-reverse-proxy-using-golang%2f&title=Writing%20your%20own%20Reverse%20Proxy%20server%20using%20Golang" aria-label="Pocket">
|
||
<i class="fab fa-get-pocket " aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="http://reddit.com/submit?url=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-your-own-reverse-proxy-using-golang%2f&title=Writing%20your%20own%20Reverse%20Proxy%20server%20using%20Golang" aria-label="reddit">
|
||
<i class="fab fa-reddit " aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="http://www.tumblr.com/share/link?url=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-your-own-reverse-proxy-using-golang%2f&name=Writing%20your%20own%20Reverse%20Proxy%20server%20using%20Golang&description=Writing%20a%20Reverse%20Proxy%20server%20in%20Go%20is%20a%20matter%20of%20single%20digit%20lines%20of%20code%20due%20to%20its%20rock%20solid%20standard%20library%20and%20its%20low%20level%20network%20plumbing%20capabilities.%20Recently%20I%20came%20across%20some%20use%20cases%20where%20I%20needed%20to%20write%20my%20own%20Reverse%20Proxy%20server%20and%20of%20course%20Go%20was%20the%20pragmatic%20choice.%0aLet%20us%20first%20start%20with%20defining%20Reverse%20Proxy%20and%20its%20common%20uses.%0aReverse%20Proxy%20A%20Reverse%20Proxy%20is%20an%20intermediary%20server%20that%20sits%20between%20multiple%20clients%20and%20servers%2c%20and%20directs%20client%20requests%20to%20appropriate%20backend%20server." aria-label="Tumblr">
|
||
<i class="fab fa-tumblr " aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="https://news.ycombinator.com/submitlink?u=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-your-own-reverse-proxy-using-golang%2f&t=Writing%20your%20own%20Reverse%20Proxy%20server%20using%20Golang" aria-label="Hacker News">
|
||
<i class="fab fa-hacker-news " aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
|
||
</div>
|
||
|
||
<div id="toc">
|
||
<nav id="TableOfContents">
|
||
<ul>
|
||
<li><a href="#reverse-proxy">Reverse Proxy</a></li>
|
||
<li><a href="#personal-use-cases">Personal Use Cases</a></li>
|
||
<li><a href="#lets-code">Let’s Code</a>
|
||
<ul>
|
||
<li><a href="#conditions">Conditions</a></li>
|
||
<li><a href="#maingo"><code>main.go</code></a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#next-steps">Next Steps</a></li>
|
||
</ul>
|
||
</nav>
|
||
</div>
|
||
|
||
</span>
|
||
</div>
|
||
|
||
|
||
<article class="post" itemscope itemtype="http://schema.org/BlogPosting">
|
||
<header>
|
||
<h1 class="posttitle" itemprop="name headline">
|
||
Writing your own Reverse Proxy server using Golang
|
||
</h1>
|
||
<div class="meta">
|
||
|
||
<div class="postdate">
|
||
|
||
<time datetime="2021-10-12 22:05:04 +0500 PKT" itemprop="datePublished">2021-10-12</time>
|
||
|
||
</div>
|
||
|
||
|
||
<div class="article-read-time">
|
||
<i class="far fa-clock"></i>
|
||
|
||
5 minute read
|
||
</div>
|
||
|
||
|
||
|
||
<div class="article-tag">
|
||
<i class="fas fa-tag"></i>
|
||
|
||
|
||
<a class="tag-link" href="/tags/golang" rel="tag">golang</a>
|
||
|
||
,
|
||
<a class="tag-link" href="/tags/reverse-proxy" rel="tag">reverse-proxy</a>
|
||
|
||
,
|
||
<a class="tag-link" href="/tags/proxy" rel="tag">proxy</a>
|
||
|
||
,
|
||
<a class="tag-link" href="/tags/go" rel="tag">go</a>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
</header>
|
||
|
||
|
||
|
||
<div class="content" itemprop="articleBody">
|
||
<p>Writing a Reverse Proxy server in Go is a matter of single digit lines of code due to its rock solid standard library and its low level network plumbing capabilities. Recently I came across some use cases where I needed to write my own Reverse Proxy server and of course Go was the pragmatic choice.</p>
|
||
<p>Let us first start with defining Reverse Proxy and its common uses.</p>
|
||
<h2 id="reverse-proxy">Reverse Proxy</h2>
|
||
<p>A <code>Reverse Proxy</code> is an intermediary server that sits between multiple clients and servers, and directs client requests to appropriate backend server. It is commonly used for:</p>
|
||
<ul>
|
||
<li>
|
||
<p><strong>Load Balancing –</strong> It can distribute the load across multiple available backend servers in such manner that maximizes speed and capacity utilization while ensuring no one server is overloaded, which can degrade performance.</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>Security –</strong> By intercepting requests headed for your backend servers, a reverse proxy server protects their identities and acts as an additional defense against security attacks.</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>Caching –</strong> It can cache content to serve, and you can deploy regional proxies to cache content related to specific regions.</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>TLS Termination –</strong> We can use the reverse proxy to terminate the SSL/TLS connection with the clients in cases where backend servers are being hosted on the same secure VPC network, and it’s ok to let the <code>reverse proxy</code> <-> <code>backend server</code> connection insecure.</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>More –</strong> Literally, there are a lot more use cases that can take leverage of reverse proxies, that I leave for you to explore.</p>
|
||
</li>
|
||
</ul>
|
||
<h2 id="personal-use-cases">Personal Use Cases</h2>
|
||
<p>Some common use cases I personally use <code>reverse proxy</code> server for are:</p>
|
||
<ul>
|
||
<li>
|
||
<p><strong>TLS Termination –</strong> Lets say I have multiple servers running on the same VPC network, each has it’s own copy of the <code>TLS certificate</code> issued to same domain name, it makes sense to let the <code>reverse proxy</code> handle the TLS handshake and terminate the secure connection and forward it insecurely to appropriate backend server on the same VPC isolated from public reach, now instead of having <code>TLS certs</code> on every backend server you can have them on proxy only.</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>Single VPS Dev Environment –</strong> I have multiple backend servers for a single project, each with separate responsibilities, that I need to deploy but the servers would sit ideal 99% of the time I don’t want to spin up multiple VPS for each for a dev environment, which would cost me money, also I don’t want to explicitly mention the <code>port numbers</code> on the client side to have the servers run on the same VPS. To solve this, I can run all the backend servers and <code>reverse proxy</code> on a single VPS, and have the requests proxied to servers running on different ports.</p>
|
||
</li>
|
||
</ul>
|
||
<p>Also sometimes for <code>Authorization</code>, <code>Caching</code> or implementing a feature like <code>Request Limit</code> per client.</p>
|
||
<h2 id="lets-code">Let’s Code</h2>
|
||
<p>Enough with the uses, lets jump over to implementation. You are probably already familiar with Go standard library’s <code>net/http</code> package, there’s also a utility package for it you might have or might not have used, that is <code>net/http/httputil</code>. We’ll be using this utility package because it has the type <code>ReverseProxy</code> defined which makes writing a reverse proxy so simple using Go.</p>
|
||
<h3 id="conditions">Conditions</h3>
|
||
<p>Let’s define some condition based on which we’ll be forwarding the requests to appropriate server. For this tutorial lets stick to those conditions being the domain name or host of the requests:</p>
|
||
<ol>
|
||
<li>If request came on host <code>sub1.xyz.com</code>, forward to <code>localhost:8080</code>.</li>
|
||
<li>If request came on host <code>sub2.xyz.com</code>, forward to <code>localhost:8181</code>.</li>
|
||
<li>If request came from an unknown host, forward to <code>localhost:8888</code>.</li>
|
||
</ol>
|
||
<h3 id="maingo"><code>main.go</code></h3>
|
||
<p>Now lets create a <code>main.go</code> file, which will do the following:</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1</span><span> <span style="color:#ff79c6">package</span> main
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2</span><span>
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3</span><span> <span style="color:#ff79c6">import</span> <span style="color:#f1fa8c">"net/http"</span>
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4</span><span>
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5</span><span> <span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">main</span>() {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6</span><span> http.<span style="color:#50fa7b">HandleFunc</span>(<span style="color:#f1fa8c">"/"</span>, proxy)
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7</span><span> <span style="color:#ff79c6">if</span> err <span style="color:#ff79c6">:=</span> http.<span style="color:#50fa7b">ListenAndServe</span>(<span style="color:#f1fa8c">":4433"</span>, <span style="color:#ff79c6">nil</span>); err <span style="color:#ff79c6">!=</span> <span style="color:#ff79c6">nil</span> {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8</span><span> <span style="color:#8be9fd;font-style:italic">panic</span>(err)
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9</span><span> }
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10</span><span> }
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11</span><span>
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12</span><span> <span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">proxy</span>(w http.ResponseWriter, r <span style="color:#ff79c6">*</span>http.Request) {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13</span><span> <span style="color:#6272a4">// We will get to this later ...
|
||
</span></span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14</span><span><span style="color:#6272a4"></span> }
|
||
</span></span></code></pre></div><p>Now that we have a basic server in place, let’s write our conditions for <code>reverse proxy</code> and use it to forward requests inside our <code>proxy</code> handler.</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1</span><span> <span style="color:#ff79c6">package</span> main
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2</span><span>
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3</span><span> <span style="color:#ff79c6">import</span> (
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4</span><span> <span style="color:#f1fa8c">"net/http"</span>
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5</span><span> <span style="color:#f1fa8c">"net/http/httputil"</span>
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6</span><span> <span style="color:#f1fa8c">"net/url"</span>
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7</span><span> <span style="color:#f1fa8c">"strings"</span>
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8</span><span> )
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9</span><span>
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10</span><span> <span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">main</span>() {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11</span><span> http.<span style="color:#50fa7b">HandleFunc</span>(<span style="color:#f1fa8c">"/"</span>, proxy)
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12</span><span> <span style="color:#ff79c6">if</span> err <span style="color:#ff79c6">:=</span> http.<span style="color:#50fa7b">ListenAndServe</span>(<span style="color:#f1fa8c">":4433"</span>, <span style="color:#ff79c6">nil</span>); err <span style="color:#ff79c6">!=</span> <span style="color:#ff79c6">nil</span> {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13</span><span> <span style="color:#8be9fd;font-style:italic">panic</span>(err)
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14</span><span> }
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15</span><span> }
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16</span><span>
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17</span><span> <span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">proxy</span>(w http.ResponseWriter, r <span style="color:#ff79c6">*</span>http.Request) {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18</span><span> <span style="color:#ff79c6">if</span> strings.<span style="color:#50fa7b">HasSuffix</span>(r.Host, <span style="color:#f1fa8c">"://sub1.xyz.com"</span>) { <span style="color:#6272a4">// forward requests from sub1.xyz.com to localhost:8080
|
||
</span></span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19</span><span><span style="color:#6272a4"></span> <span style="color:#ff79c6">if</span> url, err <span style="color:#ff79c6">:=</span> url.<span style="color:#50fa7b">Parse</span>(<span style="color:#f1fa8c">"http://localhost:8080"</span>); err <span style="color:#ff79c6">==</span> <span style="color:#ff79c6">nil</span> {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20</span><span> proxy <span style="color:#ff79c6">:=</span> httputil.<span style="color:#50fa7b">NewSingleHostReverseProxy</span>(url)
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21</span><span> proxy.<span style="color:#50fa7b">ServeHTTP</span>(w, r)
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22</span><span> }
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23</span><span> } <span style="color:#ff79c6">else</span> <span style="color:#ff79c6">if</span> strings.<span style="color:#50fa7b">HasSuffix</span>(r.Host, <span style="color:#f1fa8c">"://sub2.xyz.com"</span>) { <span style="color:#6272a4">// forward requests from sub2.xyz.com to localhost:8181
|
||
</span></span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24</span><span><span style="color:#6272a4"></span> <span style="color:#ff79c6">if</span> url, err <span style="color:#ff79c6">:=</span> url.<span style="color:#50fa7b">Parse</span>(<span style="color:#f1fa8c">"http://localhost:8181"</span>); err <span style="color:#ff79c6">==</span> <span style="color:#ff79c6">nil</span> {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25</span><span> proxy <span style="color:#ff79c6">:=</span> httputil.<span style="color:#50fa7b">NewSingleHostReverseProxy</span>(url)
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26</span><span> proxy.<span style="color:#50fa7b">ServeHTTP</span>(w, r)
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27</span><span> }
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28</span><span> } <span style="color:#ff79c6">else</span> { <span style="color:#6272a4">// forward requests from unknown host to localhost:8888
|
||
</span></span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29</span><span><span style="color:#6272a4"></span> <span style="color:#ff79c6">if</span> url, err <span style="color:#ff79c6">:=</span> url.<span style="color:#50fa7b">Parse</span>(<span style="color:#f1fa8c">"http://localhost:8888"</span>); err <span style="color:#ff79c6">==</span> <span style="color:#ff79c6">nil</span> {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30</span><span> proxy <span style="color:#ff79c6">:=</span> httputil.<span style="color:#50fa7b">NewSingleHostReverseProxy</span>(url)
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31</span><span> proxy.<span style="color:#50fa7b">ServeHTTP</span>(w, r)
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32</span><span> }
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">33</span><span> }
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">34</span><span> }
|
||
</span></span></code></pre></div><p>The <code>httputil</code>’s <code>NewSingleHostReverseProxy(url)</code> function returns a new <code>ReverseProxy</code> that routes requests to <code>url</code> provided. And that’s all, or what minimal we need to run a reverse proxy based on conditions we defined.</p>
|
||
<p>But there are many ways we can refactor this code, starting from using predefined <code>proxy</code> objects instead of creating a new one on each request. Or I prefer a map <code>map[string]*httputil.ReverseProxy</code> which maps hosts to proxies, so we can use the map to check if a proxy exists for host then use it, else use the default one. I’ll leave the refactoring to anyone following this tutorial to keep it short.</p>
|
||
<h2 id="next-steps">Next Steps</h2>
|
||
<p>Though it is pretty simple to write a <code>reverse proxy</code> using Go’s standard library, but it’s missing a lot of good stuff that comes with <code>nginx</code> or other solutions available. So you can start by implementing some of them into your own <code>reverse proxy</code> that is if the available solutions aren’t feasible and you are having to develop your own, like:</p>
|
||
<ul>
|
||
<li>Caching</li>
|
||
<li>Logging</li>
|
||
<li>Protocol Support (HTTP, HTTPS, HTTP/1.1, HTTP/2, …)</li>
|
||
<li>Rate Limit</li>
|
||
<li>TLS Support</li>
|
||
<li>Load Balancing</li>
|
||
<li>…</li>
|
||
</ul>
|
||
|
||
</div>
|
||
</article>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div id="footer-post-container">
|
||
<div id="footer-post">
|
||
|
||
<div id="nav-footer" style="display: none">
|
||
<ul>
|
||
|
||
<li><a href="/">Home</a></li>
|
||
|
||
<li><a href="/tags">Tags</a></li>
|
||
|
||
<li><a href="/about">About</a></li>
|
||
|
||
</ul>
|
||
</div>
|
||
|
||
|
||
<div id="toc-footer" style="display: none">
|
||
<nav id="TableOfContents">
|
||
<ul>
|
||
<li><a href="#reverse-proxy">Reverse Proxy</a></li>
|
||
<li><a href="#personal-use-cases">Personal Use Cases</a></li>
|
||
<li><a href="#lets-code">Let’s Code</a>
|
||
<ul>
|
||
<li><a href="#conditions">Conditions</a></li>
|
||
<li><a href="#maingo"><code>main.go</code></a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#next-steps">Next Steps</a></li>
|
||
</ul>
|
||
</nav>
|
||
</div>
|
||
|
||
|
||
<div id="share-footer" style="display: none">
|
||
|
||
<ul>
|
||
|
||
|
||
|
||
|
||
|
||
<li>
|
||
<a class="icon" href="http://www.facebook.com/sharer.php?u=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-your-own-reverse-proxy-using-golang%2f" aria-label="Facebook">
|
||
<i class="fab fa-facebook fa-lg" aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="https://twitter.com/share?url=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-your-own-reverse-proxy-using-golang%2f&text=Writing%20your%20own%20Reverse%20Proxy%20server%20using%20Golang" aria-label="Twitter">
|
||
<i class="fab fa-twitter fa-lg" aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="http://www.linkedin.com/shareArticle?url=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-your-own-reverse-proxy-using-golang%2f&title=Writing%20your%20own%20Reverse%20Proxy%20server%20using%20Golang" aria-label="Linkedin">
|
||
<i class="fab fa-linkedin fa-lg" aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="https://pinterest.com/pin/create/bookmarklet/?url=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-your-own-reverse-proxy-using-golang%2f&is_video=false&description=Writing%20your%20own%20Reverse%20Proxy%20server%20using%20Golang" aria-label="Pinterest">
|
||
<i class="fab fa-pinterest fa-lg" aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="mailto:?subject=Writing%20your%20own%20Reverse%20Proxy%20server%20using%20Golang&body=Check out this article: https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-your-own-reverse-proxy-using-golang%2f" aria-label="Email">
|
||
<i class="fas fa-envelope fa-lg" aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="https://getpocket.com/save?url=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-your-own-reverse-proxy-using-golang%2f&title=Writing%20your%20own%20Reverse%20Proxy%20server%20using%20Golang" aria-label="Pocket">
|
||
<i class="fab fa-get-pocket fa-lg" aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="http://reddit.com/submit?url=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-your-own-reverse-proxy-using-golang%2f&title=Writing%20your%20own%20Reverse%20Proxy%20server%20using%20Golang" aria-label="reddit">
|
||
<i class="fab fa-reddit fa-lg" aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="http://www.tumblr.com/share/link?url=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-your-own-reverse-proxy-using-golang%2f&name=Writing%20your%20own%20Reverse%20Proxy%20server%20using%20Golang&description=Writing%20a%20Reverse%20Proxy%20server%20in%20Go%20is%20a%20matter%20of%20single%20digit%20lines%20of%20code%20due%20to%20its%20rock%20solid%20standard%20library%20and%20its%20low%20level%20network%20plumbing%20capabilities.%20Recently%20I%20came%20across%20some%20use%20cases%20where%20I%20needed%20to%20write%20my%20own%20Reverse%20Proxy%20server%20and%20of%20course%20Go%20was%20the%20pragmatic%20choice.%0aLet%20us%20first%20start%20with%20defining%20Reverse%20Proxy%20and%20its%20common%20uses.%0aReverse%20Proxy%20A%20Reverse%20Proxy%20is%20an%20intermediary%20server%20that%20sits%20between%20multiple%20clients%20and%20servers%2c%20and%20directs%20client%20requests%20to%20appropriate%20backend%20server." aria-label="Tumblr">
|
||
<i class="fab fa-tumblr fa-lg" aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="https://news.ycombinator.com/submitlink?u=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-your-own-reverse-proxy-using-golang%2f&t=Writing%20your%20own%20Reverse%20Proxy%20server%20using%20Golang" aria-label="Hacker News">
|
||
<i class="fab fa-hacker-news fa-lg" aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
|
||
</div>
|
||
|
||
<div id="actions-footer">
|
||
|
||
<a id="menu-toggle" class="icon" href="#" onclick="$('#nav-footer').toggle();return false;" aria-label="Menu">
|
||
<i class="fas fa-bars fa-lg" aria-hidden="true"></i> Menu</a>
|
||
|
||
<a id="toc-toggle" class="icon" href="#" onclick="$('#toc-footer').toggle();return false;" aria-label="TOC">
|
||
<i class="fas fa-list fa-lg" aria-hidden="true"></i> TOC</a>
|
||
|
||
<a id="share-toggle" class="icon" href="#" onclick="$('#share-footer').toggle();return false;" aria-label="Share">
|
||
<i class="fas fa-share-alt fa-lg" aria-hidden="true"></i> share</a>
|
||
<a id="top" style="display:none" class="icon" href="#" onclick="$('html, body').animate({ scrollTop: 0 }, 'fast');" aria-label="Top of Page">
|
||
<i class="fas fa-chevron-up fa-lg" aria-hidden="true"></i> Top</a>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<footer id="footer">
|
||
<div class="footer-left">
|
||
Copyright © 2022 Basit Ali
|
||
</div>
|
||
<div class="footer-right">
|
||
<nav>
|
||
<ul>
|
||
|
||
<li><a href="/">Home</a></li>
|
||
|
||
<li><a href="/tags">Tags</a></li>
|
||
|
||
<li><a href="/about">About</a></li>
|
||
|
||
</ul>
|
||
</nav>
|
||
</div>
|
||
</footer>
|
||
|
||
|
||
</div>
|
||
</body>
|
||
|
||
<link rel="stylesheet" href=/lib/font-awesome/css/all.min.css>
|
||
<script src=/lib/jquery/jquery.min.js></script>
|
||
<script src=/js/main.js></script>
|
||
|
||
<script src=/js/code-copy.js></script>
|
||
|
||
|
||
|
||
|
||
</html>
|