---
title: "OpenResty Is The Enhanced NGINX With Dynamic Requests and Responses - API7.ai"
description: "OpenResty is the enhanced NGINX with dynamic requests and responses, and this article explains the APIs for manipulating Request and Response in OpenResty."
url: "https://api7.ai/learning-center/openresty/handling-requests-responses-dynamically"
---

# OpenResty Is The Enhanced NGINX With Dynamic Requests and Responses

API7.ai

October 23, 2022

[OpenResty (NGINX + Lua)](https://api7.ai/learning-center/openresty)

After the previous introduction, you must understand the concept of OpenResty and how to learn it. This article will guide us on how OpenResty handles client requests and responses.

Although OpenResty is an NGINX-based web server, it is fundamentally different from NGINX: NGINX is driven by static configuration files, while OpenResty is driven by Lua API, offering more flexibility and programmability.

Let me take you through the benefits of the Lua API.

## API Categories

First, we need to know that the OpenResty API is divided into the following broad categories.

- Request and response processing.
- SSL-related.
- Shared dict.
- Cosocket.
- Handling four layers of traffic.
- Process and worker.
- Access to NGINX variables and configuration.
- String, time, codec, and other general functions, etc.

Here, I suggest you also open OpenResty's Lua API documentation and check it against the [API list](https://github.com/openresty/lua-nginx-module/#nginx-api-for-lua) to see if you can relate to this category.

OpenResty APIs exist not only in the lua-nginx-module project, but also in the lua-resty-core project, such as `ngx.ssl`, `ngx.base64`, `ngx.errlog`, `ngx.process`, `ngx.re.split`, `ngx.resp.add_header`, `ngx.balancer`, `ngx.semaphore`, `ngx.ocsp`, and other APIs.

For APIs that are not in the lua-nginx-module project, you need to require them separately to use them. For example, if you want to use the split function, you need to call it as follows.

```
$ resty -e 'local ngx_re = require "ngx.re"
 local res, err = ngx_re.split("a,b,c,d", ",", nil, {pos = 5})
 print(res)
 '
```

Of course, it may confuse you: in the `lua-nginx-module` project, there are several APIs starting with `ngx.re.sub`, `ngx.re.find`, etc. Why is it that the API `ngx.re.split` is the only one that needs to require first before being used?

As we mentioned in the previous `lua-resty-core` chapter, the new OpenResty APIs are implemented in the `lua-rety-core` repository by way of FFI, so there is inevitably a sense of fragmentation. I'm looking forward to solving this problem in the future by merging the `lua-nginx-module` and `lua-resty-core` projects.

## Request

Next, let's look at how OpenResty handles client requests and responses. First, let's look at the API for handling requests, but there are more than 20 APIs starting with `ngx.req`, so how do we start?

We know that HTTP request messages consist of three parts: the request line, the request header, and the request body, so I'll introduce the API in these three parts.

### Request Line

The first is the request line, which contains the request method, URI, and HTTP protocol version for HTTP. In NGINX, you can get this value using a built-in variable, while in OpenResty, it corresponds to the `ngx.var.*` API. Let's look at two examples.

- The built-in variable `$scheme`, which represents the name of the protocol in NGINX, is either `http` or `https`; in OpenResty, you can use `ngx.var.scheme` to return to the same value.
- `$request_method` represents the request method like `GET`, `POST`, etc.; in OpenResty, you can return to the same value via `ngx.var.request_method`.

You can visit the NGINX official documentation to get a complete list of NGINX built-in variables: <http://nginx.org/en/docs/http/ngx_http_core_module.html#variables>.

So the question arises: why does OpenResty provide a separate API for request lines when you can get the data in the request line by returning the value of a variable like `ngx.var.*`?

The result contains many factors:

- First of all, it is not recommended to repeatedly read `ngx.var` due to its inefficient performance.
- Second, out of consideration of the program-friendly aspect, `ngx.var` returns a string, not a Lua object. It isn't easy to handle when getting `args`, which may return multiple values.
- Third, from the aspect of flexibility, most of `ngx.var` is read-only, and only a few variables are writable, such as `$args` and `limit_rate`. However, we often need to modify the method, URI, and args.

Therefore, OpenResty provides several APIs dedicated to manipulating request lines, which can rewrite the request line for subsequent operations such as redirection.

Let's look at how to get the HTTP protocol version number through the API. OpenResty API `ngx.req.http_version` does the same thing as NGINX `$server_protocol` variable: returning to the version number of HTTP protocol. However, the return value of this API is not a string but in numeric format, the possible values of which are `2.0`, `1.0`, `1.1`, and `0.9`. `Nil` is returned if the result is out of the range of these values.

Let's look at the method of getting the request in the request line. As mentioned, the role and return value of `ngx.req.get_method` and NGINX's `$request_method` variables are the same: in string format.

However, the parameter format of the current HTTP request method `ngx.req.set_method` is not a string but built-in numeric constants. For example, the following code rewrites the request method to POST.

```
ngx.req.set_method(ngx.HTTP_POST)
```

To verify that the built-in constant, `ngx.HTTP_POST` is indeed a number and not a string, you can print out its value and see if the output is 8.

```
resty -e 'print(ngx.HTTP_POST)'
```

In this way, the return value of the `get` method is a string, while the input value of the `set` method is a number. It is OK when the set method passes a confusing value because the API can crash and report with a `500` error. However, in the following judging logic:

```
if (ngx.req.get_method() == ngx.HTTP_POST) then
    -- do something
 end
```

This kind of code works fine, report no errors, and is hard to find even during code reviews. I made a similar mistake before and can still remember it: I had already gone through two rounds of code reviews and incomplete test cases to try to cover it. Ultimately, an online environment anomaly traced me to the problem.

There is no practical way to solve such a problem except to be more careful or to add another layer of encapsulation. When you design your business API, you can also consider and keep the consistent parameter format of `get` and `set` methods, even if you need to sacrifice some performance.

In addition, among the methods for rewriting request lines, there are two APIs, `ngx.req.set_uri` and `ngx.req.set_uri_args`, which can be used to rewrite URI and args. Let's look at this NGINX configuration.

```
rewrite ^ /foo?a=3? break;
```

So, how can we solve it with the equivalent Lua API? The answer is the following two lines of code.

```
ngx.req.set_uri_args("a=3")
ngx.req.set_uri("/foo")
```

If you have read the official documentation, you will find that `ngx.req.set_uri` has a second parameter: `jump`, which is "false" by default. If you set it as "true", it is equal to setting the flag of the `rewrite` command to `last` instead of `break` in the example above.

However, I'm not too fond of the flag configuration of the `rewrite` command as it's unreadable and unrecognizable and far less intuitive and maintainable than code.

### Request Header

As we know, HTTP request headers are in the `key : value` format, for example:

```
  Accept: text/css,*/*;q=0.1
  Accept-Encoding: gzip, deflate, br
```

In OpenResty, you can use `ngx.req.get_headers` to parse and get the request headers, and the return value type is table.

```
local h, err = ngx.req.get_headers()
  if err == "truncated" then
      -- one can choose to ignore or reject the current request here
  end
  for k, v in pairs(h) do
      ...
  end
```

It is defaulted to return to the first 100 headers. If the number exceeds 100, it will report a `truncated` error, leaving it up to the developer to decide how to handle it. You may wonder why to take it this way, which I will mention later in the section on security vulnerabilities.

However, we should note that OpenResty provides no specific API for getting a specified request header, which means no form as `ngx.req.header['host']`. If you have such a need, you must rely on the NGINX variable `$http_xxx` to achieve it. Then in OpenResty, you can get it by `ngx.var.http_xxx`.

Now let's look at how we should rewrite and delete the request header. The APIs for both operations is quite intuitive:

```
ngx.req.set_header("Content-Type", "text/css")
ngx.req.clear_header("Content-Type")
```

Of course, the official documentation also mentions other methods to remove the request header, such as setting the value of the title to `nil`, etc. However, I still recommend using `clear_header` to do it uniformly for code clarity.

### Request Body

Finally, let's look at the request body. For performance reasons, OpenResty does not actively read the request body unless you force the `lua_need_request_body` directive to be enabled in `nginx.conf`. In addition, for larger request bodies, OpenResty saves the contents to a temporary file on disk, so the entire process of reading the request body looks like this.

```
ngx.req.read_body()
local data = ngx.req.get_body_data()
if not data then
    local tmp_file = ngx.req.get_body_file()
     -- io.open(tmp_file)
     -- ...
 end
```

This code has an IO-blocking operation to read the disk file. You should adjust the configuration of the `client_body_buffer_size` (16 KB by default on 64-bit systems) to minimize the blocking operations; you can also configure `client_body_buffer_size` and `client_max_body_size` to be the same and handle them entirely in memory, depending on the size of your memory and the number of concurrent requests you take.

In addition, the request body can be rewritten. The two APIs `ngx.req.set_body_data` and `ngx.req.set_body_file` accept a string and a local disk file as input parameters to accomplish the rewriting of the request body. However, this type of operation is uncommon, and you can check the documentation for more details.

## Response

After the request is processed, we need to send a response back to the client. Like the request message, the response message also consists of several parts: the status line, the response header, and the response body. I will introduce the corresponding APIs according to these three parts.

### Status Line

The main thing we are concerned about in the status line is the status code. By default, the HTTP status code returned is 200, which is the constant `ngx.HTTP_OK` built into OpenResty. But in the world of code, it is always the code that handles the most exceptional cases.

If you detect the request message and find that it is a malicious request, then you need to terminate the request:

```
ngx.exit(ngx.HTTP_BAD_REQUEST)
```

However, there is a particular constant in OpenResty's HTTP status code: `ngx.OK`. In the situation of `ngx.exit(ngx.OK)`, the request exits the current processing phase and moves on to the next stage rather than returning directly to the client.

Of course, you can also choose not to exit and just rewrite the status code using `ngx.status`, as written in the following way.

```
ngx.status = ngx.HTTP_FORBIDDEN
```

You can look them up in the [documentation](https://github.com/openresty/lua-nginx-module/#http-status-constants) if you want to know more about the status code constants.

### Response Header

Regarding the response header, there are two ways you can set it up. The first one is the simplest.

```
ngx.header.content_type = 'text/plain'
ngx.header["X-My-Header"] = 'blah blah'
ngx.header["X-My-Header"] = nil -- delete
```

Here the `ngx.header` holds the response header information, which can be read, modified, and deleted.

The second way to set the response header is `ngx_resp.add_header`, from the `lua-resty-core` repository, which adds a header message, called with:

```
local ngx_resp = require "ngx.resp"
ngx_resp.add_header("Foo", "bar")
```

The difference with the first method is that `add_header` does not overwrite an existing field of the same name.

### Response Body

Finally, look at the response body. In OpenResty, you can use `ngx.say` and `ngx.print` to output the response body.

```
ngx.say('hello, world')
```

The functionality of the two APIs is identical, the only difference being that `ngx.say` has a line feed at the end.

To avoid the inefficiency of string splicing, `ngx.say / ngx.print` supports strings and array formats as parameters.

```
$ resty -e 'ngx.say({"hello", ", ", "world"})'
 hello, world
```

This method skips the string splicing at the Lua level and leaves it to the C functions to handle.

## Summary

Let's review today's content. We introduce the OpenResty APIs associated with the request and response messages. As you can see, the OpenResty API is more flexible and powerful than the NGINX directive.

Consequently, is the Lua API provided by OpenResty enough to meet your needs when you are handling HTTP requests? Please leave your comments and share this article with your colleagues and friends so that we can communicate and improve together.

Share article link

Share

Related Posts

[![OpenResty FAQ | How OpenResty Is Used in Practice](https://static.api7.ai/uploads/2023/02/12/jbv9M0zi_Hello%20World%20in%20OpenResty%20%283%29.png)](https://api7.ai/learning-center/openresty/faq-5)

## [OpenResty FAQ | How OpenResty Is Used in Practice](https://api7.ai/learning-center/openresty/faq-5)

[![Part 3: How to Build a Microservices API Gateway Using OpenResty](https://static.api7.ai/uploads/2023/02/07/3XJXY9Jy_45.png)](https://api7.ai/learning-center/openresty/how-to-build-a-microservices-api-gateway-part-3)

## [Part 3: How to Build a Microservices API Gateway Using OpenResty](https://api7.ai/learning-center/openresty/how-to-build-a-microservices-api-gateway-part-3)

Table of Contents

- [API Categories](#api-categories)

- [Request](#request)

  - [Request Line](#request-line)
  - [Request Header](#request-header)
  - [Request Body](#request-body)

- [Response](#response)

  - [Status Line](#status-line)
  - [Response Header](#response-header)
  - [Response Body](#response-body)

- [Summary](#summary)
