ref: master
etc/noosfero/varnish-accept-language.vcl
C{ /* ------------------------------------------------------------------ */ /* THIS FILE IS AUTOMATICALLY GENERATED BY ./gen_vcl.pl. DO NOT EDIT. */ /* * Accept-language header normalization * * - Parses client Accept-Language HTTP header * - Tries to find the best match with the supported languages * - Writes the best match as req.http.X-Varnish-Accept-Language * * First version: Cosimo, 21/Jan/2010 * Last update: Cosimo, 03/Nov/2011 * * http://github.com/cosimo/varnish-accept-language * */ #include/* isupper */ #include #include /* qsort */ #include #define DEFAULT_LANGUAGE "en" #define SUPPORTED_LANGUAGES ":en:cs:de:eo:es:fr:hy:it:pt:ru:" #define vcl_string char #define LANG_LIST_SIZE 16 #define HDR_MAXLEN 256 #define LANG_MAXLEN 8 #define RETURN_LANG(x) { \ strncpy(lang, x, LANG_MAXLEN); \ return; \ } #define RETURN_DEFAULT_LANG RETURN_LANG(DEFAULT_LANGUAGE) #define PUSH_LANG(x,y) { \ /* fprintf(stderr, "Pushing lang [%d] %s %.4f\n", curr_lang, x, y); */ \ /* We have to copy, otherwise root_lang will be the same every time */ \ strncpy(pl[curr_lang].lang, x, LANG_MAXLEN); \ pl[curr_lang].q = y; \ curr_lang++; \ } struct lang_list { vcl_string lang[LANG_MAXLEN]; float q; }; /* In-place lowercase of a string */ static void strtolower(char *s) { register char *c; for (c=s; *c; c++) { if (isupper(*c)) { *c = tolower(*c); } } return; } /* Checks if a given language is in the static list of the ones we support */ int is_supported(vcl_string *lang) { vcl_string *supported_languages = SUPPORTED_LANGUAGES; vcl_string match_str[LANG_MAXLEN + 3] = ""; /* :, :, \0 = 3 */ int is_supported = 0; /* We want to match 'zh-cn' and 'zh-CN' too */ strtolower(lang); /* Search ": :" in supported languages string */ strncpy(match_str, ":", 1); strncat(match_str, lang, LANG_MAXLEN); strncat(match_str, ":\0", 2); if (strstr(supported_languages, match_str)) is_supported = 1; return is_supported; } /* Used by qsort() below */ int sort_by_q(const void *x, const void *y) { struct lang_list *a = (struct lang_list *)x; struct lang_list *b = (struct lang_list *)y; if (a->q > b->q) return -1; if (a->q < b->q) return 1; return 0; } /* Reads Accept-Language, parses it, and finds the first match among the supported languages. In case of no match, returns the default language. */ void select_language(const vcl_string *incoming_header, char *lang) { struct lang_list pl[LANG_LIST_SIZE]; vcl_string *lang_tok = NULL; vcl_string root_lang[3]; vcl_string *header; vcl_string header_copy[HDR_MAXLEN]; vcl_string *pos = NULL; vcl_string *q_spec = NULL; unsigned int curr_lang = 0, i = 0; float q; /* Empty or default string, return default language immediately */ if ( !incoming_header || (0 == strcmp(incoming_header, "en-US")) || (0 == strcmp(incoming_header, "en-GB")) || (0 == strcmp(incoming_header, DEFAULT_LANGUAGE)) || (0 == strcmp(incoming_header, "")) ) RETURN_DEFAULT_LANG; /* Tokenize Accept-Language */ header = strncpy(header_copy, incoming_header, sizeof(header_copy)); while ((lang_tok = strtok_r(header, " ,", &pos))) { q = 1.0; if ((q_spec = strstr(lang_tok, ";q="))) { /* Truncate language name before ';' */ *q_spec = '\0'; /* Get q value */ sscanf(q_spec + 3, "%f", &q); } /* Wildcard language '*' should be last in list */ if ((*lang_tok) == '*') q = 0.0; /* Push in the prioritized list */ PUSH_LANG(lang_tok, q); /* For cases like 'en-GB', we also want the root language in the final list */ if ('-' == lang_tok[2]) { root_lang[0] = lang_tok[0]; root_lang[1] = lang_tok[1]; root_lang[2] = '\0'; PUSH_LANG(root_lang, q - 0.001); } /* For strtok_r() to proceed from where it left off */ header = NULL; /* Break out if stored max no. of languages */ if (curr_lang >= LANG_LIST_SIZE) break; } /* Sort by priority */ qsort(pl, curr_lang, sizeof(struct lang_list), &sort_by_q); /* Match with supported languages */ for (i = 0; i < curr_lang; i++) { if (is_supported(pl[i].lang)) RETURN_LANG(pl[i].lang); } RETURN_DEFAULT_LANG; } /* Reads req.http.Accept-Language and writes X-Varnish-Accept-Language */ void vcl_rewrite_accept_language(const struct vrt_ctx *ctx) { vcl_string *in_hdr; vcl_string lang[LANG_MAXLEN]; const struct gethdr_s hdr = { HDR_REQ, "\020Accept-Language:" }; const struct gethdr_s hdrUpd = { HDR_REQ, "\032X-Varnish-Accept-Language:"}; /* Get Accept-Language header from client */ in_hdr = VRT_GetHdr(ctx, &hdr); /* Normalize and filter out by list of supported languages */ memset(lang, 0, sizeof(lang)); select_language(in_hdr, lang); /* By default, use a different header name: don't mess with backend logic */ VRT_SetHdr(ctx, &hdrUpd, lang, vrt_magic_string_end); return; } /* vim: syn=c ts=4 et sts=4 sw=4 tw=0 */ /* THIS FILE IS AUTOMATICALLY GENERATED BY ./gen_vcl.pl. DO NOT EDIT. */ /* ------------------------------------------------------------------ */ }C sub vcl_recv { C{ vcl_rewrite_accept_language(ctx); }C } sub vcl_backend_response { if (beresp.http.Vary) { set beresp.http.Vary = beresp.http.Vary + ", X-Varnish-Accept-Language"; } else { set beresp.http.Vary = "X-Varnish-Accept-Language"; } }