Java中的简单HTTP服务器,仅使用Java SE API

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

澄清一下,我在网上看到的很多ServerSocket例子的问题是,他们自己进行请求解析/响应格式化和错误处理,这很乏味,容易出错,而且不太可能是全面的,出于这些原因,我试图避免它。

486430 次浏览

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

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

查看NanoHttpd

NanoHTTPD是一个轻量级的HTTP服务器,设计用于嵌入其他应用程序,在Modified BSD许可下发布。

它正在Github开发,并使用Apache Maven进行构建&单位testing"

自Java SE 6以来,在太阳 Oracle JRE中有一个内置的HTTP服务器。Java 9模块名是jdk.httpservercom.sun.net.httpserver包摘要概述了所涉及的类并包含示例。

下面是他们文档中的一个启动示例copypasted。你可以复制,粘贴,然后在Java 6+上运行。
(尽管如此,所有试图编辑它的人,因为它是一段丑陋的代码,请不要,这是一个复制粘贴,不是我的,此外,你不应该编辑引文,除非它们在原始来源中发生了变化)子> < /

package com.stackoverflow.q3732109;


import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;


import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;


public class Test {


public static void main(String[] args) throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
server.createContext("/test", new MyHandler());
server.setExecutor(null); // creates a default executor
server.start();
}


static class MyHandler implements HttpHandler {
@Override
public void handle(HttpExchange t) throws IOException {
String response = "This is the response";
t.sendResponseHeaders(200, response.length());
OutputStream os = t.getResponseBody();
os.write(response.getBytes());
os.close();
}
}


}

应该注意的是,他们的例子中的response.length()部分是坏的,它应该是response.getBytes().length。即使这样,getBytes()方法也必须显式地指定你在响应头中指定的字符集。唉,尽管对初学者有误导,但毕竟这只是一个基本的启动示例。

执行它并访问http://localhost:8000/test,你将看到以下响应:

这是反应


至于使用com.sun.*类,请注意,与一些开发人员的想法相反,这是绝对没有被众所周知的常见问题为什么开发者不应该编写调用“sun”包的程序禁止的。这个常见问题涉及到Oracle JRE内部使用的sun.*包(例如sun.misc.BASE64Encoder),而不是com.sun.*包(当你在不同的JRE上运行应用程序时,它会杀死你的应用程序)。Sun/Oracle也只是在Java SE API上开发软件,就像其他公司一样,比如Apache等等。此外,这个特定的HttpServer必须出现在每个JDK中,因此绝对没有“可移植性”的方法。类似sun.*包会发生的问题。使用com.sun.*类只是气馁(而不是被禁止的),当它涉及到某个Java API的sun.*0时,比如GlassFish (Java EE impl)、Mojarra (JSF impl)、Jersey (JAX-RS impl)等。

我强烈建议考虑简单的,特别是如果你不需要Servlet功能,而只是访问请求/响应对象。如果你需要REST,你可以把Jersey放在上面,如果你需要输出HTML或类似的东西,有Freemarker。我真的很喜欢使用这个组合可以做的事情,而且需要学习的API相对较少。

你也可以看看一些NIO应用框架,比如:

  1. 网状的:http://jboss.org/netty
  2. Apache Mina: http://mina.apache.org/或其子项目AsyncWeb: http://mina.apache.org/asyncweb/

这段代码比我们的代码更好,你只需要添加2个库:javax.servelet.jarorg.mortbay.jetty.jar

类码头:

package jetty;


import java.util.logging.Level;
import java.util.logging.Logger;
import org.mortbay.http.SocketListener;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.servlet.ServletHttpContext;


public class Jetty {


public static void main(String[] args) {
try {
Server server = new Server();
SocketListener listener = new SocketListener();


System.out.println("Max Thread :" + listener.getMaxThreads() + " Min Thread :" + listener.getMinThreads());


listener.setHost("localhost");
listener.setPort(8070);
listener.setMinThreads(5);
listener.setMaxThreads(250);
server.addListener(listener);


ServletHttpContext context = (ServletHttpContext) server.getContext("/");
context.addServlet("/MO", "jetty.HelloWorldServlet");


server.start();
server.join();


/*//We will create our server running at http://localhost:8070
Server server = new Server();
server.addListener(":8070");


//We will deploy our servlet to the server at the path '/'
//it will be available at http://localhost:8070
ServletHttpContext context = (ServletHttpContext) server.getContext("/");
context.addServlet("/MO", "jetty.HelloWorldServlet");


server.start();
*/


} catch (Exception ex) {
Logger.getLogger(Jetty.class.getName()).log(Level.SEVERE, null, ex);
}


}
}

