static char *
ngx_http_lua_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
{
    ngx_http_lua_srv_conf_t *prev = parent;
    ngx_http_lua_srv_conf_t *conf = child;
    ngx_http_ssl_srv_conf_t *sscf;

    dd("merge srv conf");

    if (conf->ssl_cert_src.len == 0) {
        conf->ssl_cert_src = prev->ssl_cert_src;
        conf->ssl_cert_handler = prev->ssl_cert_handler;
    }

    if (conf->ssl_cert_src.len) {
        sscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_ssl_module);
        if (sscf == NULL || sscf->ssl.ctx == NULL) {
            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                          "no ssl configured for the server");

            return NGX_CONF_ERROR;
        }

        SSL_CTX_set_cert_cb(sscf->ssl.ctx, ngx_http_lua_ssl_cert_handler, NULL);
    }

    return NGX_CONF_OK;
}
static char *
ngx_http_upstream_keepalive_timeout(ngx_conf_t *cf, ngx_command_t *cmd,
    void *conf)
{
    ngx_http_upstream_srv_conf_t            *uscf;
    ngx_http_upstream_keepalive_srv_conf_t  *kcf;

    ngx_str_t   *value;
    ngx_msec_t   timeout;

    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

    kcf = ngx_http_conf_upstream_srv_conf(uscf,
                                          ngx_http_upstream_keepalive_module);

    if (kcf->keepalive_timeout != NGX_CONF_UNSET_MSEC) {
        return "is duplicate";
    }

    value = cf->args->elts;

    timeout = ngx_parse_time(&value[1], 0);
    if (timeout == (ngx_msec_t) NGX_ERROR) {
        return "invalid value";
    }

    kcf->keepalive_timeout = timeout;

    return NGX_CONF_OK;
}
static char *
ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_upstream_srv_conf_t            *uscf;
    ngx_http_upstream_keepalive_srv_conf_t  *kcf;

    ngx_int_t    n;
    ngx_str_t   *value;
    ngx_uint_t   i;

    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

    kcf = ngx_http_conf_upstream_srv_conf(uscf,
                                          ngx_http_upstream_keepalive_module);

    if (kcf->original_init_upstream) {
        return "is duplicate";
    }

    kcf->original_init_upstream = uscf->peer.init_upstream
                                  ? uscf->peer.init_upstream
                                  : ngx_http_upstream_init_round_robin;

    uscf->peer.init_upstream = ngx_http_upstream_init_keepalive;

    /* read options */

    value = cf->args->elts;

    n = ngx_atoi(value[1].data, value[1].len);

    if (n == NGX_ERROR || n == 0) {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                           "invalid value \"%V\" in \"%V\" directive",
                           &value[1], &cmd->name);
        return NGX_CONF_ERROR;
    }

    kcf->max_cached = n;

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

        if (ngx_strcmp(value[i].data, "single") == 0) {
            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                               "the \"single\" parameter is deprecated");
            continue;
        }

        goto invalid;
    }

    return NGX_CONF_OK;

invalid:

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                       "invalid parameter \"%V\"", &value[i]);

    return NGX_CONF_ERROR;
}
static char *ngx_http_upstream_q_chash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_upstream_srv_conf_t            *uscf;
    ngx_http_upstream_q_chash_srv_conf_t    *uchscf;
    ngx_str_t                               *value;
    ngx_http_script_compile_t               sc;

    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
    uchscf = ngx_http_conf_upstream_srv_conf(uscf, ngx_http_upstream_q_chash_module);

    value = cf->args->elts;

    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

    sc.cf = cf;
    sc.source = &value[1];
    sc.lengths = &uchscf->lengths;
    sc.values = &uchscf->values;
    sc.complete_lengths = 1;
    sc.complete_values = 1;

    if (ngx_http_script_compile(&sc) != NGX_OK) {
        return NGX_CONF_ERROR;
    }

    uscf->peer.init_upstream = ngx_http_upstream_init_q_chash;
    uscf->flags = NGX_HTTP_UPSTREAM_CREATE
        | NGX_HTTP_UPSTREAM_WEIGHT
        | NGX_HTTP_UPSTREAM_MAX_FAILS
        | NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
        | NGX_HTTP_UPSTREAM_DOWN
        | NGX_HTTP_UPSTREAM_BACKUP;

    return NGX_CONF_OK;
}
static char *
max_connections_command (ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
  ngx_http_upstream_srv_conf_t *uscf = 
    ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
  max_connections_srv_conf_t *maxconn_cf = 
    ngx_http_conf_upstream_srv_conf(uscf, max_connections_module);
  
  /* 1. set the initialization function */
  maxconn_cf->original_init_upstream = uscf->peer.init_upstream
                                  ? uscf->peer.init_upstream
                                  : ngx_http_upstream_init_round_robin;
  uscf->peer.init_upstream = max_connections_init;

  /* 2. set the number of max_connections */
  ngx_str_t *value = cf->args->elts;
  ngx_int_t max_connections = ngx_atoi(value[1].data, value[1].len);

  if (max_connections == NGX_ERROR || max_connections == 0) {
    ngx_conf_log_error( NGX_LOG_EMERG
                      , cf
                      , 0
                      , "invalid value \"%V\" in \"%V\" directive"
                      , &value[1]
                      , &cmd->name
                      );
    return NGX_CONF_ERROR;
  }

  maxconn_cf->max_connections = (ngx_uint_t)max_connections;

  return NGX_CONF_OK;
}
static char *ngx_http_upstream_available_capacity(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_upstream_srv_conf_t *uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
    if (uscf->peer.init_upstream) {
        ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "load balancing method redefined");
    }

    uscf->peer.init_upstream = ngx_http_upstream_init_available_capacity;
    return NGX_CONF_OK;
}
static char *
ngx_http_upstream_persistence(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_upstream_srv_conf_t  *uscf;

    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
    if (ngx_http_upstream_persistence_config(&uscf->group, cf) != NGX_CONF_OK) {
        return "config error";
    }

    return NGX_CONF_OK;
}
static char *
ngx_http_lua_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
{
#if (NGX_HTTP_SSL)

    ngx_http_lua_srv_conf_t *prev = parent;
    ngx_http_lua_srv_conf_t *conf = child;
    ngx_http_ssl_srv_conf_t *sscf;

    dd("merge srv conf");

    if (conf->ssl.cert_src.len == 0) {
        conf->ssl.cert_src = prev->ssl.cert_src;
        conf->ssl.cert_src_key = prev->ssl.cert_src_key;
        conf->ssl.cert_handler = prev->ssl.cert_handler;
    }

    if (conf->ssl.cert_src.len) {
        sscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_ssl_module);
        if (sscf == NULL || sscf->ssl.ctx == NULL) {
            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                          "no ssl configured for the server");

            return NGX_CONF_ERROR;
        }

#ifdef LIBRESSL_VERSION_NUMBER

        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                      "LibreSSL does not support ssl_ceritificate_by_lua*");
        return NGX_CONF_ERROR;

#else

#   if OPENSSL_VERSION_NUMBER >= 0x1000205fL

        SSL_CTX_set_cert_cb(sscf->ssl.ctx, ngx_http_lua_ssl_cert_handler, NULL);

#   else

        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                      "OpenSSL too old to support ssl_ceritificate_by_lua*");
        return NGX_CONF_ERROR;

#   endif

#endif
    }

