máy chủ HTTP đơn giản trong Java chỉ sử dụng API Java SE


333

Có cách nào để tạo một máy chủ HTTP rất cơ bản (chỉ hỗ trợ GET / POST) trong Java bằng cách sử dụng API Java SE mà không cần viết mã để phân tích các yêu cầu HTTP theo cách thủ công và định dạng các phản hồi HTTP theo cách thủ công không? API Java SE đóng gói độc đáo chức năng máy khách HTTP trong kết nối HTTPURLC, nhưng có một chức năng tương tự cho chức năng máy chủ HTTP không?

Nói rõ hơn, vấn đề tôi gặp phải với rất nhiều ví dụ về ServerSocket mà tôi thấy trên mạng là họ tự phân tích cú pháp / định dạng phản hồi và xử lý lỗi, rất tẻ nhạt, dễ bị lỗi và không có khả năng toàn diện, và tôi đang cố gắng tránh nó vì những lý do đó.

Như một ví dụ về thao tác HTTP thủ công mà tôi đang cố gắng tránh:

http://java.sun.com/developer/technicalArticles/Networking/Webserver/WebServercode.html


3
Umm ... câu trả lời ngắn gọn là không. Nếu bạn muốn một cái gì đó xử lý bài đăng và nhận yêu cầu mà không cần viết tiêu đề http theo cách thủ công thì bạn có thể sử dụng servlets. Nhưng đó là java ee. Nếu bạn không muốn sử dụng một cái gì đó như thế thì ổ cắm và phân tích thủ công là lựa chọn duy nhất tôi biết.
Matt Phillips

3
Tôi biết điều này không theo tinh thần của SO, nhưng tôi mong bạn nên xem xét lại sự chán ghét của bạn đối với API Java EE. Như một số câu trả lời đã đề cập, có một số triển khai rất đơn giản như Cầu tàu cho phép bạn nhúng máy chủ web vào ứng dụng độc lập của mình trong khi vẫn tận dụng được api của servlet. Nếu bạn hoàn toàn không thể sử dụng API Java EE vì một số lý do hơn xin vui lòng bỏ qua nhận xét của tôi :-)
Chris Thompson

1
"Servlets" không thực sự là "Java EE". Chúng chỉ là một cách viết các plugin có thể được gọi bởi ứng dụng xung quanh để đáp ứng với hoạt động của tin nhắn (ngày nay, nói chung là các yêu cầu HTTP). Cung cấp một môi trường lưu trữ servlet "chỉ sử dụng API Java SE" chính xác là những gì Jetty và Tomcat làm. Tất nhiên bạn có thể muốn loại bỏ sự phức tạp không mong muốn nhưng sau đó bạn có thể cần phải quyết định một tập hợp con của các thuộc tính và cấu hình được phép của GET / POST. Mặc dù vậy, nó thường không có giá trị, ngoại trừ các vấn đề bảo mật / nhúng đặc biệt.
David Tonhofer

1
Có thể đáng để đi qua danh sách các máy chủ http này trước khi đưa ra quyết định. java-source.net/open-source/web-servers
ThreaT

Câu trả lời:


469

Kể từ Java SE 6, có một máy chủ HTTP dựng sẵn trong Sun Oracle JRE. Các com.sun.net.httpservertóm tắt gói phác thảo các lớp tham gia và chứa các ví dụ.

Đây là một ví dụ khởi động được sao chép từ tài liệu của họ (cho tất cả mọi người đang cố gắng chỉnh sửa nó, vì đó là một đoạn mã xấu xí, xin đừng, đây là một bản sao chép, không phải của tôi, hơn nữa bạn không bao giờ nên chỉnh sửa trích dẫn trừ khi họ đã thay đổi trong nguồn gốc). Bạn chỉ có thể sao chép 'không được' trên 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();
        }
    }

}

Đáng chú ý là response.length()phần trong ví dụ của họ là xấu, nó nên có response.getBytes().length. Ngay cả sau đó,getBytes() phương thức phải chỉ định rõ ràng bộ ký tự mà sau đó bạn chỉ định trong tiêu đề phản hồi. Than ôi, mặc dù sai lầm cho người mới bắt đầu, sau tất cả chỉ là một ví dụ khởi động cơ bản.

Thực thi nó và truy cập http: // localhost: 8000 / test và bạn sẽ thấy phản hồi sau:

Đây là câu trả lời


