Listen directive example analysis in nginx

Listen directive example analysis in nginx

Plot Review

In the previous article, we analyzed the parsing process of the location instruction. Let's briefly review this content: each location corresponds to an ngx_http_core_loc_conf_t structure, and all locations are connected together through a bidirectional queue. The data structure is relatively complex.

The listen directive

As a high-performance HTTP server, network processing is the core of nginx. Understanding network initialization will help you deepen your understanding of nginx network processing. There are two main network-related configuration commands: listen and server_name. The listen command sets the nginx listening address. For IP protocol, this address is address and port. For UNIX domain socket protocol, this address is path. A listen instruction can only specify one address or port. Address can also be a host name.

Starting from this article, we analyze the parsing process of the listen directive. The configuration of the listen directive is as follows: From the manual of nginx.org, we can get the usage of listen:

listen address[:port] [default_server] [setfib=number] [backlog=number] [rcvbuf=size] [sndbuf=size] [accept_filter=filter] [deferred] [bind] [ipv6only=on|off] [ssl] [so_keepalive=on|off|[keepidle]:[keepintvl]:[keepcnt]];

The parameters carried by a listen instruction are very complex. However, we generally pay little attention to those less commonly used parameters. The following are some common configuration methods:

listen 127.0.0.1:8000;
listen 127.0.0.1 without adding port, default listening port is 80;
listen 8000
listen *:8000
listen localhost:8000

Parse the uri and port in the listen directive

From the above content, we know that listen has multiple uses. When parsing, we need to get the port number and uri part of the listen directive. Nginx provides the ngx_parse_url() method to parse uri and port. This function will be called when parsing the listen directive.

ngx_int_t
ngx_parse_url(ngx_pool_t *pool, ngx_url_t *u)
{
 u_char *p;
 size_t len;

 p = u->url.data;
 len = u->url.len;
 // Here is the protocol for parsing the Unix domain if (len >= 5 && ngx_strncasecmp(p, (u_char *) "unix:", 5) == 0) {
 return ngx_parse_unix_domain_url(pool, u);
 }
 // Parse IPV6 protocol if (len && p[0] == '[') {
 return ngx_parse_inet6_url(pool, u);
 }
 // Parse IPV4 protocol return ngx_parse_inet_url(pool, u);
}

We use the IPV4 protocol, here we analyze the ngx_parse_inet_url() function

