Tomcat报错Invalid character found in method name. HTTP method names must be tokens 的处理

标签:

本文出自jvm123.com-java技术分享站:http://jvm123.com/2019/10/invalid-character-found.html

项目在启动后,无缘无故报错,大意就是说tomcat接收到了不合法的字符,在解析http请求的方法时出错了,完整报错如下:

o.apache.coyote.http11.Http11Processor   : Error parsing HTTP request header
  Note: further occurrences of HTTP header parsing errors will be logged at DEBUG level.
 java.lang.IllegalArgumentException: Invalid character found in method name. HTTP method names must be tokens
     at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:430) ~[tomcat-embed-core-8.5.43.jar:8.5.43]
     at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:684) ~[tomcat-embed-core-8.5.43.jar:8.5.43]
     at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.43.jar:8.5.43]
     at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:808) [tomcat-embed-core-8.5.43.jar:8.5.43]
     at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498) [tomcat-embed-core-8.5.43.jar:8.5.43]
     at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.43.jar:8.5.43]
     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_102]
     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_102]
     at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.43.jar:8.5.43]
     at java.lang.Thread.run(Thread.java:745) [na:1.8.0_102]

处理方法:

换端口,目前已知普通用户使用8888端口会报错,可能与常用的内网,或者某些常用软件通信有关。

原因分析:

既然是tomcat收到不合法的字符,所以我写了一个简单的 nio socket 服务器,想看看究竟收到了什么,nio socket 服务器代码如下,如果使用 ServerSocket 则接收不到,所以这个报错应该是出现在支持nio的tomcat服务器上。

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

public class NioServer {

    public static void main(String[] args) throws IOException {

        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(8888));
        serverSocketChannel.configureBlocking(false);

        Selector selector = Selector.open();
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
            // 注册完之后Selector就可以通过select方法来等等请求,
            // select方法有一个long类型的参数,代表最长等待时间,
            // 如果在这段时间里接收到了相应操作的请求则返回可以处理
            // 的请求的数量,否则在超时后返回0
//            // eg: 如果没接收到请求,10000毫秒后回返回0
//            int num = selector.select(10000L);
            int num = selector.select(); // 没有超时时间
//            System.out.println("selector获取到" + num + "个channel");
            if (num == 0) continue;
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();
                iterator.remove();
                boolean connectable = selectionKey.isConnectable();
                boolean valid = selectionKey.isValid();
                boolean acceptable = selectionKey.isAcceptable();
                boolean readable = selectionKey.isReadable();
                boolean writable = selectionKey.isWritable();
//                System.out.println(connectable ? "connectable" : "");
//                System.out.println(valid ? "valid" : "");
//                System.out.println(acceptable ? "acceptable" : "");
//                System.out.println(readable ? "readable" : "");
//                System.out.println(writable ? "writable" : "");

                if (acceptable) {
                    System.out.println("连接成功!");
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    socketChannel.configureBlocking(false);
                    ByteBuffer byteBuffer = Charset.forName("UTF-8").encode("你已经成功连接服务器!");
                    socketChannel.write(byteBuffer);
                    socketChannel.register(selector, SelectionKey.OP_READ);
                }

                if (readable) {
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                    socketChannel.configureBlocking(false);
                    try {
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        // 写入buffer
                        socketChannel.read(buffer);
                        // 从buffer读取并转换为char
                        buffer.flip();
                        System.out.println(Charset.defaultCharset().decode(buffer));
                    } catch (Exception e) {
                    }
                    socketChannel.register(selector, SelectionKey.OP_WRITE);
                }

                if (writable) {
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                    socketChannel.configureBlocking(false);
                    try {
                        socketChannel.write(Charset.defaultCharset().encode("server已经收到你的信息。"));
                        System.out.println("server write");
                    } catch (Exception e) {
                    }
                    socketChannel.register(selector, SelectionKey.OP_READ);
                }

            }
        }

    }
}

启动之后,每隔几秒就会收到如下字符:

Tomcat报错Invalid character found in method name. HTTP method names must be tokens 的处理插图

这很明显不是http请求的格式,tomcat解析当然会出错。

然后我在浏览器请求了localhost:8888/test123,则会打印处http请求的内容,如下:

Tomcat报错Invalid character found in method name. HTTP method names must be tokens 的处理插图(1)

验证:

之后,我将 nio server 的端口修改为8000,重启后,等待一段时间,并未收到任何信息。由此说明,在网络中,有设备在向我电脑的8888端口发送信息,而且这些信息比较短,则很可能是检测心跳的信息。

而我把项目的端口换为8000之后,也不会出现这个错误了。

发表评论