#endif  /* NGX_HTTP_SSL */
    return NGX_CONF_OK;
}
static char *
ngx_http_cloudrouter_cmd_cloud(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
    ngx_http_upstream_srv_conf_t  *uscf;
    ngx_str_t field, *value;

    value = cf->args->elts;
    field = value[1];

    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
    uscf->peer.init_upstream = ngx_http_upstream_init_cloud;
    uscf->flags = NGX_HTTP_UPSTREAM_CREATE | NGX_HTTP_UPSTREAM_BACKUP;

    return NGX_CONF_OK;
}
static char *
ngx_http_upstream_idempotent(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_upstream_srv_conf_t  *uscf;

    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

    uscf->peer.init_upstream = ngx_http_upstream_init_idempotent;

    uscf->flags = NGX_HTTP_UPSTREAM_CREATE
                  |NGX_HTTP_UPSTREAM_MAX_FAILS
                  |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
                  |NGX_HTTP_UPSTREAM_DOWN;

    return NGX_CONF_OK;
}
static char *
ngx_http_multiple_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
{
    ngx_http_multiple_ssl_srv_conf_t *prev = parent;
    ngx_http_multiple_ssl_srv_conf_t *conf = child;
    ngx_http_ssl_srv_conf_t  *sscf;

    ngx_conf_merge_value(conf->multiple_ssl_enable,
                         prev->multiple_ssl_enable, 0);
    ngx_conf_merge_str_value(conf->multiple_ssl_cert_path,
                             prev->multiple_ssl_cert_path, "");

    if (conf->multiple_ssl_enable) {
        sscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_ssl_module);
        if (sscf == NULL || sscf->ssl.ctx == NULL) {
            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                          "multiple ssl no ssl configured for the server");

            return NGX_CONF_ERROR;
        }

        if (conf->multiple_ssl_cert_path.len <= 1) {
            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                          "multiple ssl no cert path for the server");

            return NGX_CONF_ERROR;
        }

        ngx_log_error(NGX_LOG_INFO, cf->log, 0, "multiple ssl enable ON");

#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
        if (SSL_CTX_set_tlsext_servername_callback(sscf->ssl.ctx,
            ngx_http_multiple_ssl_cert_handler) == 0)
        {
            ngx_log_error(NGX_LOG_WARN, cf->log, 0,
                "The SNI is not available, multiple ssl ignore.");
        }
#endif

    }

    return NGX_CONF_OK;
}
/* TODO same as above */
static char *
max_connections_max_queue_length_command (ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
  ngx_http_upstream_srv_conf_t *uscf = 
    ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

  max_connections_srv_conf_t *maxconn_cf = 
    ngx_http_conf_upstream_srv_conf(uscf, max_connections_module);

  ngx_str_t *value = cf->args->elts;    
  ngx_int_t n = ngx_atoi(value[1].data, value[1].len);
  if (n == NGX_ERROR) {
    return "invalid number";        
  }

  maxconn_cf->max_queue_length = n;

  return NGX_CONF_OK;
}
static char *
ngx_http_google_inject_subs_domain(ngx_conf_t * cf)
{
  ngx_http_google_loc_conf_t * glcf;
  glcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_google_filter_module);
  
  ngx_http_core_srv_conf_t  *cscf;
  cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module);
  
  ngx_uint_t i, len = 512;
  char * sns_htp, * sns_ssl;
  ngx_http_server_name_t * sns = cscf->server_names.elts, * sn;
  
  for (i = 0; i < cscf->server_names.nelts; i++)
  {
    sn = sns + i;
    if (!sn->name.len) continue;
    
    sns_htp = ngx_pcalloc(cf->pool, len + 1);
    if (!sns_htp) return NGX_CONF_ERROR;
    
    sns_ssl = ngx_pcalloc(cf->pool, len + 1);
    if (!sns_ssl) return NGX_CONF_ERROR;
    
    ngx_snprintf((u_char *)sns_htp, len, "http://%V",  &sn->name);
    ngx_snprintf((u_char *)sns_ssl, len, "https://%V", &sn->name);
    
    if (glcf->ssl) {
      if (ngx_http_google_inject_subs_args(cf,
                                           "subs_filter", 2,
                                           sns_htp,
                                           sns_ssl)) return NGX_CONF_ERROR;
    } else {
      if (ngx_http_google_inject_subs_args(cf,
                                           "subs_filter", 2,
                                           sns_ssl,
                                           sns_htp)) return NGX_CONF_ERROR;
    }
  }
  
  return NGX_CONF_OK;
}
//keepalive connections
static char *
ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_upstream_srv_conf_t            *uscf;
    ngx_http_upstream_keepalive_srv_conf_t  *kcf = conf;

    ngx_int_t    n;
    ngx_str_t   *value;

    if (kcf->max_cached) {
        return "is duplicate";
    }

    /* read options */

    value = cf->args->elts;

    n = ngx_atoi(value[1].data, value[1].len);

    if (n == NGX_ERROR || n == 0) {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                           "invalid value \"%V\" in \"%V\" directive",
                           &value[1], &cmd->name);
        return NGX_CONF_ERROR;
    }

    kcf->max_cached = n;

    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

    /*
    When using load balancer methods other than the default round-robin method, it is necessary to activate them before the keepalive directive.
     */
    /* 保存原来的初始化upstream的钩子,并设置新的钩子 */ //这个是赋值均衡算法的一些初始化钩子用original_init_upstream保存
    kcf->original_init_upstream = uscf->peer.init_upstream
                                  ? uscf->peer.init_upstream
                                  : ngx_http_upstream_init_round_robin;

    uscf->peer.init_upstream = ngx_http_upstream_init_keepalive; //原始的负债均衡钩子保存在kcf->original_init_upstream

    return NGX_CONF_OK;
}
static char * ngx_http_peer_selector(ngx_conf_t * cf, ngx_command_t * cmd, void * conf)
{
    ngx_http_upstream_srv_conf_t  *uscf;

    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

    if (uscf->peer.init_upstream)
    {
        ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "load balancing method redefined");
    }

    uscf->peer.init_upstream = ngx_http_peer_selector_init;

    uscf->flags = NGX_HTTP_UPSTREAM_CREATE
                  |NGX_HTTP_UPSTREAM_WEIGHT
                  |NGX_HTTP_UPSTREAM_MAX_FAILS
                  |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
                  |NGX_HTTP_UPSTREAM_DOWN;

    return NGX_CONF_OK;
}
static char *
ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_upstream_srv_conf_t            *uscf;
    ngx_http_upstream_keepalive_srv_conf_t  *kcf = conf;

    ngx_int_t    n;
    ngx_str_t   *value;

    if (kcf->max_cached) {
        return "is duplicate";
    }

    /* read options */

    value = cf->args->elts;

    n = ngx_atoi(value[1].data, value[1].len);

    if (n == NGX_ERROR || n == 0) {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                           "invalid value \"%V\" in \"%V\" directive",
                           &value[1], &cmd->name);
        return NGX_CONF_ERROR;
    }

    kcf->max_cached = n;

    /* init upstream handler */

    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

    kcf->original_init_upstream = uscf->peer.init_upstream
                                  ? uscf->peer.init_upstream
                                  : ngx_http_upstream_init_round_robin;

    uscf->peer.init_upstream = ngx_http_upstream_init_keepalive;

    return NGX_CONF_OK;
}
/* TODO This function is probably not neccesary. Nginx provides a means of
 * easily setting scalar time values with ngx_conf_set_msec_slot() in the
 * ngx_command_t structure. I couldn't manage to make it work, not knowing
 * what I should be using for the two offset parameters. 
 */ 
