HTTPリクエストの解釈
先ほどは、リクエスト内容に寄らず常に固定の内容を返すHTTPサーバーを実装しました。 次は、リクエスト内容を読み込み、その内容を解釈できるように改良を加えていきましょう。
コネクションからのデータの読み込みとデータの処理を同時に行うと処理が煩雑になります。 そのため、はじめにイテレータを用いてコネクションからの入力処理を分離しましょう。 イテレータは、繰り返し可能なオブジェクトを表す概念で、要素を順次一つずつ取り出すことができます。 ここでは、コネクションからの行の読み込みをクラスを用いてイテレータ化します。
コネクションから1行ずつ読み込むための処理を HeaderReader
クラスとして実装します。
HeaderReader
クラスは Iterator
と Iterable
の2つのインタフェースを implements
します。
class HeaderReader implements Iterable<String>, Iterator<String> {
Client client;
boolean stop = false;
HeaderReader(Client client) {
this.client = client;
}
Iterator<String> iterator() {
return this;
}
boolean hasNext() {
return !this.stop;
}
String next() {
while (this.client.active()) {
if (this.client.available() > 0) {
String line = this.client.readStringUntil('\n');
if (line != null) {
if (line.equals("\r\n")) {
this.stop = true;
}
return line;
}
}
}
return null;
}
}
Iterator
インタフェースは hasNext
と next
の2つのメソッドを持ちます。
hasNext
メソッドは、イテレータが次の要素を持つかどうかの真理値を返します。
next
メソッドはイテレータから実際に次の値を取り出します。
Iterable
インタフェースは iterator
メソッドを実装し、何らかのイテレーター(Iterator
インタフェースを implements
したオブジェクト)を返します。
ここでは、HeaderReader
クラス自体がイテレーターであるため、HeaderReader
の iterator
メソッドは this
を返すだけです。
HeaderReader
を利用して、コネクションを処理する Handler
クラスを実装しましょう。
class Handler extends Thread {
Client client;
Handler(Client client) {
this.client = client;
}
void run() {
HeaderReader reader = new HeaderReader(this.client);
String[] request = split(reader.next().trim(), ' ');
println("Method : " + request[0]);
println("Path : " + request[1]);
println("Version : " + request[2]);
for (String line : reader) {
String[] header = split(line.trim(), ": ");
if (header.length == 2) {
println(header[0] + " : " + header[1]);
}
}
this.client.write("HTTP/1.1 200 OK\r\n");
this.client.write("\r\n");
this.client.write("hello\r\n");
this.client.stop();
}
}
run
メソッド内で、はじめに HeaderReader
のインスタンスを作成します。
そして next
メソッドを使用して、リクエスト行を取り出します。
リクエスト行は、メソッドとパス、HTTPバージョンの3つをスペース区切りで含みます。
リクエスト行に続き、リクエストヘッダーが1行ずつ続きます。
拡張for文によって、続くヘッダーを HeaderReader
から1行ずつ取り出します。
各ヘッダーには、ヘッダー名と値が「: 」で区切られて渡されます。
ヘッダーを1行ずつ読み込み、ヘッダー名と値を取り出してコンソールに出力していきます。
サーバープログラムの全体は以下のようになります。
import java.util.Iterator;
import processing.net.*;
Server myServer;
void setup() {
myServer = new Server(this, 80);
}
void draw() {
}
void serverEvent(Server server, Client client) {
Handler handler = new Handler(client);
handler.start();
}
class Handler extends Thread {
Client client;
Handler(Client client) {
this.client = client;
}
void run() {
HeaderReader reader = new HeaderReader(this.client);
String[] request = split(reader.next().trim(), ' ');
println("Method : " + request[0]);
println("Path : " + request[1]);
println("Version : " + request[2]);
for (String line : reader) {
String[] header = split(line.trim(), ": ");
if (header.length == 2) {
println(header[0] + " : " + header[1]);
}
}
this.client.write("HTTP/1.1 200 OK\r\n");
this.client.write("\r\n");
this.client.write("hello\r\n");
this.client.stop();
}
}
class HeaderReader implements Iterable<String>, Iterator<String> {
Client client;
boolean stop = false;
HeaderReader(Client client) {
this.client = client;
}
Iterator<String> iterator() {
return this;
}
boolean hasNext() {
return !this.stop;
}
String next() {
while (this.client.active()) {
if (this.client.available() > 0) {
String line = this.client.readStringUntil('\n');
if (line != null) {
if (line.equals("\r\n")) {
this.stop = true;
}
return line;
}
}
}
return null;
}
}