Servlet类:

package jetty;


import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class HelloWorldServlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException
{
String appid = httpServletRequest.getParameter("appid");
String conta = httpServletRequest.getParameter("conta");


System.out.println("Appid : "+appid);
System.out.println("Conta : "+conta);


httpServletResponse.setContentType("text/plain");
PrintWriter out = httpServletResponse.getWriter();
out.println("Hello World!");
out.close();
}
}

Apache Commons HttpCore项目怎么样?

来自网站:… HttpCore目标< / p >

  • 实现最基本的HTTP传输方面
  • 良好的性能和清晰度之间的平衡&的表达能力 李API < / >
  • 小(可预测的)内存占用
  • 自包含库(没有JRE以外的外部依赖)

只需几行代码就可以创建一个仅使用JDK和servlet api提供J2EE servlet基本支持的httpserver。

我发现这对于单元测试servlet非常有用,因为它启动速度比其他轻量级容器快得多(我们在生产中使用jetty)。

大多数非常轻量级的httpserver都不支持servlet,但是我们需要它们,所以我想分享一下。

下面的示例提供了基本的servlet支持,或者为尚未实现的内容抛出和UnsupportedOperationException异常。它使用com.sun.net.httpserver.HttpServer来提供基本的http支持。

import java.io.*;
import java.lang.reflect.*;
import java.net.InetSocketAddress;
import java.util.*;


import javax.servlet.*;
import javax.servlet.http.*;


import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;


@SuppressWarnings("deprecation")
public class VerySimpleServletHttpServer {
HttpServer server;
private String contextPath;
private HttpHandler httpHandler;


public VerySimpleServletHttpServer(String contextPath, HttpServlet servlet) {
this.contextPath = contextPath;
httpHandler = new HttpHandlerWithServletSupport(servlet);
}


public void start(int port) throws IOException {
InetSocketAddress inetSocketAddress = new InetSocketAddress(port);
server = HttpServer.create(inetSocketAddress, 0);
server.createContext(contextPath, httpHandler);
server.setExecutor(null);
server.start();
}


public void stop(int secondsDelay) {
server.stop(secondsDelay);
}


public int getServerPort() {
return server.getAddress().getPort();
}


}


final class HttpHandlerWithServletSupport implements HttpHandler {


private HttpServlet servlet;


private final class RequestWrapper extends HttpServletRequestWrapper {
private final HttpExchange ex;
private final Map<String, String[]> postData;
private final ServletInputStream is;
private final Map<String, Object> attributes = new HashMap<>();


private RequestWrapper(HttpServletRequest request, HttpExchange ex, Map<String, String[]> postData, ServletInputStream is) {
super(request);
this.ex = ex;
this.postData = postData;
this.is = is;
}


@Override
public String getHeader(String name) {
return ex.getRequestHeaders().getFirst(name);
}


@Override
public Enumeration<String> getHeaders(String name) {
return new Vector<String>(ex.getRequestHeaders().get(name)).elements();
}


@Override
public Enumeration<String> getHeaderNames() {
return new Vector<String>(ex.getRequestHeaders().keySet()).elements();
}


@Override
public Object getAttribute(String name) {
return attributes.get(name);
}


@Override
public void setAttribute(String name, Object o) {
this.attributes.put(name, o);
}


@Override
public Enumeration<String> getAttributeNames() {
return new Vector<String>(attributes.keySet()).elements();
}


@Override
public String getMethod() {
return ex.getRequestMethod();
}


@Override
public ServletInputStream getInputStream() throws IOException {
return is;
}


@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}


@Override
public String getPathInfo() {
return ex.getRequestURI().getPath();
}


@Override
public String getParameter(String name) {
String[] arr = postData.get(name);
return arr != null ? (arr.length > 1 ? Arrays.toString(arr) : arr[0]) : null;
}


@Override
public Map<String, String[]> getParameterMap() {
return postData;
}


@Override
public Enumeration<String> getParameterNames() {
return new Vector<String>(postData.keySet()).elements();
}
}