Đối với việc sử dụng com.sun.*các lớp, hãy lưu ý rằng điều này trái ngược với những gì một số nhà phát triển nghĩ, tuyệt đối không bị cấm bởi Câu hỏi thường gặp nổi tiếng Tại sao Nhà phát triển không nên viết các chương trình gọi các gói 'mặt trời' . Câu hỏi thường gặp đó liên quan đến sun.*gói (chẳng hạn sun.misc.BASE64Encoder) đối với việc sử dụng nội bộ của Oracle JRE (do đó sẽ giết ứng dụng của bạn khi bạn chạy nó trên một JRE khác), chứ không phải com.sun.*gói. Sun / Oracle cũng chỉ tự phát triển phần mềm trên API Java SE giống như mọi công ty khác như Apache, v.v. Việc sử dụng com.sun.*các lớp chỉ được khuyến khích (nhưng không bị cấm) khi nó liên quan đến việc triển khai một API Java nhất định, chẳng hạn như GlassFish (Java EE impl), Mojarra (JSF impl), Jersey (JAX-RS impl), v.v.


19
@Waldheinz: giống như @Software bạn đang bối rối sun.*với com.sun.*. Chẳng hạn, bạn có thấy tài liệu nào về sun.*API không? Nhìn vào đây: java.sun.com/products/jdk/faq/faq-sun-packages.html Nó có nói gì com.sun.*không? Phần mềm com.sun.*này chỉ được sử dụng cho phần mềm công cộng của riêng họ mà không phải là một phần của API Java. Họ cũng phát triển phần mềm trên API Java, giống như mọi công ty khác.
BalusC

4
Tôi nghĩ rằng đây là một máy chủ http rất đẹp để sử dụng trong các trường hợp thử nghiệm tích hợp. cảm ơn vì gợi ý
Andreas Petersson

13
Nếu bạn đang sử dụng Eclipse và gặp lỗi như "Hạn chế truy cập: Loại httpExchange không truy cập được do hạn chế trên thư viện bắt buộc ...", stackoverflow.com/a/10642163 cho biết cách vô hiệu hóa kiểm tra truy cập đó.
Samuli Pahaoja

13
FWIW này cũng có mặt trong OpenJDK.
Jason C

6
Các lớp được đề cập ở đây được gắn thẻ @jdk.Exportedtrong mã nguồn OpenJDK, có nghĩa là API được coi là công khai và sẽ có sẵn trên Java 9 (một số com.sun.*gói khác sẽ không khả dụng do Project Jigsaw).
Jules

42

Thủ tục thanh toán NanoHttpd

"NanoHTTPD là một máy chủ HTTP trọng lượng nhẹ được thiết kế để nhúng vào các ứng dụng khác, được phát hành theo giấy phép BSD đã sửa đổi.

Nó đang được phát triển tại Github và sử dụng Apache Maven để xây dựng & thử nghiệm đơn vị "


4
Một lưu ý: Có khả năng NanoHTTPD không có bảo vệ chống lại các cuộc tấn công đi bộ trên cây - bạn nên kiểm tra điều này nếu nó sẽ được phục vụ trên một địa chỉ công cộng. Bằng cách này, tôi có nghĩa là các cuộc tấn công trong đó một yêu cầu như GET /../../blahblah http/1.1được đưa ra và máy chủ đi trên gốc trang web và vào vùng đất tệp hệ thống, phục vụ các tệp có thể được sử dụng để thỏa hiệp hoặc tấn công hệ thống từ xa, như tệp mật khẩu.
Lawrence Dol

7
Điều đó dường như được sửa chữa. Phiên bản hiện tại tạo 403 nếu (uri.startsWith ("..") || uri.endsWith ("..") || uri.indexOf ("../")> = 0).
Lena Schimmel

5
Tôi không hiểu làm thế nào đây là một câu trả lời cho câu hỏi này.
kimathie

28

Các com.sun.net.httpserver giải pháp không phải là di động trên JRE. Tốt hơn hết là sử dụng API dịch vụ web chính thức trong javax.xml.ws để khởi động máy chủ HTTP tối thiểu ...

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)

EDIT: điều này thực sự hoạt động! Các mã trên trông giống như Groovy hoặc một cái gì đó. Đây là bản dịch sang Java mà tôi đã thử nghiệm:

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
+1 để có thể mang theo. Quá tệ, bạn không thể đặt loại nội dung phản hồi như vậy text/xml.
icza