static char *
max_connections_queue_timeout_command (ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
  ngx_http_upstream_srv_conf_t *uscf = 
    ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

  max_connections_srv_conf_t *maxconn_cf = 
    ngx_http_conf_upstream_srv_conf(uscf, max_connections_module);

  ngx_str_t        *value; 

  value = cf->args->elts;    

  ngx_msec_t ms = ngx_parse_time(&value[1], 0); 
  if (ms == (ngx_msec_t) NGX_ERROR) {
      return "invalid value";
  }

  maxconn_cf->queue_timeout = ms;

  return NGX_CONF_OK;
}
static char *
ngx_http_upstream_consistent(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_upstream_srv_conf_t  *uscf;
    ngx_http_upstream_consistent_data_t *ucd;

    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

    uscf->peer.init_upstream = ngx_http_upstream_init_consistent;

    uscf->flags = (NGX_HTTP_UPSTREAM_CREATE
                   | NGX_HTTP_UPSTREAM_WEIGHT);

    ucd = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_consistent_data_t));

    if (ucd == NULL) {
        return NGX_CONF_ERROR;
    }

    uscf->peer.data = ucd;

    return NGX_CONF_OK;
}
static char *
ngx_http_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_referer_conf_t  *rlcf = conf;

    u_char                    *p;
    ngx_str_t                 *value, uri, name;
    ngx_uint_t                 i, n;
    ngx_http_variable_t       *var;
    ngx_http_server_name_t    *sn;
    ngx_http_core_srv_conf_t  *cscf;

    ngx_str_set(&name, "invalid_referer");

    var = ngx_http_add_variable(cf, &name,
                                NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOHASH);
    if (var == NULL) {
        return NGX_CONF_ERROR;
    }

    var->get_handler = ngx_http_referer_variable;

    if (rlcf->keys == NULL) {
        rlcf->keys = ngx_pcalloc(cf->temp_pool, sizeof(ngx_hash_keys_arrays_t));
        if (rlcf->keys == NULL) {
            return NGX_CONF_ERROR;
        }

        rlcf->keys->pool = cf->pool;
        rlcf->keys->temp_pool = cf->pool;

        if (ngx_hash_keys_array_init(rlcf->keys, NGX_HASH_SMALL) != NGX_OK) {
            return NGX_CONF_ERROR;
        }
    }

    value = cf->args->elts;

    for (i = 1; i < cf->args->nelts; i++) {
        if (value[i].len == 0) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "invalid referer \"%V\"", &value[i]);
            return NGX_CONF_ERROR;
        }

        if (ngx_strcmp(value[i].data, "none") == 0) {
            rlcf->no_referer = 1;
            continue;
        }

        if (ngx_strcmp(value[i].data, "blocked") == 0) {
            rlcf->blocked_referer = 1;
            continue;
        }

        ngx_str_null(&uri);

        if (ngx_strcmp(value[i].data, "server_names") == 0) {

            cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module);

            sn = cscf->server_names.elts;
            for (n = 0; n < cscf->server_names.nelts; n++) {

#if (NGX_PCRE)
                if (sn[n].regex) {

                    if (ngx_http_add_regex_referer(cf, rlcf, &sn[n].name,
                                                   sn[n].regex->regex)
                        != NGX_OK)
                    {
                        return NGX_CONF_ERROR;
                    }

                    continue;
                }
#endif

                if (ngx_http_add_referer(cf, rlcf->keys, &sn[n].name, &uri)
                    != NGX_OK)
                {
                    return NGX_CONF_ERROR;
                }
            }

            continue;
        }

        if (value[i].data[0] == '~') {
            if (ngx_http_add_regex_referer(cf, rlcf, &value[i], NULL) != NGX_OK)
            {
                return NGX_CONF_ERROR;
            }

            continue;
        }

        p = (u_char *) ngx_strchr(value[i].data, '/');

        if (p) {
            uri.len = (value[i].data + value[i].len) - p;
            uri.data = p;
            value[i].len = p - value[i].data;
        }

        if (ngx_http_add_referer(cf, rlcf->keys, &value[i], &uri) != NGX_OK) {
            return NGX_CONF_ERROR;
        }
    }

    return NGX_CONF_OK;
}
static char *
ngx_http_upstream_session_sticky(ngx_conf_t *cf, ngx_command_t *cmd,
    void *conf)
{
    ngx_int_t                        rc;
    ngx_uint_t                       i;
    ngx_str_t                       *value;
    ngx_http_upstream_srv_conf_t    *uscf;
    ngx_http_upstream_ss_srv_conf_t *sscf = conf;

    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

    uscf->peer.init_upstream = ngx_http_upstream_session_sticky_init_upstream;

    uscf->flags = NGX_HTTP_UPSTREAM_CREATE
                | NGX_HTTP_UPSTREAM_WEIGHT
                | NGX_HTTP_UPSTREAM_MAX_FAILS
                | NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
                | NGX_HTTP_UPSTREAM_DOWN;

    value = cf->args->elts;
    for (i = 1; i < cf->args->nelts; i++) {
        if (ngx_strncmp(value[i].data, "cookie=", 7) == 0){
            sscf->cookie.data = value[i].data + 7;
            sscf->cookie.len = value[i].len - 7;
            if (sscf->cookie.len == 0) {
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid cookie");
                return NGX_CONF_ERROR;
            }
            continue;
        }

        if (ngx_strncmp(value[i].data, "domain=", 7) == 0) {
            sscf->domain.data = value[i].data + 7;
            sscf->domain.len = value[i].len - 7;
            if (sscf->domain.len == 0) {
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid domain");
                return NGX_CONF_ERROR;
            }
            continue;
        }

        if (ngx_strncmp(value[i].data, "path=", 5) == 0) {
            sscf->path.data = value[i].data + 5;
            sscf->path.len = value[i].len - 5;
            if (sscf->path.len == 0) {
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid path");
                return NGX_CONF_ERROR;
            }
            continue;
        }

        if (ngx_strncmp(value[i].data, "maxage=", 7) == 0) {
            sscf->maxage.data = value[i].data + 7;
            sscf->maxage.len = value[i].len - 7;
            rc = ngx_atoi(sscf->maxage.data, sscf->maxage.len);
            if (rc == NGX_ERROR) {
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid maxage");
                return NGX_CONF_ERROR;
            }
            continue;
        }

        if (ngx_strncmp(value[i].data, "maxidle=", 8) == 0) {
            sscf->maxidle = ngx_atotm(value[i].data + 8, value[i].len - 8);
            if (sscf->maxidle <= NGX_ERROR) {
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid maxidle");
                return NGX_CONF_ERROR;
            }

            if (sscf->maxlife == NGX_CONF_UNSET) {
                sscf->maxlife = NGX_MAX_INT32_VALUE;
            }

            continue;
        }

        if (ngx_strncmp(value[i].data, "maxlife=", 8) == 0) {
            sscf->maxlife = ngx_atotm(value[i].data + 8, value[i].len - 8);
            if (sscf->maxlife <= NGX_ERROR) {
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid maxlife");
                return NGX_CONF_ERROR;
            }

            if (sscf->maxidle == NGX_CONF_UNSET) {
                sscf->maxidle = NGX_MAX_INT32_VALUE;
            }
            continue;
        }

        if (ngx_strncmp(value[i].data, "mode=", 5) == 0) {
            value[i].data = value[i].data + 5;
            value[i].len = value[i].len - 5;

            if (ngx_strncmp(value[i].data, "insert", 6) == 0) {
                sscf->flag |= NGX_HTTP_SESSION_STICKY_INSERT;

            } else if (ngx_strncmp(value[i].data, "prefix", 6) == 0) {
                sscf->flag |= NGX_HTTP_SESSION_STICKY_PREFIX;
                sscf->flag &= (~NGX_HTTP_SESSION_STICKY_INSERT);

            } else if (ngx_strncmp(value[i].data, "rewrite", 7) == 0) {
                sscf->flag |= NGX_HTTP_SESSION_STICKY_REWRITE;
                sscf->flag &= (~NGX_HTTP_SESSION_STICKY_INDIRECT);
                sscf->flag &= (~NGX_HTTP_SESSION_STICKY_INSERT);

            } else {
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid mode");
                return NGX_CONF_ERROR;
            }

            continue;
        }

        if (ngx_strncmp(value[i].data, "option=", 7) == 0) {
            value[i].data = value[i].data + 7;
            value[i].len = value[i].len - 7;

            if (ngx_strncmp(value[i].data, "indirect", 8) == 0) {
                sscf->flag |= NGX_HTTP_SESSION_STICKY_INDIRECT;

            } else if (ngx_strncmp(value[i].data, "direct", 6) == 0) {
                sscf->flag &= ~NGX_HTTP_SESSION_STICKY_INDIRECT;

            } else {
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid option");
                return NGX_CONF_ERROR;
            }

            continue;
        }

        if (ngx_strncmp(value[i].data, "fallback=", 9) == 0) {
            value[i].data = value[i].data + 9;
            value[i].len = value[i].len - 9;

            if (ngx_strncmp(value[i].data, "on", 2) == 0) {
                sscf->flag |= NGX_HTTP_SESSION_STICKY_FALLBACK_ON;

            } else if (ngx_strncmp(value[i].data, "off", 3) == 0) {
                sscf->flag |= NGX_HTTP_SESSION_STICKY_FALLBACK_OFF;

            } else {
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid fallback");
                return NGX_CONF_ERROR;
            }

            continue;
        }

        if (ngx_strncmp(value[i].data, "hash=", 5) == 0) {
            value[i].data = value[i].data + 5;
            value[i].len = value[i].len - 5;

            if (ngx_strncmp(value[i].data, "plain", 5) == 0) {
                sscf->flag = (sscf->flag & (~NGX_HTTP_SESSION_STICKY_MD5))
                           | NGX_HTTP_SESSION_STICKY_PLAIN;

            } else if (ngx_strncmp(value[i].data, "md5", 4) == 0) {
                sscf->flag = (sscf->flag & (~NGX_HTTP_SESSION_STICKY_PLAIN))
                           | NGX_HTTP_SESSION_STICKY_MD5;

            } else {
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid hash mode");
                return NGX_CONF_ERROR;
            }

            continue;
        }

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid argument");
        return NGX_CONF_ERROR;
    }

    return NGX_CONF_OK;
}
static char *
ngx_http_eval_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_eval_loc_conf_t  *ecf, *pecf = conf;

    char                      *rv;
    void                      *mconf;
    ngx_str_t                  name;
    ngx_uint_t                 i;
    ngx_conf_t                 save;
    ngx_http_module_t         *module;
    ngx_http_conf_ctx_t       *ctx, *pctx;
    ngx_http_core_loc_conf_t  *clcf, *pclcf, *rclcf;
    ngx_http_core_srv_conf_t  *cscf;

    if(ngx_http_eval_add_variables(cf, cmd, conf) != NGX_CONF_OK) {
        return NGX_CONF_ERROR;
    }

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

    pctx = cf->ctx;
    ctx->main_conf = pctx->main_conf;
    ctx->srv_conf = pctx->srv_conf;

    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
    if (ctx->loc_conf == NULL) {
        return NGX_CONF_ERROR;
    }

    for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->type != NGX_HTTP_MODULE) {
            continue;
        }

        module = ngx_modules[i]->ctx;

        if (module->create_loc_conf) {

            mconf = module->create_loc_conf(cf);
            if (mconf == NULL) {
                 return NGX_CONF_ERROR;
            }

            ctx->loc_conf[ngx_modules[i]->ctx_index] = mconf;
        }
    }

    ecf = ctx->loc_conf[ngx_http_eval_module.ctx_index];

    pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index];

    clcf = ctx->loc_conf[ngx_http_core_module.ctx_index];

    name.len = sizeof("/eval_") - 1 + NGX_OFF_T_LEN;

    name.data = ngx_palloc(cf->pool, name.len);

    if(name.data == NULL) {
        return NGX_CONF_ERROR;
    }

    name.len = ngx_sprintf(name.data, "/eval_%O", (off_t)(uintptr_t)clcf) - name.data;

    clcf->loc_conf = ctx->loc_conf;
    clcf->name = name;
    clcf->exact_match = 0;
    clcf->noname = 0;
    clcf->internal = 1;
    clcf->noregex = 1;

    cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module);

    rclcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index];

    if (ngx_http_add_location(cf, &rclcf->locations, clcf) != NGX_OK) {
        return NGX_CONF_ERROR;
    }

    pecf->eval_location = clcf->name;

    save = *cf;
    cf->ctx = ctx;
    cf->cmd_type = NGX_HTTP_LOC_CONF;

    rv = ngx_conf_parse(cf, NULL);

    *cf = save;

    return rv;
}
static char *ngx_http_ssl_ct_merge_srv_conf(ngx_conf_t *cf, void *parent,
    void *child)
{
    /* merge config */
    ngx_ssl_ct_srv_conf_t *prev = parent;
    ngx_ssl_ct_srv_conf_t *conf = child;

    ngx_conf_merge_value(conf->enable, prev->enable, 0);
    ngx_conf_merge_str_value(conf->sct, prev->sct, "");

    /* validate config */
    if (conf->enable)
    {
        if (conf->sct.len == 0)
        {
            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                "no \"ssl_ct_static_scts\" is defined for the \"ssl_ct\""
                "directive");
            return NGX_CONF_ERROR;
        }
    }
    else
    {
        return NGX_CONF_OK;
    }

    /* get ngx_http_ssl_module configuration and check if SSL is enabled */
    ngx_http_ssl_srv_conf_t *ssl_conf = ngx_http_conf_get_module_srv_conf(cf,
        ngx_http_ssl_module);

    if (!ssl_conf->ssl.ctx)
    {
        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
            "\"ssl_ct\" can only be enabled if ssl is enabled");
        return NGX_CONF_ERROR;
    }

    /* read .sct files */
    ngx_ssl_ct_ext *sct_list = ngx_ssl_ct_read_static_scts(cf, &conf->sct);
    if (!sct_list)
    {
        /* ngx_ssl_ct_read_static_scts calls ngx_log_error */
        return NGX_CONF_ERROR;
    }

    /* add OpenSSL TLS extension */
