views:

143

answers:

3

I'm serving some files locally via HTTP using QTcpSocket. My problem is that only wget downloads the file properly, firefox adds four extra bytes to the end. This is the header I send:

  HTTP/1.0 200 Ok
  Content-Length: 382917;
  Content-Type: application/x-shockwave-flash;
  Content-Disposition: attachment; filename=file.swf;

This is the code used to send the response:

        QTextStream os(socket);
        os.setAutoDetectUnicode(true);

        QString name = tokens[1].right(tokens[1].length() - 1);
        QString resname = ":/" + name; // the served file is a Qt resource
        QFile f(resname); f.open(QIODevice::ReadOnly);

        os << "HTTP/1.0 200 Ok\r\n" <<
              "Content-Length: " << f.size() << ";\r\n" <<
              "Content-Type: application/x-shockwave-flash;\r\n" <<
              "Content-Disposition: attachment; filename=" << name << 
              ";\r\n\r\n";

        os.flush();

        QDataStream ds(socket);

        ds << f.readAll();

        socket->close();

        if (socket->state() == QTcpSocket::UnconnectedState)
        {
            delete socket;
        }

As I stated above, wget gets it right and downloads the file properly. The problem is that Firefox (and my target application, a Flash ActiveX instance) don't.

The four extra bytes are always the same: 4E E9 A5 F4

Hex dump

My question is what am I doing wrong, and what should I change to get it right? Thanks in advance.

+1  A: 

You should not be terminating the lines with semicolons. At first glance this seems like the most likely problem.

I don't know much about QDataStream (or QT in general), however a quick look at the QDataStream documentation mentions operator<<(char const*). If you are passing a null terminated string to QDataStream, you are almost certainly going over the end of the final buffer.

Try using QDataStream::writeRawBytes().

If you remove the semicolons, then the clients should at least read the correct number of bytes for the response and ignore the last four bytes.

I'd leave out "Content-Disposition" too. That's a MIME thing, not an HTTP thing.

janm
I removed them, but nothing changed.
Tamás Szelei
`Content-Disposition` is certainly part of HTTP. RFC2616, section 19.5.1.
bobince
@bobince: Section 19.5 is "additional features", discussing things that implementors should be aware of, but not depend on. Section 15.5 of RFC2616 says "Content-Disposition is not part of the HTTPstandard, but since it is widely implemented, we are documenting its use and risks for implementors"
janm
In any case, a content disposition of "attachment" is incorrect when trying to get a flash plugin to display flash content.
janm
That's presumably deliberate, to *stop* the browser trying to display the file inline, and offer it for download instead.
bobince
Thanks, writeRawBytes was the solution. The << operator formats the data so that it inserts the size of the written bytearray in the stream. That's what the four extra bytes were. Now it works ok, but the flashplayer won't display it. What might be wrong? I checked the header of a youtube flash, and it's nothing different. I also removed the Content-Disposition.
Tamás Szelei
Glad it worked. From a server point of view, the header seems fine. I suspect your next issue is client-side ...
janm
I managed to solved it. I added an answer below, if you are interested. Thanks for your help again.
Tamás Szelei
A: 

There shouldn't be any semicolons at the end of the line.

jpalecek
A: 

So I found the whole solution to the question, and I think someone might need it, so here it is:

The first problem were the four extra bytes. The reason for this is that according to the QDataStream documentation, "each item written to the stream is written in a predefined binary format that varies depending on the item's type". And as QFile.readAll() returned a QByteArray, QDataStream.operator<< wrote that object in the following format:

  • If the byte array is null: 0xFFFFFFFF (quint32)
  • Otherwise: the array size (quint32) followed by the array bytes, i.e. size bytes

(link)

So, the four extra bytes were the four bytes of quint32 that denoted the array size.

The solution, according to janm's answer was to use the writeRawBytes() function.

QDataStream ds(socket);
ds.writeRawData(f.readAll().data(), f.size());

Wget probably got it right the first time because it strictly enforces the Content-Length field of the HTTP header, while apparently firefox does not.

The second problem was that despite the right header and working sockets, the flashplayer did not display the desired content at all. I experimented with various fields to make it work, and noticed that by uploading to a real server, it works all right. I copied the header from server, and tadaa! it works. This is the header:

  HTTP/1.1 200 OK
  Server: Apache/2.2.15 (Fedora)
  Accept-Ranges: bytes
  Content-Length: 382917
  Content-Type: application/x-shockwave-flash
  Keep-Alive: timeout=15, max=100
  Connection: Keep-Alive

At first I only tried setting the version to 1.1, but that didn't help. Probably it's the keepalive thing, but honestly, I don't care at all as long as it works :).

Tamás Szelei