private final class ResponseWrapper extends HttpServletResponseWrapper {
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
final ServletOutputStream servletOutputStream = new ServletOutputStream() {


@Override
public void write(int b) throws IOException {
outputStream.write(b);
}
};


private final HttpExchange ex;
private final PrintWriter printWriter;
private int status = HttpServletResponse.SC_OK;


private ResponseWrapper(HttpServletResponse response, HttpExchange ex) {
super(response);
this.ex = ex;
printWriter = new PrintWriter(servletOutputStream);
}


@Override
public void setContentType(String type) {
ex.getResponseHeaders().add("Content-Type", type);
}


@Override
public void setHeader(String name, String value) {
ex.getResponseHeaders().add(name, value);
}


@Override
public javax.servlet.ServletOutputStream getOutputStream() throws IOException {
return servletOutputStream;
}


@Override
public void setContentLength(int len) {
ex.getResponseHeaders().add("Content-Length", len + "");
}


@Override
public void setStatus(int status) {
this.status = status;
}


@Override
public void sendError(int sc, String msg) throws IOException {
this.status = sc;
if (msg != null) {
printWriter.write(msg);
}
}


@Override
public void sendError(int sc) throws IOException {
sendError(sc, null);
}


@Override
public PrintWriter getWriter() throws IOException {
return printWriter;
}


public void complete() throws IOException {
try {
printWriter.flush();
ex.sendResponseHeaders(status, outputStream.size());
if (outputStream.size() > 0) {
ex.getResponseBody().write(outputStream.toByteArray());
}
ex.getResponseBody().flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
ex.close();
}
}
}


public HttpHandlerWithServletSupport(HttpServlet servlet) {
this.servlet = servlet;
}


@SuppressWarnings("deprecation")
@Override
public void handle(final HttpExchange ex) throws IOException {
byte[] inBytes = getBytes(ex.getRequestBody());
ex.getRequestBody().close();
final ByteArrayInputStream newInput = new ByteArrayInputStream(inBytes);
final ServletInputStream is = new ServletInputStream() {


@Override
public int read() throws IOException {
return newInput.read();
}
};


Map<String, String[]> parsePostData = new HashMap<>();


try {
parsePostData.putAll(HttpUtils.parseQueryString(ex.getRequestURI().getQuery()));


// check if any postdata to parse
parsePostData.putAll(HttpUtils.parsePostData(inBytes.length, is));
} catch (IllegalArgumentException e) {
// no postData - just reset inputstream
newInput.reset();
}
final Map<String, String[]> postData = parsePostData;


RequestWrapper req = new RequestWrapper(createUnimplementAdapter(HttpServletRequest.class), ex, postData, is);


ResponseWrapper resp = new ResponseWrapper(createUnimplementAdapter(HttpServletResponse.class), ex);


try {
servlet.service(req, resp);
resp.complete();
} catch (ServletException e) {
throw new IOException(e);
}
}


private static byte[] getBytes(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
while (true) {
int r = in.read(buffer);
if (r == -1)
break;
out.write(buffer, 0, r);
}
return out.toByteArray();
}


@SuppressWarnings("unchecked")
private static <T> T createUnimplementAdapter(Class<T> httpServletApi) {
class UnimplementedHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
throw new UnsupportedOperationException("Not implemented: " + method + ", args=" + Arrays.toString(args));
}
}


return (T) Proxy.newProxyInstance(UnimplementedHandler.class.getClassLoader(),
new Class<?>[] { httpServletApi },
new UnimplementedHandler());
}
}

com.sun.net.httpserver解决方案不能跨JREs移植。最好在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);
}
}

我喜欢这个问题,因为这是一个不断创新的领域,总是需要有一个轻型服务器,特别是当谈到小型设备中的嵌入式服务器时。我认为答案可以分为两大类。

  1. 瘦服务器:服务器上的静态内容,具有最小的处理,上下文或会话处理。
  2. 小型服务器:表面上a具有许多类似httpd的服务器品质,并且占用的空间尽可能小。

虽然我可能认为HTTP库,如:码头Apache Http组件网状的和其他更像一个原始的HTTP处理设施。标签是非常主观的,取决于你被要求为小网站提供的东西的种类。我是根据问题的精神,特别是关于……

  • "...无需编写代码手动解析HTTP请求和手动格式化HTTP响应……”

这些原始工具可以让您做到这一点(如其他答案所述)。它们并不真正适合于制作轻便、嵌入式或迷你服务器的现成风格。迷你服务器可以提供与全功能web服务器(比如Tomcat)类似的功能,没有铃声和口哨,低容量,99%的时间性能良好。瘦服务器看起来更接近最初的说法,只是比原始的稍微多一点,可能只有有限的子集功能,足以让您在90%的时间内看起来不错。我对原始的想法是让我在没有额外设计和编码的情况下,在75% - 89%的时间里看起来很好。我认为如果/当你达到WAR文件的级别时,我们把“小”留给bonsi服务器,看起来像一个大服务器做的所有事情都更小。

瘦服务器选项

Mini-server选项:

  • 火花Java……有了很多帮助器结构,比如过滤器、模板等,就有可能做得很好。
  • MadVoc……目标是成为盆景,而且很可能是这样;-)

