Почему я получаю пустой запрос от Jakarta Commons HttpClient? - PullRequest
0 голосов
/ 08 марта 2010

У меня проблема с HttpClient из Jakarta Commons. До того, как мой самописанный 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();

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

Alex

Ответы [ 2 ]

1 голос
/ 08 марта 2010

Это может быть связано со вторым условием в вашем цикле 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];
}

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

1 голос
/ 08 марта 2010

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

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

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

Минимальное исправление - изменить эту строку на:

} while (byteIn != -1);

EDIT

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

РЕДАКТИРОВАТЬ 2

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

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

...