1
Tôi nghĩ rằng bạn có thể thực hiện <code> class Server thực hiện Nhà cung cấp <DataSource> {</ code> ... và sau đó chỉ định Kiểu nội dung trong phương thức <code> getContentType () </ code> của DataSource. Ngoài ra, bạn cũng có thể tiêm WebServiceContext: <code> @Resource WebServiceContext ctx; </ code> để đặt các tiêu đề khác và đọc các tham số yêu cầu. Thật không may, thiết lập kiểu nội dung thông qua WebServiceContext không hoạt động.
gruenewa

4
Bạn có thể giải thích lý do tại sao com.sun.net.HttpServer không thể di động trên các JRE không?
javabeangrinder

3
Không, tôi không nghĩ vậy. Nó sẽ không hoạt động trên triển khai Java của IBM và có thể cả những thứ khác. Và ngay cả khi nó hoạt động ngay bây giờ, các API nội bộ vẫn được phép thay đổi. Tại sao không sử dụng API chính thức?
gruenewa

1
Liên kết này: docs.oracle.com/javase/9/docs/api/java.xml.ws-summary.html nói rằng mô-đun java.xml.ws không được dùng nữa kể từ Java 9.
Erel Segal-Halevi

22

Tôi thích câu hỏi này bởi vì đây là một lĩnh vực liên tục đổi mới và luôn cần có một máy chủ nhẹ đặc biệt là khi nói về các máy chủ nhúng trong các thiết bị (er) nhỏ. Tôi nghĩ rằng câu trả lời rơi vào hai nhóm rộng.

  1. Thin-server : máy chủ nội dung tĩnh với xử lý tối thiểu, xử lý bối cảnh hoặc phiên.
  2. Máy chủ nhỏ : bề ngoài có vẻ như có nhiều phẩm chất máy chủ giống như httpD với dấu chân nhỏ như bạn có thể thoát khỏi.

Trong khi tôi có thể xem xét các thư viện HTTP như: Cầu tàu , Thành phần HTTP , Netty và các thư viện khác giống như một cơ sở xử lý HTTP thô. Việc ghi nhãn rất chủ quan và phụ thuộc vào loại điều bạn đã gọi để cung cấp cho các trang web nhỏ. Tôi làm cho sự khác biệt này theo tinh thần của câu hỏi, đặc biệt là nhận xét về ...

  • "... không cần viết mã để phân tích các yêu cầu HTTP theo cách thủ công và định dạng thủ công các phản hồi HTTP ..."

Những công cụ thô này cho phép bạn làm điều đó (như được mô tả trong các câu trả lời khác). Họ không thực sự cho mình một phong cách sẵn sàng để tạo ra một máy chủ nhẹ, nhúng hoặc máy chủ mini. Máy chủ mini là thứ có thể cung cấp cho bạn chức năng tương tự như máy chủ web đầy đủ chức năng (như nói, Tomcat ) không có chuông và còi, âm lượng thấp, hiệu suất tốt 99% thời gian. Một máy chủ mỏng dường như gần với cụm từ gốc hơn một chút so với thô có lẽ với chức năng tập hợp con hạn chế, đủ để khiến bạn trông đẹp đến 90%. Ý tưởng về nguyên liệu của tôi sẽ khiến tôi trông ổn 75% - 89% thời gian mà không cần thiết kế thêm và mã hóa. Tôi nghĩ rằng nếu / khi bạn đạt đến cấp độ tệp WAR, chúng tôi đã để lại "nhỏ" cho các máy chủ bonsi trông giống như mọi thứ mà một máy chủ lớn làm nhỏ hơn.

Tùy chọn máy chủ mỏng

Tùy chọn máy chủ mini:

  • Spark Java ... Những điều tốt là có thể với rất nhiều cấu trúc của trình trợ giúp như Bộ lọc, Mẫu, v.v.
  • MadVoc ... nhằm mục đích trở thành cây cảnh và cũng có thể là như vậy ;-)

Trong số những điều khác cần xem xét, tôi bao gồm xác thực, xác thực, quốc tế hóa, sử dụng một cái gì đó như FreeMaker hoặc công cụ mẫu khác để kết xuất trang. Mặt khác, việc quản lý chỉnh sửa và tham số hóa HTML có thể giúp làm việc với HTTP trông giống như noughts-n-cross. Đương nhiên tất cả phụ thuộc vào mức độ linh hoạt của bạn. Nếu đó là một máy FAX điều khiển bằng menu, nó có thể rất đơn giản. Càng nhiều tương tác, khung của bạn càng ' dày ' hơn . Câu hỏi hay, chúc may mắn!


21

Hãy xem máy chủ web "Cầu tàu" Cầu tàu . Phần mềm nguồn mở tuyệt vời dường như đáp ứng mọi yêu cầu của bạn.

Nếu bạn khăng khăng tự lăn thì hãy xem lớp "httpMessage".


