views:

63

answers:

3

Hey gang,

I'm working on my first homework project in a web programming class, which is to write a simple web server in Java. I'm at the point where I have data being transmitted back and forth, and to the untrained eye, my baby server seems to be working fine. However, I can't find a way to send appropriate responses. (In other words, an invalid page request would show a 404-ish HTML page, but it still returns a 200 OK status when I view response headers).

I'm limited to being able to use standard network libraries for socket management and standard I/O libraries to read and write bytes and strings from an input stream. Here's some pertinent code:

From my main...

            ServerSocket servSocket = new ServerSocket(port, 10);           // Bind the socket to the port
        System.out.println("Opened port " + port + " successfully!");

                while(true) {
            //Accept the incoming socket, which means that the server process will
            //wait until the client connects, then prepare to handle client commands
            Socket newDataSocket = servSocket.accept();
            System.out.println("Client socket created and connected to server socket...");

            handleClient(newDataSocket);                                //Call handleClient method
        }   

From the handleClient method...(inside a loop that parses the request method and path)

                    if(checkURL.compareTo("/status") == 0)  {   // Check to see if status page has been requested
                    System.out.println("STATUS PAGE");      // TEMPORARY. JUST TO MAKE SURE WE ARE PROPERLY ACCESSING STATUS PAGE
                    sendFile("/status.html", dataStream);
                }
                else {
                    sendFile(checkURL, dataStream);         // If not status, just try the input as a file name
                }

From sendFile method...

        File f = new File(where);                                               // Create the file object
    if(f.exists() == true) {                                                        // Test if the file even exists so we can handle a 404 if not.
        DataInputStream din;
        try {
            din = new DataInputStream(new FileInputStream(f));
            int len = (int) f.length();                                     // Gets length of file in bytes
            byte[] buf = new byte[len];
            din.readFully(buf);
            writer.write("HTTP/1.1 200 OK\r\n");                            // Return status code for OK (200)
            writer.write("Content-Length: " + len + "\r\n");                // WAS WRITING TO THE WRONG STREAM BEFORE!
            writer.write("Content-Type: "+type+"\r\n\r\n\r\n");             // TODO VERIFY NEW CONTENT-TYPE CODE
            out.write(buf);                                                 // Writes the FILE contents to the client
            out.flush();
            out.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();                                            // Not really handled since that's not part of project spec, strictly for debug.
        }
    }
    else {
        writer.write("HTTP/1.1 404 Not Found\r\n");                         // Attempting to handle 404 as simple as possible.
        writer.write("Content-Type: text/html\r\n\r\n\r\n");
        sendFile("/404.html", sock);
    }

Can anybody explain how, in the conditional from sendFile, I can change the response in the 404 block (Like I said before, the response headers still show 200 OK)? This is bugging the crap out of me, and I just want to use the HTTPResponse class but I can't. (Also, content length and type aren't displayed if f.exists == true.)

Thanks!

+3  A: 

Edit It looks to me like in the 404 situation, you're sending something like this:

HTTP/1.1 404 Not Found
Content-Type: text/html
HTTP/1.1 200 OK
Content-Length: 1234
Content-Type: text/html

...followed by the 404 page. Note the 200 line following the 404. This is because your 404 handling is calling sendFile, which is outputting the 200 response status code. This is probably confusing the receiver.

Old answer that missed that:

An HTTP response starts with a status line followed (optionally) by a series of headers, and then (optionally) includes a response body. The status line and headers are just lines in a defined format, like (to pick a random example):

HTTP/1.0 404 Not Found

To implement your small HTTP server, I'd recommend having a read through the spec and seeing what the responses should look like. It's a bit of a conceptual leap, but they really are just lines of text returned according to an agreed format. (Well, it was a conceptual leap for me some years back, anyway. I was used to environments that over-complicated things.)

It can also be helpful to do things like this from your favorite command line:

telnet www.google.com 80
GET /thispagewontbefound

...and press Enter. You'll get something like this:

HTTP/1.0 404 Not Found
Content-Type: text/html; charset=UTF-8
X-Content-Type-Options: nosniff
Date: Sun, 12 Sep 2010 23:01:14 GMT
Server: sffe
Content-Length: 1361
X-XSS-Protection: 1; mode=block

...followed by some HTML to provide a friendly 404 page. The first line above is the status line, the rest are headers. There's a blank line between the status line/headers and the first line of content (e.g., the page).

T.J. Crowder
Right, I understand all that...but I thought that's what I was doing in my sendFile method by using writer.write and out.write(buf)? Or am I missing something bigger here?
rownage
@rownage: Er, quite. Sorry 'bout that, I should go to bed. Updated, I think I see an issue. Whether it's *the* issue is another question, but it's certainly *an* issue. :-)
T.J. Crowder
Certainly an issue, wasn't the one that solved my problem. Thanks for your advice though, definitely helped!
rownage
A: 

Based on your reported symptoms, I think the real problem is that you are not actually talking to your server at all! The evidence is that 1) you cannot get a 404 response, and 2) a 200 response does not have the content length and type. Neither of these should be possible ... if you are really talking to the code listed above.

Maybe:

  • you are talking to an older version of your code; i.e. something is going wrong in your build / deploy cycle,
  • you are (mistakenly) trying to deploy / run your code in a web container (Jetty, Tomcat, etc), or
  • your client code / browser is actually talking to a different server due to proxying, an incorrect URL, or something like that.

I suggest that you add some trace printing / logging at appropriate points of your code to confirm that it is actually being invoked.

Stephen C
+1  A: 

The problem you are seeing is most likely related to a missing flush() on your writer. Depending on which type of Writer you use the bytes are first written to a buffer that needs to be flushed to the stream. This would explain why Content-Length and Content-Type are missing in the output. Just flush it before you write additional data to the stream.

Further you call sendFile("/404.html", sock);. You did not post the full method here - but I suppose that you call it recursively inside sendFile and thus send the 200 OK status for your file /404.html.

Moritz
Never would have thought of that. Made a nice hefty String that had all necessary stuff so I can just do one write() followed by a flush, and it's showing up great. Thanks! (Also, I was recursively calling sendfile, but the flush problem solved everything). Thank you!
rownage