How to use $phpVm->header()?

BSLFE

Joined: 2008-08-04
Posts: 13
Posted: Tue, 2009-04-07 18:15

I'm looking at the DownloadItemView class and I'm trying to understand how it manages to send custom HTTP headers to the client. From what I understood, the photo.tpl template creates a link to the DownloadItem view which then handles the HTTP header creation. However, I cannot do the same in my custom module, although I have copied the code from the DownloadItemview class and modified it for my needs. It's identical except for the "Content-Disposition" header.

This is how I think it works:
1. user clicks downloaditemview link
2. the renderImmediate function makes sure the requested item data is valid and calls _sendFile() which sends the file
3. _sendFile() makes sure the user has the appropriate permissions, sends the custom HTTP header and then the binary data.

Is there a step I am missing which the DownloadItem class does? Any help will be appreciated!

 
alecmyers

Joined: 2006-08-01
Posts: 4342
Posted: Tue, 2009-04-07 18:27

what is your code doing then?

 
BSLFE

Joined: 2008-08-04
Posts: 13
Posted: Tue, 2009-04-07 19:10

It does nothing after it sent the binary data, just like the DownloadItem view class.

 
alecmyers

Joined: 2006-08-01
Posts: 4342
Posted: Tue, 2009-04-07 19:14

I mean, you haven't explained your problem very well.

Quote:
I cannot do the same in my custom module

Perhaps if you elaborate on that someone might be able to provide some help.

 
BSLFE

Joined: 2008-08-04
Posts: 13
Posted: Tue, 2009-04-07 19:21

I'm not asking about my custom module but I'd like to understand the DownloadItem view class. I hope I can solve my problem myself once I understood DownloadItem view.

 
alecmyers

Joined: 2006-08-01
Posts: 4342
Posted: Tue, 2009-04-07 20:00

What is it that you don't understand about the class?

 
BSLFE

Joined: 2008-08-04
Posts: 13
Posted: Tue, 2009-04-07 20:42
BSLFE wrote:
I'm looking at the DownloadItemView class and I'm trying to understand how it manages to send custom HTTP headers to the client. From what I understood, the photo.tpl template creates a link to the DownloadItem view which then handles the HTTP header creation.

 
alecmyers

Joined: 2006-08-01
Posts: 4342
Posted: Tue, 2009-04-07 21:20

You know, for someone who's asking for support you might trying being a bit less "smart", and bit more keen to help those who are prepared to help you.

Quote:
'm trying to understand how it manages to send custom HTTP headers to the client.

I'm looking at DownloadItem.inc now, and its use of the $phpVm seems fairly straightforward. It sends a custom header every time you call $phpVm->header(...) as you can verify by looking at GalleryPhpVm.class file. I can't believe that you haven't already worked that out, so I'm at a loss to understand what kind of extra understanding you're asking for.

So, let me ask again - I could quote myself but I won't descend to it - what is it that you don't understand about the class?

 
BSLFE

Joined: 2008-08-04
Posts: 13
Posted: Wed, 2009-04-08 07:45

I have worked out that $phpVm->header() sends headers of course, however my module doesn't do that. It sends out "text/html" for the content-type when instead binary data is being sent. I know the renderImmediate function is meant for such modules that are supposed to deliver custom output and thus custom headers. As far as I know I have shaped my module exactly after the Downloaditem class, but the Downloaditem class must have do something different that I cannot derive from looking at the code alone.

I understand, once the user clicks on the link to download an image, the Downloaditem view is the only "code" involved in delivering this image, correct? Or are there other steps before the Downloaditem class?

 
alecmyers

Joined: 2006-08-01
Posts: 4342
Posted: Wed, 2009-04-08 08:47
Quote:
I understand, once the user clicks on the link to download an image, the Downloaditem view is the only "code" involved in delivering this image, correct? Or are there other steps before the Downloaditem class?

No, all gallery accesses (including a url with ...g2_view=core.DownloadItem... in it) go through main.php, so obviously there's more code than just one class involved.

The content type is sent in this line, in _sendFile(...):

$phpVm->header('Content-type: ' . $data['mimeType']);

and $data['mimeType'] is previously derived via this line, in the class's renderImmediate function:

$ret = $this->_sendFile(array('derivativePath' => $path,
				      'mimeType' => $item->getMimeType(),
				      'pseudoFileName' => $pseudoFileName));
 
BSLFE

Joined: 2008-08-04
Posts: 13
Posted: Wed, 2009-04-08 20:30

Of course I know that main.php is a vital part of every gallery2 process. What I meant was that at some point DownloadItem gets "control" over the HTTP header output and I wonder what it does in order to get to that point. I think I have understood the DownloadItem code but I must be missing something else that's not part of DownloadItem which causes my problems.

About the mime type: yes I know what DownloadItem does in order to determine the mime-type out of the gallery item. I have specified the HTTP header Content-type according to my output data but still the client receives the text/html header.

So I'm searching for something else in the gallery2 code (but not it in DownloadItem) which permits DownloadItem to manipulate the HTTP headers. I'd be thrilled to learn what that could be :)

 
alecmyers

Joined: 2006-08-01
Posts: 4342
Posted: Wed, 2009-04-08 23:09
Quote:
DownloadItem gets "control" over the HTTP header output

No - it just outputs the headers one by one, in real time, so to speak, each time the header(...) function is called. The phpVm->header(..) function directly calls the php function header(). Nothing more complicated than that.

Quote:
I'm searching for something else in the gallery2 code (but not it in DownloadItem) which permits DownloadItem to manipulate the HTTP headers.

There's nothing that sophisticated.

Do some basic debugging, change the code in obvious ways, print strings to the output, change the headers at the point you think they're being output to a bit of random text to make sure that's where they're coming from, follow the excution flow with echo statements etc. Usual stuff.

EDIT: thought: see the php page http://uk2.php.net/header which has this comment "The optional replace parameter indicates whether the header should replace a previous similar header,..." - maybe you're somehow sending two sets of headers in error?

 
BSLFE

Joined: 2008-08-04
Posts: 13
Posted: Mon, 2009-04-13 13:22

After days that turned into weeks of searching and questioning myself I finally found the solution to my problem and it's quite stupid IMO :) My module.inc file had two newlines at the end. That would prevent my header() calls from working as expected. After I removed the newlines (by incident I have to add) the module worked and the headers got changed as expected.

I don't know if this is a "feature" or security measure or a bug but I hope I will save someone else the time of dealing with this problem.

 
alecmyers

Joined: 2006-08-01
Posts: 4342
Posted: Mon, 2009-04-13 14:36

It's neither a bug nor a security feature - it's correct operation. If you include even a single character (including whitespace) before the opening "<?php" or after the closing "?>" of any .php or .inc file then php sends that/those characters directly as output, as if it were regular html.

Once output is sent, it's too late to call header(..) http://uk.php.net/header contains this in pretty much the first paragraph:

Quote:
Remember that header() must be called before any actual output is sent, either by normal HTML tags, blank lines in a file, or from PHP. It is a very common error to read code with include(), or require(), functions, or another file access function, and have spaces or empty lines that are output before header() is called. The same problem exists when using a single PHP/HTML file.

If you are displaying errors in php (such as by putting Gallery into debug mode), you get a "headers already sent" warning in the output stream. It also shows up in the php/apache error log.

Glad you found the problem.