Tôi nghĩ api cầu tàu phụ thuộc vào servlet.
chối cãi

4
@Irreputable: Không, Jetty là một máy chủ web có tính mô-đun cao, có một thùng chứa servlet là một trong những mô-đun tùy chọn.
Lawrence Dol

"Là một chức năng tương tự cho chức năng máy chủ" - vâng, đó là API "servlet". Container servlet gọi lớp của bạn sau khi nó phân tích cú pháp các tiêu đề, cookie, v.v.
James Anderson

1
Chỉ dành cho bản ghi - Jetty đi kèm với việc triển khai API Servlet của riêng nó và hoạt động tốt với Java SE
James Anderson

4
Cầu tàu quá lớn và có quá nhiều đường cong học tập trước khi sử dụng sản xuất thực tế trở thành một khả năng.
ThreaT

18

Đã có lúc tôi tìm kiếm một thứ tương tự - một máy chủ HTTP nhẹ nhưng đầy đủ chức năng mà tôi có thể dễ dàng nhúng và tùy chỉnh. Tôi tìm thấy hai loại giải pháp tiềm năng:

  • Máy chủ đầy đủ không quá nhẹ hoặc đơn giản (đối với định nghĩa cực kỳ nhẹ.)
  • Các máy chủ thực sự nhẹ không phải là máy chủ HTTP, nhưng các ví dụ ServerSocket được tôn vinh thậm chí không tuân thủ RFC từ xa và không hỗ trợ chức năng cơ bản thường cần.

Vì vậy, ... tôi bắt đầu viết JLHTTP - Máy chủ HTTP nhẹ Java .

Bạn có thể nhúng nó vào bất kỳ dự án nào dưới dạng tệp nguồn đơn (nếu khá dài) hoặc dưới dạng jar ~ 50K (~ 35K bị tước) mà không phụ thuộc. Nó phấn đấu để tuân thủ RFC và bao gồm tài liệu mở rộng và nhiều tính năng hữu ích trong khi vẫn giữ sự phình to ở mức tối thiểu.

Các tính năng bao gồm: máy chủ ảo, phân phát tệp từ đĩa, ánh xạ loại mime thông qua tệp mime.types tiêu chuẩn, tạo chỉ mục thư mục, tệp chào mừng, hỗ trợ cho tất cả các phương thức HTTP, hỗ trợ tiêu đề có điều kiện và hỗ trợ tiêu đề If- *, mã hóa chuyển mã khối, gzip / khử nén, HTTPS cơ bản (do JVM cung cấp), nội dung một phần (tiếp tục tải xuống), xử lý đa dữ liệu / biểu mẫu để tải lên tệp, nhiều trình xử lý ngữ cảnh thông qua API hoặc chú thích, phân tích tham số (chuỗi truy vấn hoặc x-www-form-urlencoding cơ thể), v.v.

Tôi hy vọng những người khác thấy nó hữu ích :-)


Phương pháp chính là một ví dụ tốt về sử dụng cơ bản và Câu hỏi thường gặp đi vào nhiều chi tiết. Nếu bạn có đề xuất cải thiện các tài liệu hiện có, vui lòng liên hệ trực tiếp với tôi!
amichair



8

Có thể tạo một httpserver cung cấp hỗ trợ cơ bản cho các máy chủ J2EE chỉ với JDK và api của servlet chỉ trong một vài dòng mã.

Tôi đã thấy điều này rất hữu ích cho các máy chủ thử nghiệm đơn vị, vì nó bắt đầu nhanh hơn nhiều so với các container nhẹ khác (chúng tôi sử dụng cầu tàu để sản xuất).

Hầu hết các httpservers rất nhẹ không cung cấp hỗ trợ cho các servlet, nhưng chúng tôi cần chúng, vì vậy tôi nghĩ tôi muốn chia sẻ.

Ví dụ dưới đây cung cấp hỗ trợ servlet cơ bản, hoặc ném và UnsupportedOperationException cho những thứ chưa được triển khai. Nó sử dụng com.sun.net.httpserver.HttpServer để hỗ trợ http cơ bản.

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());
    }
}

Điều này thiếu một số phương thức trên ServletOutputStream và ServletInputStream
HomeIsWhereThePcIs

phiên bản mới hơn của aplet servlet, trên phù hợp với 3.0 trở xuống. Chỉ cần thêm methdos bị thiếu khi cần vào ví dụ
f.carlsen

6

