src/Pure/General/http.scala
author wenzelm
Thu, 02 Mar 2017 16:23:39 +0100
changeset 65087 11e332c4e39f
parent 65086 548efa2bda66
child 65088 18f2d388fab4
permissions -rw-r--r--
clarified errors;
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
65077
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
     1
/*  Title:      Pure/General/http.scala
63823
ca8b737b08cf minimal HTTP server;
wenzelm
parents:
diff changeset
     2
    Author:     Makarius
ca8b737b08cf minimal HTTP server;
wenzelm
parents:
diff changeset
     3
65077
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
     4
HTTP server support.
63823
ca8b737b08cf minimal HTTP server;
wenzelm
parents:
diff changeset
     5
*/
ca8b737b08cf minimal HTTP server;
wenzelm
parents:
diff changeset
     6
ca8b737b08cf minimal HTTP server;
wenzelm
parents:
diff changeset
     7
package isabelle
ca8b737b08cf minimal HTTP server;
wenzelm
parents:
diff changeset
     8
ca8b737b08cf minimal HTTP server;
wenzelm
parents:
diff changeset
     9
65078
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    10
import java.net.{InetAddress, InetSocketAddress, URI}
63823
ca8b737b08cf minimal HTTP server;
wenzelm
parents:
diff changeset
    11
import com.sun.net.httpserver.{HttpExchange, HttpHandler, HttpServer}
ca8b737b08cf minimal HTTP server;
wenzelm
parents:
diff changeset
    12
65079
8a5c2b86c5f6 handler for Isabelle fonts;
wenzelm
parents: 65078
diff changeset
    13
import scala.collection.immutable.SortedMap
8a5c2b86c5f6 handler for Isabelle fonts;
wenzelm
parents: 65078
diff changeset
    14
63823
ca8b737b08cf minimal HTTP server;
wenzelm
parents:
diff changeset
    15
65077
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
    16