在其他需要考虑的事情中,我将包括身份验证、验证、国际化,使用诸如FreeMaker或其他模板工具来呈现页面输出。否则,管理HTML编辑和参数化可能会使使用HTTP看起来像划叉。当然,这完全取决于你需要多灵活。如果它是一个菜单驱动的传真机,它可以非常简单。交互越多,你的框架就需要'更厚的'。好问题,祝你好运!

Spark是最简单的,这里有一个快速开始指南:http://sparkjava.com/

付款简单的。它是一个非常简单的嵌入式服务器,内置了对各种操作的支持。我特别喜欢它的线程模型..

神奇的!

查看takes。查看https://github.com/yegor256/takes以获得快速信息

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

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

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

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

功能包括:虚拟主机,文件服务从磁盘,mime类型映射通过标准mime。类型文件,目录索引生成,欢迎文件,支持所有HTTP方法,条件ETags和If-*头支持,分块传输编码,gzip/deflate压缩,基本HTTPS(由JVM提供),部分内容(下载继续),文件上传的多部分/表单数据处理,通过API或注释的多个上下文处理程序,参数解析(查询字符串或x-www-form-urlencoded body)等。

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

你可以编写一个非常简单的嵌入式Jetty Java服务器。

嵌入式Jetty意味着服务器(Jetty)与应用程序一起发布,而不是将应用程序部署在外部Jetty服务器上。

因此,如果在非嵌入式方法中,你的webapp内置到WAR文件中,部署到一些外部服务器(Tomcat / Jetty / etc),在嵌入式Jetty中,你在相同的代码库中编写webapp并实例化Jetty服务器。

一个嵌入式Jetty Java服务器的例子,你可以git克隆和使用

试试这个https://github.com/devashish234073/Java-Socket-Http-Server/blob/master/README.md

这个API使用套接字创建了一个HTTP服务器。

  1. 它以文本的形式从浏览器获取请求
  2. 解析它来检索URL信息、方法、属性等。
  3. 使用定义的URL映射创建动态响应
  4. 将响应发送到浏览器。

例如,下面是Response.java类中的构造函数如何将原始响应转换为http响应:

public Response(String resp){
Date date = new Date();
String start = "HTTP/1.1 200 OK\r\n";
String header = "Date: "+date.toString()+"\r\n";
header+= "Content-Type: text/html\r\n";
header+= "Content-length: "+resp.length()+"\r\n";
header+="\r\n";
this.resp=start+header+resp;
}

以上所有的回答关于单主线程请求处理器的细节。

设置:

 server.setExecutor(java.util.concurrent.Executors.newCachedThreadPool());

允许多个请求服务通过多个线程使用执行器服务。

因此,结束代码将如下所示:

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
public class App {
public static void main(String[] args) throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
server.createContext("/test", new MyHandler());
//Thread control is given to executor service.
server.setExecutor(java.util.concurrent.Executors.newCachedThreadPool());
server.start();
}
static class MyHandler implements HttpHandler {
@Override
public void handle(HttpExchange t) throws IOException {
String response = "This is the response";
long threadId = Thread.currentThread().getId();
System.out.println("I am thread " + threadId );
response = response + "Thread Id = "+threadId;
t.sendResponseHeaders(200, response.length());
OutputStream os = t.getResponseBody();
os.write(response.getBytes());
os.close();
}
}
}

自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报头,我甚至在text/plain中响应CLI工具,如curl或以更优雅的HTML方式响应任何其他浏览器。

很酷很简单。

一个非常基本的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();
}
}
}

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

Java 18开始,你可以用Java标准库创建简单的web服务器:

class Main {
public static void main(String[] args) {
var port = 8000;
var rootDirectory = Path.of("C:/Users/Mahozad/Desktop/");
var outputLevel = OutputLevel.VERBOSE;
var server = SimpleFileServer.createFileServer(
new InetSocketAddress(port),
rootDirectory,
outputLevel
);
server.start();
}
}

默认情况下,这将显示指定根目录的目录列表。您可以将index . html文件(以及CSS和JS文件等其他资产)放在该目录中来显示它们。

示例(我把这些放在桌面中,上面指定为我的目录根):

index . html:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Java 18 Simple Web Server</title>
<link rel="stylesheet" href="styles.css">
<style>h1 { color: blue; }</style>
<script src="scripts.js" defer>
let element = document.getElementsByTagName("h1")[0];
element.style.fontSize = "48px";
</script>
</head>
<body>
<h1>I'm <i>index.html</i> in the root directory.</h1>
</body>
</html>

旁注

对于Java标准库HTTP 客户端,请参阅文章Java 11新的HTTP客户端API

这是我简单的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/