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