How do I configure Lighttpd to call the FastCGI application if no static file exists at the specified path?
The FastCGI module in Lighttpd supports the configuration option "check-local". If it is set to "enable", Lighttpd will check whether the requested file exists in the document root or not. It will return the file if it exists or responds with a 404 error. However, the FastCGI app does not get executed, if the file does not exist.
I want to implement the following scenario:
User requests /images/example.jpg
Image exists => Return the image
Image does not exist => Generate the image with the FastCGI app
I am using the following configuration in Lighttpd:
server.modules += ( "mod_fastcgi" )
$HTTP["host"] == "localhost" {
fastcgi.server += ( "/" =>
((
"bin-path" => "/path/to/fcgi/app",
"socket" => "/var/run/lighttpd/foobar.socket",
"max-procs" => 8,
"check-local" => "disable"
))
)
}
Edit:
I just found a workaround. Maybe there is a better solution?
server.modules += ( "mod_fastcgi" )
server.modules += ( "mod_rewrite" )
$HTTP["host"] == "localhost" {
url.rewrite-if-not-file = (
"^(.*)$" => "$1?create=true"
)
$HTTP["querystring"] =~ "create=true" {
fastcgi.server += ( "/" =>
((
"bin-path" => "/path/to/fcgi/app",
"socket" => "/var/run/lighttpd/foobar.socket",
"max-procs" => 8,
"check-local" => "disable"
))
)
}
}
Related
I want to create a seperate log file for each HTTP request made to the application. When ever a request is made to the application, it has to generate a log file in the following format
debug20220713.log
debug20220713_001.log
debug20220713_002.log
Here in each log file there should be only one log available.
Log.Logger = new LoggerConfiguration()
.Enrich.WithExceptionDetails()
.Enrich.FromLogContext()
.WriteTo.Async(y =>
y.Logger(m =>
{
m.WriteTo.File(
new ExpressionTemplate(jsonErrorTemplate),
"error.log", LogEventLevel.Warning,
rollingInterval: RollingInterval.Day);
m.Filter.ByIncludingOnly(e => e.Level == LogEventLevel.Warning || e.Level == LogEventLevel.Error);
})
)
.WriteTo.Async(x =>
x.Logger(l =>
{
l.WriteTo.File(new ExpressionTemplate(jsonLogTemplate),
"debug.log", LogEventLevel.Information,
rollingInterval: RollingInterval.Day);
l.Filter.ByIncludingOnly(e => e.Level == LogEventLevel.Information);
}))
.CreateLogger();
I ended up creating own verision of RollingFileSink which matches my requirement. but internally I still use FileSink class. When I call constructor of FileSink I get this error
"This type and constructor will be removed from the public API in a future version; use WriteTo.File() instead."
I want to have a fake URL for the yii2 advanced application
below is the sample I want to achieve
Home Page
www.example.com/er45se5t
Users page
www.example.com/er45se5t/users/index
Explanation:
Here er45se5t is a token or an identifier. I need this token to be present in URL for every request.
Solutions tried:
I tried to save token in session and set it and added it with all URLs
added these rules
'<token:\w+>' => 'site/index',
'<token:\w+>/<controller:\w+>' => '<controller>/index'
Problem
When I logout this session got cleared and cannot add this token to URL.
so after logging out, I want the URL to look like this
www.example.com/er45se5t
or
www.example.com/er45se5t/site/login
You can add component named CustomUrlManager with content:
namespace frontend\components;
use yii\web\UrlManager;
class CustomUrlManager extends UrlManager
{
public function createUrl($params)
{
$defaultToken='er45se5t';
if( isset($params['token']) ){
//you can add whatever here
$token=$params['token'];
unset($params['token']);
} else {
//you can add whatever here
$token=$defaultToken;
}
$url = parent::createUrl($params);
if( $url == '/' ){
return '/'.$token;
}else{
return '/'.$token.$url;
}
}
}
And in config file as component:
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'class'=>'frontend\components\CustomUrlManager',
'rules'=>[
]
],
And you can redirect www.example.com/ to some www.example.com/er45se5t on default controller
I have stumbled upon an unexpected behavior of YouTube liveStream API.
Whenever I request liveStream.list method, the only streams I get back are the ones initiated by my encoding software or liveStream.insert calls.
What I am trying to retrieve instead is the stream that is proposed by YouTube in the
default encoder setup:
Am I doing something wrong?
You can retrieve information about the default, or "Stream Now" broadcast by passing persistent for the broadcastType parameter in liveBroadcast.list.
Here's my final complete code for this , $api_stream_url contains ServerURL/StreamKey
listLiveBroadcasts persistent – Return only persistent broadcasts.
listLiveStreams cdn - format, rtmp, streamName, ingestionAddress
try {
// Execute an API request that lists broadcasts owned by the user who
// authorized the request.
$broadcastsResponse = $youtube->liveBroadcasts->listLiveBroadcasts(
'id,snippet,contentDetails',
array(
'broadcastType' => 'persistent',
'mine' => 'true',
));
$boundStreamId = $broadcastsResponse['items']['0']['contentDetails']['boundStreamId'];
$streamsResponse = $youtube->liveStreams->listLiveStreams('id,snippet,cdn', array(
// 'mine' => 'true',
'id' => $boundStreamId
));
/**
*
* (
[format] => 720p
[ingestionType] => rtmp
[ingestionInfo] => Array
(
[streamName] => 4vem-9233-mz3y-detq
[ingestionAddress] => rtmp://a.rtmp.youtube.com/live2
[backupIngestionAddress] => rtmp://b.rtmp.youtube.com/live2?backup=1
)
[resolution] => 720p
[frameRate] => 30fps
)
*/
$ingestionInfo = $streamsResponse['items']['0']['cdn']['ingestionInfo'];
/**
* (
[publishedAt] => 2016-06-08T12:58:38.000Z
[channelId] => UCcdDtElpr9XM5MA1stpK
[title] => Default Stream
[description] =>
[isDefaultStream] => 1
)
*/
$broadcastsResponse = $streamsResponse['items']['0']['snippet'];
$api_stream_key = $ingestionInfo->streamName;
$api_stream_address = $ingestionInfo->ingestionAddress;
/**
* Example:
* rtmp://a.rtmp.youtube.com/live2/4vem-9233-mz3y-detq
*/
$api_stream_url = $api_stream_address."/".$api_stream_key;
return false;
} catch (Google_Service_Exception $e) {
$htmlBody = sprintf('<p>A service error occurred: <code>%s</code></p>',
htmlspecialchars($e->getMessage()));
} catch (Google_Exception $e) {
$htmlBody = sprintf('<p>An client error occurred: <code>%s</code></p>',
htmlspecialchars($e->getMessage()));
}
I'm trying to specify a charset in the HTTP header of my Lighttpd-setup. I've tried numerous suggestions I've found throughout StackExchange's websites.
1. Tried looking in the mime.types file, so I could just add ; charset=utf-8 at the end of whatever file-types I wanted to specify a charset for in the HTTP header, but the mime.types-file is looking nothing like I expected: http://pastebin.com/QMKJ8Lqj
2. Tried changing create-mime.assign.pl from this:
#!/usr/bin/perl -w
use strict;
open MIMETYPES, "/etc/mime.types" or exit;
print "mimetype.assign = (\n";
my %extensions;
while(<MIMETYPES>) {
chomp;
s/\#.*//;
next if /^\w*$/;
if(/^([a-z0-9\/+-.]+)\s+((?:[a-z0-9.+-]+[ ]?)+)$/) {
foreach(split / /, $2) {
# mime.types can have same extension for different
# mime types
next if $extensions{$_};
$extensions{$_} = 1;
print "\".$_\" => \"$1\",\n";
}
}
}
print ")\n";
Into this:
#!/usr/bin/perl -w
use strict;
open MIMETYPES, "/etc/mime.types" or exit;
print "mimetype.assign = (\n";
my %extensions;
while(<MIMETYPES>) {
chomp;
s/\#.*//;
next if /^\w*$/;
if(/^([a-z0-9\/+-.]+)\s+((?:[a-z0-9.+-]+[ ]?)+)$/) {
my $pup = $1;
foreach(split / /, $2) {
# mime.types can have same extension for different
# mime types
next if $extensions{$_};
next if not defined $pup;
next if $pup eq '';
$extensions{$_} = 1;
if ($pup =~ /^text\//) {
print "\".$_\" => \"$pup; charset=utf-8\",\n";
} else {
print "\".$_\" => \"$pup\",\n";
}
}
}
}
print ")\n";
And restarted the Lighttpd server afterwards - nothing.
3. Afterwards I tried adding the following to the lighttpd.conf file:
mimetype.assign = (
".css" => "text/css; charset=utf-8",
".html" => "text/html; charset=utf-8",
".htm" => "text/html; charset=utf-8",
".js" => "text/javascript; charset=utf-8",
".text" => "text/plain; charset=utf-8",
".txt" => "text/plain; charset=utf-8",
".xml" => "text/xml; charset=utf-8"
)
And it gave me an error that it couldn't restart the Lighttpd server, because it found duplicate config variables of the "mimetype.assign" variable - one in create-mime.assign.pl and one in lighttpd.conf. I know I could try by removing include_shell "/usr/share/lighttpd/create-mime.assign.pl" from lighttpd.conf, so that there isn't any duplicate config variables, but what about all the other mime-types?
General info:
Lighttpd version: 1.4.28
PHP version: 5.3.29-1
Linux: Debian 6.0 Squeeze
Lighttpd.conf: http://pastebin.com/N6GrdUsi
Please try a newer version of lighttpd.
I am looking at 1.4.36 and doc/scripts/create-mime.conf.pl contains a list of extensions to which it appends "; charset=utf-8"
# text/* subtypes to serve as "text/...; charset=utf-8"
# text/html IS NOT INCLUDED: html has its own method for defining charset
# (<meta>), but the standards specify that content-type in HTTP wins over
# the setting in the html document.
my %text_utf8 = map { $_ => 1 } qw( # ......
You can find it in the git sources: https://github.com/lighttpd/lighttpd1.4/blob/master/doc/scripts/create-mime.conf.pl
I need to authenticate and authorize the application using Google's Service Account flow in perl.
Google does not seem to list perl as a supported language in their documentation.
Has any one faced this issue? Pointers to any code out there?
Search for perl Google OAUTH and you'll find many different approaches.
For an example of a quick web server that can be used to collect OAUTH tokens for your suitably configured Google Cloud API Project see this:
#!perl
use strict; use warnings; ## required because I can't work out how to get percritic to use my modern config
package goauth;
# ABSTRACT: CLI tool with mini http server for negotiating Google OAuth2 Authorisation access tokens that allow offline access to Google API Services on behalf of the user.
#
# Supports multiple users
# similar to that installed as part of the WebService::Google module
# probably originally based on https://gist.github.com/throughnothing/3726907
# OAuth2 for Google. You can find the key (CLIENT ID) and secret (CLIENT SECRET) from the app console here under "APIs & Auth"
# and "Credentials" in the menu at https://console.developers.google.com/project.
# See also https://developers.google.com/+/quickstart/.
use strict;
use warnings;
use Carp;
use Mojolicious::Lite;
use Data::Dumper;
use Config::JSON;
use Tie::File;
use feature 'say';
use Net::EmptyPort qw(empty_port);
use Crypt::JWT qw(decode_jwt);
my $filename;
if ( $ARGV[0] )
{
$filename = $ARGV[0];
}
else
{
$filename = './gapi.json';
}
if ( -e $filename )
{
say "File $filename exists";
input_if_not_exists( ['gapi/client_id', 'gapi/client_secret', 'gapi/scopes'] ); ## this potentially allows mreging with a json file with data external
## to the app or to augment missing scope from file generated from
## earlier versions of goauth from other libs
runserver();
}
else
{
say "JSON file '$filename' with OAUTH App Secrets and user tokens not found. Creating new file...";
setup();
runserver();
}
sub setup
{
## TODO: consider allowing the gapi.json to be either seeded or to extend the credentials.json provided by Google
my $oauth = {};
say "Obtain project app client_id and client_secret from http://console.developers.google.com/";
print "client_id: ";
$oauth->{ client_id } = _stdin() || croak( 'client_id is required and has no default' );
print "client_secret: ";
$oauth->{ client_secret } = _stdin() || croak( 'client secret is required and has no default' );
print 'scopes ( space sep list): eg - email profile https://www.googleapis.com/auth/plus.profile.emails.read '
. "https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/contacts.readonly https://mail.google.com\n";
$oauth->{ scopes } = _stdin(); ## no croak because empty string is allowed an will evoke defaults
## set default scope if empty string provided
if ( $oauth->{ scopes } eq '' )
{
$oauth->{ scopes }
= 'email profile https://www.googleapis.com/auth/plus.profile.emails.read '
. 'https://www.googleapis.com/auth/calendar '
. 'https://www.googleapis.com/auth/contacts.readonly https://mail.google.com';
}
my $tokensfile = Config::JSON->create( $filename );
$tokensfile->set( 'gapi/client_id', $oauth->{ client_id } );
$tokensfile->set( 'gapi/client_secret', $oauth->{ client_secret } );
$tokensfile->set( 'gapi/scopes', $oauth->{ scopes } );
say 'OAuth details updated!';
# Remove comment for Mojolicious::Plugin::JSONConfig compatibility
tie my #array, 'Tie::File', $filename or croak $!;
shift #array;
untie #array;
return 1;
}
sub input_if_not_exists
{
my $fields = shift;
my $config = Config::JSON->new( $filename );
for my $i ( #$fields )
{
if ( !defined $config->get( $i ) )
{
print "$i: ";
#chomp( my $val = <STDIN> );
my $val = _stdin();
$config->set( $i, $val );
}
}
return 1;
}
sub runserver
{
my $port = empty_port( 3000 );
say "Starting web server. Before authorization don't forget to allow redirect_uri to http://127.0.0.1 in your Google Console Project";
$ENV{ 'GOAUTH_TOKENSFILE' } = $filename;
my $config = Config::JSON->new( $ENV{ 'GOAUTH_TOKENSFILE' } );
# authorize_url and token_url can be retrieved from OAuth discovery document
# https://github.com/marcusramberg/Mojolicious-Plugin-OAuth2/issues/52
plugin "OAuth2" => {
google => {
key => $config->get( 'gapi/client_id' ), # $config->{gapi}{client_id},
secret => $config->get( 'gapi/client_secret' ), #$config->{gapi}{client_secret},
authorize_url => 'https://accounts.google.com/o/oauth2/v2/auth?response_type=code',
token_url => 'https://www.googleapis.com/oauth2/v4/token' ## NB Google credentials.json specifies "https://www.googleapis.com/oauth2/v3/token"
}
};
# Marked for decomission
# helper get_email => sub {
# my ( $c, $access_token ) = #_;
# my %h = ( 'Authorization' => 'Bearer ' . $access_token );
# $c->ua->get( 'https://www.googleapis.com/auth/plus.profile.emails.read' => form => \%h )->res->json;
# };
helper get_new_tokens => sub {
my ( $c, $auth_code ) = #_;
my $hash = {};
$hash->{ code } = $c->param( 'code' );
$hash->{ redirect_uri } = $c->url_for->to_abs->to_string;
$hash->{ client_id } = $config->get( 'gapi/client_id' );
$hash->{ client_secret } = $config->get( 'gapi/client_secret' );
$hash->{ grant_type } = 'authorization_code';
my $tokens = $c->ua->post( 'https://www.googleapis.com/oauth2/v4/token' => form => $hash )->res->json;
return $tokens;
};
get "/" => sub {
my $c = shift;
$c->{ config } = $config;
app->log->info( "Will store tokens in" . $config->getFilename( $config->pathToFile ) );
if ( $c->param( 'code' ) ) ## postback from google
{
app->log->info( "Authorization code was retrieved: " . $c->param( 'code' ) );
my $tokens = $c->get_new_tokens( $c->param( 'code' ) );
app->log->info( "App got new tokens: " . Dumper $tokens);
if ( $tokens )
{
my $user_data;
if ( $tokens->{ id_token } )
{
# my $jwt = Mojo::JWT->new(claims => $tokens->{id_token});
# carp "Mojo header:".Dumper $jwt->header;
# my $keys = $c->get_all_google_jwk_keys(); # arrayref
# my ($header, $data) = decode_jwt( token => $tokens->{id_token}, decode_header => 1, key => '' ); # exctract kid
# carp "Decode header :".Dumper $header;
$user_data = decode_jwt( token => $tokens->{ id_token }, kid_keys => $c->ua->get( 'https://www.googleapis.com/oauth2/v3/certs' )->res->json, );
#carp "Decoded user data:" . Dumper $user_data;
}
#$user_data->{email};
#$user_data->{family_name}
#$user_data->{given_name}
# $tokensfile->set('tokens/'.$user_data->{email}, $tokens->{access_token});
$config->addToHash( 'gapi/tokens/' . $user_data->{ email }, 'access_token', $tokens->{ access_token } );
if ( $tokens->{ refresh_token } )
{
$config->addToHash( 'gapi/tokens/' . $user_data->{ email }, 'refresh_token', $tokens->{ refresh_token } );
}
else ## with access_type=offline set we should receive a refresh token unless user already has an active one.
{
carp('Google JWT Did not incude a refresh token - when the access token expires services will become inaccessible');
}
}
$c->render( json => $config->get( 'gapi' ) );
}
else ## PRESENT USER DEFAULT PAGE TO REQUEST GOOGLE AUTH'D ACCESS TO SERVICES
{
$c->render( template => 'oauth' );
}
};
app->secrets( ['putyourownsecretcookieseedhereforsecurity' . time] ); ## NB persistence cookies not required beyond server run
app->start( 'daemon', '-l', "http://*:$port" );
return 1;
}
## replacement for STDIN as per https://coderwall.com/p/l9-uvq/reading-from-stdin-the-good-way
sub _stdin
{
my $io;
my $string = q{};
$io = IO::Handle->new();
if ( $io->fdopen( fileno( STDIN ), 'r' ) )
{
$string = $io->getline();
$io->close();
}
chomp $string;
return $string;
}
=head2 TODO: Improve user interface of the HTML templates beneath DATA section
=over 1
=item * include Auth with Google button from Google Assets and advertise scopes reqeusted on the oauth.html
=item * More informative details on post-authentication page - perhaps include scopes, filename updated and instructions on revoking
=back
=cut
__DATA__
## oauth.html.ep
<%= link_to "Click here to get Google OAUTH2 tokens", $c->oauth2->auth_url("google",
authorize_query => { access_type => 'offline'},
scope => $c->{config}->get('gapi/scopes'), ## scope => "email profile https://www.googleapis.com/auth/plus.profile.emails.read https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/contacts.readonly",
)
%>
<br>
<br>
<a href="https://developers.google.com/+/web/api/rest/oauth#authorization-scopes">
Check more about authorization scopes</a>
Once you have a token in your gapi.json you can check the available scopes with curl using <pre>curl https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=<YOUR_ACCESS_TOKEN></pre>
__END__