Giter Site home page Giter Site logo

cmorten / oak-http-proxy Goto Github PK

View Code? Open in Web Editor NEW
42.0 3.0 5.0 246 KB

Proxy middleware for Deno Oak HTTP servers. ๐Ÿฟ ๐Ÿฆ•

Home Page: https://cmorten.github.io/oak-http-proxy/

License: MIT License

Makefile 1.74% TypeScript 98.15% HTML 0.10%
deno denoland deno-module denojs oak http-proxy oak-http-proxy proxy-middleware deno-doc

oak-http-proxy's Introduction

oak-http-proxy

Proxy middleware for Deno Oak HTTP servers.

GitHub tag Test deno doc PRs Welcome GitHub issues GitHub stars GitHub forks oak-http-proxy License Maintenance

oak-http-proxy latest /x/ version oak-http-proxy dependency count oak-http-proxy dependency outdatedness oak-http-proxy cached size

import { proxy } from "https://deno.land/x/[email protected]/mod.ts";
import { Application } from "https://deno.land/x/[email protected]/mod.ts";

const app = new Application();

app.use(proxy("https://github.com/oakserver/oak"));

await app.listen({ port: 3000 });

Installation

This is a Deno module available to import direct from this repo and via the Deno Registry.

Before importing, download and install Deno.

You can then import oak-http-proxy straight into your project:

import { proxy } from "https://deno.land/x/[email protected]/mod.ts";

oak-http-proxy is also available on nest.land, a package registry for Deno on the Blockchain.

import { proxy } from "https://x.nest.land/[email protected]/mod.ts";

Docs

Usage

URL

The url argument that can be a string, URL or a function that returns a string or URL. This is used as the url to proxy requests to. Query string parameters and hashes are transferred from incoming request urls onto the proxy url.

router.get("/string", proxy("http://google.com"));

router.get("/url", proxy(new URL("http://google.com")));

router.get(
  "/function",
  proxy((ctx) => new URL("http://google.com"))
);

Note: Unmatched path segments of the incoming request url are not transferred to the outbound proxy URL. For dynamic proxy urls use the function form.

Streaming

Proxy requests and user responses are piped/streamed/chunked by default.

If you define a response modifier (srcResDecorator, srcResHeaderDecorator), or need to inspect the response before continuing (filterRes), streaming is disabled, and the request and response are buffered. This can cause performance issues with large payloads.

Proxy Options

You can also provide several options which allow you to filter, customize and decorate proxied requests and responses.

app.use(proxy("http://google.com", proxyOptions));

filterReq(req, res) (supports Promises)

The filterReq option can be used to limit what requests are proxied.

Return false to continue to execute the proxy; return true to skip the proxy for this request.

app.use(
  "/proxy",
  proxy("www.google.com", {
    filterReq: (req, res) => {
      return req.method === "GET";
    },
  })
);

Promise form:

app.use(
  proxy("localhost:12346", {
    filterReq: (req, res) => {
      return new Promise((resolve) => {
        resolve(req.method === "GET");
      });
    },
  })
);

srcResDecorator(req, res, proxyRes, proxyResData) (supports Promise)

Decorate the inbound response object from the proxied request.

router.get(
  "/proxy",
  proxy("www.example.com", {
    srcResDecorator: (req, res, proxyRes, proxyResData) => {
      data = JSON.parse(new TextDecoder().decode(proxyResData));
      data.newProperty = "exciting data";

      return JSON.stringify(data);
    },
  })
);
app.use(
  proxy("httpbin.org", {
    srcResDecorator: (req, res, proxyRes, proxyResData) => {
      return new Promise((resolve) => {
        proxyResData.message = "Hello Deno!";

        setTimeout(() => {
          resolve(proxyResData);
        }, 200);
      });
    },
  })
);
304 - Not Modified

When your proxied service returns 304 Not Modified this step will be skipped, since there should be no body to decorate.

Exploiting references

The intent is that this be used to modify the proxy response data only.

Note: The other arguments are passed by reference, so you can currently exploit this to modify either response's headers, for instance, but this is not a reliable interface.

memoizeUrl

Defaults to true.

When true, the url argument will be parsed on first request, and memoized for subsequent requests.

When false, url argument will be parsed on each request.

For example:

function coinToss() {
  return Math.random() > 0.5;
}

function getUrl() {
  return coinToss() ? "http://yahoo.com" : "http://google.com";
}

app.use(
  proxy(getUrl, {
    memoizeUrl: false,
  })
);

In this example, when memoizeUrl: false, the coinToss occurs on each request, and each request could get either value.

Conversely, When memoizeUrl: true, the coinToss would occur on the first request, and all additional requests would return the value resolved on the first request.

srcResHeaderDecorator

Decorate the inbound response headers from the proxied request.

router.get(
  "/proxy",
  proxy("www.google.com", {
    srcResHeaderDecorator(headers, req, res, proxyReq, proxyRes) {
      return headers;
    },
  })
);

filterRes(proxyRes, proxyResData) (supports Promise form)

Allows you to inspect the proxy response, and decide if you want to continue processing (via oak-http-proxy) or continue onto the next middleware.

router.get(
  "/proxy",
  proxy("www.google.com", {
    filterRes(proxyRes) {
      return proxyRes.status === 404;
    },
  })
);

proxyErrorHandler

By default, oak-http-proxy will throw any errors except ECONNRESET and ECONTIMEDOUT via ctx.throw(err), so that your application can handle or react to them, or just drop through to your default error handling.

If you would like to modify this behavior, you can provide your own proxyErrorHandler.

// Example of skipping all error handling.

app.use(
  proxy("localhost:12346", {
    proxyErrorHandler(err, ctx, next) {
      ctx.throw(err);
    },
  })
);

// Example of rolling your own error handler

