是否有一种方法可以使用Java SE API在Java中创建一个非常基本的HTTP服务器(只支持GET/POST),而不需要编写代码手动解析HTTP请求和手动格式化HTTP响应?Java SE API在HttpURLConnection中很好地封装了HTTP客户机功能,但是是否有类似的HTTP服务器功能呢?

需要明确的是,我在网上看到的许多ServerSocket示例的问题是,它们自己进行请求解析/响应格式化和错误处理,这很乏味,容易出错,而且不太全面,出于这些原因,我试图避免使用它。


当前回答

这是我简单的web服务器,在JMeter中用于测试webhook(这就是为什么它会在收到请求后关闭并结束自己)。

import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class HttpServer {

    private static int extractContentLength(StringBuilder sb) {
        int length = 0;
        String[] lines = sb.toString().split("\\n");
        for (int i = 0; i < lines.length; i++) {
            String s = lines[i];
            if (s.toLowerCase().startsWith("Content-Length:".toLowerCase()) && i <= lines.length - 2) {
                String slength = s.substring(s.indexOf(":") + 1, s.length()).trim();
                length = Integer.parseInt(slength);
                System.out.println("Length = " + length);
                return length;
            }
        }
        return 0;
    }

    public static void main(String[] args) throws IOException {
        
        
        int port = Integer.parseInt(args[0]);
        System.out.println("starting HTTP Server on port " + port);

        StringBuilder outputString = new StringBuilder(1000);

        ServerSocket serverSocket = new ServerSocket(port);
        serverSocket.setSoTimeout(3 * 60 * 1000); // 3 minutes timeout
        while (true) {

            outputString.setLength(0); // reset buff

            Socket clientSocket = serverSocket.accept(); // blocking
            PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);

            try {

                boolean isBodyRead = false;
                int dataBuffer;
                while ((dataBuffer = clientSocket.getInputStream().read()) != -1) {

                    if (dataBuffer == 13) { // CR
                        if (clientSocket.getInputStream().read() == 10) { // LF
                            outputString.append("\n");
                        }
                    } else {
                        outputString.append((char) dataBuffer);
                    }
                    
                    // do we have Content length
                    int len = extractContentLength(outputString);
                    if (len > 0) {
                        int actualLength = len - 1; // we need to substract \r\n
                        for (int i = 0; i < actualLength; i++) {
                            int body = clientSocket.getInputStream().read();
                            outputString.append((char) body);
                        }
                        isBodyRead = true;
                        break;
                    }

                } // end of reading while

                if (isBodyRead) {
                    // response headers
                    out.println("HTTP/1.1 200 OK");
                    out.println("Connection: close");
                    out.println(); // must have empty line for HTTP
                    
                    out.flush(); 
                    out.close(); // close clients connection
                }

            } catch (IOException ioEx) {
                System.out.println(ioEx.getMessage());
            }

            System.out.println(outputString.toString());
            break; // stop server - break while true
            
        } // end of outer while true
        
        serverSocket.close();

    } // end of method

}

你可以这样测试:

curl -X POST -H "Content-Type: application/json" -H "Connection: close" -d '{"name": "gustinmi", "email": "gustinmi at google dot com "}' -v http://localhost:8081/

其他回答

看看“Jetty”web服务器Jetty。一流的开源软件,似乎可以满足您的所有要求。

如果你坚持要创建自己的类,那么可以看看“httpMessage”类。

httpserver解决方案不能跨jre移植。最好使用javax.xml.ws中的官方webservices API来引导一个最小的HTTP服务器…

import java.io._
import javax.xml.ws._
import javax.xml.ws.http._
import javax.xml.transform._
import javax.xml.transform.stream._

@WebServiceProvider
@ServiceMode(value=Service.Mode.PAYLOAD) 
class P extends Provider[Source] {
  def invoke(source: Source) = new StreamSource( new StringReader("<p>Hello There!</p>"));
}

