来自 Android 上的 NanoHTTPD 的意外 HTTP 400 状态代码
Unexpected HTTP 400 status code from NanoHTTPD on Android
朋友们!
我的 Android 应用程序中 nanohttpd
偶尔收到意外的 HTTP 400 响应。该错误遵循特定模式。我已经研究了一段时间了,但我已经到了需要不同角度或其他帮助来指引我正确方向的地步。
能否请您看看并分享您的想法甚至直接的观点和建议?
- 为什么我会收到此 HTTP 400 状态代码?
- 为什么只在给定的情况下? (我一点都不想要!)
一些背景知识
我在我的Android项目中运行nanohttpd
作为临时隔离层(由于服务器端还不够成熟)。我已将 nanohttpd
服务器隔离在 Android Service
中,一旦它被创建,我就从我的自定义 Application
对象开始。这样 nanohttpd
不受任何特定 Activity
的生命周期限制,而是可以独立于整个应用程序逻辑和组件生命周期。
问题
现在,(几乎)一切都运行良好且花花公子:我可以开始 nanohttpd
并执行一些初始登录请求,我预期的模拟响应甚至已交付。但是,当我执行第一个 "GET" 请求时,nanohttpd
向我抛出 400 Bad 请求状态, 但只是第一次 。如果我退出 Activity
负责特定的 "GET" 请求,并再次启动它(从主屏幕),它会完美地交付具有 200 状态的预期负载。
到目前为止我做了什么
我仔细查看了 nanohttpd
源代码,试图找出设置此 400 状态的位置和原因。使用此状态代码的地方并不多。粗略地说只有here, here and here。由于我不处理多部分内容,所以我只剩下第一个和第三个 "here"。但是-当然-我一生都找不到 400 状态的根本原因,也找不到哪个确切的块导致了我的状态。当我调试代码时,一切正常。
一些代码
我的 nanohttpd
Service
(MyNanoHttpdService
) 大概是这样的:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (ACTION_START.equals(intent.getAction())) {
String errorMessage = null;
if (myNanoHttpd == null) {
String hostUrl = intent.getStringExtra(EXTRA_HOST);
Uri uri = Utils.notEmpty(hostUrl) ? Uri.parse(hostUrl) : Uri.EMPTY;
myNanoHttpd = new MyNanoHttpd(this, uri.getHost(), uri.getPort(), null);
}
if (!myNanoHttpd.isAlive()) {
try {
myNanoHttpd.start();
} catch (IOException e) {
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
e.printStackTrace(printWriter);
errorMessage = stringWriter.toString();
stopSelf();
}
}
final ResultReceiver resultReceiver = intent.getParcelableExtra(EXTRA_RESULT_LISTENER);
if (resultReceiver != null) {
int status = myNanoHttpd.isAlive() ? CODE_SUCCESS : CODE_FAILURE;
Bundle bundle = new Bundle();
bundle.putString(EXTRA_MESSAGE, errorMessage);
resultReceiver.send(status, bundle);
}
}
return Service.START_STICKY;
}
这就是我从自定义 Application
对象启动服务、初始化客户端状态并获取一些内容的方式:
@Override
public void onCreate() {
super.onCreate();
// Yes, that is a Java 8 Lambda you see there!
MyNanoHttpdService
.start(this, "http://localhost:8080")
.withStartupListener((status, message) -> {
if (status == 0) {
// POST REQUEST: Works like a charm
myNetworkHelper.login();
// GET REQUEST: Always fails on first launch
myNetworkHelper.getContent();
} else {
Log.e("LOG_TAG", "Couldn't start MyNanoHttpd: " + message);
}
});
}
可以安全地假设包装便利代码(.withStartupListener(...)
- 本质上包装了上面 Service
使用的 ResultReceiver
- 和 myNetworkHelper
对象)按预期工作。此外,在生产中,getContent()
调用将从 Activity
或 Fragment
发出,但为了方便起见,我暂时将其移至 Application
。
我可能已经找到问题的根本原因,甚至可能暂时找到解决方法。
如果我的调查是正确的,那么问题是由之前 (POST) 请求中未使用的数据造成的,污染了当前 (POST) 请求。
NanoHTTPD
代码库中的 This 行(NanoHTTPD.HTTPSession.execute()
方法中的 header 解析块,就在调用任何自定义 serve(...)
之前方法 - 我上面问题中的第三个 "here")是抛出 HTTP 400 状态代码的那一行,正如代码所暗示的那样,"method"
[=36= 没有正确的值].
该值 - 我希望以明文形式 "POST" - 被先前请求的 JSON 内容 body 的部分内容污染。一意识到这一点,我就尝试在我的自定义 MyNanoHttpd.serve(IHTTPSession session)
方法中使用整个请求 body,如下所示:
@Override
public Response serve(IHTTPSesion session) {
InputStream inputStream = session.getInputStream();
inputStream.skip(inputStream.available());
// or
// inputStream.skip(Long.MAX_VALUE);
// or even
// inputStream.close();
...
}
但是,这没有用,因为我不断收到各种异常。我最终轻轻地修改了 NanoHTTPD
代码,安全地关闭了非常 NanoHTTPD.HTTPSession.execute()
方法的 finally
块中的输入流。
尽管如此,我正在考虑联系 NanoHTTPD 社区以讨论合适且可持续的解决方案。
朋友们!
我的 Android 应用程序中 nanohttpd
偶尔收到意外的 HTTP 400 响应。该错误遵循特定模式。我已经研究了一段时间了,但我已经到了需要不同角度或其他帮助来指引我正确方向的地步。
能否请您看看并分享您的想法甚至直接的观点和建议?
- 为什么我会收到此 HTTP 400 状态代码?
- 为什么只在给定的情况下? (我一点都不想要!)
一些背景知识
我在我的Android项目中运行nanohttpd
作为临时隔离层(由于服务器端还不够成熟)。我已将 nanohttpd
服务器隔离在 Android Service
中,一旦它被创建,我就从我的自定义 Application
对象开始。这样 nanohttpd
不受任何特定 Activity
的生命周期限制,而是可以独立于整个应用程序逻辑和组件生命周期。
问题
现在,(几乎)一切都运行良好且花花公子:我可以开始 nanohttpd
并执行一些初始登录请求,我预期的模拟响应甚至已交付。但是,当我执行第一个 "GET" 请求时,nanohttpd
向我抛出 400 Bad 请求状态, 但只是第一次 。如果我退出 Activity
负责特定的 "GET" 请求,并再次启动它(从主屏幕),它会完美地交付具有 200 状态的预期负载。
到目前为止我做了什么
我仔细查看了 nanohttpd
源代码,试图找出设置此 400 状态的位置和原因。使用此状态代码的地方并不多。粗略地说只有here, here and here。由于我不处理多部分内容,所以我只剩下第一个和第三个 "here"。但是-当然-我一生都找不到 400 状态的根本原因,也找不到哪个确切的块导致了我的状态。当我调试代码时,一切正常。
一些代码
我的 nanohttpd
Service
(MyNanoHttpdService
) 大概是这样的:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (ACTION_START.equals(intent.getAction())) {
String errorMessage = null;
if (myNanoHttpd == null) {
String hostUrl = intent.getStringExtra(EXTRA_HOST);
Uri uri = Utils.notEmpty(hostUrl) ? Uri.parse(hostUrl) : Uri.EMPTY;
myNanoHttpd = new MyNanoHttpd(this, uri.getHost(), uri.getPort(), null);
}
if (!myNanoHttpd.isAlive()) {
try {
myNanoHttpd.start();
} catch (IOException e) {
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
e.printStackTrace(printWriter);
errorMessage = stringWriter.toString();
stopSelf();
}
}
final ResultReceiver resultReceiver = intent.getParcelableExtra(EXTRA_RESULT_LISTENER);
if (resultReceiver != null) {
int status = myNanoHttpd.isAlive() ? CODE_SUCCESS : CODE_FAILURE;
Bundle bundle = new Bundle();
bundle.putString(EXTRA_MESSAGE, errorMessage);
resultReceiver.send(status, bundle);
}
}
return Service.START_STICKY;
}
这就是我从自定义 Application
对象启动服务、初始化客户端状态并获取一些内容的方式:
@Override
public void onCreate() {
super.onCreate();
// Yes, that is a Java 8 Lambda you see there!
MyNanoHttpdService
.start(this, "http://localhost:8080")
.withStartupListener((status, message) -> {
if (status == 0) {
// POST REQUEST: Works like a charm
myNetworkHelper.login();
// GET REQUEST: Always fails on first launch
myNetworkHelper.getContent();
} else {
Log.e("LOG_TAG", "Couldn't start MyNanoHttpd: " + message);
}
});
}
可以安全地假设包装便利代码(.withStartupListener(...)
- 本质上包装了上面 Service
使用的 ResultReceiver
- 和 myNetworkHelper
对象)按预期工作。此外,在生产中,getContent()
调用将从 Activity
或 Fragment
发出,但为了方便起见,我暂时将其移至 Application
。
我可能已经找到问题的根本原因,甚至可能暂时找到解决方法。
如果我的调查是正确的,那么问题是由之前 (POST) 请求中未使用的数据造成的,污染了当前 (POST) 请求。
NanoHTTPD
代码库中的 This 行(NanoHTTPD.HTTPSession.execute()
方法中的 header 解析块,就在调用任何自定义 serve(...)
之前方法 - 我上面问题中的第三个 "here")是抛出 HTTP 400 状态代码的那一行,正如代码所暗示的那样,"method"
[=36= 没有正确的值].
该值 - 我希望以明文形式 "POST" - 被先前请求的 JSON 内容 body 的部分内容污染。一意识到这一点,我就尝试在我的自定义 MyNanoHttpd.serve(IHTTPSession session)
方法中使用整个请求 body,如下所示:
@Override
public Response serve(IHTTPSesion session) {
InputStream inputStream = session.getInputStream();
inputStream.skip(inputStream.available());
// or
// inputStream.skip(Long.MAX_VALUE);
// or even
// inputStream.close();
...
}
但是,这没有用,因为我不断收到各种异常。我最终轻轻地修改了 NanoHTTPD
代码,安全地关闭了非常 NanoHTTPD.HTTPSession.execute()
方法的 finally
块中的输入流。
尽管如此,我正在考虑联系 NanoHTTPD 社区以讨论合适且可持续的解决方案。