Tôi hoàn toàn có thể khuyên bạn nên tìm hiểu về Simple , đặc biệt nếu bạn không cần các khả năng của Servlet mà chỉ cần truy cập vào các đối tượng yêu cầu / phản hồi. Nếu bạn cần REST, bạn có thể đặt Jersey lên trên nó, nếu bạn cần xuất HTML hoặc tương tự có Freemarker. Tôi thực sự thích những gì bạn có thể làm với sự kết hợp này và có khá ít API để học.


+1. Tôi thích những ý tưởng đằng sau Simple. Tuy nhiên, có vấn đề xảy ra khi cố gắng sử dụng HTTPS vì Mamba lấy đi tính năng "có thể nhúng" từ Simple.
ThreaT

6

Mã này tốt hơn mã của chúng tôi, bạn chỉ cần thêm 2 lib : javax.servelet.jarorg.mortbay.jetty.jar .

Cầu tàu lớp:

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);
        }

    }
} 

Lớp 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();
    }
}

2
Câu hỏi yêu cầu một giải pháp Java SE hoàn toàn. Bạn sẽ thấy cầu tàu triển khai API Java EE.
Sridhar

Cầu tàu chạy hoàn toàn tốt khi sử dụng Java SE tiêu chuẩn và do đó phù hợp với các yêu cầu. Nó triển khai các phần của API Java EE, nó không cần nó. Có một sự khác biệt.
David Tonhofer

1
Điều này không đủ điều kiện. "Chỉ sử dụng API Java SE" . *.Servlet.jar*.jetty.jarrõ ràng không phải là một phần của Java SE.
icza

tôi có cần thiết lập cầu tàu không? hoặc tôi có thể chỉ loại bỏ hai lọ đó và chạy tệp này?
Paul Preibisch


4

Tất cả các câu trả lời ở trên chi tiết về Trình xử lý yêu cầu theo luồng chính.

cài đặt:

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

Cho phép phục vụ nhiều yêu cầu thông qua nhiều luồng sử dụng dịch vụ thực thi.

Vì vậy, mã kết thúc sẽ là một cái gì đó như dưới đây:

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();
        }
    }
}

3

kiểm tra đơn giản . nó là một máy chủ có thể nhúng khá đơn giản với sự hỗ trợ tích hợp cho khá nhiều hoạt động. Tôi đặc biệt thích mô hình luồng của nó ..

Kinh ngạc!



2

Làm thế nào về dự án Apache Commons HttpCore ?

Từ trang web: ... Mục tiêu httpCore

  • Thực hiện các khía cạnh vận chuyển HTTP cơ bản nhất
  • Cân bằng giữa hiệu suất tốt và sự rõ ràng & tính biểu cảm của API
  • Dấu chân bộ nhớ nhỏ (có thể dự đoán)
  • Thư viện độc lập (không phụ thuộc bên ngoài ngoài JRE)

Đó có lẽ là quá thấp. Ít nhất người ta nên nhắm đến một giải pháp gọi mã của bạn ở cấp API servlet, trừ khi người ta muốn tự mình xử lý tất cả các khái niệm như chunking, mã hóa, v.v. Nó có thể là niềm vui mặc dù.
David Tonhofer

2

Hãy thử https://github.com/devashish234073/Java-Socket-Http-Server/blob/master/README.md

API này đã tạo một máy chủ HTTP bằng cách sử dụng các socket.

  1. Nó nhận được yêu cầu từ trình duyệt dưới dạng văn bản
  2. Phân tích nó để lấy thông tin URL, phương thức, thuộc tính, v.v.
  3. Tạo phản hồi động bằng cách sử dụng ánh xạ URL được xác định
  4. Gửi phản hồi đến trình duyệt.

Ví dụ: đây là cách mà hàm tạo trong Response.javalớp chuyển đổi một phản hồi thô thành một phản hồi 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;
}

1

Bạn có thể viết một máy chủ Java Jetty nhúng khá đơn giản .

Jetty nhúng có nghĩa là máy chủ (Cầu tàu) được vận chuyển cùng với ứng dụng thay vì triển khai ứng dụng trên máy chủ Jetty bên ngoài.

Vì vậy, nếu theo cách tiếp cận không được nhúng, ứng dụng web của bạn được tích hợp vào tệp WAR được triển khai cho một số máy chủ bên ngoài ( Tomcat / Jetty / etc), trong Jetty nhúng, bạn viết ứng dụng web và khởi tạo máy chủ cầu cảng trong cùng một cơ sở mã.

Một ví dụ cho máy chủ Java Jetty nhúng, bạn có thể git clone và sử dụng: https://github.com/stas-slu/embedded-jetty-java-server-example

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.