#ifndef OPENSSL_IS_BORINGSSL
    if (SSL_CTX_add_server_custom_ext(ssl_conf->ssl.ctx, NGX_SSL_CT_EXT,
        &ngx_ssl_ct_ext_cb, NULL, sct_list, NULL, NULL) == 0)
    {
        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
            "SSL_CTX_add_server_custom_ext failed");
        ngx_pfree(cf->pool, sct_list);
        return NGX_CONF_ERROR;
    }
#else
    if (SSL_CTX_set_signed_cert_timestamp_list(ssl_conf->ssl.ctx, sct_list->buf,
        sct_list->len) == 0)
    {
        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
            "SSL_CTX_set_signed_cert_timestamp_list failed");
        ngx_pfree(cf->pool, sct_list);
        return NGX_CONF_ERROR;
    }
#endif

    return NGX_CONF_OK;
}
static char *
ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
{
    ngx_http_ssl_srv_conf_t *prev = parent;
    ngx_http_ssl_srv_conf_t *conf = child;

    ngx_pool_cleanup_t                 *cln;
    ngx_http_core_srv_conf_t           *cscf;
    ngx_http_ssl_pphrase_dialog_conf_t  dialog;

    if (conf->enable == NGX_CONF_UNSET) {
        if (prev->enable == NGX_CONF_UNSET) {
            conf->enable = 0;

        } else {
            conf->enable = prev->enable;
            conf->file = prev->file;
            conf->line = prev->line;
        }
    }

    ngx_conf_merge_value(conf->session_timeout,
                         prev->session_timeout, 300);

    ngx_conf_merge_value(conf->prefer_server_ciphers,
                         prev->prefer_server_ciphers, 0);

    ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols,
                         (NGX_CONF_BITMASK_SET|NGX_SSL_SSLv3|NGX_SSL_TLSv1
                          |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));

    ngx_conf_merge_uint_value(conf->verify, prev->verify, 0);
    ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1);

    ngx_conf_merge_str_value(conf->certificate, prev->certificate, "");
    ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, "");

    ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");

    ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate,
                         "");
    ngx_conf_merge_str_value(conf->crl, prev->crl, "");

    ngx_conf_merge_str_value(conf->ecdh_curve, prev->ecdh_curve,
                         NGX_DEFAULT_ECDH_CURVE);

    ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);

    ngx_conf_merge_str_value(conf->pass_phrase_dialog, prev->pass_phrase_dialog,
                         "builtin");


    conf->ssl.log = cf->log;

    if (conf->enable) {

        if (conf->certificate.len == 0) {
            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                          "no \"ssl_certificate\" is defined for "
                          "the \"ssl\" directive in %s:%ui",
                          conf->file, conf->line);
            return NGX_CONF_ERROR;
        }

        if (conf->certificate_key.len == 0) {
            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                          "no \"ssl_certificate_key\" is defined for "
                          "the \"ssl\" directive in %s:%ui",
                          conf->file, conf->line);
            return NGX_CONF_ERROR;
        }

    } else {

        if (conf->certificate.len == 0) {
            return NGX_CONF_OK;
        }

        if (conf->certificate_key.len == 0) {
            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                          "no \"ssl_certificate_key\" is defined "
                          "for certificate \"%V\"", &conf->certificate);
            return NGX_CONF_ERROR;
        }
    }

    if (ngx_ssl_create(&conf->ssl, conf->protocols, conf) != NGX_OK) {
        return NGX_CONF_ERROR;
    }

