Почему я получаю пустой запрос от Jakarta Commons HttpClient?

У меня проблема с Jakarta Commons HttpClient. Прежде чем мой самописный HttpServer получит настоящий запрос, есть один запрос, который совершенно пуст. Это первая проблема. Первая проблема решена. Это было вызвано ненужным URLConnection! Вторая проблема заключается в том, что иногда данные запроса заканчиваются после третьей или четвертой строки http-запроса:

POST / HTTP/1.1
User-Agent: Jakarta Commons-HttpClient/3.1
Host: 127.0.0.1:4232

Для отладки я использую Axis TCPMonitor. Там все в порядке, но пустой запрос.

Как я обрабатываю поток:

<удар>

StringBuffer requestBuffer = new StringBuffer();

InputStreamReader is = new InputStreamReader(socket.getInputStream(), "UTF-8");

int byteIn = -1;
do {
    byteIn = is.read();
    if (byteIn > 0) {
        requestBuffer.append((char) byteIn);
    }
} while (byteIn != -1 && is.ready());

String requestData = requestBuffer.toString();

Найден новый способ обработки потока. Я читаю все параметры заголовка и использую 'content-length' для чтения данных поста.

InputStream is = mySocket.getInputStream();
if (is == null) {
    return;
}
BufferedReader in = new BufferedReader(new InputStreamReader(is, "UTF-8"));

// Read the request line
// ...
// ...

// Parse the header
Properties header = new Properties();
if (st.hasMoreTokens()) {
    String line = in.readLine();
    while (line != null && line.trim().length() > 0) {
        int p = line.indexOf(':');
        header.put(line.substring(0, p).trim().toLowerCase(), line.substring(p + 1).trim());
        line = in.readLine();
    }
}

// If the method is POST, there may be parameters
// in data section, too, read it:
String postLine = "";
if (method.equalsIgnoreCase("POST")) {
    long size = 0x7FFFFFFFFFFFFFFFl;
    String contentLength = header.getProperty("content-length");
    if (contentLength != null) {
        try {
            size = Integer.parseInt(contentLength);
        } catch (NumberFormatException ex) {
        }
    }
    postLine = "";
    char buf[] = new char[512];
    int read = in.read(buf);
    while (read >= 0 && size > 0 && !postLine.endsWith("\r\n")) {
        size -= read;
        postLine += String.valueOf(buf, 0, read);
        if (size > 0) {
            read = in.read(buf);
        }
    }
    postLine = postLine.trim();
    decodeParms(postLine, parms);
}

Как я отправляю запрос:

client.getParams().setSoTimeout(30000);

method = new PostMethod(url.getPath());
method.getParams().setContentCharset("utf-8");
method.setRequestHeader("Content-Type", "application/xml; charset=utf-8");
method.addRequestHeader("Connection", "close");
method.setFollowRedirects(false);

byte[] requestXml = getRequestXml();

method.setRequestEntity(new InputStreamRequestEntity(new ByteArrayInputStream(requestXml)));

client.executeMethod(method);

int statusCode = method.getStatusCode();

Есть ли у кого-нибудь из вас идеи, как решить эти проблемы?

Алекс


person alexvetter    schedule 08.03.2010    source источник


Ответы (2)


Это может быть связано со вторым условием в вашем цикле while, метод isReady() может вернуть false, когда следующее чтение может заблокироваться, но вам на самом деле все равно, блокируется он или нет, поэтому мы можем просто удалить его (вы подробнее можно прочитать здесь: http://java.sun.com/j2se/1.5.0/docs/api/java/io/InputStreamReader.html#ready%28%29 ). Попробуйте изменить на это:

byte[] buf = new byte[500];
while((is.read(buf))>-1){
  requestBuffer.append(new String(buf).trim());
  buf = new byte[500];
}

Теперь вы должны получить весь запрос.

person simonlord    schedule 08.03.2010
comment
Когда я удаляю is.ready(), время чтения слишком велико. Ответ приходит примерно через 20 секунд. - person alexvetter; 08.03.2010
comment
Когда вы говорите, что «ответ возвращается», вы имеете в виду, что чтение XML-запроса в буфер запроса занимает 20 секунд? - person simonlord; 08.03.2010
comment
Весь запрос занимает ~ 20 секунд с чтением XML запроса и отправкой ответа. - person alexvetter; 08.03.2010
comment
Хорошо, я ожидаю, что это вызвано другой частью реализации вашего сервера - попробуйте обернуть основные части (например, получение запроса, создание ответа, отправка ответа) с помощью некоторого кода синхронизации и некоторых операторов печати. например, длинный старт = System.currentTimeMillis(); ... получить запрос ... System.out.println(Задание бла взял: + (System.currentTimeMillis () - запуск) + миллис для завершения); это должно помочь вам определить, какая часть вашего приложения занимает больше всего времени. - person simonlord; 08.03.2010
comment
Это я был виноват. Проблема в том, что поток на самом деле не заканчивается. Я решил проблему, используя параметр 'content-length' в заголовке http. Я добавил новый фрагмент кода к своему вопросу. Редактировать А теперь я читаю поток с буфером... - person alexvetter; 10.03.2010

Я не знаю о первой проблеме, но я думаю, что ваша вторая проблема связана с этим:

} while (byteIn != -1 && is.ready());

Если отправитель недостаточно быстро отправляет данные, получатель может вызвать is.ready() до отправки следующего пакета. Это приведет к тому, что is.ready() вернет false, что приведет к остановке цикла.

Минимальное исправление состоит в том, чтобы изменить эту строку на:

} while (byteIn != -1);

ИЗМЕНИТЬ

Но на самом деле вам нужно переписать метод в соответствии с ответом @simonlord. Очень плохая идея читать небуферизованный поток по одному байту за раз. В конечном итоге вы выполняете системный вызов для каждого вызова read, что ужасно неэффективно.

ИЗМЕНИТЬ 2

Причина, по которой удаление is.ready() вызвало задержки, заключается в том, что вы не уделяли должного внимания протоколу HTTP. Проблема заключалась в том, что код HttpClient держал сторону запроса TCP-соединения открытой, чтобы разрешить повторное использование соединения. Простым (но неоптимальным) решением было бы настроить HttpClient для закрытия стороны запроса соединения. Ваш код сразу бы увидел EOF. То, что вы на самом деле сделали, было другим решением.

Откровенно говоря, вам не следует даже пытаться реализовать протокол HTTP на стороне сервера, если вы не готовы глубоко понять всю спецификацию HTTP и добросовестно реализовать ее. Скорее всего, существующая реализация будет быстрее и надежнее, чем все, что вы можете собрать вместе. Проблема с реализацией подмножества спецификации заключается в том, что вашему серверу может потребоваться взаимодействие с реальным браузером, который использует части спецификации, которые вы не удосужились реализовать/протестировать.

person Stephen C    schedule 08.03.2010
comment
Когда я удаляю is.ready(), время чтения слишком велико. Ответ приходит примерно через 20 секунд. - person alexvetter; 08.03.2010