Newer
Older
myNWP / src / j3 / Thttpd.java
@motoki miura motoki miura on 13 Sep 2022 6 KB fix
package j3;
/** いんちきHTTPサーバThttpd.java スレッド対応版

このプログラムはポート番号8000番で動作するサーバです。
使い方: java j3.Thttpd ファイル名 ポート番号
WWWクライアントからの接続に対して、引数で指定したファイルを返します。
また、そのファイルがあるフォルダを、DocumentRootとします。
 */

// ライブラリの利用
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.time.LocalDateTime;
import java.util.Enumeration;

// Thttpdクラス
public class Thttpd {
    public static void main(String args[]) {
        // サーバソケット
        ServerSocket servsock = null;
        Socket sock;

        System.out.println(args.length);

        if (args.length == 0 || args[0].length() < 1) { // 引数がない場合、以下のファイルを返すことにする
            args = new String[] { "j3/index.html", "8000" };// Linuxの場合、通常はcd NWP/srcしているはず
            // args = new String[] { "src/j3/index.html", "8000" }; //
            // windowsの場合、user.dir=NWP
        }
        int portnum = Integer.parseInt(args[1]);
        System.out.println("このサーバは" + args[0] + "を返します。ポート番号は " + portnum + " です。");

        showUrls(portnum);
        try {
            // サーバ用ソケットの作成(デフォルトでは、ポート番号8000番)
            servsock = new ServerSocket(portnum);
            while (true) {
                sock = servsock.accept();// 接続要求の受付
                new RequestHandler(sock, args[0]);
            }
        } catch (IOException e) {
            System.out.println("異常終了:ポート番号(" + portnum + ")がすでに使われているようです");
            System.exit(1); // 異常終了は1
        }
    }

    /**
     * ネットワークインタフェースを調べて、接続可能なIPアドレスについて表示する
     */
    public static void showUrls(int pnum) {
        Enumeration<NetworkInterface> interfaces;
        try {
            interfaces = NetworkInterface.getNetworkInterfaces();
            while (interfaces.hasMoreElements()) {
                NetworkInterface n = (NetworkInterface) interfaces.nextElement();
                Enumeration<InetAddress> addresses = n.getInetAddresses();
                while (addresses.hasMoreElements()) {
                    InetAddress i = (InetAddress) addresses.nextElement();
                    String ip = i.getHostAddress();
                    String[] seg = ip.split("\\."); // .で文字列分割
                    if (seg.length == 4) // 分割したセグメントが4つあればIPv4
                        System.out.println("http://" + ip + ":" + pnum);
                }
            }
        } catch (SocketException e1) {
            e1.printStackTrace();
        }

    }
}

class RequestHandler implements Runnable {
    Socket sock;
    Thread thread;

    // 入出力
    DataOutputStream dostr;
    BufferedReader in;
    FileInputStream infile = null;
    byte[] buff = new byte[1024];
    String file;

    RequestHandler(Socket s, String file) {
        sock = s;
        this.file = file;
        thread = new Thread(this);
        thread.start();
    }

    public void run() {
        try {
            process();
        } catch (IOException e) {
            e.printStackTrace();
        }
        thread = null;
    }

    void process() throws IOException {
        System.out.println("---\nConnection Requst from: " + (sock.getInetAddress()));
        // 読み書き用オブジェクトの生成
        in = new BufferedReader(new InputStreamReader(sock.getInputStream()));
        dostr = new DataOutputStream(sock.getOutputStream());
        // read headers
        StringBuffer request = new StringBuffer();
        String line;
        String[] get = null; // GETリクエストをスペースで区切った配列
        while ((line = in.readLine()) != null) {
            System.out.println(line);
            // 先頭がGETだったら
            if (line.toLowerCase().startsWith("get ")) {
                get = line.split(" "); // GET / HTTP/1.1 をスペースで区切った配列get を得る
            }
            request.append(line + "\r\n");
            if (line.length() < 1)
                break;
        }

        String CRLF = "\r\n";
        // Response Headerの出力
        String response = "HTTP/1.1 200" + CRLF + "Content-type: text/html; charset=UTF-8" + CRLF + CRLF;
        // もし、リクエストがfaviconだったら
        if (get[1].endsWith(".ico")) {
            response = "HTTP/1.1 200" + CRLF + "Content-type: image/x-icon" + CRLF + CRLF;
        } else if (get[1].endsWith(".java")) {
            response = "HTTP/1.1 200" + CRLF + "Content-type: text/plain; charset=UTF-8" + CRLF + CRLF;
        }
        if (!get[1].equals("/")) { // リクエストファイルがあれば、/index.html の部分を書き換える
            file = file.replaceAll("/index.html", get[1]);
        }
        // オブジェクトinfileを作り,ファイルを準備します
        try {
            infile = new FileInputStream(file);
        } catch (Exception e) {
            // ファイル準備の失敗
            System.err.println(file + " ファイルがありません");
            response = "HTTP/1.1 404" + CRLF + CRLF;
        }
        dostr.write(response.getBytes());

        // Response Bodyの出力
        boolean cont = true;
        while (cont) {
            // ファイルからの読み込みとネットワーク出力
            try {
                int n = infile.read(buff);
                dostr.write(buff, 0, n);
            } catch (Exception e) { // end of file
                cont = false;
            }
        }
        if (file.endsWith("index.html")) {
            // おまけ:レスポンスヘッダを表示
            dostr.write(request.toString().getBytes());
            // おまけ:時刻表示
            dostr.write("</pre> \n\n <h2>Current DateTime (Thttpdが動的に生成した情報)</h2>".getBytes());
            dostr.write(LocalDateTime.now().toString().getBytes());
        }

        // 接続終了
        sock.close();
        infile.close();
    }
}