#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME

    if (SSL_CTX_set_tlsext_servername_callback(conf->ssl.ctx,
                                               ngx_http_ssl_servername)
        == 0)
    {
        ngx_log_error(NGX_LOG_WARN, cf->log, 0,
            "nginx was built with SNI support, however, now it is linked "
            "dynamically to an OpenSSL library which has no tlsext support, "
            "therefore SNI is not available");
    }

#endif

    cln = ngx_pool_cleanup_add(cf->pool, 0);
    if (cln == NULL) {
        return NGX_CONF_ERROR;
    }

    cln->handler = ngx_ssl_cleanup_ctx;
    cln->data = &conf->ssl;

    cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module);
    dialog.ssl = &conf->ssl;
    dialog.type = &conf->pass_phrase_dialog;
    if (cscf->server_name.len != 0) {
        dialog.server_name = &cscf->server_name;
    } else {
        dialog.server_name = &ngx_http_ssl_unknown_server_name;
    }

    if (ngx_ssl_certificate(cf, &conf->ssl, &conf->certificate,
                            &conf->certificate_key, &dialog)
        != NGX_OK)
    {
        return NGX_CONF_ERROR;
    }

    if (SSL_CTX_set_cipher_list(conf->ssl.ctx,
                                (const char *) conf->ciphers.data)
        == 0)
    {
        ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,
                      "SSL_CTX_set_cipher_list(\"%V\") failed",
                      &conf->ciphers);
    }

    if (conf->verify) {

        if (conf->client_certificate.len == 0) {
            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                          "no ssl_client_certificate for ssl_client_verify");
            return NGX_CONF_ERROR;
        }

        if (ngx_ssl_client_certificate(cf, &conf->ssl,
                                       &conf->client_certificate,
                                       conf->verify_depth)
            != NGX_OK)
        {
            return NGX_CONF_ERROR;
        }

        if (ngx_ssl_crl(cf, &conf->ssl, &conf->crl) != NGX_OK) {
            return NGX_CONF_ERROR;
        }
    }

    if (conf->prefer_server_ciphers) {
        SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
    }

    /* a temporary 512-bit RSA key is required for export versions of MSIE */
    SSL_CTX_set_tmp_rsa_callback(conf->ssl.ctx, ngx_ssl_rsa512_key_callback);

    if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) {
        return NGX_CONF_ERROR;
    }

    if (ngx_ssl_ecdh_curve(cf, &conf->ssl, &conf->ecdh_curve) != NGX_OK) {
        return NGX_CONF_ERROR;
    }

    ngx_conf_merge_value(conf->builtin_session_cache,
                         prev->builtin_session_cache, NGX_SSL_NONE_SCACHE);

    if (conf->shm_zone == NULL) {
        conf->shm_zone = prev->shm_zone;
    }

    if (ngx_ssl_session_cache(&conf->ssl, &ngx_http_ssl_sess_id_ctx,
                              conf->builtin_session_cache,
                              conf->shm_zone, conf->session_timeout)
        != NGX_OK)
    {
        return NGX_CONF_ERROR;
    }

    return NGX_CONF_OK;
}
char *
ngx_http_lua_balancer_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
    void *conf)
{
    u_char                      *p;
    u_char                      *name;
    ngx_str_t                   *value;
    ngx_http_lua_srv_conf_t     *lscf = conf;

    ngx_http_upstream_srv_conf_t      *uscf;

    dd("enter");

    /*  must specify a content handler */
    if (cmd->post == NULL) {
        return NGX_CONF_ERROR;
    }

    if (lscf->balancer.handler) {
        return "is duplicate";
    }

    value = cf->args->elts;

    lscf->balancer.handler = (ngx_http_lua_srv_conf_handler_pt) cmd->post;

    if (cmd->post == ngx_http_lua_balancer_handler_file) {
        /* Lua code in an external file */

        name = ngx_http_lua_rebase_path(cf->pool, value[1].data,
                                        value[1].len);
        if (name == NULL) {
            return NGX_CONF_ERROR;
        }

        lscf->balancer.src.data = name;
        lscf->balancer.src.len = ngx_strlen(name);

        p = ngx_palloc(cf->pool, NGX_HTTP_LUA_FILE_KEY_LEN + 1);
        if (p == NULL) {
            return NGX_CONF_ERROR;
        }

        lscf->balancer.src_key = p;

        p = ngx_copy(p, NGX_HTTP_LUA_FILE_TAG, NGX_HTTP_LUA_FILE_TAG_LEN);
        p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len);
        *p = '\0';

    } else {
        /* inlined Lua code */

        lscf->balancer.src = value[1];

        p = ngx_palloc(cf->pool, NGX_HTTP_LUA_INLINE_KEY_LEN + 1);
        if (p == NULL) {
            return NGX_CONF_ERROR;
        }

        lscf->balancer.src_key = p;

        p = ngx_copy(p, NGX_HTTP_LUA_INLINE_TAG, NGX_HTTP_LUA_INLINE_TAG_LEN);
        p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len);
        *p = '\0';
    }

    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

    if (uscf->peer.init_upstream) {
        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                           "load balancing method redefined");
    }

    uscf->peer.init_upstream = ngx_http_lua_balancer_init;

    uscf->flags = NGX_HTTP_UPSTREAM_CREATE
                  |NGX_HTTP_UPSTREAM_WEIGHT
                  |NGX_HTTP_UPSTREAM_MAX_FAILS
                  |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
                  |NGX_HTTP_UPSTREAM_DOWN;

    return NGX_CONF_OK;
}
/**
 * Configuration setup function that installs the content handler.
 *
 * @param cf
 *   Module configuration structure pointer.
 * @param cmd
 *   Module directives structure pointer.
 * @param conf
 *   Module configuration structure pointer.
 * @return string
 *   Status of the configuration setup.
 */
