DDG
view release on metacpan or search on metacpan
lib/DDG/Rewrite.pm view on Meta::CPAN
my $wrap_string_callback = $self->has_callback && $self->wrap_string_callback;
my $uses_echo_module = $wrap_jsonp_callback || $wrap_string_callback;
my $content_type_javascript = $self->content_type_javascript;
my $callback = $self->callback;
my ($spice_name) = $self->path =~ m{^/js/spice/(.+)/$};
$spice_name =~ s|/|_|og if $spice_name;
my $cfg = "location ^~ ".$self->path." {\n";
my $timeouts = $self->has_upstream_timeouts && $self->upstream_timeouts;
if (ref $timeouts eq 'HASH' && keys %$timeouts) {
$cfg .= "\tproxy_connect_timeout $timeouts->{connect};\n" if $timeouts->{connect};
$cfg .= "\tproxy_send_timeout $timeouts->{send};\n" if $timeouts->{send};
$cfg .= "\tproxy_read_timeout $timeouts->{read};\n" if $timeouts->{read};
}
if ( $self->headers ) {
if ( ref $self->headers eq 'HASH' ) {
for my $header ( sort keys %{$self->headers} ) {
$cfg .= "\tproxy_set_header $header \"" . $self->headers->{$header} . "\";\n";
}
}
elsif ( ref $self->headers eq 'ARRAY' ) {
for my $header ( @{ $self->headers } ) {
$cfg .= "\tproxy_set_header $header;\n";
}
}
else {
$cfg .= "\tproxy_set_header " . $self->headers . ";\n";
}
}
if ( $self->has_post_body ) {
$cfg .= "\tproxy_method POST;\n";
$cfg .= "\tproxy_set_body '" . $self->post_body . "';\n";
# This block sets the proxy cache key from the spice name and the combined
# set of captured GET parameters. The 'map' builds a hash of these capture
# parameters as keys to ensure each one occurs only once. We can then pull these
# out consistently by calling 'sort keys' on the returned hash and 'join' turns
# the sorted keys into a single string.
# e.g. post_body '{"method":"$2","query":"$1","cleaned_query":"$1"}'
# Would give a $cache_keys value of '$1$2'
my $cache_keys = join '', sort keys %{ {
map { $_ => 1 } ( $self->post_body =~ m/\$[0-9]+/g )
} };
$cfg .= "\tproxy_cache_key spice_${spice_name}_$cache_keys;\n"
}
if($uses_echo_module) {
# we need to make sure we have plain text coming back until we have a way
# to unilaterally gunzip responses from the upstream since the echo module
# will intersperse plaintext with gzip which results in encoding errors.
# https://github.com/agentzh/echo-nginx-module/issues/30
$cfg .= "\tproxy_set_header Accept-Encoding '';\n";
}
if($uses_echo_module || $content_type_javascript) {
# This is a workaround that deals with endpoints that don't support callback functions.
# So endpoints that don't support callback functions return a content-type of 'application/json'
# because what they're returning is not meant to be executed in the first place.
# Setting content-type to application/javascript for those endpoints solves blocking due to
# mime type mismatches.
$cfg .= "\tmore_set_headers 'Content-Type: application/javascript; charset=utf-8';\n";
}
$cfg .= "\techo_before_body '$callback(';\n" if $wrap_jsonp_callback;
$cfg .= "\techo_before_body '$callback".qq|("';\n| if $wrap_string_callback;
my $upstream;
if( $spice_name ) {
$upstream = '$'.$spice_name.'_upstream';
$cfg .= "\tset $upstream $scheme://$host:$port;\n";
} else {
warn "Error: Problem finding spice name in ".$self->path; return
}
$cfg .= "\trewrite ^".$self->path.($self->has_from ? $self->from : "(.*)")." ".$uri_path." break;\n";
$cfg .= "\tproxy_pass $upstream;\n";
$cfg .= "\tproxy_set_header ".$self->proxy_x_forwarded_for.";\n" if $is_duckduckgo;
$cfg .= "\tproxy_ssl_server_name on;\n" if $scheme =~ /https/;
if($self->has_proxy_cache_valid) {
# This tells Nginx how long the response should be kept.
$cfg .= "\tproxy_cache_valid " . $self->proxy_cache_valid . ";\n";
# Some response headers from the endpoint can affect `proxy_cache_valid` so we ignore them.
# http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ignore_headers
$cfg .= "\tproxy_ignore_headers X-Accel-Expires Expires Cache-Control Set-Cookie;\n";
}
$cfg .= "\tproxy_ssl_session_reuse ".$self->proxy_ssl_session_reuse.";\n" if $self->has_proxy_ssl_session_reuse;
$cfg .= "\techo_after_body ');';\n" if $wrap_jsonp_callback;
$cfg .= "\techo_after_body '\");';\n" if $wrap_string_callback;
# proxy_intercept_errors is used to handle endpoints that don't return 200 OK
# When we get errors from the endpoint, instead of replying a blank page, it should reply the function instead with no parameters,
# e.g., ddg_spice_dictionary_definition();. The benefit of doing that is that we know for sure that the Spice failed, and we can do
# something about it (we know that the Spice failed because it should return Spice.failed('...') when the parameters are not valid).
if($callback) {
$cfg .= "\tproxy_intercept_errors on;\n";
if ($self->error_fallback) {
$cfg .= "\terror_page 301 302 303 403 500 502 503 504 =200 /js/failed/$callback;\n";
$cfg .= "\terror_page 404 =200 \@404_$callback;\n";
} else {
$cfg .= "\terror_page 301 302 303 403 404 500 502 503 504 =200 /js/failed/$callback;\n";
}
}
$cfg .= "\texpires 1s;\n";
$cfg .= "}\n";
if ($self->error_fallback) {
my $fallback = $self->error_fallback;
$cfg .= "location \@404_$callback".qq( {\n);
$cfg .= "\techo_before_body '$callback(';\n" if $wrap_jsonp_callback;
$cfg .= qq(\techo '{"fallback": "$fallback"}';\n);
$cfg .= "\techo_after_body ');';\n" if $wrap_jsonp_callback;
$cfg .= qq( }\n);
}
return $cfg;
}
has _missing_envs => (
( run in 1.250 second using v1.01-cache-2.11-cpan-524268b4103 )