Last active
December 12, 2015 08:29
-
-
Save relax-more/4744570 to your computer and use it in GitHub Desktop.
[java] Web+DBPress No47より、第3章を写経した
HTTPをWebのしくみ webServerを実装する
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main.java.me.relaxmore.study.servertest.server; | |
import static java.net.HttpURLConnection.HTTP_NOT_FOUND; | |
import static java.net.HttpURLConnection.HTTP_NOT_IMPLEMENTED; | |
import static java.net.HttpURLConnection.HTTP_OK; | |
import java.io.File; | |
import java.io.FileInputStream; | |
import java.io.FileNotFoundException; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.net.URI; | |
/** | |
* HTTPリクエストをハンドリングするクラス。 | |
*/ | |
public class HttpHandler { | |
private final File documentRoot; | |
private final String directoryIndex; | |
public HttpHandler(String path, String directoryIndex) { | |
documentRoot = new File(path); | |
this.directoryIndex = directoryIndex; | |
} | |
/** | |
* HTTPリクエストのハンドリングを開始します。<br> | |
* メソッド毎に処理を振り分けます。<br> | |
* 対応していないメソッドの場合は Status-Code 501 Not Implemented を送信します。<br> | |
* リクエストURIで指定されたファイルがオープンできない場合は Status-Code 404 Not Found を送信します。 | |
* @param req リクエストオブジェクト。 | |
* @param res レスポンスオブジェクト。 | |
* @throws IOException 入出力例外が発生。 | |
*/ | |
public void handle(Request req, Response res) throws IOException { | |
try { | |
if ("GET".equals(req.getMethod())) { | |
doGet(req, res); | |
} else if ("HEAD".equals(req.getMethod())) { | |
doHead(req, res); | |
} else { | |
// 対応していないメソッドの場合 | |
res.sendResponseHeader(HTTP_NOT_IMPLEMENTED); | |
} | |
} catch (FileNotFoundException e) { | |
// 指定されたファイルが見つからない場合 | |
res.sendResponseHeader(HTTP_NOT_FOUND); | |
} | |
} | |
/** | |
* リクエストURIで指定されたファイルを入力ストリームとして取得します。<br> | |
* 入力ストリームがオープンできた場合は Status-Code 200 OK を送信します。 | |
* @param req リクエストオブジェクト。 | |
* @param res レスポンスオブジェクト。 | |
* @return 指定されたファイルの入力ストリーム。 | |
* @throws FileNotFoundException リクエストURIで指定されたファイルをオープンできなかった場合に発生。 | |
* @throws IOException 入出力例外が発生。 | |
*/ | |
private InputStream getContentInputStream(Request req, Response res) | |
throws FileNotFoundException, IOException { | |
File content = getContent(req.getRequestUri()); | |
InputStream is = new FileInputStream(content); | |
res.setHeader("Content-Type", Utils.getMediaType(content)); | |
res.setHeader("Content-Length", Long.toString(content.length())); | |
return is; | |
} | |
/** | |
* リクエストURIで指定されたファイルオブジェクトを取得します。<br> | |
* 指定されたパスがディレクトリの場合は、ディレクトリインデックスファイルを返します。 | |
* @param requestUri リクエストURI。 | |
* @return ファイルオブジェクト。 | |
*/ | |
private File getContent(URI requestUri) { | |
File content = new File(documentRoot, requestUri.getPath()); | |
if (content.isDirectory()) { | |
return new File(content, directoryIndex); | |
} | |
return content; | |
} | |
/** | |
* HEADメソッドを処理します。 | |
* @param req リクエストオブジェクト。 | |
* @param res レスポンスオブジェクト。 | |
* @throws IOException | |
* @throws FileNotFoundException | |
*/ | |
private void doHead(Request req, Response res) throws IOException, | |
FileNotFoundException { | |
getContentInputStream(req, res).close(); | |
res.sendResponseHeader(HTTP_OK); | |
} | |
/** | |
* GETメソッドを処理します。<br> | |
* リクエストURIで指定されたファイルの内容をメッセージ本体に出力します。 | |
* @param req リクエストオブジェクト。 | |
* @param res レスポンスオブジェクト。 | |
* @throws FileNotFoundException リクエストURIで指定されたファイルをオープンできなかった場合に発生。 | |
* @throws IOException 入出力例外が発生。 | |
*/ | |
private void doGet(Request req, Response res) throws FileNotFoundException, | |
IOException { | |
InputStream is = getContentInputStream(req, res); | |
try { | |
res.sendResponseHeader(HTTP_OK); | |
Utils.copy(is, res.getMessageBody()); | |
} finally { | |
is.close(); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main.java.me.relaxmore.study.servertest.server; | |
import java.io.IOException; | |
import java.net.InetSocketAddress; | |
import java.net.ServerSocket; | |
import java.net.Socket; | |
import java.util.concurrent.ExecutorService; | |
import java.util.concurrent.Executors; | |
import main.java.me.relaxmore.study.servertest.server.Request.BadRequestException; | |
public class MyServer { | |
private final ServerSocket socket; | |
private final ExecutorService threads; | |
private final HttpHandler httpHandler; | |
public MyServer(int port, int nThreads, String path, String directoryIndex) throws IOException{ | |
socket = new ServerSocket(port); | |
socket.setReuseAddress(true); | |
socket.bind(new InetSocketAddress(port)); | |
threads = Executors.newFixedThreadPool(nThreads); | |
httpHandler = new HttpHandler(path, directoryIndex); | |
} | |
public void listen(){ | |
while(true){ | |
try { | |
final Socket conn = socket.accept(); | |
threads.execute(new Runnable() { | |
@Override | |
public void run() { | |
handler(conn); | |
} | |
}); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
} | |
} | |
private void handler(Socket conn){ | |
try { | |
Response res = new Response(conn.getOutputStream()); | |
try { | |
Request req = new Request(conn.getInputStream()); | |
httpHandler.handle(req, res); | |
} catch (BadRequestException e) { | |
e.printStackTrace(); | |
}finally{ | |
if(!conn.isClosed()){ | |
conn.close(); | |
} | |
} | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
} | |
public static void main (String[] args) throws IOException{ | |
MyServer server = new MyServer(8081, 150, "anyPath", "index.html"); | |
server.listen(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main.java.me.relaxmore.study.servertest.server; | |
import java.io.BufferedReader; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.io.InputStreamReader; | |
import java.net.URI; | |
import java.net.URISyntaxException; | |
public class Request { | |
private final String method; | |
private final URI requestUri; | |
public Request(InputStream is) throws IOException, BadRequestException { | |
// InputStream を Reader に変換 | |
// リクエストヘッダの文字コードはUS-ASCII | |
final BufferedReader reader = new BufferedReader(new InputStreamReader(is, "US-ASCII")); | |
final String line = reader.readLine(); | |
if (line == null) { | |
// 入力が存在しない | |
throw new BadRequestException(); | |
} | |
String[] items = line.split(" "); | |
if (items.length < 2) { | |
// リクエストラインの構文エラー | |
throw new BadRequestException(); | |
} | |
method = items[0]; | |
try { | |
requestUri = new URI(items[1]); | |
} catch (URISyntaxException e) { | |
// リクエストURIの構文エラー | |
throw new BadRequestException(e); | |
} | |
} | |
public String getMethod() { | |
return method; | |
} | |
public URI getRequestUri() { | |
return requestUri; | |
} | |
public static class BadRequestException extends Exception { | |
private static final long serialVersionUID = 1L; | |
public BadRequestException() { | |
} | |
public BadRequestException(Throwable cause) { | |
super(cause); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main.java.me.relaxmore.study.servertest.server; | |
import java.io.BufferedWriter; | |
import java.io.IOException; | |
import java.io.OutputStream; | |
import java.io.OutputStreamWriter; | |
import java.io.Writer; | |
import java.util.HashMap; | |
import java.util.Map; | |
public class Response { | |
private static final String CRLF = "¥r¥n"; | |
private final OutputStream os; | |
private final Map<String, String> headers = new HashMap<String, String>(); | |
/** | |
* レスポンスヘッダを既に送信したことをあらわす。 | |
*/ | |
private boolean sentResponseHeader = false; | |
public Response(OutputStream os) { | |
this.os = os; | |
setHeader("Server", "MyWebServer/1.0"); | |
setHeader("Connection", "close"); | |
} | |
public void setHeader(String fieldName, String fieldValue) { | |
headers.put(fieldName, fieldValue); | |
} | |
/** | |
* レスポンスヘッダをクライアントに送信します。 | |
* @param statusCode HTTPステータスコード。 | |
* @throws IOException レスポンスヘッダを返す際に入出力例外が発生。 | |
*/ | |
public void sendResponseHeader(int statusCode) throws IOException { | |
// 2回目以降の呼び出しは無視する | |
if (sentResponseHeader) { | |
return; | |
} | |
sentResponseHeader = true; | |
// レスポンスヘッダの文字コードはUS-ASCII | |
Writer writer = new BufferedWriter(new OutputStreamWriter(os, "US-ASCII")); | |
setHeader("Date", Utils.getDate()); | |
sendStatusLine(writer, statusCode); | |
sendHeaders(writer); | |
writer.write(CRLF); | |
writer.flush(); | |
} | |
public OutputStream getMessageBody() { | |
return os; | |
} | |
private void sendStatusLine(Writer writer, int statusCode) | |
throws IOException { | |
writer.write("HTTP/1.1 " + statusCode + " " | |
+ Utils.getStatusPhrase(statusCode) + CRLF); | |
} | |
private void sendHeaders(Writer writer) throws IOException { | |
for (Map.Entry<String, String> e : headers.entrySet()) { | |
writer.write(e.getKey() + ": " + e.getValue() + CRLF); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main.java.me.relaxmore.study.servertest.server; | |
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST; | |
import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR; | |
import static java.net.HttpURLConnection.HTTP_NOT_FOUND; | |
import static java.net.HttpURLConnection.HTTP_NOT_IMPLEMENTED; | |
import static java.net.HttpURLConnection.HTTP_OK; | |
import java.io.BufferedInputStream; | |
import java.io.BufferedOutputStream; | |
import java.io.File; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.io.OutputStream; | |
import java.net.URLConnection; | |
import java.text.DateFormat; | |
import java.text.SimpleDateFormat; | |
import java.util.Collections; | |
import java.util.Date; | |
import java.util.HashMap; | |
import java.util.Locale; | |
import java.util.Map; | |
import java.util.TimeZone; | |
public final class Utils { | |
private static final int COPY_BUFFER_SIZE = 8192; | |
private static final Map<Integer, String> STATUS_PHRASE; | |
/** | |
* ステータスフレーズを初期化します。 | |
*/ | |
static { | |
Map<Integer, String> tmp = new HashMap<Integer, String>(); | |
tmp.put(HTTP_OK, "OK"); | |
tmp.put(HTTP_BAD_REQUEST, "Bad Request"); | |
tmp.put(HTTP_NOT_FOUND, "Not Found"); | |
tmp.put(HTTP_NOT_IMPLEMENTED, "Not Implemented"); | |
tmp.put(HTTP_INTERNAL_ERROR, "Internal Server Error"); | |
STATUS_PHRASE = Collections.unmodifiableMap(tmp); | |
} | |
public static void copy(InputStream is, OutputStream os) throws IOException { | |
InputStream bis = new BufferedInputStream(is); | |
OutputStream bos = new BufferedOutputStream(os); | |
byte[] buffer = new byte[COPY_BUFFER_SIZE]; | |
for (int len; (len = bis.read(buffer)) != -1;) { | |
bos.write(buffer, 0, len); | |
} | |
bos.flush(); | |
} | |
public static String getMediaType(File content) { | |
return URLConnection.getFileNameMap().getContentTypeFor(content.getName()); | |
} | |
public static String getStatusPhrase(int statusCode) { | |
return STATUS_PHRASE.get(statusCode); | |
} | |
public static String getDate() { | |
DateFormat df = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z",Locale.US); | |
df.setTimeZone(TimeZone.getTimeZone("GMT")); | |
return df.format(new Date()); | |
} | |
private Utils() { | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
jakarta commons の FileUpload と Java の FileChannel と | |
http://archive.guma.jp/2009/12/jakarta-commons-fileupload-java-filechannel.html | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment