Как я могу отправить HTTP-ответ, используя только стандартные сетевые библиотеки?

Я работаю над своим первым домашним проектом в классе веб-программирования, который должен написать простой веб-сервер на Java. Я нахожусь в точке, где данные передаются туда и обратно, и на неопытный взгляд мой детский сервер работает нормально. Однако я не могу найти способ отправить соответствующие ответы. (Другими словами, недопустимый запрос страницы покажет HTML-страницу с ошибкой 404, но все равно возвращает статус 200 OK, когда я просматриваю заголовки ответов).

Я ограничен возможностью использовать стандартные сетевые библиотеки для управления сокетами и стандартные библиотеки ввода-вывода для чтения и записи байтов и строк из входного потока. Вот соответствующий код:

Из моего основного...

            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
        }   

Из метода handleClient... (внутри цикла, который анализирует метод и путь запроса)

                    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
                }

Из метода sendFile...

        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);
    }

Кто-нибудь может объяснить, как в условном из sendFile я могу изменить ответ в блоке 404 (как я уже говорил, заголовки ответов по-прежнему показывают 200 OK)? Это выводит меня из себя, и я просто хочу использовать класс HTTPResponse, но не могу. (Кроме того, длина и тип содержимого не отображаются, если f.exists == true.)

Спасибо!


person rownage    schedule 12.09.2010    source источник
comment
В коде, который мы не видим, вы когда-нибудь добавляли переменную записи в поток вывода? Также какой тип данных является записываемым и исходящим? (не хочу предполагать)   -  person Sean    schedule 13.09.2010
comment
@gameaddict: у меня есть писатель PrintWriter = new PrintWriter(sock.getOutputStream()); в самом начале метода. Извините, что не включил это, пытаясь оставаться кратким! Кроме того, модуль записи — PrintWriter, выход — DataOutputStream.   -  person rownage    schedule 13.09.2010
comment
Попробуйте изменить свой заголовок от писателя и поместить его в строку. затем добавьте его в outputStream в строковом формате. Посмотрите, поможет ли это. Вы также можете посмотреть, как не вызывать sendFile() из вашего else. если 404.html не найден, вы можете получить бесконечный цикл. Вы можете сделать это в... если (файл существует) файл - это тот, который вы передали, иначе файл - это документ 404 ..... Тогда попробуйте поймать.   -  person Sean    schedule 13.09.2010


Ответы (3)


Проблема, которую вы видите, скорее всего, связана с отсутствием flush() на вашем writer. В зависимости от того, какой тип Writer вы используете, байты сначала записываются в буфер, который необходимо сбросить в поток. Это объясняет, почему в выводе отсутствуют Content-Length и Content-Type. Просто сбросьте его перед записью дополнительных данных в поток.

Далее звоните sendFile("/404.html", sock);. Вы не разместили здесь полный метод, но я предполагаю, что вы вызываете его рекурсивно внутри sendFile и, таким образом, отправляете статус 200 OK для вашего файла /404.html.

person Moritz    schedule 12.09.2010
comment
Никогда бы не подумал об этом. Сделал красивую здоровенную строку, в которой было все необходимое, так что я могу просто выполнить одну операцию write() с последующим сбросом, и все отлично работает. Спасибо! (Кроме того, я рекурсивно вызывал sendfile, но проблема с флешем все решила). Благодарю вас! - person rownage; 13.09.2010

Изменить Мне кажется, что в ситуации 404 вы отправляете что-то вроде этого:

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

...за которым следует страница 404. Обратите внимание на строку 200 после 404. Это связано с тем, что ваша обработка 404 вызывает sendFile, который выводит код состояния ответа 200. Вероятно, это сбивает с толку получателя.

Старый ответ, в котором это пропущено:

Ответ HTTP начинается со строки состояния, за которой следует (необязательно) ряд заголовков, а затем (необязательно) включает тело ответа. Строка состояния и заголовки — это просто строки в определенном формате, например (для случайного примера):

HTTP/1.0 404 Not Found

Чтобы реализовать свой небольшой HTTP-сервер, я бы рекомендовал прочитать спецификацию и увидеть, как должны выглядеть ответы. Это небольшой концептуальный скачок, но на самом деле это просто строки текста, возвращаемые в соответствии с согласованным форматом. (Ну, в любом случае, несколько лет назад для меня это был концептуальный скачок. Я привык к чрезмерно сложным средам.)

Также может быть полезно делать подобные вещи из вашей любимой командной строки:

telnet www.google.com 80
GET /thispagewontbefound

...и нажмите Enter. Вы получите что-то вроде этого:

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

...за которым следует некоторый HTML, чтобы обеспечить дружественную страницу 404. Первая строка выше — строка состояния, остальные — заголовки. Между строкой состояния/заголовком и первой строкой содержимого (например, страницы) есть пустая строка.

person T.J. Crowder    schedule 12.09.2010
comment
Верно, я все это понимаю... но я думал, что именно это я и делаю в своем методе sendFile, используя write.write и out.write(buf)? Или я пропустил что-то большее здесь? - person rownage; 13.09.2010
comment
@rownage: Э-э, вполне. Извини за это, я должен идти спать. Обновлено, кажется, я вижу проблему. Другой вопрос, является ли это проблемой, но это определенно проблема. :-) - person T.J. Crowder; 13.09.2010
comment
Конечно, проблема, не та, которая решила мою проблему. Но спасибо за совет, однозначно помог! - person rownage; 13.09.2010

Основываясь на ваших сообщениях о симптомах, я думаю, что настоящая проблема заключается в том, что вы вообще не разговариваете со своим сервером! Доказательством является то, что 1) вы не можете получить ответ 404 и 2) ответ 200 не имеет длины и типа содержимого. Ни то, ни другое не должно быть возможным ... если вы действительно разговариваете с кодом, указанным выше.

Может быть:

  • вы разговариваете со старой версией своего кода; то есть что-то идет не так в вашем цикле сборки/развертывания,
  • вы (ошибочно) пытаетесь развернуть/запустить свой код в веб-контейнере (Jetty, Tomcat и т. д.) или
  • ваш клиентский код/браузер на самом деле разговаривает с другим сервером из-за проксирования, неправильного URL-адреса или чего-то в этом роде.

Я предлагаю вам добавить некоторую печать/регистрацию трассировки в соответствующих точках вашего кода, чтобы подтвердить, что он действительно вызывается.

person Stephen C    schedule 12.09.2010