Jump to content

Changing Content-Type header causes two database updates instead of one.


iarp

Recommended Posts

<?php
require_once('init.php');

$results = $db->prepare('SELECT file_location, file_type, file_size, id as media_id WHERE id = ? LIMIT 1;');
$results->execute(array($_GET['id']));

while ($row = $results->fetch()) {

	header('Content-Type: ' . $row['file_type']);
	header('Content-Length: ' . $row['file_size']);

	$media_ins = $db->prepare('UPDATE media SET total_clicks = total_clicks + 1 WHERE id = ?');
	$media_ins->execute(array($row['id']));
        readfile($row['file_location']);

}

For some reason, putting the header('Content-Type....') causes the UPDATE statement to trigger twice increasing the total_clicks by 2. Commenting out the content-type line causes a single update to occur.

 

init.php contains nothing more than session_start and the database connection. When i comment-out the content-type line the page is blank with no errors or messages.

 

I'm not sure why this is occurring.

Link to comment
Share on other sites

Doing a tad more research. Turns out it's Chrome sending two separate requests. I've checked $_GET, $_POST, $_SERVER to determine if there is anyway I can stop the update from occurring on this secondary request and everything is identical

Link to comment
Share on other sites

Are you sure this is your code? The query to select the file isn't even syntactically valid (there's no FROM clause).

 

Besides that, why is there a limit when you select a row by its ID? Shouldn't the ID be unique anyway? And why do you call fetch() in a loop when you've just double-checked that you never get more than one row? Why not just fetch this row?

 

Passing any content type that happens to reside in the database right to the client also isn't a good idea. In fact, it's a serious vulnerability. Anybody who can set the type has the power to change the meaning of the file. For example, a harmless image may be turned into a malicious HTML document to attack your users.

 

You need a whitelist of acceptable types. I'm sure (or at least I hope) you already have that for the upload, so just reuse it here.

Link to comment
Share on other sites

@Jacques1, the select statement is invalid not because of lack of FROM clause in the string (it's optional) but because of WHERE one :)

 

@iarp, it's time to start to think like a programmer. You need to learn more about debugging process. Just google it :)  

Link to comment
Share on other sites

What me or you or somebody else think, it doesn't really matter. For the sql parser, this is a syntactically invalid query returning a fatal error.

 

@iarp, I don't think that you are able to execute global variables directly to the db server using a prepared method like in your example. 

Edited by jazzman1
Link to comment
Share on other sites

For the sql parser, this is a syntactically invalid query returning a fatal error.

 

... which is what I just said.

 

What's your point? What are you trying to tell us? Did you want to remind us that MySQL supports SELECT queries with no clauses? Thanks, but this is neither new nor relevant here. The problem is that iarp has forgotten the FROM clause.

 

 

 

@iarp, I don't think that you are able to execute global variables directly to the db server using a prepared method like in your example. 

 

Of course you can, try it out. Or rather: What makes you think it's not possible? PDO doesn't care where the value comes from.

Link to comment
Share on other sites

 

Did you want to remind us that MySQL supports SELECT queries with no clauses?

 

No, I did not say that! I was my fault. I've just forgot that the sql parser (in most db servers just for query performance) start reading a query first from where condition before to go further on. So, the parser like in the example above doesn't even know that this is a select statement.

 

 

Of course you can, try it out

 

I will try later on, when I get home.

 

Are you thinking that I'm here wasting my time arguing with you or somebody else?

Link to comment
Share on other sites

Oh wow. OK so to clear up some confusion, the query I use has 2 other tables joined in. For simplicity sakes i removed the joins and just wrote out the query to use the media table, the FROM is in my query as it should be.

 

Google Chrome, the first time you ever visit a website that has not been cached chrome sends out two requests at the same time. I have seen two to three other explanations as to why this occurs namely:

  1. It sends one request for the favicon to save into cache, and the other request to get the actual page data
  2. It sends two simultaneous connections, if one dies and the other works it just seamlessly flips to the working connection.
  3. What I am serving is not typically a page, but rather media. Chrome sends one connection requesting a page and once it figures out its media it sends another requesting the media itself.

To fix this I have had to wrap the UPDATE statements in an if statement checking for an existing and non-empty $_SERVER['HTTP_RANGE'].

 

I saved the $_SERVER array information for both requests and compared the data. One request was sent for the media, the other for page data. I found that whenever HTTP_REFERER existed that it always asked for page data and when HTTP_RANGE existed that it was looking for the media itself.

Edited by iarp
Link to comment
Share on other sites

Here is a comparison of the only differences between the two $_SERVER arrays.

Array
(
    [HTTP_ACCEPT] => text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
    [HTTP_ACCEPT_ENCODING] => gzip,deflate,sdch
)
Array
(
    [HTTP_ACCEPT] => */*
    [HTTP_ACCEPT_ENCODING] => identity;q=1, *;q=0
    [HTTP_REFERER] => https://mydomain/linker/l/123
    [HTTP_RANGE] => bytes=0-
)

All I care about is counting whenever the media itself has been delivered. So watching for the HTTP_RANGE I think should work just fine.

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.