{"id":64435,"date":"2022-05-18T09:03:46","date_gmt":"2022-05-18T09:03:46","guid":{"rendered":"https:\/\/www.cryptocabaret.com\/?p=64435"},"modified":"2022-05-18T09:03:46","modified_gmt":"2022-05-18T09:03:46","slug":"a-guide-to-pipy-a-programmable-network-proxy-for-cloud","status":"publish","type":"post","link":"https:\/\/www.cryptocabaret.com\/?p=64435","title":{"rendered":"A guide to Pipy, a programmable network proxy for cloud"},"content":{"rendered":"<p><span class=\"field field--name-title field--type-string field--label-hidden\">A guide to Pipy, a programmable network proxy for cloud<\/span><br \/>\n<span class=\"field field--name-uid field--type-entity-reference field--label-hidden\"><a title=\"View user profile.\" href=\"https:\/\/opensource.com\/users\/alinaqvi\" class=\"username\">Ali Naqvi<\/a><\/span><br \/>\n<span class=\"field field--name-created field--type-created field--label-hidden\">Wed, 05\/18\/2022 &#8211; 03:00<\/span><\/p>\n<div data-drupal-selector=\"rate-node-70024\" class=\"rate-widget-thumbs-up\" title=\"Register or Login to like.\">\n<div class=\"rate-thumbs-up-btn-up vote-pending\"><a href=\"https:\/\/opensource.com\/user\/register\">Register<\/a> or <a href=\"https:\/\/opensource.com\/user\/login?current=\/rss.xml\">Login<\/a> to like<\/div>\n<div class=\"rate-score\"><a href=\"https:\/\/opensource.com\/user\/register\">Register<\/a> or <a href=\"https:\/\/opensource.com\/user\/login?current=\/rss.xml\">Login<\/a> to like<\/div>\n<\/div>\n<div class=\"clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item\">\n<p>Pipy is an open source, cloud-native, network stream processor. It is modular by design and can create a high-performance network proxy. It&#8217;s written in C++ and is built on top of the Asio asynchronous I\/O library. Pipy is ideal for a variety of use cases ranging from edge routers, load balancers, proxy solutions, API gateways, static HTTP servers, service mesh sidecars, and more.<\/p>\n<p>Pipy also comes with built-in JavaScript support through PipyJS. PipyJS is highly customizable and predictable in performance, with no garbage collection overhead. Currently, PipyJS is part of the Pipy code base, but it has no dependency on it and in the future it may be moved to a standalone package.<\/p>\n<h2>Pipy quick start guide<\/h2>\n<p>You can run the production version of Pipy using Podman or Docker with one of the tutorial scripts provided on the official Pipy Git repository. The Pipy container image can be configured with a few environment variables:<\/p>\n<ul>\n<li>\n<p><code>PIPY_CONFIG_FILE=\/to\/config-file&gt;<\/code> sets the location of the Pipy configuration file.<\/p>\n<\/li>\n<li>\n<p><code>PIPY_SPAWN=n<\/code> sets the number of Pipy instances you want to start, where <code>n<\/code> is the number of instances. This is a zero-based index, so 0 represents 1 instance. For example, use <code>PIPY_SPAWN=3<\/code> for 4 instances.<\/p>\n<\/li>\n<\/ul>\n<p>Start the Pipy server with this example script:<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\">$ docker run <span class=\"re5\">--rm<\/span> <span class=\"re5\">-e<\/span> <span class=\"re2\">PIPY_CONFIG_FILE<\/span>=<br>\nhttps:<span class=\"sy0\">\/\/<\/span>raw.githubusercontent.com<span class=\"sy0\">\/<\/span>flomesh-io<span class=\"sy0\">\/<\/span>pipy<span class=\"sy0\">\/<\/span>main<span class=\"sy0\">\/<\/span>tutorial<span class=\"sy0\">\/<\/span>01-hello <br><span class=\"re5\">-e<\/span> <span class=\"re2\">PIPY_SPAWN<\/span>=<span class=\"nu0\">1<\/span> <span class=\"re5\">-p<\/span> <span class=\"nu0\">8080<\/span>:<span class=\"nu0\">8080<\/span> flomesh<span class=\"sy0\">\/<\/span>pipy-pjs:latest<\/div><\/div><\/pre>\n<p>You may notice that instead of a local file, this code provides a link to a remote Pipy script through the environment variable <code>PIPY_CONFIG_FILE<\/code>. Pipy is smart enough to handle that.<\/p>\n<p>For your reference, here are the contents of the file <code>tutorial\/01-hello\/hello.js<\/code>:<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"javascript geshifilter-javascript\">pipy<span class=\"br0\">(<\/span><span class=\"br0\">)<\/span><br>\n.<span class=\"me1\">listen<\/span><span class=\"br0\">(<\/span><span class=\"nu0\">8080<\/span><span class=\"br0\">)<\/span><br>\n.<span class=\"me1\">serveHTTP<\/span><span class=\"br0\">(<\/span><br><span class=\"kw1\">new<\/span> Message<span class=\"br0\">(<\/span><span class=\"st0\">'Hi, there!<span class=\"es0\">n<\/span>'<\/span><span class=\"br0\">)<\/span><br><span class=\"br0\">)<\/span><\/div><\/div><\/pre>\n<p>This simple script defines one Port pipeline, which listens on port 8080 and returns &#8220;Hi, there!&#8221; for each HTTP request received on the listening port.<\/p>\n<p>As you&#8217;ve exposed local port 8080 with the docker run command, you can proceed with a test on the same port:<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\"><span class=\"co4\">$ <\/span>curl http:<span class=\"sy0\">\/\/<\/span>localhost:<span class=\"nu0\">8080<\/span><\/div><\/div><\/pre>\n<p>Executing the above command displays <code><strong>Hi, there!<\/strong><\/code> on the console.<\/p>\n<p>For learning, development, or debugging purposes it&#8217;s recommended to proceed with the local installation (either build Pipy from sources or download a release for your OS) of Pipy, as it comes with an admin web console along with documentation and tutorials.<\/p>\n<p>Once installed locally, running <code>pipy<\/code> without any arguments starts the admin console on port 6060, but it can be configured to listen on the different port with the <code>--admin-port<\/code> option.<\/p>\n<article class=\"align-center media media--type-image media--view-mode-default\">\n<div class=\"field field--name-field-media-image field--type-image field--label-hidden field__item\">  <img decoding=\"async\" src=\"https:\/\/www.cryptocabaret.com\/wp-content\/uploads\/2022\/05\/pipy1.png\" width=\"972\" height=\"771\" alt=\"Pipy admin console listening on port 6060\" loading=\"lazy\"><\/div>\n<div class=\"field field--name-field-caption field--type-text-long field--label-hidden caption field__item\"><span class=\"caption__byline\">Image by: <\/span><\/p>\n<p>(Ali Naqvi,  CC BY-SA 40)<\/p>\n<\/div>\n<\/article>\n<p>To build Pipy from source, or to install a precompiled binary for your operating system, refer to README.md on the <a href=\"https:\/\/github.com\/flomesh-io\/pipy\" target=\"_blank\" rel=\"noopener\">Pipy<\/a> Git repository.<\/p>\n<\/p>\n<div class=\"embedded-resource-list callout-float-right\">\n<div class=\"field field--name-title field--type-string field--label-hidden field__item\">Explore the open source cloud<\/div>\n<div class=\"field field--name-links field--type-link field--label-hidden field__items\">\n<div class=\"field__item\"><a href=\"https:\/\/www.redhat.com\/en\/services\/training\/do092-developing-cloud-native-applications-microservices-architectures?intcmp=7013a000002gp8aAAA\">Free online course: Developing cloud-native applications with microservices<\/a><\/div>\n<div class=\"field__item\"><a href=\"https:\/\/www.redhat.com\/en\/engage\/managed-cloud-services-s-202202210207?intcmp=7013a000002gp8aAAA\">eBook: Modernize your IT with managed cloud services<\/a><\/div>\n<div class=\"field__item\"><a href=\"https:\/\/cloud.redhat.com\/products\/dedicated\/?intcmp=7013a000002gp8aAAA\">Try for 60 days: Red Hat OpenShift Dedicated<\/a><\/div>\n<div class=\"field__item\"><a href=\"https:\/\/www.redhat.com\/en\/services\/training\/do080-deploying-containerized-applications-technical-overview?intcmp=7013a000002gp8aAAA\">Free online course: Containers, Kubernetes and Red Hat OpenShift<\/a><\/div>\n<div class=\"field__item\"><a href=\"https:\/\/www.redhat.com\/en\/topics\/containers\/what-is-kubernetes?intcmp=7013a000002gp8aAAA\">What is Kubernetes?<\/a><\/div>\n<div class=\"field__item\"><a href=\"https:\/\/www.redhat.com\/en\/topics\/edge-computing?intcmp=7013a000002gp8aAAA\">Understanding edge computing<\/a><\/div>\n<div class=\"field__item\"><a href=\"https:\/\/www.redhat.com\/architect\/?intcmp=7013a000002gp8aAAA\">Latest articles for IT architects<\/a><\/div>\n<\/p><\/div>\n<\/p><\/div>\n<h3>Running Pipy in a terminal<\/h3>\n<p>To start a Pipy proxy, run Pipy with a PipyJS script file, for example, the script in <code>tutorial\/01-hello\/hello.js<\/code> if you need a simple echo server that responds with the same message body received with every incoming request:<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\"><span class=\"co4\">$ <\/span>pipy tutorial<span class=\"sy0\">\/<\/span>01-hello<span class=\"sy0\">\/<\/span>hello.js<\/div><\/div><\/pre>\n<p>Alternatively, while developing and debugging, one can start Pipy with a builtin web UI:<\/p>\n<pre>\n<span class=\"geshifilter\"><code class=\"bash geshifilter-bash\"><span class=\"co4\">$ <\/span> pipy tutorial<span class=\"sy0\">\/<\/span>01-hello<span class=\"sy0\">\/<\/span>hello.js <span class=\"re5\">--admin-port<\/span>=<span class=\"nu0\">6060<\/span><\/code><\/span><\/pre>\n<p>To see all command-line options, use the <code>--help<\/code> flag:<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\"><span class=\"co4\">$ <\/span> pipy <span class=\"re5\">--help<\/span><\/div><\/div><\/pre>\n<h2>Pipy is a stream processor<\/h2>\n<p>Pipy operates on network streams using an event-driven pipeline where it consumes the input stream, performs user-provided transformations, and outputs the stream. A pipy data stream takes raw data and abstracts it into an event. An event can belong to one of four categories:<\/p>\n<ul>\n<li><strong>Data<\/strong>: Network streams are composed of data bytes and come in chunks. Pipy abstracts out chunks into a Data event.<\/li>\n<li>\n<p><strong>MessageStart, MessageEnd, StreamEnd<\/strong>: These three non-data events work as markers, giving the raw byte streams high-level semantics for business logic to rely on.<\/p>\n<\/li>\n<\/ul>\n<h2>Pipy Design<\/h2>\n<p>The internal workings of Pipy are similar to <a href=\"https:\/\/opensource.com\/article\/19\/4\/interprocess-communication-linux-channels\" target=\"_blank\" rel=\"noopener\">Unix Pipelines<\/a> but unlike Unix pipelines, which deal with discreet bytes, Pipy deals with streams of events.<\/p>\n<p>Pipy processes incoming streams through a chain of filters, where each filter deals with general concerns like request logging, authentication, SSL offloading, request forwarding, and so on. Each filter reads from its input and writes to its output, with the output of one filter connected to the input of the next.<\/p>\n<h3>Pipelines<\/h3>\n<p>A chain of filters is called a pipeline and Pipy categorizes pipelines in 3 different categories according to their input sources.<\/p>\n<ul>\n<li>\n<p><strong>Port pipeline:<\/strong> Reads in Data events from a network port, processes them, and then writes the result back to the same port. This is the most commonly used request and response model. For instance, when Pipy works like an HTTP server, the input to a Port pipeline is an HTTP request from the clients, and the output from the pipeline would be an HTTP response sent back to clients.<\/p>\n<\/li>\n<li>\n<p><strong>Timer pipeline:<\/strong> Gets a pair of MessageStart and MessageEnd events as its input periodically. Useful when <a href=\"https:\/\/en.wikipedia.org\/wiki\/Cron\" target=\"_blank\" rel=\"noopener\">Cron<\/a> <a href=\"https:\/\/en.wikipedia.org\/wiki\/Cron\" target=\"_blank\" rel=\"noopener\">job-like<\/a> functionality is required.<\/p>\n<\/li>\n<li>\n<p><strong>Sub-pipeline:<\/strong> Works in conjunction with a join filter, such as link, which takes in events from its predecessor pipeline, feeds them into a sub-pipeline for processing, reads back the output from the sub-pipeline, and then pumps it down to the next filter.<\/p>\n<p>The best way to look at sub-pipelines and join filters is to think of them as callees and callers of a subroutine in procedural programming. The input to the joint filter is the subroutine&#8217;s parameters, the output from the joint filter is its return value.<\/p>\n<p>A root pipeline, such as Port or Timer, cannot be called from join filters.<\/p>\n<p>To get a list of builtin filters and their parameters:<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\">$ \u00a0pipy <span class=\"re5\">--list-filters<\/span><br>\n$ \u00a0pipy <span class=\"re5\">--help-filters<\/span><\/div><\/div><\/pre>\n<\/li>\n<\/ul>\n<h3>Context<\/h3>\n<p>Another important notion in Pipy is that of contexts. A context is a set of variables attached to a pipeline. Every pipeline gets access to the same set of variables across a Pipy instance. In other words, contexts have the same shape. When you start a Pipy instance, the first thing you do is define the shape of the context by defining variable(s) and their initial values.<\/p>\n<p>Every root pipeline clones the initial context you define at the start. When a sub-pipeline starts, it either shares or clones its parent&#8217;s context, depending on which joint filter you use. For instance, a link filter shares its parent&#8217;s context while a demux filter clones it.<\/p>\n<p>To the scripts embedded in a pipeline, these context variables are their global variables, which means that these variables are always accessible to scripts from anywhere if they live in the same script file.<\/p>\n<p>This might seem odd to a seasoned programmer because global variables usually mean they are globally unique. You have only one set of these variables, whereas in Pipy we can have many sets of them (contexts) depending on how many root pipelines are open for incoming network connections and how many sub-pipelines clone their parents&#8217; contexts.<\/p>\n<h2>Writing a Network Proxy<\/h2>\n<p>Suppose you&#8217;re running separate instances of different services and you want to add a proxy to forward traffic to the relevant services based on the request URL path. This would give you the benefit of exposing a single URL and scaling your services in the back end without users having to remember a distinct service URL. In normal situations, your services would be running on different nodes and each service could have multiple instances running. In this example, assume you&#8217;re running the services below, and want to distribute traffic to them based on the URI.<\/p>\n<ul>\n<li>\n<p>service-hi at <code>\/hi\/*<\/code> (127.0.0.1:8080, 127.0.0.1:8082)<\/p>\n<\/li>\n<li>\n<p>service-echo at <code>\/echo<\/code> (127.0.0.1:8081)<\/p>\n<\/li>\n<li>\n<p>service-tell-ip at <code>\/ip_\/_*<\/code> (127.0.0.1:8082)<\/p>\n<\/li>\n<\/ul>\n<p>Pipy scripts are written in JavaScript, and you can use any text editor of your choice to edit them. Alternatively, if you have installed Pipy locally, you can use Pipy admin Web UI, which comes with syntax highlighting, auto-completion, hints, as well as the ability to run scripts, all from the same console.<\/p>\n<p>Start a Pipy instance, without any arguments, so the Pipy admin console launches on port 6060. Now open your favorite web browser and navigate to <a href=\"http:\/\/localhost:6060\/\" target=\"_blank\" rel=\"noopener\">[http:\/\/localhost:6060](http:\/\/localhost:6060\/<\/a> to see the built-in Pipy Administration Web UI.<\/p>\n<article class=\"align-center media media--type-image media--view-mode-default\">\n<div class=\"field field--name-field-media-image field--type-image field--label-hidden field__item\">  <img decoding=\"async\" src=\"https:\/\/www.cryptocabaret.com\/wp-content\/uploads\/2022\/05\/pipy2.png\" width=\"972\" height=\"771\" alt=\"Built-in Pipy administration web UI\" loading=\"lazy\"><\/div>\n<div class=\"field field--name-field-caption field--type-text-long field--label-hidden caption field__item\"><span class=\"caption__byline\">Image by: <\/span><\/p>\n<p>(Ali Naqvi,  CC BY-SA 40)<\/p>\n<\/div>\n<\/article>\n<h2>Create a Pipy program<\/h2>\n<p>A good design practice is that code and configurations are separated. Pipy supports such modular design through its Plugins, which you can think of as JavaScript modules. That said, you store your configuration data in the config folder, and your coding logic in separate files under the plugins folder. The main proxy server script is stored in the root folder, the main proxy script (<code>proxy.js<\/code>) will include and combine the functionality defined in separate modules. In the end, your final folder structure is:<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\">\u251c\u2500\u2500 config<br>\n\u2502 \u251c\u2500\u2500 balancer.json<br>\n\u2502 \u251c\u2500\u2500 proxy.json<br>\n\u2502 \u2514\u2500\u2500 router.json<br>\n\u251c\u2500\u2500 plugins<br>\n\u2502 \u251c\u2500\u2500 balancer.js<br>\n\u2502 \u251c\u2500\u2500 default.js<br>\n\u2502 \u2514\u2500\u2500 router.js<br>\n\u2514\u2500\u2500 proxy.js<\/div><\/div><\/pre>\n<p>\u00a0<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0 1.Click <strong>New Codebase<\/strong>, enter <code>\/proxy<\/code> for the Codebase <em>name<\/em> in the dialog and then click <strong>Create<\/strong>.<\/p>\n<ol start=\"2\">\n<li>\n<p>Click the <strong>+<\/strong> button to add a new file. Enter <code>\/config\/proxy.json<\/code> for its filename and then click <strong>Create<\/strong>. This is the configuration file used to configure your proxy.<\/p>\n<\/li>\n<li>\n<p>You now see <code>proxy.json<\/code> listed under the config folder in the left pane. Click on the file to open it and add the configuration shown below and make sure you save your file by clicking the disk icon on the top panel.<br \/>\n\t\u00a0<\/p>\n<pre>\n<code class=\"language-json\">{\n\"listen\": 8000,\n\"plugins\": [\n\"plugins\/router.js\",\n\"plugins\/balancer.js\",\n\"plugins\/default.js\" ]\n}<\/code><\/pre>\n<\/li>\n<li>\n<p>Repeat steps 2 and 3 to create another file, \/config\/router.json, to store route information. Enter this configuration data:<\/p>\n<pre>\n<code class=\"language-json\">{\n\"routes\": {\n\"\/hi\/*\": \"service-hi\",\n\"\/echo\": \"service-echo\",\n\"\/ip\/*\": \"service-tell-ip\" }\n}<\/code><\/pre>\n<\/li>\n<li>\n<p>Repeat steps 2 and 3 to create another file, \/config\/balancer.json to store your service-to-target map. Enter the following data:<\/p>\n<pre>\n<code class=\"language-json\">{\n\"services\": {\n\"service-hi\" : [\"127.0.0.1:8080\", \"127.0.0.1:8082\"],\n\"service-echo\" : [\"127.0.0.1:8081\"],\n\"service-tell-ip\" : [\"127.0.0.1:8082\"] }\n}<\/code><\/pre>\n<\/li>\n<li>\n<p>Now it&#8217;s time to write your very first Pipy script, which will be used as a default fallback when your server receives a request for which you don&#8217;t have any target (an endpoint) configured. Create the file <code>\/plugins\/default.js<\/code>. The name here is just a convention and Pipy doesn&#8217;t rely on names, so you can choose any name you like. The script will contain the code shown below, which returns the HTTP Status code 404 with a message of No handler found:<br \/>\n\t\u00a0<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"javascript geshifilter-javascript\">pipy<span class=\"br0\">(<\/span><span class=\"br0\">)<\/span><br>\n.<span class=\"me1\">pipeline<\/span><span class=\"br0\">(<\/span><span class=\"st0\">'request'<\/span><span class=\"br0\">)<\/span><br>\n.<span class=\"me1\">replaceMessage<\/span><span class=\"br0\">(<\/span><br><span class=\"kw1\">new<\/span> Message<span class=\"br0\">(<\/span><span class=\"br0\">{<\/span> status<span class=\"sy0\">:<\/span> <span class=\"nu0\">404<\/span> <span class=\"br0\">}<\/span><span class=\"sy0\">,<\/span> <span class=\"st0\">'No handler found'<\/span><span class=\"br0\">)<\/span><span class=\"br0\">)<\/span><\/div><\/div><\/pre>\n<\/li>\n<\/ol>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 7.Create the file <code>\/plugins\/router.js<\/code>, which stores your routing logic:<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"javascript geshifilter-javascript\"><span class=\"br0\">(<\/span>config <span class=\"sy0\">=&gt;<\/span><br>\npipy<span class=\"br0\">(<\/span><span class=\"br0\">{<\/span><br>\n_router<span class=\"sy0\">:<\/span> <span class=\"kw1\">new<\/span> algo.<span class=\"me1\">URLRouter<\/span><span class=\"br0\">(<\/span>config.<span class=\"me1\">routes<\/span><span class=\"br0\">)<\/span><span class=\"sy0\">,<\/span> <span class=\"br0\">}<\/span><span class=\"br0\">)<\/span><br>\n.<span class=\"kw5\">export<\/span><span class=\"br0\">(<\/span><span class=\"st0\">'router'<\/span><span class=\"sy0\">,<\/span> <span class=\"br0\">{<\/span><br>\n__serviceID<span class=\"sy0\">:<\/span> <span class=\"st0\">''<\/span><span class=\"sy0\">,<\/span> <span class=\"br0\">}<\/span><span class=\"br0\">)<\/span><br>\n.<span class=\"me1\">pipeline<\/span><span class=\"br0\">(<\/span><span class=\"st0\">'request'<\/span><span class=\"br0\">)<\/span><br>\n.<span class=\"me1\">handleMessageStart<\/span><span class=\"br0\">(<\/span><br>\nmsg <span class=\"sy0\">=&gt;<\/span> <span class=\"br0\">(<\/span><br>\n__serviceID <span class=\"sy0\">=<\/span> _router.<span class=\"me1\">find<\/span><span class=\"br0\">(<\/span><br>\nmsg.<span class=\"me1\">head<\/span>.<span class=\"me1\">headers<\/span>.<span class=\"me1\">host<\/span><span class=\"sy0\">,<\/span><br>\nmsg.<span class=\"me1\">head<\/span>.<span class=\"me1\">path<\/span><span class=\"sy0\">,<\/span> <span class=\"br0\">)<\/span><br><span class=\"br0\">)<\/span> <span class=\"br0\">)<\/span><br><span class=\"br0\">)<\/span><span class=\"br0\">(<\/span>JSON.<span class=\"me1\">decode<\/span><span class=\"br0\">(<\/span>pipy.<span class=\"me1\">load<\/span><span class=\"br0\">(<\/span><span class=\"st0\">'config\/router.json'<\/span><span class=\"br0\">)<\/span><span class=\"br0\">)<\/span><span class=\"br0\">)<\/span><\/div><\/div><\/pre>\n<ol start=\"8\">\n<li>\n<p>Create the file <code>\/plugins\/balancer.js<\/code>, which stores your load balancing logic as a side-note. Pipy comes with multiple Load Balancing algorithms, but for simplicity, you&#8217;re using the Round Robin algorithm here.<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"javascript geshifilter-javascript\"><span class=\"br0\">(<\/span>config <span class=\"sy0\">=&gt;<\/span><br><br>\npipy<span class=\"br0\">(<\/span><span class=\"br0\">{<\/span><br>\n\u00a0 _services<span class=\"sy0\">:<\/span> <span class=\"br0\">(<\/span><br>\n\u00a0 \u00a0 <span class=\"kw4\">Object<\/span>.<span class=\"me1\">fromEntries<\/span><span class=\"br0\">(<\/span><br>\n\u00a0 \u00a0 \u00a0 <span class=\"kw4\">Object<\/span>.<span class=\"me1\">entries<\/span><span class=\"br0\">(<\/span>config.<span class=\"me1\">services<\/span><span class=\"br0\">)<\/span>.<span class=\"me1\">map<\/span><span class=\"br0\">(<\/span><br>\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"br0\">(<\/span><span class=\"br0\">[<\/span>k<span class=\"sy0\">,<\/span> v<span class=\"br0\">]<\/span><span class=\"br0\">)<\/span> <span class=\"sy0\">=&gt;<\/span> <span class=\"br0\">[<\/span><br>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 k<span class=\"sy0\">,<\/span> <span class=\"kw1\">new<\/span> algo.<span class=\"me1\">RoundRobinLoadBalancer<\/span><span class=\"br0\">(<\/span>v<span class=\"br0\">)<\/span><br>\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"br0\">]<\/span><br>\n\u00a0 \u00a0 \u00a0 <span class=\"br0\">)<\/span><br>\n\u00a0 \u00a0 <span class=\"br0\">)<\/span><br>\n\u00a0 <span class=\"br0\">)<\/span><span class=\"sy0\">,<\/span><br><br>\n\u00a0 _balancer<span class=\"sy0\">:<\/span> <span class=\"kw2\">null<\/span><span class=\"sy0\">,<\/span><br>\n\u00a0 _balancerCache<span class=\"sy0\">:<\/span> <span class=\"kw2\">null<\/span><span class=\"sy0\">,<\/span><br>\n\u00a0 _target<span class=\"sy0\">:<\/span> <span class=\"st0\">''<\/span><span class=\"sy0\">,<\/span><br><span class=\"br0\">}<\/span><span class=\"br0\">)<\/span><br><br>\n.<span class=\"kw5\">import<\/span><span class=\"br0\">(<\/span><span class=\"br0\">{<\/span><br>\n\u00a0 __turnDown<span class=\"sy0\">:<\/span> <span class=\"st0\">'proxy'<\/span><span class=\"sy0\">,<\/span><br>\n\u00a0 __serviceID<span class=\"sy0\">:<\/span> <span class=\"st0\">'router'<\/span><span class=\"sy0\">,<\/span><br><span class=\"br0\">}<\/span><span class=\"br0\">)<\/span><br><br>\n.<span class=\"me1\">pipeline<\/span><span class=\"br0\">(<\/span><span class=\"st0\">'session'<\/span><span class=\"br0\">)<\/span><br>\n\u00a0 .<span class=\"me1\">handleStreamStart<\/span><span class=\"br0\">(<\/span><br>\n\u00a0 \u00a0 <span class=\"br0\">(<\/span><span class=\"br0\">)<\/span> <span class=\"sy0\">=&gt;<\/span> <span class=\"br0\">(<\/span><br>\n\u00a0 \u00a0 \u00a0 _balancerCache <span class=\"sy0\">=<\/span> <span class=\"kw1\">new<\/span> algo.<span class=\"me1\">Cache<\/span><span class=\"br0\">(<\/span><br>\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"co1\">\/\/ k is a balancer, v is a target<\/span><br>\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"br0\">(<\/span>k \u00a0<span class=\"br0\">)<\/span> <span class=\"sy0\">=&gt;<\/span> k.<span class=\"me1\">select<\/span><span class=\"br0\">(<\/span><span class=\"br0\">)<\/span><span class=\"sy0\">,<\/span><br>\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"br0\">(<\/span>k<span class=\"sy0\">,<\/span>v<span class=\"br0\">)<\/span> <span class=\"sy0\">=&gt;<\/span> k.<span class=\"me1\">deselect<\/span><span class=\"br0\">(<\/span>v<span class=\"br0\">)<\/span><span class=\"sy0\">,<\/span><br>\n\u00a0 \u00a0 \u00a0 <span class=\"br0\">)<\/span><br>\n\u00a0 \u00a0 <span class=\"br0\">)<\/span><br>\n\u00a0 <span class=\"br0\">)<\/span><br>\n\u00a0 .<span class=\"me1\">handleStreamEnd<\/span><span class=\"br0\">(<\/span><br>\n\u00a0 \u00a0 <span class=\"br0\">(<\/span><span class=\"br0\">)<\/span> <span class=\"sy0\">=&gt;<\/span> <span class=\"br0\">(<\/span><br>\n\u00a0 \u00a0 \u00a0 _balancerCache.<span class=\"me1\">clear<\/span><span class=\"br0\">(<\/span><span class=\"br0\">)<\/span><br>\n\u00a0 \u00a0 <span class=\"br0\">)<\/span><br>\n\u00a0 <span class=\"br0\">)<\/span><br><br>\n.<span class=\"me1\">pipeline<\/span><span class=\"br0\">(<\/span><span class=\"st0\">'request'<\/span><span class=\"br0\">)<\/span><br>\n\u00a0 .<span class=\"me1\">handleMessageStart<\/span><span class=\"br0\">(<\/span><br>\n\u00a0 \u00a0 <span class=\"br0\">(<\/span><span class=\"br0\">)<\/span> <span class=\"sy0\">=&gt;<\/span> <span class=\"br0\">(<\/span><br>\n\u00a0 \u00a0 \u00a0 _balancer <span class=\"sy0\">=<\/span> _services<span class=\"br0\">[<\/span>__serviceID<span class=\"br0\">]<\/span><span class=\"sy0\">,<\/span><br>\n\u00a0 \u00a0 \u00a0 _balancer <span class=\"sy0\">&amp;&amp;<\/span> <span class=\"br0\">(<\/span>_target <span class=\"sy0\">=<\/span> _balancerCache.<span class=\"kw1\">get<\/span><span class=\"br0\">(<\/span>_balancer<span class=\"br0\">)<\/span><span class=\"br0\">)<\/span><span class=\"sy0\">,<\/span><br>\n\u00a0 \u00a0 \u00a0 _target <span class=\"sy0\">&amp;&amp;<\/span> <span class=\"br0\">(<\/span>__turnDown <span class=\"sy0\">=<\/span> <span class=\"kw2\">true<\/span><span class=\"br0\">)<\/span><br>\n\u00a0 \u00a0 <span class=\"br0\">)<\/span><br>\n\u00a0 <span class=\"br0\">)<\/span><br>\n\u00a0 .<span class=\"me1\">link<\/span><span class=\"br0\">(<\/span><br>\n\u00a0 \u00a0 <span class=\"st0\">'forward'<\/span><span class=\"sy0\">,<\/span> <span class=\"br0\">(<\/span><span class=\"br0\">)<\/span> <span class=\"sy0\">=&gt;<\/span> <span class=\"kw4\">Boolean<\/span><span class=\"br0\">(<\/span>_target<span class=\"br0\">)<\/span><span class=\"sy0\">,<\/span><br>\n\u00a0 \u00a0 <span class=\"st0\">''<\/span><br>\n\u00a0 <span class=\"br0\">)<\/span><br><br>\n.<span class=\"me1\">pipeline<\/span><span class=\"br0\">(<\/span><span class=\"st0\">'forward'<\/span><span class=\"br0\">)<\/span><br>\n\u00a0 .<span class=\"me1\">muxHTTP<\/span><span class=\"br0\">(<\/span><br>\n\u00a0 \u00a0 <span class=\"st0\">'connection'<\/span><span class=\"sy0\">,<\/span><br>\n\u00a0 \u00a0 <span class=\"br0\">(<\/span><span class=\"br0\">)<\/span> <span class=\"sy0\">=&gt;<\/span> _target<br>\n\u00a0 <span class=\"br0\">)<\/span><br><br>\n.<span class=\"me1\">pipeline<\/span><span class=\"br0\">(<\/span><span class=\"st0\">'connection'<\/span><span class=\"br0\">)<\/span><br>\n\u00a0 .<span class=\"me1\">connect<\/span><span class=\"br0\">(<\/span><br>\n\u00a0 \u00a0 <span class=\"br0\">(<\/span><span class=\"br0\">)<\/span> <span class=\"sy0\">=&gt;<\/span> _target<br>\n\u00a0 <span class=\"br0\">)<\/span><br><br><span class=\"br0\">)<\/span><span class=\"br0\">(<\/span>JSON.<span class=\"me1\">decode<\/span><span class=\"br0\">(<\/span>pipy.<span class=\"me1\">load<\/span><span class=\"br0\">(<\/span><span class=\"st0\">'config\/balancer.json'<\/span><span class=\"br0\">)<\/span><span class=\"br0\">)<\/span><span class=\"br0\">)<\/span><\/div><\/div><\/pre>\n<\/li>\n<li>\n<p>Now write the entry point, or the proxy server script, to use the above plugins. Creating a new code base (step 1) creates a default <code>main.js<\/code> file as an entry point. You can use that as your main entry point, or if you prefer to go with a different name, feel free to delete <code>main.js<\/code> and create a new file with the name of your choice. For this example, delete it and create a new file named <code>\/proxy.js<\/code>. Make sure you click the top flag icon to make it the main entry point, to ensure script execution is started when you hit the run button (the arrow icon on the right).<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"javascript geshifilter-javascript\"><span class=\"br0\">(<\/span>config <span class=\"sy0\">=&gt;<\/span><br><br>\npipy<span class=\"br0\">(<\/span><span class=\"br0\">)<\/span><br><br>\n.<span class=\"kw5\">export<\/span><span class=\"br0\">(<\/span><span class=\"st0\">'proxy'<\/span><span class=\"sy0\">,<\/span> <span class=\"br0\">{<\/span><br>\n\u00a0 __turnDown<span class=\"sy0\">:<\/span> <span class=\"kw2\">false<\/span><span class=\"sy0\">,<\/span><br><span class=\"br0\">}<\/span><span class=\"br0\">)<\/span><br><br>\n.<span class=\"me1\">listen<\/span><span class=\"br0\">(<\/span>config.<span class=\"me1\">listen<\/span><span class=\"br0\">)<\/span><br>\n\u00a0 .<span class=\"me1\">use<\/span><span class=\"br0\">(<\/span>config.<span class=\"me1\">plugins<\/span><span class=\"sy0\">,<\/span> <span class=\"st0\">'session'<\/span><span class=\"br0\">)<\/span><br>\n\u00a0 .<span class=\"me1\">demuxHTTP<\/span><span class=\"br0\">(<\/span><span class=\"st0\">'request'<\/span><span class=\"br0\">)<\/span><br><br>\n.<span class=\"me1\">pipeline<\/span><span class=\"br0\">(<\/span><span class=\"st0\">'request'<\/span><span class=\"br0\">)<\/span><br>\n\u00a0 .<span class=\"me1\">use<\/span><span class=\"br0\">(<\/span><br>\n\u00a0 \u00a0 config.<span class=\"me1\">plugins<\/span><span class=\"sy0\">,<\/span><br>\n\u00a0 \u00a0 <span class=\"st0\">'request'<\/span><span class=\"sy0\">,<\/span><br>\n\u00a0 \u00a0 <span class=\"st0\">'response'<\/span><span class=\"sy0\">,<\/span><br>\n\u00a0 \u00a0 <span class=\"br0\">(<\/span><span class=\"br0\">)<\/span> <span class=\"sy0\">=&gt;<\/span> __turnDown<br>\n\u00a0 <span class=\"br0\">)<\/span><br><br><span class=\"br0\">)<\/span><span class=\"br0\">(<\/span>JSON.<span class=\"me1\">decode<\/span><span class=\"br0\">(<\/span>pipy.<span class=\"me1\">load<\/span><span class=\"br0\">(<\/span><span class=\"st0\">'config\/proxy.json'<\/span><span class=\"br0\">)<\/span><span class=\"br0\">)<\/span><span class=\"br0\">)<\/span><\/div><\/div><\/pre>\n<\/li>\n<\/ol>\n<p>So far, your workspace looks like this:<\/p>\n<article class=\"align-center media media--type-image media--view-mode-default\">\n<div class=\"field field--name-field-media-image field--type-image field--label-hidden field__item\">  <img decoding=\"async\" src=\"https:\/\/www.cryptocabaret.com\/wp-content\/uploads\/2022\/05\/pipy3.png\" width=\"944\" height=\"593\" alt=\"Image of workspace\" loading=\"lazy\"><\/div>\n<div class=\"field field--name-field-caption field--type-text-long field--label-hidden caption field__item\"><span class=\"caption__byline\">Image by: <\/span><\/p>\n<p>(Ali Naqvi,  CC BY-SA 40)<\/p>\n<\/div>\n<\/article>\n<p>To run your script, click the play icon button (4th from right). Pipy runs your proxy script, and you see output similar to this:<\/p>\n<article class=\"align-center media media--type-image media--view-mode-default\">\n<div class=\"field field--name-field-media-image field--type-image field--label-hidden field__item\">  <img decoding=\"async\" src=\"https:\/\/www.cryptocabaret.com\/wp-content\/uploads\/2022\/05\/pipy4.png\" width=\"950\" height=\"680\" alt=\"Image of output\" loading=\"lazy\"><\/div>\n<div class=\"field field--name-field-caption field--type-text-long field--label-hidden caption field__item\"><span class=\"caption__byline\">Image by: <\/span><\/p>\n<p>(Ali Naqvi,  CC BY-SA 40)<\/p>\n<\/div>\n<\/article>\n<p>This shows that your proxy server is listening on port 8000 (which you configured in your <code>\/config\/proxy.json<\/code>). Use <a href=\"https:\/\/www.redhat.com\/sysadmin\/social-media-curl\" target=\"_blank\" rel=\"noopener\">curl to run a test<\/a>:<br \/>\n\u00a0<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\">$ curl <span class=\"re5\">-i<\/span> <span class=\"br0\">[<\/span>http:<span class=\"sy0\">\/\/<\/span>localhost:<span class=\"nu0\">8000<\/span><span class=\"br0\">]<\/span><span class=\"br0\">(<\/span>http:<span class=\"sy0\">\/\/<\/span>localhost:<span class=\"nu0\">8000<\/span><span class=\"br0\">)<\/span><br>\nHTTP<span class=\"sy0\">\/<\/span><span class=\"nu0\">1.1<\/span> <span class=\"nu0\">404<\/span> Not Found<br>\ncontent-length: <span class=\"nu0\">10<\/span><br>\nconnection: keep-alive<br>\nNo handler found<\/div><\/div><\/pre>\n<p>That response makes sense because you haven&#8217;t configured any target for root. Try one of your configured routes, such as <code>\/hi<\/code>:<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\">$ curl <span class=\"re5\">-i<\/span> <span class=\"br0\">[<\/span>http:<span class=\"sy0\">\/\/<\/span>localhost:<span class=\"nu0\">8000<\/span><span class=\"sy0\">\/<\/span>hi<span class=\"br0\">]<\/span><span class=\"br0\">(<\/span>http:<span class=\"sy0\">\/\/<\/span>localhost:<span class=\"nu0\">8000<\/span><span class=\"sy0\">\/<\/span>hi<span class=\"br0\">)<\/span><br>\nHTTP<span class=\"sy0\">\/<\/span><span class=\"nu0\">1.1<\/span> <span class=\"nu0\">502<\/span> Connection Refused<br>\ncontent-length: <span class=\"nu0\">0<\/span><br>\nconnection: keep-alive<\/div><\/div><\/pre>\n<p>You get <code>502 Connection Refused<\/code> because you have no service running on your configured target port.<\/p>\n<p>You can update <code>\/config\/balancer.json<\/code> with details like the host and port of your already running services to make it fit for your use case, or you can just write a script in Pipy to listen on your configured ports, and return simple messages.<\/p>\n<p>Save this code to a file on your local computer named <code>mock-proxy.js<\/code>, and remember the location where you stored it:<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\">pipy<span class=\"br0\">(<\/span><span class=\"br0\">)<\/span><br><br>\n.listen<span class=\"br0\">(<\/span><span class=\"nu0\">8080<\/span><span class=\"br0\">)<\/span><br>\n\u00a0 .serveHTTP<span class=\"br0\">(<\/span><br>\n\u00a0 \u00a0 new Message<span class=\"br0\">(<\/span><span class=\"st_h\">'Hi, there!n'<\/span><span class=\"br0\">)<\/span><br>\n\u00a0 <span class=\"br0\">)<\/span><br><br>\n.listen<span class=\"br0\">(<\/span><span class=\"nu0\">8081<\/span><span class=\"br0\">)<\/span><br>\n\u00a0 .serveHTTP<span class=\"br0\">(<\/span><br>\n\u00a0 \u00a0 msg =<span class=\"sy0\">&gt;<\/span> new Message<span class=\"br0\">(<\/span>msg.body<span class=\"br0\">)<\/span><br>\n\u00a0 <span class=\"br0\">)<\/span><br><br>\n.listen<span class=\"br0\">(<\/span><span class=\"nu0\">8082<\/span><span class=\"br0\">)<\/span><br>\n\u00a0 .serveHTTP<span class=\"br0\">(<\/span><br>\n\u00a0 \u00a0 msg =<span class=\"sy0\">&gt;<\/span> new Message<span class=\"br0\">(<\/span><br>\n\u00a0 \u00a0 \u00a0 <span class=\"sy0\">`<\/span>You are requesting <span class=\"co1\">${msg.head.path}<\/span> from <span class=\"co1\">${__inbound.remoteAddress}<\/span>n<span class=\"sy0\">`<\/span><br>\n\u00a0 \u00a0 <span class=\"br0\">)<\/span><br>\n\u00a0 <span class=\"br0\">)<\/span><\/div><\/div><\/pre>\n<p>Open a new terminal window and run this script with Pipy (change <code>\/path\/to<\/code> to the location where you stored this script file):<br \/>\n\u00a0<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\">$ pipy <span class=\"sy0\">\/<\/span>path<span class=\"sy0\">\/<\/span>to<span class=\"sy0\">\/<\/span>mock-proxy.js<br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>config<span class=\"br0\">]<\/span><br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>config<span class=\"br0\">]<\/span> Module <span class=\"sy0\">\/<\/span>mock-proxy.js<br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>config<span class=\"br0\">]<\/span> ================<br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>config<span class=\"br0\">]<\/span><br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>config<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>Listen on :::<span class=\"nu0\">8080<\/span><span class=\"br0\">]<\/span><br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>config<span class=\"br0\">]<\/span> -----<span class=\"sy0\">&gt;|<\/span><br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>config<span class=\"br0\">]<\/span> <span class=\"sy0\">|<\/span><br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>config<span class=\"br0\">]<\/span> serveHTTP<br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>config<span class=\"br0\">]<\/span> <span class=\"sy0\">|<\/span><br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>config<span class=\"br0\">]<\/span> <span class=\"sy0\">-----<span class=\"sy0\">|<\/span><br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>config<span class=\"br0\">]<\/span><br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>config<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>Listen on :::<span class=\"nu0\">8081<\/span><span class=\"br0\">]<\/span><br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>config<span class=\"br0\">]<\/span> -----<span class=\"sy0\">&gt;|<\/span><br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>config<span class=\"br0\">]<\/span> <span class=\"sy0\">|<\/span><br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>config<span class=\"br0\">]<\/span> serveHTTP<br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>config<span class=\"br0\">]<\/span> <span class=\"sy0\">|<\/span><br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>config<span class=\"br0\">]<\/span> <span class=\"sy0\">-----<span class=\"sy0\">|<\/span><br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>config<span class=\"br0\">]<\/span><br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>config<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>Listen on :::<span class=\"nu0\">8082<\/span><span class=\"br0\">]<\/span><br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>config<span class=\"br0\">]<\/span> -----<span class=\"sy0\">&gt;|<\/span><br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>config<span class=\"br0\">]<\/span> <span class=\"sy0\">|<\/span><br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>config<span class=\"br0\">]<\/span> serveHTTP<br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>config<span class=\"br0\">]<\/span> <span class=\"sy0\">|<\/span><br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>config<span class=\"br0\">]<\/span> <span class=\"sy0\">-----<span class=\"sy0\">|<\/span><br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>config<span class=\"br0\">]<\/span><br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>listener<span class=\"br0\">]<\/span> Listening on port <span class=\"nu0\">8080<\/span> at ::<br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>listener<span class=\"br0\">]<\/span> Listening on port <span class=\"nu0\">8081<\/span> at ::<br><span class=\"nu0\">2022<\/span>-01-<span class=\"nu0\">11<\/span> <span class=\"nu0\">18<\/span>:<span class=\"nu0\">56<\/span>:<span class=\"nu0\">31<\/span> <span class=\"br0\">[<\/span>INF<span class=\"br0\">]<\/span> <span class=\"br0\">[<\/span>listener<span class=\"br0\">]<\/span> Listening on port <span class=\"nu0\">8082<\/span> at ::<\/span><\/span><\/span><\/div><\/div><\/pre>\n<p>You now have your mock services listening on ports 8080, 8081, and 8082. Do a test again on your proxy server to see the correct response returned from your mock service.<\/p>\n<h2>Summary<\/h2>\n<p>You&#8217;ve used a number of Pipy features, including variable declaration, importing and exporting variables, plugins, Pipelines, sub-pipelines, filter chaining, Pipy filters like <code>handleMessageStart<\/code>, <code>handleStreamStart<\/code>, and link, and Pipy classes like JSON, <code>algo.URLRouter<\/code>, <code>algo.RoundRobinLoadBalancer<\/code>, <code>algo.Cache<\/code>, and others. For more information, read the excellent <a href=\"https:\/\/flomesh.io\/\" target=\"_blank\" rel=\"noopener\">Pipy documentation<\/a>, and through Pipy&#8217;s admin web UI, and follow the step-by-step tutorials that come with it.<\/p>\n<h2>Conclusion<\/h2>\n<p>Pipy from <a href=\"https:\/\/flomesh.io\/\" target=\"_blank\" rel=\"noopener\">Flomesh<\/a> is an open source, extremely fast, and lightweight network traffic processor. You can use it in a variety of use cases ranging from edge routers, load balancing and proxying (forward and reverse), API gateways, static HTTP servers, service mesh sidecars, and many other applications. Pipy is in active development and is maintained by full-time committers and contributors.<\/p>\n<\/div>\n<div class=\"clearfix text-formatted field field--name-field-article-subhead field--type-text-long field--label-hidden field__item\">\n<p>Pipy is an open source, extremely fast, and lightweight network traffic processor. It has a variety of use cases including edge routers, load balancing and proxying, API gateways, static HTTP servers, service mesh sidecars, and many other applications.<\/p>\n<\/div>\n<div class=\"field field--name-field-lead-image field--type-entity-reference field--label-hidden field__item\">\n<article class=\"media media--type-image media--view-mode-caption\">\n<div class=\"field field--name-field-media-image field--type-image field--label-hidden field__item\">  <img decoding=\"async\" src=\"https:\/\/www.cryptocabaret.com\/wp-content\/uploads\/2022\/05\/lenovo-thinkpad-laptop-concentration-focus-windows-office-2.png\" width=\"799\" height=\"447\" alt=\"Business woman on laptop sitting in front of window\" title=\"Woman using laptop concentrating\" loading=\"lazy\"><\/div>\n<div class=\"field field--name-field-caption field--type-text-long field--label-hidden caption field__item\"><span class=\"caption__byline\">Image by: <\/span><\/p>\n<p>Image by Mapbox Uncharted ERG,\u00a0<a href=\"https:\/\/creativecommons.org\/licenses\/by\/3.0\/us\/\" rel=\"noreferrer nofollow noopener\" target=\"_blank\">CC-BY 3.0 US<\/a><\/p>\n<\/div>\n<\/article>\n<\/div>\n<div class=\"field field--name-field-tags field--type-entity-reference field--label-hidden field__items\">\n<div class=\"field__item\"><a href=\"https:\/\/opensource.com\/tags\/containers\" hreflang=\"en\">Containers<\/a><\/div>\n<div class=\"field__item\"><a href=\"https:\/\/opensource.com\/tags\/networking\" hreflang=\"en\">Networking<\/a><\/div>\n<\/p><\/div>\n<div class=\"hidden field field--name-field-listicle-title field--type-string field--label-hidden field__item\">What to read next<\/div>\n<div class=\"field field--name-field-default-license field--type-list-string field--label-hidden field__item\"><a rel=\"license\" href=\"http:\/\/creativecommons.org\/licenses\/by-sa\/4.0\/\"><br \/>\n        <img decoding=\"async\" alt=\"Creative Commons License\" src=\"https:\/\/www.cryptocabaret.com\/wp-content\/uploads\/2022\/05\/cc-by-sa--25.png\" title=\"This work is licensed under a Creative Commons Attribution-Share Alike 4.0 International License.\"><\/a>This work is licensed under a Creative Commons Attribution-Share Alike 4.0 International License.<\/div>\n<section class=\"field field--name-field-comments field--type-comment field--label-hidden comment-wrapper\">\n<div class=\"comments__count\">\n<div class=\"login\"><a href=\"https:\/\/opensource.com\/user\/register?absolute=1\">Register<\/a> or <a href=\"https:\/\/opensource.com\/user\/login?current=\/rss.xml&amp;absolute=1\">Login<\/a> to post a comment.<\/div>\n<\/p><\/div>\n<\/section>\n<p class=\"wpematico_credit\"><small>Powered by <a href=\"http:\/\/www.wpematico.com\" target=\"_blank\" rel=\"noopener\">WPeMatico<\/a><\/small><\/p>\n","protected":false},"excerpt":{"rendered":"<p>A guide to Pipy, a programmable network proxy for cloud Ali Naqvi Wed, 05\/18\/2022 &#8211; 03:00 Register or Login to like Register or Login to like Pipy is an open source, cloud-native, network stream processor. It is modular by design and can create a high-performance network proxy. It&#8217;s written in C++ and is built on [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":64436,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[307],"tags":[],"class_list":["post-64435","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-open-source"],"_links":{"self":[{"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=\/wp\/v2\/posts\/64435","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=64435"}],"version-history":[{"count":0,"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=\/wp\/v2\/posts\/64435\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=\/wp\/v2\/media\/64436"}],"wp:attachment":[{"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=64435"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=64435"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=64435"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}