// u.url = "80";
// u.listen = 1;
// u.default_port = 80;
static ngx_int_t
ngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u)
{
 u_char *p, *host, *port, *last, *uri, *args;
 size_t len;
 ngx_int_t n;
 struct sockaddr_in *sin;
#if (NGX_HAVE_INET6)
 struct sockaddr_in6 *sin6;
#endif

 u->socklen = sizeof(struct sockaddr_in);
 sin = (struct sockaddr_in *) &u->sockaddr;
 sin->sin_family = AF_INET; //IPV4 type u->family = AF_INET; 

 host = u->url.data; // "80"

 last = host + u->url.len; // The position of the last character of host port = ngx_strlchr(host, last, ':'); // Find port, here is NULL

 uri = ngx_strlchr(host, last, '/'); // Find uri, here is NULL

 args = ngx_strlchr(host, last, '?'); // Find the parameter args, which is NULL here

 if (args) {
 if (uri == NULL || args < uri) {
 uri = args;
 }
 }

 if (uri) {
 if (u->listen || !u->uri_part) {
 u->err = "invalid host";
 return NGX_ERROR;
 }

 u->uri.len = last - uri;
 u->uri.data = uri;

 last = uri;

 if (uri < port) {
 port = NULL;
 }
 }

 if (port) {
 port++;

 len = last - port;

 n = ngx_atoi(port, len);

 if (n < 1 || n > 65535) {
 u->err = "invalid port";
 return NGX_ERROR;
 }

 u->port = (in_port_t) n;
 sin->sin_port = htons((in_port_t) n);

 u->port_text.len = len;
 u->port_text.data = port;

 last = port - 1;

 } else {
 if (uri == NULL) {

 if (u->listen) {

 /* test value as port only */

 n = ngx_atoi(host, last - host);

 if (n != NGX_ERROR) {

 if (n < 1 || n > 65535) {
 u->err = "invalid port";
 return NGX_ERROR;
 }

 u->port = (in_port_t) n;
 sin->sin_port = htons((in_port_t) n);

 u->port_text.len = last - host;
 u->port_text.data = host;

 u->wildcard = 1;

 return NGX_OK;
 }
 }
 }

 u->no_port = 1;
 u->port = u->default_port;
 sin->sin_port = htons(u->default_port);
 }

 len = last - host;

 if (len == 0) {
 u->err = "no host";
 return NGX_ERROR;
 }

 u->host.len = len;
 u->host.data = host;

 if (u->listen && len == 1 && *host == '*') {
 sin->sin_addr.s_addr = INADDR_ANY;
 u->wildcard = 1;
 return NGX_OK;
 }

 sin->sin_addr.s_addr = ngx_inet_addr(host, len);

 if (sin->sin_addr.s_addr != INADDR_NONE) {

 if (sin->sin_addr.s_addr == INADDR_ANY) {
 u->wildcard = 1;
 }

 u->naddrs = 1;

 u->addrs = ngx_pcalloc(pool, sizeof(ngx_addr_t));
 if (u->addrs == NULL) {
 return NGX_ERROR;
 }

 sin = ngx_pcalloc(pool, sizeof(struct sockaddr_in));
 if (sin == NULL) {
 return NGX_ERROR;
 }

 ngx_memcpy(sin, &u->sockaddr, sizeof(struct sockaddr_in));

 u->addrs[0].sockaddr = (struct sockaddr *) sin;
 u->addrs[0].socklen = sizeof(struct sockaddr_in);

 p = ngx_pnalloc(pool, u->host.len + sizeof(":65535") - 1);
 if (p == NULL) {
 return NGX_ERROR;
 }

 u->addrs[0].name.len = ngx_sprintf(p, "%V:%d",
  &u->host, u->port) - p;
 u->addrs[0].name.data = p;

 return NGX_OK;
 }

 if (u->no_resolve) {
 return NGX_OK;
 }

 if (ngx_inet_resolve_host(pool, u) != NGX_OK) {
 return NGX_ERROR;
 }

 u->family = u->addrs[0].sockaddr->sa_family;
 u->socklen = u->addrs[0].socklen;
 ngx_memcpy(&u->sockaddr, u->addrs[0].sockaddr, u->addrs[0].socklen);

 switch (u->family) {

#if (NGX_HAVE_INET6)
 case AF_INET6:
 sin6 = (struct sockaddr_in6 *) &u->sockaddr;

 if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
 u->wildcard = 1;
 }

 break;
#endif

 default: /* AF_INET */
 sin = (struct sockaddr_in *) &u->sockaddr;

 if (sin->sin_addr.s_addr == INADDR_ANY) {
 u->wildcard = 1;
 }

 break;
 }

 return NGX_OK;
}

This function parses the address and port number of our listen. In our configuration file, the port number is 80, and there is no listening address configured, so u->wildcard = 1, indicating that this is a wildcard, and we want to listen to this port number of all IP addresses of the server.

Parsing the listen directive

Let's take a look at the listen configuration from the source code:

{ 
 ngx_string("listen"),
 NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,
 ngx_http_core_listen,
 NGX_HTTP_SRV_CONF_OFFSET,
 0,
 NULL 
}

From the configuration file, we can know that listen can only appear in the server module and can have multiple parameters.

The corresponding processing function is ngx_http_core_listen. Let's analyze this function. We have deleted some codes for error judgment.

static char *
ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
 ngx_http_core_srv_conf_t *cscf = conf;

 ngx_str_t *value, size;
 ngx_url_t u;
 ngx_uint_t n;
 ngx_http_listen_opt_t lsopt;

 cscf->listen = 1;

 value = cf->args->elts;

 ngx_memzero(&u, sizeof(ngx_url_t));

 u.url = value[1];
 u.listen = 1;
 u.default_port = 80;

 if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
 return NGX_CONF_ERROR;
 }

 ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t));

 ngx_memcpy(&lsopt.sockaddr.sockaddr, &u.sockaddr, u.socklen);

 lsopt.socklen = u.socklen;
 lsopt.backlog = NGX_LISTEN_BACKLOG;
 lsopt.rcvbuf = -1;
 lsopt.sndbuf = -1;
#if (NGX_HAVE_SETFIB)
 lsopt.setfib = -1;
#endif
#if (NGX_HAVE_TCP_FASTOPEN)
 lsopt.fastopen = -1;
#endif
 lsopt.wildcard = u.wildcard;
#if (NGX_HAVE_INET6)
 lsopt.ipv6only = 1;
#endif

 (void) ngx_sock_ntop(&lsopt.sockaddr.sockaddr, lsopt.socklen, lsopt.addr,
  NGX_SOCKADDR_STRLEN, 1);

 for (n = 2; n < cf->args->nelts; n++) {

 if (ngx_strcmp(value[n].data, "default_server") == 0
 || ngx_strcmp(value[n].data, "default") == 0)
 {
 lsopt.default_server = 1;
 continue;
 }
 // The other codes here are all for processing various parameters of listen, which are useless for our analysis here}

 if (ngx_http_add_listen(cf, cscf, &lsopt) == NGX_OK) {
 return NGX_CONF_OK;
 }

 return NGX_CONF_ERROR;
}