app.use(
  proxy("localhost:12346", {
    proxyErrorHandler(err, ctx, next) {
      switch (err && err.code) {
        case "ECONNRESET": {
          ctx.response.status = 405;

          return;
        }
        case "ECONNREFUSED": {
          ctx.response.status = 200;

          return;
        }
        default: {
          ctx.throw(err);
        }
      }
    },
  })
);

proxyReqUrlDecorator(url, req) (supports Promise form)

Decorate the outbound proxied request url.

The returned url is used for the fetch method internally.

router.get(
  "/proxy",
  proxy("www.google.com", {
    proxyReqUrlDecorator(url, req) {
      url.pathname = "/";

      return url;
    },
  })
);

You can also use Promises:

router.get(
  "/proxy",
  proxy("localhost:3000", {
    proxyReqOptDecorator(url, req) {
      return new Promise((resolve, reject) => {
        if (url.pathname === "/login") {
          url.port = 8080;
        }

        resolve(url);
      });
    },
  })
);

Generally it is advised to use the function form for the passed URL argument as this provides the full context object whereas the proxyReqOptDecorator passes only the context.request object.

Potential use cases for proxyReqOptDecorator include:

  • Overriding default protocol behaviour.
  • Overriding default query-string and hash transfer behaviour.

proxyReqInitDecorator(proxyReqOpts, req) (supports Promise form)

Decorate the outbound proxied request initialization options.

This configuration will be used within the fetch method internally to make the request to the provided url.

router.get(
  "/proxy",
  proxy("www.google.com", {
    proxyReqInitDecorator(proxyReqOpts, srcReq) {
      // you can update headers
      proxyReqOpts.headers.set("Content-Type", "text/html");
      // you can change the method
      proxyReqOpts.method = "GET";

      return proxyReqOpts;
    },
  })
);

You can also use Promises:

router.get(
  "/proxy",
  proxy("www.google.com", {
    proxyReqOptDecorator(proxyReqOpts, srcReq) {
      return new Promise((resolve, reject) => {
        proxyReqOpts.headers.set("Content-Type", "text/html");

        resolve(proxyReqOpts);
      });
    },
  })
);

secure

Normally, your proxy request will be made on the same protocol as the url parameter. If you'd like to force the proxy request to be https, use this option.

app.use(
  "/proxy",
  proxy("http://www.google.com", {
    secure: true,
  })
);

Note: if the proxy is passed a url without a protocol then HTTP will be used by default unless overridden by this option.

preserveHostHeader

You can copy the host HTTP header to the proxied Oak server using the preserveHostHeader option.

router.get(
  "/proxy",
  proxy("www.google.com", {
    preserveHostHeader: true,
  })
);

parseReqBody

The parseReqBody option allows you to control whether the request body should be parsed and sent with the proxied request. If set to false then an incoming request body will not be sent with the proxied request.

reqAsBuffer

Configure whether the proxied request body should be sent as a UInt8Array buffer.

Ignored if parseReqBody is set to false.

router.get(
  "/proxy",
  proxy("www.google.com", {
    reqAsBuffer: true,
  })
);

reqBodyEncoding

The request body encoding to use. Currently only "utf-8" is supported.

Ignored if parseReqBody is set to false.

router.get(
  "/post",
  proxy("httpbin.org", {
    reqBodyEncoding: "utf-8",
  })
);

reqBodyLimit

The request body size limit to use.

Ignored if reqBodyLimit is set to Infinity.

router.get(
  "/post",
  proxy("httpbin.org", {
    reqBodyLimit: 10_485_760, // 10MB
  })
);

timeout

Configure a timeout in ms for the outbound proxied request.

If not provided the request will never time out.

Timed-out requests will respond with 504 status code and a X-Timeout-Reason header.

router.get(
  "/",
  proxy("httpbin.org", {
    timeout: 2000, // in milliseconds, two seconds
  })
);

Contributing

Contributing guide


License

oak-http-proxy is licensed under the MIT License.

oak-http-proxy's People

Contributors

asos-craigmorten avatar cmorten avatar langtutheky avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

oak-http-proxy's Issues

[email protected] error

Issue

When I want to use this library as a dependency or even check it out, I get an error on the [email protected] library.

Setup:

deno --version
deno 1.9.1 (release, x86_64-apple-darwin)
v8 9.1.269.5
typescript 4.2.2

Details

error: TS2612 [ERROR]: Property 'resolve' will overwrite the base property in 'Deferred<undefined>'. If this is intentional, add an initializer. Otherwise, add a 'declare' modifier or remove the redundant declaration.
    public readonly resolve!: () => void;
                    ~~~~~~~
    at https://deno.land/x/[email protected]/tools/Deferred.ts:57:21
make: *** [build] Error 1

302 redirect not working

Issue

Setup: set a sever who have 302 redirect function, then use proxy to that server, when fetch proxy server, 302 will not redirect the browser page, and it's response status:200, the content is redirected url resposne content. also the cookies not follow to this 302 redirect action, end up request is unauth request.

  • Deno Version: deno 1.36.2 (release, x86_64-pc-windows-msvc)
  • v8 Version: v8 11.7.439.1
  • Typescript Version: typescript 5.1.6
  • Oak HTTP Proxy Version: [email protected]
  • Oak Version: [email protected]

Details

Snipaste_2023-08-23_14-25-30

image_2023-08-23_14-26-44

Iis it possible to proxy streaming responses?

Issue

I have tried to find anything in documentation about this and it didn't work "out-of-the-box" for me. But I would like to proxy a streamed response.

Did I miss anything? Or is this not/already implemented?

error: proxying 304 response gives error

When the upstream server returns 304, the proxy still tries to send the body and results in the following error:

[uncaught application error]: TypeError - Response with null body status cannot have body

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.