val address = "http://127.0.0.1:8080/"
Endpoint.create(HTTPBinding.HTTP_BINDING, new P()).publish(address)

println("Service running at "+address)
println("Type [CTRL]+[C] to quit!")

Thread.sleep(Long.MaxValue)

编辑:这实际上是工作!上面的代码看起来像Groovy之类的。以下是我测试的Java翻译:

import java.io.*;
import javax.xml.ws.*;
import javax.xml.ws.http.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;

@WebServiceProvider
@ServiceMode(value = Service.Mode.PAYLOAD)
public class Server implements Provider<Source> {

    public Source invoke(Source request) {
        return  new StreamSource(new StringReader("<p>Hello There!</p>"));
    }

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

        String address = "http://127.0.0.1:8080/";
        Endpoint.create(HTTPBinding.HTTP_BINDING, new Server()).publish(address);

        System.out.println("Service running at " + address);
        System.out.println("Type [CTRL]+[C] to quit!");

        Thread.sleep(Long.MAX_VALUE);
    }
}

从Java 11开始,旧的com.sun.net.httpserver再次成为一个公开且被接受的API。你可以把它作为HttpServer类,作为jdk的一部分。httpserver模块。参见https://docs.oracle.com/en/java/javase/11/docs/api/jdk.httpserver/com/sun/net/httpserver/HttpServer.html

这个类实现了一个简单的HTTP服务器。HttpServer绑定到一个IP地址和端口号,并侦听来自该地址上客户端的传入TCP连接。子类httpserver实现了一个处理HTTPS请求的服务器。

因此,除了它的局限性之外,没有理由再避免使用它了。

我使用它在服务器应用程序中发布控件接口。从客户端请求读取User-agent头,我甚至以文本/纯方式响应CLI工具,如curl或以更优雅的HTML方式响应任何其他浏览器。

很酷很简单。

曾经有一段时间,我正在寻找类似的东西——一个轻量级但功能齐全的HTTP服务器,我可以轻松地嵌入和定制。我发现了两种可能的解决方案:

不是那么轻量级或简单的完整服务器(对于轻量级的极端定义)。 真正的轻量级服务器不是HTTP服务器,而是华丽的ServerSocket示例,甚至不远程兼容rfc,不支持通常需要的基本功能。

所以…我开始编写JLHTTP——Java轻量级HTTP服务器。

您可以将它作为单个(如果相当长)源文件嵌入到任何项目中,或者作为一个~50K的jar (~35K剥离),没有依赖关系。它努力与rfc兼容,包括大量的文档和许多有用的特性,同时将膨胀保持在最低限度。

Features include: virtual hosts, file serving from disk, mime type mappings via standard mime.types file, directory index generation, welcome files, support for all HTTP methods, conditional ETags and If-* header support, chunked transfer encoding, gzip/deflate compression, basic HTTPS (as provided by the JVM), partial content (download continuation), multipart/form-data handling for file uploads, multiple context handlers via API or annotations, parameter parsing (query string or x-www-form-urlencoded body), etc.

我希望其他人会觉得有用:-)

一个非常基本的HTTP服务器在TCP套接字级别的例子:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class NaiveHttpServer {

  public static void main(String[] args) throws IOException {
    String hostname = InetAddress.getLocalHost().getHostName();
    ServerSocket serverSocket = new ServerSocket(8089);
    while (true) {
      Socket clientSocket = serverSocket.accept();
      PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
      BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
      String s = in.readLine();
      System.out.println(s);
      while ("\r\n".equals(in.readLine())); 
      if ("GET /hostname HTTP/1.1".equals(s)) {
        out.println("HTTP/1.1 200 OK");
        out.println("Connection: close");
        out.println("Content-Type: text/plain");
        out.println("Content-Length:" + hostname.length());
        out.println();
        out.println(hostname);
      } else {
        out.println("HTTP/1.1 404 Not Found");
        out.println("Connection: close");
        out.println();    
      }
      out.flush();
    }
  }
}

该示例提供计算机的主机名。