static char *ngx_http_acme(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_ssl_srv_conf_t *sscf; /* pointer to core location configuration */
    int ret;

    // TODO (KK) Pull the different parts out as own methods for readability

    /*
     * TODO (KK) Get the config directory path (e.g. /etc/nginx)
     */


    /*
     * TODO (KK) Init acme dir (mkdirs)
     */


    /*
     * ACME communication - getting a certificate
     */

    if(ngx_http_acme_main(cf, conf) != NGX_CONF_OK) {
        ngx_log_error(NGX_LOG_ERR, cf->log, 0, "Error while gathering certificate from ACME server");
        return NGX_CONF_ERROR;
    }

    /*
     * TODO (KK) Install certificate (right now it just copies an example cert)
     */
    {
        ngx_copy_file_t   cpyf;

        ngx_log_error(NGX_LOG_NOTICE, cf->log, 0, "Installing certificate and key");

        cpyf.size = -1;
        cpyf.buf_size = 0;
        cpyf.access =  NGX_FILE_DEFAULT_ACCESS;
        cpyf.time = -1;
        cpyf.log = cf->log;

        /* Copy certificate */
        ret = ngx_copy_file((u_char *)ACME_DEV_FROM_CERT_PATH, (u_char *)ACME_DEV_CERT_PATH, &cpyf);

        /* Copy private key */
        if(ret == NGX_OK) {
            /* Only 0600 access for private key */
            cpyf.access = NGX_FILE_OWNER_ACCESS;

            ret = ngx_copy_file((u_char *)ACME_DEV_FROM_KEY_PATH, (u_char *)ACME_DEV_KEY_PATH, &cpyf);
        }

        if(ret != NGX_OK) {
            ngx_log_error(NGX_LOG_ERR, cf->log, 0, "Installing the certificate or private key failed");
            return NGX_CONF_ERROR;
        }
    }

    /*
     * Fool the SSL module into using the ACME certificates
     */
    /* Get SSL module configuration */
    sscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_ssl_module);

    // TODO (KK) Report warning when ssl configs are not set (acme w/o ssl activated in the same server context is an error)
    // --> Maybe ignore acme config then and issue a warning

    if(sscf) {
//        ngx_log_error(NGX_LOG_NOTICE, cf->log, 0, "Found SSL certificate path: %s", sscf->certificate.data);

        /* Spoof SSL cert */
        sscf->certificates = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t));
        if (sscf->certificates == NULL) {
            return NGX_CONF_ERROR;
        }
        ((ngx_str_t *) sscf->certificates->elts)[0] = (ngx_str_t) ngx_string(ACME_DIR "/" ACME_LIVE_DIR "/" ACME_DEV_SERVER_NAME "/" ACME_CERT);

        /* Spoof SSL cert key */
        sscf->certificate_keys = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t));
        if (sscf->certificate_keys == NULL) {
            return NGX_CONF_ERROR;
        }
        ((ngx_str_t *) sscf->certificate_keys->elts)[0] = (ngx_str_t) ngx_string(ACME_DIR "/" ACME_LIVE_DIR "/" ACME_DEV_SERVER_NAME "/" ACME_CERT);
    }


    return NGX_CONF_OK;
} /* ngx_http_acme */
// Overwrite the nginx "server" directive based on its
// implementation of "ngx_http_upstream_server" from
// src/http/ngx_http_upstream.c (nginx version 1.7.7), and should be kept in
// sync with nginx's source code. Customizations noted in comments.
// This make possible use the same syntax of nginx comercial version.
static char * ngx_http_upstream_dynamic_server_directive(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) {
    // BEGIN CUSTOMIZATION: differs from default "server" implementation
    ngx_http_upstream_srv_conf_t                  *uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
    ngx_http_upstream_dynamic_server_main_conf_t  *udsmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_upstream_dynamic_servers_module);
    ngx_http_upstream_dynamic_server_conf_t       *dynamic_server = NULL;
    // END CUSTOMIZATION

    time_t                       fail_timeout;
    ngx_str_t                   *value, s;
    ngx_url_t                    u;
    ngx_int_t                    weight, max_fails;
    ngx_uint_t                   i;
    ngx_http_upstream_server_t  *us;

#if nginx_version <= 1007002
    if (uscf->servers == NULL) {
        uscf->servers = ngx_array_create(cf->pool, 4,
                                         sizeof(ngx_http_upstream_server_t));
        if (uscf->servers == NULL) {
            return NGX_CONF_ERROR;
        }
    }
#endif

    us = ngx_array_push(uscf->servers);
    if (us == NULL) {
        return NGX_CONF_ERROR;
    }

    ngx_memzero(us, sizeof(ngx_http_upstream_server_t));

    value = cf->args->elts;

    weight = 1;
    max_fails = 1;
    fail_timeout = 10;

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

        if (ngx_strncmp(value[i].data, "weight=", 7) == 0) {

            if (!(uscf->flags & NGX_HTTP_UPSTREAM_WEIGHT)) {
                goto not_supported;
            }

            weight = ngx_atoi(&value[i].data[7], value[i].len - 7);

            if (weight == NGX_ERROR || weight == 0) {
                goto invalid;
            }

            continue;
        }

        if (ngx_strncmp(value[i].data, "max_fails=", 10) == 0) {

            if (!(uscf->flags & NGX_HTTP_UPSTREAM_MAX_FAILS)) {
                goto not_supported;
            }

            max_fails = ngx_atoi(&value[i].data[10], value[i].len - 10);

            if (max_fails == NGX_ERROR) {
                goto invalid;
            }

            continue;
        }

        if (ngx_strncmp(value[i].data, "fail_timeout=", 13) == 0) {

            if (!(uscf->flags & NGX_HTTP_UPSTREAM_FAIL_TIMEOUT)) {
                goto not_supported;
            }

            s.len = value[i].len - 13;
            s.data = &value[i].data[13];

            fail_timeout = ngx_parse_time(&s, 1);

            if (fail_timeout == (time_t) NGX_ERROR) {
                goto invalid;
            }

            continue;
        }

        if (ngx_strcmp(value[i].data, "backup") == 0) {

            if (!(uscf->flags & NGX_HTTP_UPSTREAM_BACKUP)) {
                goto not_supported;
            }

            us->backup = 1;

            continue;
        }

        if (ngx_strcmp(value[i].data, "down") == 0) {

            if (!(uscf->flags & NGX_HTTP_UPSTREAM_DOWN)) {
                goto not_supported;
            }

            us->down = 1;

            continue;
        }

        // BEGIN CUSTOMIZATION: differs from default "server" implementation
        if (ngx_strcmp(value[i].data, "resolve") == 0) {
            // Determine if the server given is an IP address or a hostname by running
            // through ngx_parse_url with no_resolve enabled. Only if a hostname is given
            // will we add this to the list of dynamic servers that we will resolve again.

            ngx_memzero(&u, sizeof(ngx_url_t));
            u.url = value[1];
            u.default_port = 80;
            u.no_resolve = 1;
            ngx_parse_url(cf->pool, &u);
            if (!u.addrs || !u.addrs[0].sockaddr) {
                dynamic_server = ngx_array_push(&udsmcf->dynamic_servers);
                if (dynamic_server == NULL) {
                    return NGX_CONF_ERROR;
                }

                ngx_memzero(dynamic_server, sizeof(ngx_http_upstream_dynamic_server_conf_t));
                dynamic_server->server = us;
                dynamic_server->upstream_conf = uscf;

                dynamic_server->host = u.host;
                dynamic_server->port = (in_port_t) (u.no_port ? u.default_port : u.port);
            }

            continue;
        }
        // END CUSTOMIZATION

        goto invalid;
    }

    ngx_memzero(&u, sizeof(ngx_url_t));

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

    // BEGIN CUSTOMIZATION: differs from default "server" implementation
    if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
        if (u.err) {
            ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
                               "%s in upstream \"%V\"", u.err, &u.url);
        }

        // If the domain fails to resolve on start up, mark this server as down,
        // and assign a static IP that should never route. This is to account for
        // various things inside nginx that seem to expect a server to always have
        // at least 1 IP.
        us->down = 1;

        u.url = ngx_http_upstream_dynamic_server_null_route;
        u.default_port = u.port;
        u.no_resolve = 1;

        if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
            if (u.err) {
                ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
                                   "%s in upstream \"%V\"", u.err, &u.url);
            }

            return NGX_CONF_ERROR;
        }
    }
    // END CUSTOMIZATION

#if nginx_version >= 1007002
    us->name = u.url;
#endif
    us->addrs = u.addrs;
    us->naddrs = u.naddrs;
    us->weight = weight;
    us->max_fails = max_fails;
    us->fail_timeout = fail_timeout;

    return NGX_CONF_OK;

invalid:

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                       "invalid parameter \"%V\"", &value[i]);

    return NGX_CONF_ERROR;