object HTTP
63823
ca8b737b08cf minimal HTTP server;
wenzelm
parents:
diff changeset
    17
{
65081
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
    18
  /** server **/
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
    19
65078
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    20
  /* response */
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    21
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    22
  object Response
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    23
  {
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    24
    def apply(
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    25
        bytes: Bytes = Bytes.empty,
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    26
        content_type: String = "application/octet-stream"): Response =
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    27
      new Response(bytes, content_type)
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    28
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    29
    val empty: Response = apply()
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    30
    def text(s: String): Response = apply(Bytes(s), "text/plain; charset=utf-8")
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    31
    def html(s: String): Response = apply(Bytes(s), "text/html; charset=utf-8")
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    32
  }
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    33
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    34
  class Response private[HTTP](val bytes: Bytes, val content_type: String)
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    35
  {
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    36
    override def toString: String = bytes.toString
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    37
  }
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    38
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    39
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    40
  /* exchange */
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    41
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    42
  class Exchange private[HTTP](val http_exchange: HttpExchange)
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    43
  {
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    44
    def request_method: String = http_exchange.getRequestMethod
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    45
    def request_uri: URI = http_exchange.getRequestURI
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    46
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    47
    def read_request(): Bytes =
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    48
      using(http_exchange.getRequestBody)(Bytes.read_stream(_))
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    49
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    50
    def write_response(code: Int, response: Response)
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    51
    {
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    52
      http_exchange.getResponseHeaders().set("Content-Type", response.content_type)
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    53
      http_exchange.sendResponseHeaders(code, response.bytes.length.toLong)
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    54
      using(http_exchange.getResponseBody)(response.bytes.write_stream(_))
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    55
    }
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    56
  }
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    57
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    58
65077
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
    59
  /* handler */
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
    60
65080
wenzelm
parents: 65079
diff changeset
    61
  class Handler private[HTTP](val root: String, val handler: HttpHandler)
65076
8a96ab58f016 clarified signature;
wenzelm
parents: 63823
diff changeset
    62
  {
65080
wenzelm
parents: 65079
diff changeset
    63
    override def toString: String = root
65076
8a96ab58f016 clarified signature;
wenzelm
parents: 63823
diff changeset
    64
  }
8a96ab58f016 clarified signature;
wenzelm
parents: 63823
diff changeset
    65
65080
wenzelm
parents: 65079
diff changeset
    66
  def handler(root: String, body: Exchange => Unit): Handler =
wenzelm
parents: 65079
diff changeset
    67
    new Handler(root, new HttpHandler { def handle(x: HttpExchange) { body(new Exchange(x)) } })
65078
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    68
65080
wenzelm
parents: 65079
diff changeset
    69
  def get(root: String, body: URI => Option[Response]): Handler =
wenzelm
parents: 65079
diff changeset
    70
    handler(root, http =>
65078
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    71
      {
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    72
        http.read_request()
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    73
        if (http.request_method == "GET")
65087
11e332c4e39f clarified errors;
wenzelm
parents: 65086
diff changeset
    74
          Exn.capture(body(http.request_uri)) match {
11e332c4e39f clarified errors;
wenzelm
parents: 65086
diff changeset
    75
            case Exn.Res(Some(response)) =>
11e332c4e39f clarified errors;
wenzelm
parents: 65086
diff changeset
    76
              http.write_response(200, response)
11e332c4e39f clarified errors;
wenzelm
parents: 65086
diff changeset
    77
            case Exn.Res(None) =>
11e332c4e39f clarified errors;
wenzelm
parents: 65086
diff changeset
    78
              http.write_response(404, Response.empty)
11e332c4e39f clarified errors;
wenzelm
parents: 65086
diff changeset
    79
            case Exn.Exn(ERROR(msg)) =>
11e332c4e39f clarified errors;
wenzelm
parents: 65086
diff changeset
    80
              http.write_response(500, Response.text(Output.error_text(msg)))
11e332c4e39f clarified errors;
wenzelm
parents: 65086
diff changeset
    81
            case Exn.Exn(exn) => throw exn
65078
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    82
          }
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    83
        else http.write_response(400, Response.empty)
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
    84
      })
65076
8a96ab58f016 clarified signature;
wenzelm
parents: 63823
diff changeset
    85
65077
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
    86
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
    87
  /* server */
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
    88
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
    89
  class Server private[HTTP](val http_server: HttpServer)
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
    90
  {
65080
wenzelm
parents: 65079
diff changeset
    91
    def += (handler: Handler) { http_server.createContext(handler.root, handler.handler) }
wenzelm
parents: 65079
diff changeset
    92
    def -= (handler: Handler) { http_server.removeContext(handler.root) }
65077
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
    93
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
    94
    def start: Unit = http_server.start
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
    95
    def stop: Unit = http_server.stop(0)
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
    96
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
    97
    def address: InetSocketAddress = http_server.getAddress
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
    98
    def url: String = "http://" + address.getHostName + ":" + address.getPort
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
    99
    override def toString: String = url
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
   100
  }
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
   101
65081
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   102
  def server(handlers: List[Handler] = isabelle_resources): Server =
63823
ca8b737b08cf minimal HTTP server;
wenzelm
parents:
diff changeset
   103
  {
ca8b737b08cf minimal HTTP server;
wenzelm
parents:
diff changeset
   104
    val localhost = InetAddress.getByName("127.0.0.1")
65077
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
   105
    val http_server = HttpServer.create(new InetSocketAddress(localhost, 0), 0)
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
   106
    http_server.setExecutor(null)
65076
8a96ab58f016 clarified signature;
wenzelm
parents: 63823
diff changeset
   107
65077
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
   108
    val server = new Server(http_server)
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
   109
    for (handler <- handlers) server += handler
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
   110
    server
63823
ca8b737b08cf minimal HTTP server;
wenzelm
parents:
diff changeset
   111
  }
65079
8a5c2b86c5f6 handler for Isabelle fonts;
wenzelm
parents: 65078
diff changeset
   112
8a5c2b86c5f6 handler for Isabelle fonts;
wenzelm
parents: 65078
diff changeset
   113
65081
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   114
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   115
  /** Isabelle resources **/
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   116
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   117
  lazy val isabelle_resources: List[Handler] =
65085
9d53b892140a clarified;
wenzelm
parents: 65084
diff changeset
   118
    List(welcome, fonts())
65081
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   119
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   120
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   121
  /* welcome */
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   122
65085
9d53b892140a clarified;
wenzelm
parents: 65084
diff changeset
   123
  val welcome: Handler =
9d53b892140a clarified;
wenzelm
parents: 65084
diff changeset
   124
    get("/", uri =>
65081
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   125
      if (uri.toString == "/") {
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   126
        val id =
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   127
          Isabelle_System.getenv("ISABELLE_ID") match {
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   128
            case "" => Mercurial.repository(Path.explode("~~")).id()
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   129
            case id => id
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   130
          }
65084
23202c455a3e tuned message;
wenzelm
parents: 65083
diff changeset
   131
        Some(Response.text("Welcome to Isabelle/" + id + ": " + Distribution.version))
65081
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   132
      }
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   133
      else None)
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   134
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   135
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   136
  /* fonts */
65079
8a5c2b86c5f6 handler for Isabelle fonts;
wenzelm
parents: 65078
diff changeset
   137
65086
wenzelm
parents: 65085
diff changeset
   138
  private lazy val html_fonts: SortedMap[String, Bytes] =
65079
8a5c2b86c5f6 handler for Isabelle fonts;
wenzelm
parents: 65078
diff changeset
   139
    SortedMap(
65083
wenzelm
parents: 65081
diff changeset
   140
      Isabelle_System.fonts(html = true).map(path => (path.base.implode -> Bytes.read(path))): _*)
65079
8a5c2b86c5f6 handler for Isabelle fonts;
wenzelm
parents: 65078
diff changeset
   141
8a5c2b86c5f6 handler for Isabelle fonts;
wenzelm
parents: 65078
diff changeset
   142
  def fonts(root: String = "/fonts"): Handler =
8a5c2b86c5f6 handler for Isabelle fonts;
wenzelm
parents: 65078
diff changeset
   143
  {
8a5c2b86c5f6 handler for Isabelle fonts;
wenzelm
parents: 65078
diff changeset
   144
    get(root, uri =>
8a5c2b86c5f6 handler for Isabelle fonts;
wenzelm
parents: 65078
diff changeset
   145
      {
8a5c2b86c5f6 handler for Isabelle fonts;
wenzelm
parents: 65078
diff changeset
   146
        val uri_name = uri.toString
65086
wenzelm
parents: 65085
diff changeset
   147
        if (uri_name == root) Some(Response.text(cat_lines(html_fonts.map(_._1))))
wenzelm
parents: 65085
diff changeset
   148
        else html_fonts.collectFirst({ case (a, b) if uri_name == root + "/" + a => Response(b) })
65079
8a5c2b86c5f6 handler for Isabelle fonts;
wenzelm
parents: 65078
diff changeset
   149
      })
8a5c2b86c5f6 handler for Isabelle fonts;
wenzelm
parents: 65078
diff changeset
   150
  }
63823
ca8b737b08cf minimal HTTP server;
wenzelm
parents:
diff changeset
   151
}