I'm working on a homework assignment in Perl CGI using the CGI.pm module. In my code I am checking for a cookie. If the cookie exists, I want to initiate another CGI script. In other situations I was able to use similar code, but in this instance I merely get the following browser output, not the redirect that I was looking for.
Refresh: 1; URL=homepage.pl.cgi
Content-Type: text/html; charset=ISO-8859-1
Here's my code:
#get the cookie
my %SIDhash = cookie('SIDhash');
if ( exists $SIDhash{"SID"} ) {
print header(-refresh=>'0; homepage.pl.cgi');
}
What fundamentals am I not understanding here?
Thanks,
CB
This should do the trick:
print header(
-refresh => '0; url=homepage.pl.cgi',
-cookie => $cookie,
);
If you are assembling the header in pieces, in various places in your code, save the header components in a variable first, e.g.:
my %headers;
# later...
$headers{-cookie} = $cookie;
# later still:
if (exists $SIDhash{SID})
{
# we want to redirect, so print all headers and we're done.
print header(%headers, -refresh => '0; url=homepage.pl.cgi');
exit;
}
# if we're still here, nothing is printed yet.. continue preparing data and print when ready.
# ...
I'm not sure why your refresh doesn't work, but it sounds like it would be more appropriate to use:
HTTP/1.1 302 Found
Location: http://www.example.org/
Just a thought.
Try changing the line to
print header(-refresh=>'0; url=homepage.pl.cgi');
From what I can tell, this should be correct now.
This page on Wikipedia offers information on Refresh and other methods of redirection.
Related
This question already has an answer here:
Is there a way to update the headers in one feature file and use the Auth token from Karate.config.js?
(1 answer)
Closed 1 year ago.
I'm trying to setup a framework to run Graphql calls and create and E2E environment.
I've got the following setup so far but i can't seem to get the headers part of it working. i have managed to set the auth for each request and it all works but as it logs in for each request it doesn't really work as expected.
I want do the following steps:
run a login Test (different usernames valid/invalid)
run a logout test (Ensure token is removed)
Then login with correct user and extract the "set-cookie" header (to use globally for all future requests)
I was trying to use the following:
Karate-config.js
karate.callSingle('classpath:com/Auth/common-headers.feature', config);
headers.js
function fn() {
var headers = {}
headers["set-cookie"] = sessionAccessId
karate.log('Cookie Value: ', headers)
return headers
}
common-headers.feature
Feature: Login to Application and extract header
Background:
* url serverAuthenticateUri
* header Accept = 'application/json'
Scenario: 'Login to the system given credentials'
Given request { username: '#(username)', password: '#(password)'}
When method post
Then status 200
And match $.success == '#(result)'
And def myResult = response
* def sessionAccessId = responseHeaders['set-cookie'][0]
* configure headers = read('classpath:headers.js')
* print 'headers:', karate.prevRequest.headers
feature-file.feature
Feature: sample test script
Background:
* url serverBaseUri
* def caseResp = call read('classpath:com/E2E/POC/CommonFeatures/CreateCaseRequest.feature')
* def caseReqId = caseResp.response.data.createCaseAndRequest.siblings[0].id
* def caseId = caseResp.response.data.createCaseAndRequest.siblings[0].forensicCaseId
* def graphQlCallsPath = 'classpath:com/E2E/POC/GraphQl/intForensic/'
* def commmonFiles = 'classpath:E2E/CommonFiles/'
Scenario: TC1a - Request Server Details from Config DB (1st Run):
Should handle requesting Server Details Data from Config Database.
* def queryFile = graphQlCallsPath + '20-TC1a_req_req_valid_id.graphql'
* def responseFile = graphQlCallsPath + '20-TC1a_resp_req_valid_id.json'
Given def query = read(queryFile)
And replace query.reqId = caseReqId
And request { query: '#(query)' }
When method post
Then status 200
And json resp = read(responseFile)
And replace resp.reqId = caseReqId
And replace resp.caseID = caseId
And match resp == $
I can log in correctly and i get the set-cookie token but this isn't being passed on the feature-file.feature and i get an error saying "not logged in" in the response.
Any help appreciated! I might be looking at this totally wrong and i have tried to follow the shared-scope as much as i can but can't understand in.
Please make this change and hopefully that works !
headers["set-cookie"] = karate.get('sessionAccessId');
Why is explained here: (read the whole section carefully) https://github.com/intuit/karate#configure-headers
EDIT: one more suggestion:
var temp = karate.callSingle('classpath:com/Auth/common-headers.feature', config);
karate.configure('headers', { 'set-cookie': temp.sessionAccessId });
Some extra suggestions:
If you have just started with Karate - based on your question I would suggest you get one flow working in a single Scenario first without any use of call and with nothing whatsoever in karate-config.js. Hard-code everything and get it working first. Use the header keyword to set any headers you need. I also see you are trying to set a set-cookie header (which may work) but Karate has a special keyword for cookie.
And don't even think about callSingle() to start with :)
Once you get that first "hard-coded" flow working, then attempt to configure headers and then only finally try to do "framework" stuff. You seem to have jumped straight into super-complexity without getting the basics right.
Please read this other answer as well, because I suspect that you or someone in your team is attempting to introduce what I refer to as "too much re-use": https://stackoverflow.com/a/54126724/143475 - try not to do this.
Also note that your question is so complex that I have not been able to follow it, so please ask a simpler or more specifc question next time. If you still are stuck, kindly follow this process: https://github.com/intuit/karate/wiki/How-to-Submit-an-Issue
I am using karate (https://github.com/intuit/karate) for some API testing and have a test with invalid headers. I would like to print the headers out while debugging, to make sure everything is being set properly. This is how i set and attempt to print, but nothing is working. I can find anything on it in the documentation. Does anybody know? Much appreciated!!
Given path '/metadata/project/' + projectID + '/graph/' + graphID
And headers { Authorization: 'INVALID', Content-Type:#(headerValue)}
And request graphJSON
* print headers // prints nothing
* print requestHeaders // prints nothing
* print requestHeader // prints nothing
* print header // prints nothing
When method put
Then status 401 // this passes, so i know the header is being set
* print response // prints correctly
* print responseHeaders //prints correctly
How to print the headers that will be sent?
I'm surprised you don't see the headers in the console and in the log target/karate.log - this should happen by default and you can follow the instructions here: https://github.com/intuit/karate#logging
Also refer to the docs for the built-in variables instead of trying to guess them: https://github.com/intuit/karate#responseheaders
But if you really want to print the actual headers sent (very rarely needed), you can do this:
* print 'headers:', karate.prevRequest.headers
EDIT: in Karate 1.3.0 onwards this is recommended:
* print karate.response.headers
This is explained here: https://github.com/intuit/karate#karate-prevrequest
EDIT: I noticed you may be making a common mistake, when you have hyphens in the JSON key - you need to use string quotes:
And headers { Authorization: 'INVALID', 'Content-Type': '#(headerValue)' }
And yes, this is explained in the docs as well.
I have a function that creates CSV data and lets the user download it in the browser, basically like this:
function f() {
if ($fp = fopen('php://output', 'w')) {
header("Content-Type: text/csv");
header("Content-Disposition: attachment; filename=\"foo.csv\"");
fputcsv($fp, $someArrayGoesHere);
flush();
fclose($fp);
// debugging, explained below
error_log(print_r(xdebug_get_headers(), true));
}
}
The code works, presenting the user with a download dialog.
Now, I'm trying to write a PHPunit test to confirm that the two HTTP headers get set. Realizing that headers_list() doesn't work in CLI context (php_sapi_name() == 'cli'), I'm using xdebug_get_headers() to retrieve the headers for testing.
Now comes the trouble. The debugging line in my code above, when run on the web, always prints the headers I expect:
Array
(
[0] => X-Content-Type-Options: nosniff
[1] => Content-Type: text/csv
[2] => Content-Disposition: attachment; filename="foo.csv"
)
But when I run this code in phpunit, the PHPUnit_Util_Printer::write function appears to be producing output before my code sets the headers, and prevents them from being set, producing this debug output:
Cannot modify header information - headers already sent by (output started
at /usr/share/pear/PHPUnit/Util/Printer.php:172)
Array
(
[0] => Content-type: text/html
)
This earlier write is not being produced by me, so is there a way around it? How do I test for my CSV-related headers using phpunit?
I've already read the advice given in these questions, but it hasn't helped:
Headers already sent by PHP
PHPUnit - test for expected headers
PHPUnit - Unit Testing with items that need to send headers
Test PHP headers with PHPunit
Setting HTTP headers to be able to run test cases
I also tried using #runInSeparateProcess with the unit test, but the test crashes horribly: ".Unexpected non-MediaWiki exception encountered, of type "Exception", exception 'Exception' with message 'Serialization of 'Closure' is not allowed' in /usr/share/pear/PHPUnit/Util/GlobalState.php:354". (This is a MediaWiki unit test for a custom extension.)
The answer to your literal question "Why does PHPUnit interfere with setting HTTP headers in this code?" is given fairly clearly in the answer to Test PHP headers with PHPunit. PHP's header() will fail with the Cannot modify header information warning if anything has been written to stdout. When running your code via PHPUnit, content has been sent to stdout long before your code under test has been reached.
You noted a separate issue when using the #runInSeparateProcess annotation to fork a clean PHP process for your test:
Unexpected non-MediaWiki exception encountered, of type "Exception", exception 'Exception' with message 'Serialization of 'Closure' is not allowed' in /usr/share/pear/PHPUnit/Util/GlobalState.php:354
By default PHPUnit attempts to backup all of the $GLOBALS data before each test and restore it afterwards. MediaWikiTestCase turns this behavior off but it looks like your extension's tests are not. It seems likely that the configuration for your extension includes a closure and is causing the serialization failure. Adding a #backupGlobals disabled annotation to your PHPUnit_Framework_TestCase class should get you past this problem.
My application is generating some absolute links via $this->get('request')->getHost().
Problem is: when I try to run testcases, I get following error message:
[exception] 500 | Internal Server Error | Twig_Error_Runtime
[message] An exception has been thrown during the rendering of a template ("Undefined index: HTTP_HOST") in "::base.html.twig" at line 69.
Somehow it's clear to me that there is no host when calling my app via CLI, but I think there must be a way to prevent Symfony2 from throwing that error.
Anyone knows how to get rid of it?
You could create the request like this:
$request = Request::create('http://example.com/path');
That will make the HTTP host be set.
Maybe what you could do is to inject the host you need directly in the request headers before calling the getter. The host is retrieved by looking at various parameter values. First, the headers parameter X_FORWARDED_HOST is checked to see if it is set. If it is set, it is returned otherwise the method getHost checks if the headers parameter HOST is set then the if the server parameter SERVER_NAME is set and finally if the server parameter SERVER_ADDR is set.
What you could try is to set the header parameter HOST like this before calling the getHost method:
$request = $this->get('request');
$request->headers->set('HOST', 'yourhosthere');
$request->getHost(); // Should return yourhosthere
That being said, I'm not sure this will solve the problem because the error you mentioning tells us that the template tries to retrieve the value of the index HTTP_HOST but it is not defined. Looking at the methods $request->getHost and $request->getHttpHost, I don't see anything trying to retrieve a value having HTTP_HOST as the index but I could have missed it. Could you post the file ::base.html.twig to see if the problem could be lying there.
Regards,
Matt
Thanks guys- your answers lead me into the right direction.
This is no Symfony2 issue, as i figured out:
It's just the facebook API PHP wrapper which directly accesses the SERVER parameters. This code solved my issue:
$facebook = $this->container->get('facebook');
$returnUrl = 'http://'.$request->getHost();
$returnUrl .= $this->container->get('router')->generate('_validate_facebook');
$_SERVER['HTTP_HOST'] = $request->getHost();
$_SERVER['REQUEST_URI'] = $request->getRequestUri();
$loginUrl = $facebook->getLoginUrl(array(
'req_perms' => 'publish_stream',
'next' => $returnUrl,
));
return $loginUrl;
now my app runs from web and CLI again
you can request the http header to check if a web page has been edited by looking at its date but how about dynamic pages such as - php, aspx- which grabs its data from a database?
Even though you might think it's outdated I've always found Simon Willison's article on Conditional GET to be more than useful. The example is in PHP but it is so simple that you can adapt it to other languages. Here it is the example:
function doConditionalGet($timestamp) {
// A PHP implementation of conditional get, see
// http://fishbowl.pastiche.org/archives/001132.html
$last_modified = substr(date('r', $timestamp), 0, -5).'GMT';
$etag = '"'.md5($last_modified).'"';
// Send the headers
header("Last-Modified: $last_modified");
header("ETag: $etag");
// See if the client has provided the required headers
$if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ?
stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) :
false;
$if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) ?
stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) :
false;
if (!$if_modified_since && !$if_none_match) {
return;
}
// At least one of the headers is there - check them
if ($if_none_match && $if_none_match != $etag) {
return; // etag is there but doesn't match
}
if ($if_modified_since && $if_modified_since != $last_modified) {
return; // if-modified-since is there but doesn't match
}
// Nothing has changed since their last request - serve a 304 and exit
header('HTTP/1.0 304 Not Modified');
exit;
}
With this you can use HTTP verbs GET or HEAD (I think it's also possible with the others, but I can't see the reason to use them). All you need to do is adding either If-Modified-Since or If-None-Match with the respective values of headers Last-Modified or ETag sent by a previous version of the page. As of HTTP version 1.1 it's recommended ETag over Last-Modified, but both will do the work.
This is a very simple example of how a conditional GET works. First we need to retrieve the page the usual way:
GET /some-page.html HTTP/1.1
Host: example.org
First response with conditional headers and contents:
200 OK
ETag: YourETagHere
Now the conditional get request:
GET /some-page.html HTTP/1.1
Host: example.org
If-None-Match: YourETagHere
And the response indicating you can use the cached version of the page, as only the headers are going to be delivered:
304 Not Modified
ETag: YourETagHere
With this the server notified you there was no modification to the page.
I can also recommend you another article about conditional GET: HTTP conditional GET for RSS hackers.
This is the exact purpose of the ETag header, but it has to be supported by your web framework or you need to take care that your application responds properly to requests with headers If-Match, If-Not-Match and If-Range (see HTTP Ch 3.11).
You can if it uses the http response headers correctly but it's often overlooked.
Otherwise storing a local md5-hash of the content might be useful to you (unless there's an easier in-content string you could hook out). It's not ideal (because it's quite a slow process) but it's an option.
Yes, you can and should use HTTP headers to mark pages as unexpired. If they are dynamic though (PHP, ASPX, etc.) and/or database driven, you'll need to manually control setting the Expires header/sending HTTP Not Modified appropriately. ASP.NET has some SqlDependency objects for this, but they still need to be configured and managed. (Not sure if PHP has something just like it, but there's probably something in PEAR if not...)
The Last-Modified header will only be of use to you if the programmer of the site has explicitly set it to be returned.
For a regular, static page Last-Modified is the timestamp of the last modification of the HTML file. For a dynamically generated page the server can't reliably assign a Last-Modified value as it has no real way of knowing how the content has changed depending on request, so many servers don't generate the header at all.
If you have control over the page, then ensuring the Last Modified header is being set will ensure a check on Last-Modified is successful. Otherwise you may have to fetch the page and either perform a regex to find a changed section (e.g. date/time in the header of a news site). If no such obvious marker exists, then I'd second Oli's suggestion of an MD5 on the page content as a way to be sure it has changed.