用于解决用户多线路访问的nginx cross isp module

在互联网应用中都会面对多线负载与策略的问题,nginx cross isp module提供了一种简单的解决方案。

nginx cross isp module是基于nginx(http://nginx.org/)开发的,完全用c语言编写,借助于linux系统与nginx强大的功能与性能。

nginx cross isp module的主要逻辑很简单,根据访问用户的ip,从isp数据中查找,如果找到符合条件的,则根据配置,重新定向与相对应的链接地址。isp数据存在mysql中,由于isp数据一般不会经常变化,所以是nginx启动时,一次性读入。如果数据变更,可以重新启动nginx。

在实际应用中,需要为每个isp配置不同的域名,可以是二级域名。以下是nginx cross isp module的所有源码:

#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include <ngx_regex.h>

#include <pcre.h>
#include <my_global.h>
#include <mysql.h>


#define HTTP_COOKIES_MAX_EXPIRES  2145916555
#define HTTP_DOMAIN_COOKIE_NAME_LEN (size_t) 6
#define HTTP_SCHEMA_LEN (size_t) 7
#define HTTPS_SCHEMA_LEN (size_t) 8


typedef struct
{
  ngx_uint_t ip_start;
  ngx_uint_t ip_end;
  u_char isp[3];
} ngx_isp_store_t;

typedef struct
{
  u_char isp[3];
  ngx_regex_t *regex;
  ngx_str_t pattern;
  ngx_str_t replace;
  ngx_str_t domain;
} ngx_isp_domain_t;

typedef struct
{
  ngx_regex_t *regex;
  ngx_str_t pattern;
} ngx_isp_proxy_t;

typedef struct
{
  ngx_str_t host;
  ngx_int_t port;
  ngx_str_t usr;
  ngx_str_t pwd;
  ngx_str_t db;
  ngx_str_t tb;
  ngx_str_t isp_rhost_cookie_name;
  ngx_str_t isp_taddr_cookie_name;
  time_t isp_cookies_expires;
  ngx_array_t isp_store;
  ngx_array_t isp_domains;
  ngx_array_t isp_proxies;
} ngx_http_cross_isp_main_conf_t;


static u_char ngx_http_domain_cookie_name[] = "domain";
static u_char ngx_http_cookies_expires[] =
  "; expires=Thu, 31-Dec-37 23:55:55 GMT";
static u_char ngx_http_schema[] = "http://";
static u_char ngx_https_schema[] = "https://";
static u_char ngx_http_302_page[] = "<html><head>" CRLF
  "<title>302 Redirect</title></head>" CRLF
  "<body><center><h1>302 Redirect</h1>" CRLF "</center></body></html>" CRLF;


static ngx_int_t ngx_http_cross_isp_init (ngx_conf_t * cf);
static void *ngx_http_cross_isp_create_main_conf (ngx_conf_t * cf);
static char *ngx_http_cross_isp_init_main_conf (ngx_conf_t * cf, void *conf);

static char *ngx_conf_set_isp_cookies_expires (ngx_conf_t * cf,
                           ngx_command_t * cmd,
                           void *conf);
static char *ngx_conf_set_isp_domain (ngx_conf_t * cf, ngx_command_t * cmd,
                      void *conf);
static char *ngx_conf_set_isp_proxy (ngx_conf_t * cf, ngx_command_t * cmd,
                     void *conf);

static ngx_int_t ngx_http_cross_isp_handler (ngx_http_request_t * r);
static long ngx_isp_store_binary_search (ngx_array_t data, ngx_uint_t ip);
static ngx_int_t ngx_cross_isp_redirect (ngx_http_request_t * r,
                     ngx_str_t url);
static void ngx_http_write_isp_cookie (ngx_http_request_t * r,
                       u_char * cookie);

static ngx_command_t ngx_http_cross_isp_commands[] = {
  {
   ngx_string ("isp_store_host"),
   NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1,
   ngx_conf_set_str_slot,
   NGX_HTTP_MAIN_CONF_OFFSET,
   offsetof (ngx_http_cross_isp_main_conf_t, host),
   NULL},
  {
   ngx_string ("isp_store_port"),
   NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1,
   ngx_conf_set_num_slot,
   NGX_HTTP_MAIN_CONF_OFFSET,
   offsetof (ngx_http_cross_isp_main_conf_t, port),
   NULL},
  {
   ngx_string ("isp_store_usr"),
   NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1,
   ngx_conf_set_str_slot,
   NGX_HTTP_MAIN_CONF_OFFSET,
   offsetof (ngx_http_cross_isp_main_conf_t, usr),
   NULL},
  {
   ngx_string ("isp_store_pwd"),
   NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1,
   ngx_conf_set_str_slot,
   NGX_HTTP_MAIN_CONF_OFFSET,
   offsetof (ngx_http_cross_isp_main_conf_t, pwd),
   NULL},
  {
   ngx_string ("isp_store_db"),
   NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1,
   ngx_conf_set_str_slot,
   NGX_HTTP_MAIN_CONF_OFFSET,
   offsetof (ngx_http_cross_isp_main_conf_t, db),
   NULL},
  {
   ngx_string ("isp_store_tb"),
   NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1,
   ngx_conf_set_str_slot,
   NGX_HTTP_MAIN_CONF_OFFSET,
   offsetof (ngx_http_cross_isp_main_conf_t, tb),
   NULL},
  {
   ngx_string ("isp_rhost_cookie_name"),
   NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1,
   ngx_conf_set_str_slot,
   NGX_HTTP_MAIN_CONF_OFFSET,
   offsetof (ngx_http_cross_isp_main_conf_t, isp_rhost_cookie_name),
   NULL},
  {
   ngx_string ("isp_taddr_cookie_name"),
   NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1,
   ngx_conf_set_str_slot,
   NGX_HTTP_MAIN_CONF_OFFSET,
   offsetof (ngx_http_cross_isp_main_conf_t, isp_taddr_cookie_name),
   NULL},
  {
   ngx_string ("isp_cookies_expires"),
   NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1,
   ngx_conf_set_isp_cookies_expires,
   NGX_HTTP_MAIN_CONF_OFFSET,
   offsetof (ngx_http_cross_isp_main_conf_t, isp_cookies_expires),
   NULL},
  {
   ngx_string ("isp_domain"),
   NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE4,
   ngx_conf_set_isp_domain,
   NGX_HTTP_MAIN_CONF_OFFSET,
   offsetof (ngx_http_cross_isp_main_conf_t, isp_domains),
   NULL},
  {
   ngx_string ("cross_isp"),
   NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1,
   ngx_conf_set_isp_proxy,
   NGX_HTTP_MAIN_CONF_OFFSET,
   offsetof (ngx_http_cross_isp_main_conf_t, isp_proxies),
   NULL},
  ngx_null_command
};

static ngx_http_module_t ngx_http_cross_isp_module_ctx = {
  NULL,                /* preconfiguration */
  ngx_http_cross_isp_init,    /* postconfiguration */

  ngx_http_cross_isp_create_main_conf,    /* create main configuration */
  ngx_http_cross_isp_init_main_conf,    /* init main configuration */

  NULL,                /* create server configuration */
  NULL,                /* merge server configuration */

  NULL,                /* create location configuration */
  NULL                /* merge location configuration */
};

ngx_module_t ngx_http_cross_isp_module = {
  NGX_MODULE_V1,
  &ngx_http_cross_isp_module_ctx,    /* module context */
  ngx_http_cross_isp_commands,    /* module directives */
  NGX_HTTP_MODULE,        /* module type */
  NULL,                /* init master */
  NULL,                /* init module */
  NULL,                /* init process */
  NULL,                /* init thread */
  NULL,                /* exit thread */
  NULL,                /* exit process */
  NULL,                /* exit master */
  NGX_MODULE_V1_PADDING
};

static ngx_int_t
ngx_http_cross_isp_init (ngx_conf_t * cf)
{
  ngx_http_handler_pt *h;
  ngx_http_core_main_conf_t *cmcf;

  cmcf = ngx_http_conf_get_module_main_conf (cf, ngx_http_core_module);
  h = ngx_array_push (&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
  if (h == NULL)
    {
      return NGX_ERROR;
    }

  *h = ngx_http_cross_isp_handler;

  return NGX_OK;
}

static void *
ngx_http_cross_isp_create_main_conf (ngx_conf_t * cf)
{
  ngx_http_cross_isp_main_conf_t *iscf;

  iscf = ngx_pcalloc (cf->pool, sizeof (ngx_http_cross_isp_main_conf_t));
  if (iscf == NULL)
    {
      return NGX_CONF_ERROR;
    }

  if (ngx_array_init
      (&iscf->isp_store, cf->pool, 20000, sizeof (ngx_isp_store_t)) != NGX_OK)
    {
      return NGX_CONF_ERROR;
    }

  if (ngx_array_init
      (&iscf->isp_domains, cf->pool, 10, sizeof (ngx_isp_domain_t)) != NGX_OK)
    {
      return NGX_CONF_ERROR;
    }

  if (ngx_array_init
      (&iscf->isp_proxies, cf->pool, 100, sizeof (ngx_isp_proxy_t)) != NGX_OK)
    {
      return NGX_CONF_ERROR;
    }

  iscf->port = NGX_CONF_UNSET_UINT;
  iscf->isp_cookies_expires = NGX_CONF_UNSET;

  return iscf;
}

static char *
ngx_http_cross_isp_init_main_conf (ngx_conf_t * cf, void *conf)
{
  MYSQL *conn = NULL;
  MYSQL_RES *result = NULL;
  MYSQL_ROW row;
  char query[80];
  ngx_isp_store_t *elt;
  ngx_http_cross_isp_main_conf_t *iscf = conf;

  sprintf (query,
       "SELECT `ip_start`,`ip_end`,`isp` FROM `%s` ORDER BY `ip_start`",
       (char *) iscf->tb.data);

  conn = mysql_init (NULL);
  if (conn == NULL)
    {
      ngx_conf_log_error (NGX_LOG_EMERG,
              cf,
              0,
              "init mysql error(host: %V port:%d user: %V password: %V database: %V table:%V)",
              iscf->host, iscf->port, iscf->pwd, iscf->db,
              iscf->tb);
    }

  mysql_real_connect (conn, (char *) iscf->host.data, (char *) iscf->usr.data,
              (char *) iscf->pwd.data, (char *) iscf->db.data,
              (unsigned int) iscf->port, NULL, 0);
  mysql_real_query (conn, query, strlen (query));
  result = mysql_store_result (conn);
  if (result == NULL)
    {
      ngx_conf_log_error (NGX_LOG_EMERG,
              cf,
              0,
              "can not get isp data from mysql(host: %V port:%d user: %V password: %V database: %V table:%V)",
              iscf->host, iscf->port, iscf->pwd, iscf->db,
              iscf->tb);
      return NGX_CONF_ERROR;
    }

  while ((row = mysql_fetch_row (result)))
    {
      elt = (ngx_isp_store_t *) ngx_array_push (&iscf->isp_store);
      elt->ip_start = strtol (row[0], NULL, 10);
      elt->ip_end = strtol (row[1], NULL, 10);
      ngx_strlow (elt->isp, (u_char *) row[2], 3);
    }

  mysql_free_result (result);
  mysql_close (conn);

  return NGX_CONF_OK;
}

static char *
ngx_conf_set_isp_cookies_expires (ngx_conf_t * cf, ngx_command_t * cmd,
                  void *conf)
{
  ngx_http_cross_isp_main_conf_t *iscf = conf;

  ngx_str_t *value;
  if (iscf->isp_cookies_expires != NGX_CONF_UNSET)
    {
      return "is duplicate";
    }

  value = cf->args->elts;

  if (ngx_strcmp (value[1].data, "max") == 0)
    {
      iscf->isp_cookies_expires = HTTP_COOKIES_MAX_EXPIRES;
      return NGX_CONF_OK;
    }

  if (ngx_strcmp (value[1].data, "off") == 0)
    {
      iscf->isp_cookies_expires = 0;
      return NGX_CONF_OK;
    }

  iscf->isp_cookies_expires = ngx_parse_time (&value[1], 1);
  if (iscf->isp_cookies_expires == NGX_ERROR)
    {
      return "invalid value";
    }

  if (iscf->isp_cookies_expires == NGX_PARSE_LARGE_TIME)
    {
      return "value must be less than 68 years";
    }

  return NGX_CONF_OK;
}

static char *
ngx_conf_set_isp_domain (ngx_conf_t * cf, ngx_command_t * cmd, void *conf)
{
  ngx_regex_compile_t rc;
  u_char errstr[NGX_MAX_CONF_ERRSTR];

  char *p = conf;
  ngx_isp_domain_t *s;
  ngx_str_t *value;
  ngx_array_t *a;

  a = (ngx_array_t *) (p + cmd->offset);
  s = ngx_array_push (a);
  if (s == NULL)
    {
      return NGX_CONF_ERROR;
    }

  value = cf->args->elts;
  ngx_strlow (s->isp, value[1].data, 3);
  s->pattern = value[2];
  s->replace = value[3];
  s->domain = value[4];

  ngx_memzero (&rc, sizeof (ngx_regex_compile_t));

  rc.pattern = s->pattern;
  rc.pool = cf->pool;
  rc.err.len = NGX_MAX_CONF_ERRSTR;
  rc.err.data = errstr;
  if (ngx_regex_compile (&rc) != NGX_OK)
    {
      ngx_conf_log_error (NGX_LOG_EMERG, cf, 0, "%V", &rc.err);
      return NGX_CONF_ERROR;
    }

  s->regex = rc.regex;
  if (s->regex == NULL)
    {
      return NGX_CONF_ERROR;
    }

  return NGX_CONF_OK;
}

static char *
ngx_conf_set_isp_proxy (ngx_conf_t * cf, ngx_command_t * cmd, void *conf)
{
  ngx_regex_compile_t rc;
  u_char errstr[NGX_MAX_CONF_ERRSTR];

  char *p = conf;
  ngx_isp_proxy_t *s;
  ngx_str_t *value;
  ngx_array_t *a;

  a = (ngx_array_t *) (p + cmd->offset);
  s = ngx_array_push (a);
  if (s == NULL)
    {
      return NGX_CONF_ERROR;
    }

  value = cf->args->elts;
  s->pattern = value[1];

  ngx_memzero (&rc, sizeof (ngx_regex_compile_t));

  rc.pattern = s->pattern;
  rc.pool = cf->pool;
  rc.err.len = NGX_MAX_CONF_ERRSTR;
  rc.err.data = errstr;
  if (ngx_regex_compile (&rc) != NGX_OK)
    {
      ngx_conf_log_error (NGX_LOG_EMERG, cf, 0, "%V", &rc.err);
      return NGX_CONF_ERROR;
    }

  s->regex = rc.regex;
  if (s->regex == NULL)
    {
      return NGX_CONF_ERROR;
    }

  return NGX_CONF_OK;
}

static ngx_int_t
ngx_http_cross_isp_handler (ngx_http_request_t * r)
{
  ngx_http_cross_isp_main_conf_t *ipmcf;
  ngx_int_t pc1 = NGX_DECLINED, pc2 = NGX_DECLINED, ssl, rc;
  ngx_uint_t i, m;
  ngx_uint_t ip;
  ngx_str_t str_uri;
  ngx_str_t isp_rhost_cookie, isp_taddr_cookie;
  ngx_isp_proxy_t *ipt;
  ngx_isp_store_t *ist;
  ngx_isp_domain_t *idt;
  u_char *u_uri, *u_relative_uri, *p, *u_isp_rhost_cookie,
    *u_isp_taddr_cookie, *taddr;

  struct sockaddr_in *sin;
  size_t str_uri_len, str_relative_uri_len, con_schema_len, taddr_len,
    cookie_expires_len;
  char *pt;
  long ip_pos;
  int cmp_r1, cmp_r2;

  ipmcf = ngx_http_get_module_main_conf (r, ngx_http_cross_isp_module);

  ipt = ipmcf->isp_proxies.elts;

  for (i = 0; i < ipmcf->isp_proxies.nelts; i++)
    {
      rc = ngx_regex_exec (ipt[i].regex, &r->uri, NULL, 0);
      if (rc == NGX_REGEX_NO_MATCHED)
    {
      continue;
    }

      pc1 =
    ngx_http_parse_multi_header_lines (&r->headers_in.cookies,
                       &ipmcf->isp_rhost_cookie_name,
                       &isp_rhost_cookie);
      pc2 =
    ngx_http_parse_multi_header_lines (&r->headers_in.cookies,
                       &ipmcf->isp_taddr_cookie_name,
                       &isp_taddr_cookie);

      sin = (struct sockaddr_in *) (r->connection->sockaddr);
      ip = ntohl (sin->sin_addr.s_addr);
      pt = inet_ntoa (sin->sin_addr);
      if (pt == NULL)
    {
      ngx_log_error (NGX_LOG_ERR,
             r->connection->log, 0, "inet_ntoa get taddr error");
      return NGX_DECLINED;
    }

      taddr_len = strlen (pt);
      taddr = ngx_pcalloc (r->pool, taddr_len * sizeof (u_char));
      if (taddr == NULL)
    {
      ngx_log_error (NGX_LOG_ERR,
             r->connection->log,
             0, "ngx_pcalloc no args taddr error");

      return NGX_DECLINED;
    }

      p = ngx_copy (taddr, pt, taddr_len);

      cmp_r1 =
    ngx_strncmp (isp_rhost_cookie.data, r->headers_in.host->value.data,
             isp_rhost_cookie.len);
      cmp_r2 =
    ngx_strncmp (isp_taddr_cookie.data, taddr, isp_taddr_cookie.len);
      if (pc1 != NGX_DECLINED && cmp_r1 == 0 && pc2 != NGX_DECLINED
      && cmp_r2 == 0)
    {
      return NGX_DECLINED;
    }

      ip_pos = ngx_isp_store_binary_search (ipmcf->isp_store, ip);
      if (ip_pos == -1)
    {
      return NGX_DECLINED;
    }

      ist = ipmcf->isp_store.elts;
      idt = ipmcf->isp_domains.elts;

      for (m = 0; m < ipmcf->isp_domains.nelts; m++)
    {
      if (ngx_strcmp (idt[m].isp, ist[ip_pos].isp) == 0)
        {
          rc = ngx_regex_exec (idt[m].regex, &r->headers_in.host->value,
                   NULL, 0);
          if (rc >= 0)
        {
          if (ipmcf->isp_cookies_expires)
            {
              cookie_expires_len =
            sizeof (ngx_http_cookies_expires) - 1;
            }

          if (pc1 == NGX_DECLINED || cmp_r1 != 0)
            {

              u_isp_rhost_cookie = ngx_pcalloc (r->pool,
                            (ipmcf->isp_rhost_cookie_name.len
                             + 1 +
                             r->headers_in.
                             host->value.len +
                             cookie_expires_len +
                             1 +
                             HTTP_DOMAIN_COOKIE_NAME_LEN
                             + 1 +
                             idt[m].domain.len +
                             1) *
                            sizeof (u_char));
              if (u_isp_rhost_cookie == NULL)
            {
              ngx_log_error (NGX_LOG_ERR,
                     r->connection->log,
                     0,
                     "ngx_pcalloc no args u_isp_rhost_cookie error");

              return NGX_DECLINED;
            }

              p = ngx_copy (u_isp_rhost_cookie,
                    ipmcf->isp_rhost_cookie_name.data,
                    ipmcf->isp_rhost_cookie_name.len);
              *p++ = '=';

              p = ngx_copy (p, r->headers_in.host->value.data,
                    r->headers_in.host->value.len);

              if (ipmcf->isp_cookies_expires ==
              HTTP_COOKIES_MAX_EXPIRES)
            {
              p =
                ngx_copy (p, ngx_http_cookies_expires,
                      cookie_expires_len);
            }
              else if (ipmcf->isp_cookies_expires)
            {
              p =
                ngx_copy (p, ngx_http_cookies_expires,
                      sizeof ("; expires=") - 1);
              p =
                ngx_http_cookie_time (p,
                          ngx_time () +
                          ipmcf->isp_cookies_expires);
            }

              *p++ = ';';

              p = ngx_copy (p, ngx_http_domain_cookie_name,
                    HTTP_DOMAIN_COOKIE_NAME_LEN);
              *p++ = '=';

              p = ngx_copy (p, idt[m].domain.data, idt[m].domain.len);
              *p++ = '\0';

              ngx_http_write_isp_cookie (r, u_isp_rhost_cookie);
            }

          if (pc2 == NGX_DECLINED || cmp_r2 != 0)
            {
              u_isp_taddr_cookie = ngx_pcalloc (r->pool,
                            (ipmcf->isp_taddr_cookie_name.len
                             + 1 + taddr_len +
                             cookie_expires_len +
                             1 +
                             HTTP_DOMAIN_COOKIE_NAME_LEN
                             + 1 +
                             idt[m].domain.len +
                             1) *
                            sizeof (u_char));
              if (u_isp_taddr_cookie == NULL)
            {
              ngx_log_error (NGX_LOG_ERR,
                     r->connection->log,
                     0,
                     "ngx_pcalloc no args u_isp_taddr_cookie error");

              return NGX_DECLINED;
            }

              p =
            ngx_copy (u_isp_taddr_cookie,
                  ipmcf->isp_taddr_cookie_name.data,
                  ipmcf->isp_taddr_cookie_name.len);
              *p++ = '=';

              p = ngx_copy (p, taddr, taddr_len);

              if (ipmcf->isp_cookies_expires ==
              HTTP_COOKIES_MAX_EXPIRES)
            {
              p =
                ngx_copy (p, ngx_http_cookies_expires,
                      cookie_expires_len);
            }
              else if (ipmcf->isp_cookies_expires)
            {
              p =
                ngx_copy (p, ngx_http_cookies_expires,
                      sizeof ("; expires=") - 1);
              p =
                ngx_http_cookie_time (p,
                          ngx_time () +
                          ipmcf->isp_cookies_expires);
            }

              *p++ = ';';

              p =
            ngx_copy (p, ngx_http_domain_cookie_name,
                  HTTP_DOMAIN_COOKIE_NAME_LEN);
              *p++ = '=';

              p = ngx_copy (p, idt[m].domain.data, idt[m].domain.len);
              *p++ = '\0';

              ngx_http_write_isp_cookie (r, u_isp_taddr_cookie);
            }

          return NGX_DECLINED;
        }
          else
        {
          ssl = r->connection->ssl == NULL ? 0 : 1;
          con_schema_len = ssl ? HTTPS_SCHEMA_LEN : HTTP_SCHEMA_LEN;

          if (r->args.data == NULL)
            {
              u_relative_uri =
            ngx_pcalloc (r->pool,
                     (r->uri.len + 1) * sizeof (u_char));
              if (u_relative_uri == NULL)
            {
              ngx_log_error (NGX_LOG_ERR,
                     r->connection->log,
                     0,
                     "ngx_pcalloc no args u_relative_uri error");

              return NGX_DECLINED;
            }

              p = ngx_copy (u_relative_uri, r->uri.data, r->uri.len);
              *p++ = '\0';
            }
          else
            {
              u_relative_uri =
            ngx_pcalloc (r->pool,
                     (r->uri.len + r->args.len +
                      2) * sizeof (u_char));
              if (u_relative_uri == NULL)
            {
              ngx_log_error (NGX_LOG_ERR,
                     r->connection->log,
                     0,
                     "ngx_pcalloc with args u_relative_uri error");

              return NGX_DECLINED;
            }

              p = ngx_copy (u_relative_uri, r->uri.data, r->uri.len);
              *p++ = '?';

              p = ngx_copy (p, r->args.data, r->args.len);
              *p++ = '\0';
            }

          str_relative_uri_len = ngx_strlen (u_relative_uri);
          str_uri_len =
            con_schema_len + idt[m].replace.len +
            str_relative_uri_len;
          u_uri =
            ngx_pcalloc (r->pool, str_uri_len * sizeof (u_char));
          if (u_uri == NULL)
            {
              ngx_log_error (NGX_LOG_ERR,
                     r->connection->log,
                     0, "ngx_pcalloc no args u_uri error");
              return NGX_DECLINED;
            }

          p =
            (ssl ? ngx_copy (u_uri, ngx_https_schema, con_schema_len)
             : ngx_copy (u_uri, ngx_http_schema, con_schema_len));
          p = ngx_copy (p, idt[m].replace.data, idt[m].replace.len);
          p = ngx_copy (p, u_relative_uri, str_relative_uri_len);

          str_uri.len = str_uri_len;
          str_uri.data = u_uri;

          return ngx_cross_isp_redirect (r, str_uri);
        }
        }
    }
    }

  return NGX_DECLINED;
}

static long
ngx_isp_store_binary_search (ngx_array_t data, ngx_uint_t ip)
{
  long low = -1;
  long high = data.nelts;
  ngx_uint_t mid;
  ngx_isp_store_t *ist = data.elts;

  while (high - low > 1)
    {
      mid = (low + high) >> 1;

      if (ist[mid].ip_start > ip)
    {
      high = mid;
    }
      else
    {
      low = mid;
    }
    }

  if ((low == -1) || !(ist[low].ip_start <= ip && ip <= ist[low].ip_end))
    {
      return -1;
    }
  else
    {
      return low;
    }
}

static ngx_int_t
ngx_cross_isp_redirect (ngx_http_request_t * r, ngx_str_t url)
{
  ngx_buf_t *b;
  ngx_chain_t out;
  size_t ngx_http_302_page_len = ngx_strlen (ngx_http_302_page);

  r->headers_out.content_type.len = sizeof ("text/html") - 1;
  r->headers_out.content_type.data = (u_char *) "text/html";

  b = ngx_pcalloc (r->pool, sizeof (ngx_buf_t));

  out.buf = b;
  out.next = NULL;

  b->pos = ngx_http_302_page;
  b->last = ngx_http_302_page + ngx_http_302_page_len;
  b->memory = 1;
  b->last_buf = 1;

  r->headers_out.status = NGX_HTTP_MOVED_TEMPORARILY;
  r->headers_out.location = ngx_list_push (&r->headers_out.headers);
  r->headers_out.location->hash = 1;
  ngx_str_set (&r->headers_out.location->key, "Location");
  r->headers_out.location->value = url;
  r->headers_out.content_length_n = ngx_http_302_page_len;

  ngx_http_send_header (r);

  return ngx_http_write_filter (r, &out);
}

static void
ngx_http_write_isp_cookie (ngx_http_request_t * r, u_char * cookie)
{
  ngx_table_elt_t *set_cookie;

  set_cookie = ngx_list_push (&r->headers_out.headers);
  if (set_cookie == NULL)
    {
      return;
    }

  set_cookie->hash = 1;
  set_cookie->key.len = sizeof ("Set-Cookie") - 1;
  set_cookie->key.data = (u_char *) "Set-Cookie";
  set_cookie->value.len = ngx_strlen (cookie);
  set_cookie->value.data = cookie;
}
nginx cross isp module将会开源在http://code.google.com/p/easy-net/,后期将会对现有功能进行改进。

原文地址:https://www.cnblogs.com/TerryLiang/p/2028685.html