not_supported:

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                       "balancing method does not support parameter \"%V\"",
                       &value[i]);

    return NGX_CONF_ERROR;
}
static char *ngx_http_upstream_resolveMK(ngx_conf_t *cf, ngx_command_t *cmd,
    void *conf)
{
	ngx_http_upstream_srv_conf_t *uscf;
	ngx_http_upstream_resolveMK_srv_conf_t *urcf;
	ngx_http_upstream_server_t *us;

	time_t interval;
	ngx_str_t *value, domain, s;
	ngx_int_t max_ip;
	ngx_uint_t retry;
	ngx_http_upstream_resolveMK_peer_t *paddr;
	ngx_url_t u;
	ngx_uint_t i;

	interval = 10;
	max_ip = 20;
	retry = 1;
	domain.data = NULL;
	domain.len = 0;
	uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

	/* Just For Padding, upstream { } need it */
	if (uscf->servers == NULL) {
		uscf->servers = ngx_array_create(cf->pool, 1,
		                                 sizeof(ngx_http_upstream_server_t));

		if (uscf->servers == NULL) {
			return NGX_CONF_ERROR;
		}
	}

	us = ngx_array_push(uscf->servers);

	if (us == NULL) {
		return NGX_CONF_ERROR;
	}

	ngx_memzero(us, sizeof(ngx_http_upstream_server_t));
	urcf = ngx_http_conf_upstream_srv_conf(uscf,
	                                       ngx_http_upstream_resolveMK_module);
	uscf->peer.init_upstream = ngx_http_upstream_resolveMK_init;
	value = cf->args->elts;

	if (value[1].len == 0) {
		ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
		                   "domain is not given");

		return NGX_CONF_ERROR;
	}

	domain.data = value[1].data;
	domain.len  = value[1].len;

	if (ngx_strncmp(value[2].data, "service=", 8) != 0) {
		ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
		                   "service is not given");

		return NGX_CONF_ERROR;
	}

	urcf->resolver_service.len = value[2].len - 8;
	urcf->resolver_service.data = &value[2].data[8];

	for (i = 3; i < cf->args->nelts; i++) {

		if (ngx_strncmp(value[i].data, "interval=", 9) == 0) {
			s.len = value[i].len - 9;
			s.data = &value[i].data[9];
			interval = ngx_parse_time(&s, 1);

			if (interval == (time_t) NGX_ERROR) {
				goto invalid;
			}

			continue;
		}

		if (ngx_strncmp(value[i].data, "max_ip=", 7) == 0) {
			max_ip = ngx_atoi(value[i].data + 7, value[i].len - 7);

			if (max_ip == NGX_ERROR || max_ip < 1) {
				goto invalid;
			}

			continue;
		}

		if (ngx_strncmp(value[i].data, "retry_off", 9) == 0) {
			retry = 0;
			continue;
		}

		goto invalid;
	}

	urcf->peers = ngx_pcalloc(cf->pool,
	                          max_ip * sizeof(ngx_http_upstream_resolveMK_peer_t));

	if (urcf->peers == NULL) {
		ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
		                   "ngx_palloc peers fail");
		return NGX_CONF_ERROR;
	}

	urcf->resolver_interval = interval;
	urcf->resolver_domain = domain;
	urcf->resolver_max_ip = max_ip;
	urcf->upstream_retry = retry;
	ngx_memzero(&u, sizeof(ngx_url_t));
	u.url = value[1];

	if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
		if (u.err) {
			ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
			                   "%s in upstream \"%V\"", u.err, &u.url);
		}

		return NGX_CONF_ERROR;
	}

	urcf->resolved_num = 0;

	for (i = 0; i < u.naddrs ; i++) {
		paddr = &urcf->peers[urcf->resolved_num];
		paddr->sockaddr = *(struct sockaddr*)u.addrs[i].sockaddr;
		paddr->socklen = u.addrs[i].socklen;
		paddr->name = u.addrs[i].name;
		urcf->resolved_num++;

		if (urcf->resolved_num >= urcf->resolver_max_ip) {
			break;
		}
	}

	/* urcf->resolved_index = 0 */
	urcf->resolved_access = ngx_time();

	return NGX_CONF_OK;
invalid:
	ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
	                   "invalid parameter \"%V\"", &value[i]);

	return NGX_CONF_ERROR;
}
static char *
ngx_http_upstream_dynamic(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_upstream_srv_conf_t            *uscf;
    ngx_http_upstream_dynamic_srv_conf_t    *dcf;
    ngx_str_t   *value, s;
    ngx_uint_t   i;
    time_t       fail_timeout;
    ngx_int_t    fallback;

    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

    dcf = ngx_http_conf_upstream_srv_conf(uscf,
                                          ngx_http_upstream_dynamic_module);

    if (dcf->original_init_upstream) {
        return "is duplicate";
    }

    dcf->original_init_upstream = uscf->peer.init_upstream
                                  ? uscf->peer.init_upstream
                                  : ngx_http_upstream_init_round_robin;

    uscf->peer.init_upstream = ngx_http_upstream_init_dynamic;

    /* read options */

    value = cf->args->elts;

    for (i = 1; i < cf->args->nelts; i++) {

        if (ngx_strncmp(value[i].data, "fail_timeout=", 13) == 0) {

            s.len = value[i].len - 13;
            s.data = &value[i].data[13];

            fail_timeout = ngx_parse_time(&s, 1);

            if (fail_timeout == (time_t) NGX_ERROR) {
                return "invalid fail_timeout";
            }

            dcf->fail_timeout = fail_timeout;

            continue;
        }

        if (ngx_strncmp(value[i].data, "fallback=", 9) == 0) {

            s.len = value[i].len - 9;
            s.data = &value[i].data[9];

            if (ngx_strncmp(s.data, "next", 4) == 0) {
                fallback = NGX_HTTP_UPSTREAM_DYN_RESOLVE_NEXT;
            } else if (ngx_strncmp(s.data, "stale", 5) == 0) {
                fallback = NGX_HTTP_UPSTREAM_DYN_RESOLVE_STALE;
            } else if (ngx_strncmp(s.data, "shutdown", 8) == 0) {
                fallback = NGX_HTTP_UPSTREAM_DYN_RESOLVE_SHUTDOWN;
            } else {
                return "invalid fallback action";
            }

            dcf->fallback = fallback;

            continue;
        }
    }

    return NGX_CONF_OK;
}
/*
 * Based on: ngx_http_upstream.c/ngx_http_upstream_server
 * Copyright (C) Igor Sysoev
 */
char *
ngx_postgres_conf_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_str_t                         *value = cf->args->elts;
    ngx_postgres_upstream_srv_conf_t  *pgscf = conf;
    ngx_postgres_upstream_server_t    *pgs;
    ngx_http_upstream_srv_conf_t      *uscf;
    ngx_url_t                          u;
    ngx_uint_t                         i;

    dd("entering");

    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

    if (pgscf->servers == NULL) {
        pgscf->servers = ngx_array_create(cf->pool, 4,
                             sizeof(ngx_postgres_upstream_server_t));
        if (pgscf->servers == NULL) {
            dd("returning NGX_CONF_ERROR");
            return NGX_CONF_ERROR;
        }

        uscf->servers = pgscf->servers;
    }

    pgs = ngx_array_push(pgscf->servers);
    if (pgs == NULL) {
        dd("returning NGX_CONF_ERROR");
        return NGX_CONF_ERROR;
    }

    ngx_memzero(pgs, sizeof(ngx_postgres_upstream_server_t));

    /* parse the first name:port argument */

    ngx_memzero(&u, sizeof(ngx_url_t));

    u.url = value[1];
    u.default_port = 5432; /* PostgreSQL default */

    if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
        if (u.err) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "postgres: %s in upstream \"%V\"",
                               u.err, &u.url);
        }

        dd("returning NGX_CONF_ERROR");
        return NGX_CONF_ERROR;
    }

    pgs->addrs = u.addrs;
    pgs->naddrs = u.naddrs;
    pgs->port = u.port;

    /* parse various options */
    for (i = 2; i < cf->args->nelts; i++) {

        if (ngx_strncmp(value[i].data, "dbname=", sizeof("dbname=") - 1)
                == 0)
        {
            pgs->dbname.len = value[i].len - (sizeof("dbname=") - 1);
            pgs->dbname.data = &value[i].data[sizeof("dbname=") - 1];
            continue;
        }

        if (ngx_strncmp(value[i].data, "user="******"user="******"user="******"user="******"password="******"password="******"password="******"password="******"postgres: invalid parameter \"%V\" in"
                           " \"postgres_server\"", &value[i]);

        dd("returning NGX_CONF_ERROR");
        return NGX_CONF_ERROR;
    }

    uscf->peer.init_upstream = ngx_postgres_upstream_init;

    dd("returning NGX_CONF_OK");
    return NGX_CONF_OK;
}
/*
 * Function called when the sticky command is parsed on the conf file
 */
