如何从此示例 HTTP 请求中获取 body?
How do I get the body from this example HTTP request?
我试图在 Java 中找到解析 RFC-822 文档的最简单方法。假设我有一个存储 HTTP 消息的 message-queue。请求和响应。因此,它们不会以 "normal" 方式检索,方法是将 socket-connection 发送到 - 比如说 - 端口 80 和 sending/retrieving 来自那里的消息。
在下面的代码中,我故意将 "mail" headers 与 HTTP 消息混合在一起。这是为了证明两者并没有太大的不同。但这不是重点。这是代码:
package httpexample;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import org.apache.http.Header;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.impl.io.DefaultHttpRequestParser;
import org.apache.http.impl.io.HttpTransportMetricsImpl;
import org.apache.http.impl.io.SessionInputBufferImpl;
import org.apache.http.io.HttpMessageParser;
import org.apache.http.message.BasicHttpEntityEnclosingRequest;
public class HttpExample {
// RFC 822
public static void main(String[] args) throws IOException, HttpException {
String str = "POST http://localhost:8080/foobar/1234567 HTTP/1.1\n" +
"Message-ID: <19815303.1075861029555.JavaMail.ss@kk>\n" +
"Date: Wed, 6 Mar 2010 12:32:20 -0800 (PST)\n" +
"From: someone@someotherplace.com\n" +
"To: someone@someplace.com\n" +
"Subject: some subject\n" +
"Mime-Version: 1.0\n" +
"Content-Type: text/plain; charset=us-ascii\n" +
"Content-Transfer-Encoding: 7bit\n" +
"X-From: one, some <some.one@someotherplace.com>\n" +
"X-To: one\n" +
"X-cc: \n" +
"X-bcc: \n" +
"X-Origin: Bob-R\n" +
"X-FileName: rbob (Non-Privileged).pst\n" +
"\n" +
"some message\n";
ByteArrayInputStream fakeStream = new ByteArrayInputStream(
str.getBytes());
HttpTransportMetricsImpl metrics = new HttpTransportMetricsImpl();
SessionInputBufferImpl inbuffer = new SessionInputBufferImpl(metrics, 1024);
inbuffer.bind(fakeStream);
HttpMessageParser<HttpRequest> requestParser =
new DefaultHttpRequestParser(inbuffer);
BasicHttpEntityEnclosingRequest request = (BasicHttpEntityEnclosingRequest)requestParser.parse();
for (Header hdr : request.getAllHeaders()) {
System.out.println(String.format("%-30s = %s", hdr.getName(), hdr.getValue()));
}
System.out.println(String.format("Request Line: %s", request.getRequestLine()));
System.out.println(String.format("Body\n------------------\n%s",
request.getEntity()));
}
}
输出如下所示:
Message-ID = <19815303.1075861029555.JavaMail.ss@kk>
Date = Wed, 6 Mar 2010 12:32:20 -0800 (PST)
From = someone@someotherplace.com
To = someone@someplace.com
Subject = some subject
Mime-Version = 1.0
Content-Type = text/plain; charset=us-ascii
Content-Transfer-Encoding = 7bit
X-From = one, some <some.one@someotherplace.com>
X-To = one
X-cc =
X-bcc =
X-Origin = Bob-R
X-FileName = rbob (Non-Privileged).pst
Request Line: POST http://localhost:8080/foobar/1234567 HTTP/1.1
Body
------------------
null
我想不通的是如何访问消息的 body。
我希望它具有 some message\n
的内容
我在 BasicHttpEntityEnclosingRequest
中找不到任何方法可以给我这个值。在我使用的早期版本中
HttpRequest request = requestParser.parse();
而不是
BasicHttpEntityEnclosingRequest request =
(BasicHttpEntityEnclosingRequest) requestParser.parse();
我将其更改为 BasicHttpEntityEnclosingRequest
,因为它具有 getEntity
方法。但是那个 returns null
.
所以我有点迷茫。
我在哪里可以找到 body?
我认为问题可能是从您的消息中不清楚 headers body 的长度是多少,因此接收方只是忽略它。 HTTP specification 定义了几个关于如何传达此信息的选项,其中 none 似乎适用于此处:
Content-Transfer-Encoding
必须是 Transfer-Encoding
7bit
不在 the standard options. 中
- 当您使用
str.getBytes()
时,它会为您提供未在 Content-Type
. 中声明的 us-ascii
的 UTF-16 字节
所以,我会稍微更改一下您的要求:
- 使用header
Content-Type: text/plain; charset=UTF-16
- 删除header
Content-Transfer-Encoding
- 添加
Content-Lenght: 28
(28 为 "some message\n".getBytes().length()
)。
查看 DefaultHttpRequestParser 的源代码,它似乎只解析请求行和 headers,它不会尝试解析 body。
This thread is discussing the same topic. There are few solution proposals as well.
我添加了Content-Lengthheader,否则解析器会忽略POSTbody。我修改了你的代码,现在它解析 body 就好了:
package org.apache.http.examples;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import org.apache.http.Header;
import org.apache.http.HttpException;
import org.apache.http.message.BasicHttpEntityEnclosingRequest;
import org.apache.http.util.EntityUtils;
public class HttpExample {
// RFC 822
public static void main(String[] args) throws IOException, HttpException {
String str = "POST http://localhost:8080/foobar/1234567 HTTP/1.1\n" +
"Message-ID: <19815303.1075861029555.JavaMail.ss@kk>\n" +
"Date: Wed, 6 Mar 2010 12:32:20 -0800 (PST)\n" +
"From: someone@someotherplace.com\n" +
"To: someone@someplace.com\n" +
"Subject: some subject\n" +
"Mime-Version: 1.0\n" +
"Content-Type: text/plain; charset=us-ascii\n" +
"Content-Transfer-Encoding: 7bit\n" +
"X-From: one, some <some.one@someotherplace.com>\n" +
"X-To: one\n" +
"X-cc: \n" +
"X-bcc: \n" +
"X-Origin: Bob-R\n" +
"X-FileName: rbob (Non-Privileged).pst\n" +
"Content-Length: 13\n" +
"\n" +
"some message\n";
ByteArrayInputStream fakeStream = new ByteArrayInputStream(
str.getBytes());
BHttpConnectionBaseImpl b = new BHttpConnectionBaseImpl(fakeStream);
BasicHttpEntityEnclosingRequest request1 = (BasicHttpEntityEnclosingRequest) b.receiveRequestHeader();
b.receiveRequestEntity(request1);
for (Header hdr : request1.getAllHeaders()) {
System.out.println(String.format("%-30s = %s", hdr.getName(), hdr.getValue()));
}
System.out.println(String.format("Request Line: %s", request1.getRequestLine()));
System.out.println(String.format("Body\n------------------\n%s",
EntityUtils.toString( request1.getEntity() ) ));
}
}
class BHttpConnectionBaseImpl extends org.apache.http.impl.DefaultBHttpServerConnection{
private InputStream inputStream;
public BHttpConnectionBaseImpl(final InputStream inputStream) {
super(4048);
this.inputStream = inputStream;
try {
super.bind(new Socket());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
protected InputStream getSocketInputStream(final Socket socket) throws IOException {
return inputStream;
}
@Override
protected OutputStream getSocketOutputStream(final Socket socket) throws IOException {
return new ByteArrayOutputStream();
}
}
POST body 的解析发生在 org.apache.http.impl.BHttpConnectionBase.prepareInput(HttpMessage)
中,无论其唯一的构造函数是 protected 并且需要很多参数。 child org.apache.http.impl.DefaultBHttpServerConnection
有一个方便的 public 构造函数,并在 receiveRequestHeader()
中进行 header 解析。我重载的方法需要绕过一些错误检查,例如如果 Socket == null
并且能够从 fakeStream
读取请求
另一种可能有效的方法是覆盖 Socket
,尤其是它的 getInputStream()
和 getOutputStream()
,尽管我还没有测试过。然后创建一个 DefaultBHttpServerConnection
的实例并调用它的 bind
方法。其余的应该是一样的。
通过覆盖行解析器自定义解析头:
inbuffer = new SessionInputBufferImpl(new HttpTransportMetricsImpl(), reqDataLength);
inbuffer.bind(input);
HttpMessageParser<org.apache.http.HttpRequest> requestParser = new DefaultHttpRequestParser(
inbuffer,
new LineParser(),
new DefaultHttpRequestFactory(),
MessageConstraints.DEFAULT
);
获取实体主体如下:
HttpEntityEnclosingRequest ereq = (HttpEntityEnclosingRequest) req;
ContentLengthStrategy contentLengthStrategy =
StrictContentLengthStrategy.INSTANCE;
long len = contentLengthStrategy.determineLength(req);
InputStream contentStream = null;
if (len == ContentLengthStrategy.CHUNKED) {
contentStream = new ChunkedInputStream(buf);
} else if (len == ContentLengthStrategy.IDENTITY) {
contentStream = new IdentityInputStream(buf);
} else {
contentStream = new ContentLengthInputStream(buf, len);
}
BasicHttpEntity ent = new BasicHttpEntity();
ent.setContent(contentStream);
ereq.setEntity(ent);
return ereq;
我试图在 Java 中找到解析 RFC-822 文档的最简单方法。假设我有一个存储 HTTP 消息的 message-queue。请求和响应。因此,它们不会以 "normal" 方式检索,方法是将 socket-connection 发送到 - 比如说 - 端口 80 和 sending/retrieving 来自那里的消息。
在下面的代码中,我故意将 "mail" headers 与 HTTP 消息混合在一起。这是为了证明两者并没有太大的不同。但这不是重点。这是代码:
package httpexample;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import org.apache.http.Header;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.impl.io.DefaultHttpRequestParser;
import org.apache.http.impl.io.HttpTransportMetricsImpl;
import org.apache.http.impl.io.SessionInputBufferImpl;
import org.apache.http.io.HttpMessageParser;
import org.apache.http.message.BasicHttpEntityEnclosingRequest;
public class HttpExample {
// RFC 822
public static void main(String[] args) throws IOException, HttpException {
String str = "POST http://localhost:8080/foobar/1234567 HTTP/1.1\n" +
"Message-ID: <19815303.1075861029555.JavaMail.ss@kk>\n" +
"Date: Wed, 6 Mar 2010 12:32:20 -0800 (PST)\n" +
"From: someone@someotherplace.com\n" +
"To: someone@someplace.com\n" +
"Subject: some subject\n" +
"Mime-Version: 1.0\n" +
"Content-Type: text/plain; charset=us-ascii\n" +
"Content-Transfer-Encoding: 7bit\n" +
"X-From: one, some <some.one@someotherplace.com>\n" +
"X-To: one\n" +
"X-cc: \n" +
"X-bcc: \n" +
"X-Origin: Bob-R\n" +
"X-FileName: rbob (Non-Privileged).pst\n" +
"\n" +
"some message\n";
ByteArrayInputStream fakeStream = new ByteArrayInputStream(
str.getBytes());
HttpTransportMetricsImpl metrics = new HttpTransportMetricsImpl();
SessionInputBufferImpl inbuffer = new SessionInputBufferImpl(metrics, 1024);
inbuffer.bind(fakeStream);
HttpMessageParser<HttpRequest> requestParser =
new DefaultHttpRequestParser(inbuffer);
BasicHttpEntityEnclosingRequest request = (BasicHttpEntityEnclosingRequest)requestParser.parse();
for (Header hdr : request.getAllHeaders()) {
System.out.println(String.format("%-30s = %s", hdr.getName(), hdr.getValue()));
}
System.out.println(String.format("Request Line: %s", request.getRequestLine()));
System.out.println(String.format("Body\n------------------\n%s",
request.getEntity()));
}
}
输出如下所示:
Message-ID = <19815303.1075861029555.JavaMail.ss@kk>
Date = Wed, 6 Mar 2010 12:32:20 -0800 (PST)
From = someone@someotherplace.com
To = someone@someplace.com
Subject = some subject
Mime-Version = 1.0
Content-Type = text/plain; charset=us-ascii
Content-Transfer-Encoding = 7bit
X-From = one, some <some.one@someotherplace.com>
X-To = one
X-cc =
X-bcc =
X-Origin = Bob-R
X-FileName = rbob (Non-Privileged).pst
Request Line: POST http://localhost:8080/foobar/1234567 HTTP/1.1
Body
------------------
null
我想不通的是如何访问消息的 body。
我希望它具有 some message\n
我在 BasicHttpEntityEnclosingRequest
中找不到任何方法可以给我这个值。在我使用的早期版本中
HttpRequest request = requestParser.parse();
而不是
BasicHttpEntityEnclosingRequest request =
(BasicHttpEntityEnclosingRequest) requestParser.parse();
我将其更改为 BasicHttpEntityEnclosingRequest
,因为它具有 getEntity
方法。但是那个 returns null
.
所以我有点迷茫。
我在哪里可以找到 body?
我认为问题可能是从您的消息中不清楚 headers body 的长度是多少,因此接收方只是忽略它。 HTTP specification 定义了几个关于如何传达此信息的选项,其中 none 似乎适用于此处:
Content-Transfer-Encoding
必须是Transfer-Encoding
7bit
不在 the standard options. 中
- 当您使用
str.getBytes()
时,它会为您提供未在Content-Type
. 中声明的
us-ascii
的 UTF-16 字节
所以,我会稍微更改一下您的要求:
- 使用header
Content-Type: text/plain; charset=UTF-16
- 删除header
Content-Transfer-Encoding
- 添加
Content-Lenght: 28
(28 为"some message\n".getBytes().length()
)。
查看 DefaultHttpRequestParser 的源代码,它似乎只解析请求行和 headers,它不会尝试解析 body。
This thread is discussing the same topic. There are few solution proposals as well.
我添加了Content-Lengthheader,否则解析器会忽略POSTbody。我修改了你的代码,现在它解析 body 就好了:
package org.apache.http.examples;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import org.apache.http.Header;
import org.apache.http.HttpException;
import org.apache.http.message.BasicHttpEntityEnclosingRequest;
import org.apache.http.util.EntityUtils;
public class HttpExample {
// RFC 822
public static void main(String[] args) throws IOException, HttpException {
String str = "POST http://localhost:8080/foobar/1234567 HTTP/1.1\n" +
"Message-ID: <19815303.1075861029555.JavaMail.ss@kk>\n" +
"Date: Wed, 6 Mar 2010 12:32:20 -0800 (PST)\n" +
"From: someone@someotherplace.com\n" +
"To: someone@someplace.com\n" +
"Subject: some subject\n" +
"Mime-Version: 1.0\n" +
"Content-Type: text/plain; charset=us-ascii\n" +
"Content-Transfer-Encoding: 7bit\n" +
"X-From: one, some <some.one@someotherplace.com>\n" +
"X-To: one\n" +
"X-cc: \n" +
"X-bcc: \n" +
"X-Origin: Bob-R\n" +
"X-FileName: rbob (Non-Privileged).pst\n" +
"Content-Length: 13\n" +
"\n" +
"some message\n";
ByteArrayInputStream fakeStream = new ByteArrayInputStream(
str.getBytes());
BHttpConnectionBaseImpl b = new BHttpConnectionBaseImpl(fakeStream);
BasicHttpEntityEnclosingRequest request1 = (BasicHttpEntityEnclosingRequest) b.receiveRequestHeader();
b.receiveRequestEntity(request1);
for (Header hdr : request1.getAllHeaders()) {
System.out.println(String.format("%-30s = %s", hdr.getName(), hdr.getValue()));
}
System.out.println(String.format("Request Line: %s", request1.getRequestLine()));
System.out.println(String.format("Body\n------------------\n%s",
EntityUtils.toString( request1.getEntity() ) ));
}
}
class BHttpConnectionBaseImpl extends org.apache.http.impl.DefaultBHttpServerConnection{
private InputStream inputStream;
public BHttpConnectionBaseImpl(final InputStream inputStream) {
super(4048);
this.inputStream = inputStream;
try {
super.bind(new Socket());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
protected InputStream getSocketInputStream(final Socket socket) throws IOException {
return inputStream;
}
@Override
protected OutputStream getSocketOutputStream(final Socket socket) throws IOException {
return new ByteArrayOutputStream();
}
}
POST body 的解析发生在 org.apache.http.impl.BHttpConnectionBase.prepareInput(HttpMessage)
中,无论其唯一的构造函数是 protected 并且需要很多参数。 child org.apache.http.impl.DefaultBHttpServerConnection
有一个方便的 public 构造函数,并在 receiveRequestHeader()
中进行 header 解析。我重载的方法需要绕过一些错误检查,例如如果 Socket == null
并且能够从 fakeStream
另一种可能有效的方法是覆盖 Socket
,尤其是它的 getInputStream()
和 getOutputStream()
,尽管我还没有测试过。然后创建一个 DefaultBHttpServerConnection
的实例并调用它的 bind
方法。其余的应该是一样的。
通过覆盖行解析器自定义解析头:
inbuffer = new SessionInputBufferImpl(new HttpTransportMetricsImpl(), reqDataLength);
inbuffer.bind(input);
HttpMessageParser<org.apache.http.HttpRequest> requestParser = new DefaultHttpRequestParser(
inbuffer,
new LineParser(),
new DefaultHttpRequestFactory(),
MessageConstraints.DEFAULT
);
获取实体主体如下:
HttpEntityEnclosingRequest ereq = (HttpEntityEnclosingRequest) req;
ContentLengthStrategy contentLengthStrategy =
StrictContentLengthStrategy.INSTANCE;
long len = contentLengthStrategy.determineLength(req);
InputStream contentStream = null;
if (len == ContentLengthStrategy.CHUNKED) {
contentStream = new ChunkedInputStream(buf);
} else if (len == ContentLengthStrategy.IDENTITY) {
contentStream = new IdentityInputStream(buf);
} else {
contentStream = new ContentLengthInputStream(buf, len);
}
BasicHttpEntity ent = new BasicHttpEntity();
ent.setContent(contentStream);
ereq.setEntity(ent);
return ereq;