The overall process of this function is to parse the various parameters of the listen instruction and generate a ngx_http_listen_opt_t. As the name suggests, this structure is used to save some listening port options (listening port option). A function ngx_parse_url() is called here. We have analyzed it above. The function of this function is to parse the address and port in the URL.

Then the most important part is coming. The ngx_http_core_listen() function calls the ngx_http_add_listen() function at the end, which saves the listen port information to the ports dynamic array of the ngx_http_core_main_conf_t structure.

ngx_http_add_listen() function

// cf: configuration structure // cscf: configuration structure of the server where the listen directive is located // lsopt: listen option generated by ngx_http_core_listen()
ngx_int_t
ngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
 ngx_http_listen_opt_t *lsopt)
{
 in_port_t p;
 ngx_uint_t i;
 struct sockaddr *sa;
 ngx_http_conf_port_t *port;
 ngx_http_core_main_conf_t *cmcf;
 // Get the main_conf structure of the ngx_http_core_module module ngx_http_core_main_conf_t
 cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
 // The ports field is an array if (cmcf->ports == NULL) {
  cmcf->ports = ngx_array_create(cf->temp_pool, 2,
          sizeof(ngx_http_conf_port_t));
  if (cmcf->ports == NULL) {
   return NGX_ERROR;
  }
 }

 sa = &lsopt->sockaddr.sockaddr;
 p = ngx_inet_get_port(sa);

 port = cmcf->ports->elts;
 for (i = 0; i < cmcf->ports->nelts; i++) {

  if (p != port[i].port || sa->sa_family != port[i].family) {
   continue;
  }

  /* a port is already in the port list */

  return ngx_http_add_addresses(cf, cscf, &port[i], lsopt);
 }

 /* add a port to the port list */

 port = ngx_array_push(cmcf->ports);
 if (port == NULL) {
  return NGX_ERROR;
 }

 port->family = sa->sa_family;
 port->port = p;
 port->addrs.elts = NULL;

 return ngx_http_add_address(cf, cscf, port, lsopt);
}

This function saves the port number information to the port field of the ngx_http_core_main_conf_t structure.

Summarize

The above is the full content of this article. I hope that the content of this article will have certain reference learning value for your study or work. If you have any questions, you can leave a message to communicate. Thank you for your support for 123WORDPRESS.COM.

You may also be interested in:
  • Detailed explanation of nginx log configuration instructions
  • Nginx configuration directive location matcher priority and security issues
  • Notes on using the nginx proxy_pass directive '/'
  • Detailed explanation of Nginx Gzip module enablement and configuration instructions
  • nginx try_files directive determines whether a file exists
  • How to use the nginx add_header directive
  • Common instructions for nginx HTTP module configuration
  • Using Nginx's map command to redirect pages
  • A brief analysis of the difference between the root and alias directives in Nginx configuration
  • Detailed explanation of Nginx SSI directive configuration

<<:  Mysql some complex sql statements (query and delete duplicate rows)

>>:  In-depth explanation of the global status of WeChat applet

Recommend

Using MySQL database with Python 3.4 under Windows 7

The detailed process of using MySQL database with...

Solution to mysql ERROR 1045 (28000) problem

I encountered mysql ERROR 1045 and spent a long t...

mysql: [ERROR] unknown option '--skip-grant-tables'

MySQL database reports ERROR 1045 (28000): Access...

MySQL 8.0.12 winx64 decompression version installation graphic tutorial

Recorded the installation of mysql-8.0.12-winx64 ...

Summary of several common logs in MySQL

Preface: In the MySQL system, there are many diff...

Nginx dynamic and static separation implementation case code analysis

Separation of static and dynamic Dynamic requests...

Detailed analysis of each stage of nginx's http request processing

When writing the HTTP module of nginx, it is nece...

How to implement two-way binding function in vue.js with pure JS

Table of contents First, let's talk about the...

Solution to the conflict between Linux kernel and SVN versions

Phenomenon The system could compile the Linux sys...

Installation and configuration of mysql 8.0.15 under Centos7

This article shares with you the installation and...

Essential conditional query statements for MySQL database

Table of contents 1. Basic grammar 2. Filter by c...

How to understand the difference between ref toRef and toRefs in Vue3

Table of contents 1. Basics 1.ref 2. toRef 3. toR...