static char *
ngx_http_sticky_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    time_t                        expires = NGX_CONF_UNSET;
    ngx_str_t                     tmp;
    ngx_str_t                     name = ngx_string("route");
    ngx_str_t                     domain = ngx_string("");
    ngx_str_t                     path = ngx_string("");
    ngx_str_t                     hmac_key = ngx_string("");
    ngx_str_t                    *value = cf->args->elts;
    ngx_uint_t                    i;
    ngx_uint_t                    no_fallback = 0;
    ngx_http_sticky_srv_conf_t   *sticky_conf;
    ngx_http_upstream_srv_conf_t *upstream_conf;
    ngx_http_sticky_misc_hash_pt  hash = NGX_CONF_UNSET_PTR;
    ngx_http_sticky_misc_hmac_pt  hmac = NULL;

    /* parse all elements */
    for (i = 1; i < cf->args->nelts; i++) {

        if (ngx_strncmp(value[i].data, "name=", 5) == 0) {

            /* save what's after "name=" */
            name.len = value[i].len - 5;
            name.data = value[i].data + 5;
            continue;
        } 

        if (ngx_strncmp(value[i].data, "domain=", 7) == 0) {

            /* save what's after "domain=" */
            domain.len = value[i].len - 7;
            domain.data = value[i].data + 7;
            continue;
        }

        if (ngx_strncmp(value[i].data, "path=", 5) == 0) {

            /* save what's after "path=" */
            path.len = value[i].len - 5;
            path.data = value[i].data + 5;
        }

        if (ngx_strncmp(value[i].data, "expires=", 8) == 0) {

            /* extract value */
            tmp.len =  value[i].len - 8;
            tmp.data = value[i].data + 8;

            /* convert to time, save and validate */
            expires = ngx_parse_time(&tmp, 1);
            if (expires == NGX_ERROR || expires < 1) {
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                   "invalid value \"%V\"", &value[i]);
                return NGX_CONF_ERROR;
            }

            continue;
        }

        /* is "hash=" is starting the argument ? */
        if (ngx_strncmp(value[i].data, "hash=", 5) == 0) {

            /* only hash or hmac can be used, not both */
            if (hmac) {
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                    "please choose between \"hash=\" and \"hmac=\"");
                return NGX_CONF_ERROR;
            }

            /* extract value to temp */
            tmp.len =  value[i].len - ngx_strlen("hash=");
            tmp.data = (u_char *)(value[i].data + sizeof("hash=") - 1);

            /* is hash=index */
            if (ngx_strncmp(tmp.data, "index", sizeof("index") - 1) == 0 ) {
                hash = NULL;
                continue;
            }

            /* is hash=md5 */
            if (ngx_strncmp(tmp.data, "md5", sizeof("md5") - 1) == 0 ) {
                hash = ngx_http_sticky_misc_md5;
                continue;
            }

            /* is hash=sha1 */
            if (ngx_strncmp(tmp.data, "sha1", sizeof("sha1") - 1) == 0 ) {
                hash = ngx_http_sticky_misc_sha1;
                continue;
            }

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "wrong value for \"hash=\": index, md5 or sha1");

            return NGX_CONF_ERROR;
        }

        if (ngx_strncmp(value[i].data, "hmac=", 5) == 0) {

            /* only hash or hmac can be used, not both */
            if (hash != NGX_CONF_UNSET_PTR) {
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                    "please choose between \"hash=\" and \"hmac=\"");
                return NGX_CONF_ERROR;
            }

            /* extract value */
            tmp.len =  value[i].len - 5;
            tmp.data = value[i].data + 5;

            /* is hmac=md5 ? */
            if (ngx_strncmp(tmp.data, "md5", sizeof("md5") - 1) == 0 ) {
                hmac = ngx_http_sticky_misc_hmac_md5;
                continue;
            }

            /* is hmac=sha1 ? */
            if (ngx_strncmp(tmp.data, "sha1", sizeof("sha1") - 1) == 0 ) {
                hmac = ngx_http_sticky_misc_hmac_sha1;
                continue;
            }

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "wrong value for \"hmac=\": md5 or sha1");
            return NGX_CONF_ERROR;
        }

        if (ngx_strncmp(value[i].data, "hmac_key=", 9) == 0) {

            /* save what's after "hmac_key=" */
            hmac_key.len = value[i].len - 9;
            hmac_key.data = value[i].data + 9;
            continue;
        }

        /* is "no_fallback" flag present ? */
        if (ngx_strncmp(value[i].data, "no_fallback",
                        sizeof("no_fallback") - 1) == 0 ) {
            no_fallback = 1;
            continue;
        }

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                           "invalid arguement (%V)", &value[i]);
        return NGX_CONF_ERROR;
    }

    /* if has and hmac have not been set, default to md5 */
    if (hash == NGX_CONF_UNSET_PTR && hmac == NULL) {
        hash = ngx_http_sticky_misc_md5;
    }

    /* don't allow meaning less parameters */
    if (hmac_key.len > 0 && hash != NGX_CONF_UNSET_PTR) {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                           "\"hmac_key=\" is meaningless when "
                           "\"hash\" is used. Please remove it.");
        return NGX_CONF_ERROR;
    }

    /* ensure we have an hmac key if hmac's been set */
    if (hmac_key.len == 0 && hmac != NULL) {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "please specify "
                           "\"hmac_key=\" when using \"hmac\"");
        return NGX_CONF_ERROR;
    }

    /* ensure hash is NULL to avoid conflicts later */
    if (hash == NGX_CONF_UNSET_PTR) {
        hash = NULL;
    }

    /* save the sticky parameters */
    sticky_conf = ngx_http_conf_get_module_srv_conf(cf,
                                                    ngx_http_sticky_module);
    sticky_conf->cookie_name = name;
    sticky_conf->cookie_domain = domain;
    sticky_conf->cookie_path = path;
    sticky_conf->cookie_expires = expires;
    sticky_conf->hash = hash;
    sticky_conf->hmac = hmac;
    sticky_conf->hmac_key = hmac_key;
    sticky_conf->no_fallback = no_fallback;
    sticky_conf->peers = NULL; /* ensure it's null before running */

    upstream_conf = ngx_http_conf_get_module_srv_conf(cf,
                                                      ngx_http_upstream_module);

    /* 
     * ensure another upstream module has not been already loaded
     * peer.init_upstream is set to null and the upstream module use RR if not set
     * But this check only works when the other module is declared before sticky
     */
    if (upstream_conf->peer.init_upstream) {

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                           "You can't use sticky with another upstream module");

        return NGX_CONF_ERROR;
    }

    /* configure the upstream to get back to this module */
    upstream_conf->peer.init_upstream = ngx_http_init_upstream_sticky;

    upstream_conf->flags = NGX_HTTP_UPSTREAM_CREATE
                         | NGX_HTTP_UPSTREAM_MAX_FAILS
                         | NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
                         | NGX_HTTP_UPSTREAM_DOWN;

    return NGX_CONF_OK;
}