src/Pure/General/http.scala
author nipkow
Thu, 03 Feb 2022 10:33:55 +0100
changeset 75061 57df04e4f018
parent 74945 4dc90b43ba94
child 75104 08bb0d32b2e3
permissions -rw-r--r--
tuned output syntax: INV and VAR are now blocks
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
73438
69d449f0ca04 tuned comments;
wenzelm
parents: 73430
diff changeset
     4
HTTP client and 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
73417
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    10
import java.io.{File => JFile}
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    11
import java.net.{InetSocketAddress, URI, URL, URLConnection, HttpURLConnection}
63823
ca8b737b08cf minimal HTTP server;
wenzelm
parents:
diff changeset
    12
import com.sun.net.httpserver.{HttpExchange, HttpHandler, HttpServer}
ca8b737b08cf minimal HTTP server;
wenzelm
parents:
diff changeset
    13
ca8b737b08cf minimal HTTP server;
wenzelm
parents:
diff changeset
    14
65077
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
    15
object HTTP
63823
ca8b737b08cf minimal HTTP server;
wenzelm
parents:
diff changeset
    16
{
73416
08aa4c1ed579 clarified signature: more explicit HTTP operations;
wenzelm
parents: 73413
diff changeset
    17
  /** content **/
08aa4c1ed579 clarified signature: more explicit HTTP operations;
wenzelm
parents: 73413
diff changeset
    18
73417
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    19
  val mime_type_bytes: String = "application/octet-stream"
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    20
  val mime_type_text: String = "text/plain; charset=utf-8"
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    21
  val mime_type_html: String = "text/html; charset=utf-8"
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    22
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    23
  val default_mime_type: String = mime_type_bytes
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    24
  val default_encoding: String = UTF8.charset_name
73416
08aa4c1ed579 clarified signature: more explicit HTTP operations;
wenzelm
parents: 73413
diff changeset
    25
73417
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    26
  sealed case class Content(
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    27
    bytes: Bytes,
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    28
    file_name: String = "",
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    29
    mime_type: String = default_mime_type,
73429
8970081c6500 elapsed time to download content (and for the server to provide content);
wenzelm
parents: 73425
diff changeset
    30
    encoding: String = default_encoding,
8970081c6500 elapsed time to download content (and for the server to provide content);
wenzelm
parents: 73425
diff changeset
    31
    elapsed_time: Time = Time.zero)
73416
08aa4c1ed579 clarified signature: more explicit HTTP operations;
wenzelm
parents: 73413
diff changeset
    32
  {
73440
3696bb4085ed tuned signature (again);
wenzelm
parents: 73439
diff changeset
    33
    def text: String = new String(bytes.array, encoding)
74945
4dc90b43ba94 support for Flarum server;
wenzelm
parents: 74094
diff changeset
    34
    def json: JSON.T = JSON.parse(text)
73416
08aa4c1ed579 clarified signature: more explicit HTTP operations;
wenzelm
parents: 73413
diff changeset
    35
  }
08aa4c1ed579 clarified signature: more explicit HTTP operations;
wenzelm
parents: 73413
diff changeset
    36
73417
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    37
  def read_content(file: JFile): Content =
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    38
  {
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    39
    val bytes = Bytes.read(file)
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    40
    val file_name = file.getName
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    41
    val mime_type =
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    42
      Option(URLConnection.guessContentTypeFromName(file_name)).getOrElse(default_mime_type)
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    43
    Content(bytes, file_name = file_name, mime_type = mime_type)
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    44
  }
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    45
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    46
  def read_content(path: Path): Content = read_content(path.file)
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    47
73416
08aa4c1ed579 clarified signature: more explicit HTTP operations;
wenzelm
parents: 73413
diff changeset
    48
08aa4c1ed579 clarified signature: more explicit HTTP operations;
wenzelm
parents: 73413
diff changeset
    49
08aa4c1ed579 clarified signature: more explicit HTTP operations;
wenzelm
parents: 73413
diff changeset
    50
  /** client **/
08aa4c1ed579 clarified signature: more explicit HTTP operations;
wenzelm
parents: 73413
diff changeset
    51
73417
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    52
  val NEWLINE: String = "\r\n"
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    53
73416
08aa4c1ed579 clarified signature: more explicit HTTP operations;
wenzelm
parents: 73413
diff changeset
    54
  object Client
08aa4c1ed579 clarified signature: more explicit HTTP operations;
wenzelm
parents: 73413
diff changeset
    55
  {
73439
cb127ce2c092 tuned --- following hints by IntelliJ;
wenzelm
parents: 73438
diff changeset
    56
    val default_timeout: Time = Time.seconds(180)
73422
fc7a0ea94c43 support timeout, similar to perl LWP::UserAgent;
wenzelm
parents: 73421
diff changeset
    57
fc7a0ea94c43 support timeout, similar to perl LWP::UserAgent;
wenzelm
parents: 73421
diff changeset
    58
    def open_connection(url: URL,
fc7a0ea94c43 support timeout, similar to perl LWP::UserAgent;
wenzelm
parents: 73421
diff changeset
    59
      timeout: Time = default_timeout,
fc7a0ea94c43 support timeout, similar to perl LWP::UserAgent;
wenzelm
parents: 73421
diff changeset
    60
      user_agent: String = ""): HttpURLConnection =
73421
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
    61
    {
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
    62
      url.openConnection match {
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
    63
        case connection: HttpURLConnection =>
73422
fc7a0ea94c43 support timeout, similar to perl LWP::UserAgent;
wenzelm
parents: 73421
diff changeset
    64
          if (0 < timeout.ms && timeout.ms <= Integer.MAX_VALUE) {
fc7a0ea94c43 support timeout, similar to perl LWP::UserAgent;
wenzelm
parents: 73421
diff changeset
    65
            val ms = timeout.ms.toInt
fc7a0ea94c43 support timeout, similar to perl LWP::UserAgent;
wenzelm
parents: 73421
diff changeset
    66
            connection.setConnectTimeout(ms)
fc7a0ea94c43 support timeout, similar to perl LWP::UserAgent;
wenzelm
parents: 73421
diff changeset
    67
            connection.setReadTimeout(ms)
fc7a0ea94c43 support timeout, similar to perl LWP::UserAgent;
wenzelm
parents: 73421
diff changeset
    68
          }
73421
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
    69
          proper_string(user_agent).foreach(s => connection.setRequestProperty("User-Agent", s))
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
    70
          connection
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
    71
        case _ => error("Bad URL (not HTTP): " + quote(url.toString))
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
    72
      }
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
    73
    }
73417
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    74
73421
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
    75
    def get_content(connection: HttpURLConnection): Content =
73417
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    76
    {
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    77
      val Charset = """.*\bcharset="?([\S^"]+)"?.*""".r
73429
8970081c6500 elapsed time to download content (and for the server to provide content);
wenzelm
parents: 73425
diff changeset
    78
8970081c6500 elapsed time to download content (and for the server to provide content);
wenzelm
parents: 73425
diff changeset
    79
      val start = Time.now()
73417
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    80
      using(connection.getInputStream)(stream =>
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    81
      {
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    82
        val bytes = Bytes.read_stream(stream, hint = connection.getContentLength)
73429
8970081c6500 elapsed time to download content (and for the server to provide content);
wenzelm
parents: 73425
diff changeset
    83
        val stop = Time.now()
8970081c6500 elapsed time to download content (and for the server to provide content);
wenzelm
parents: 73425
diff changeset
    84
73417
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    85
        val file_name = Url.file_name(connection.getURL)
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    86
        val mime_type = Option(connection.getContentType).getOrElse(default_mime_type)
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    87
        val encoding =
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    88
          (connection.getContentEncoding, mime_type) match {
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    89
            case (enc, _) if enc != null => enc
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    90
            case (_, Charset(enc)) => enc
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    91
            case _ => default_encoding
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    92
          }
73429
8970081c6500 elapsed time to download content (and for the server to provide content);
wenzelm
parents: 73425
diff changeset
    93
        Content(bytes, file_name = file_name, mime_type = mime_type,
8970081c6500 elapsed time to download content (and for the server to provide content);
wenzelm
parents: 73425
diff changeset
    94
          encoding = encoding, elapsed_time = stop - start)
73417
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    95
      })
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    96
    }
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
    97
73422
fc7a0ea94c43 support timeout, similar to perl LWP::UserAgent;
wenzelm
parents: 73421
diff changeset
    98
    def get(url: URL, timeout: Time = default_timeout, user_agent: String = ""): Content =
fc7a0ea94c43 support timeout, similar to perl LWP::UserAgent;
wenzelm
parents: 73421
diff changeset
    99
      get_content(open_connection(url, timeout = timeout, user_agent = user_agent))
73417
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
   100
73422
fc7a0ea94c43 support timeout, similar to perl LWP::UserAgent;
wenzelm
parents: 73421
diff changeset
   101
    def post(url: URL, parameters: List[(String, Any)],
fc7a0ea94c43 support timeout, similar to perl LWP::UserAgent;
wenzelm
parents: 73421
diff changeset
   102
      timeout: Time = default_timeout,
fc7a0ea94c43 support timeout, similar to perl LWP::UserAgent;
wenzelm
parents: 73421
diff changeset
   103
      user_agent: String = ""): Content =
73416
08aa4c1ed579 clarified signature: more explicit HTTP operations;
wenzelm
parents: 73413
diff changeset
   104
    {
73422
fc7a0ea94c43 support timeout, similar to perl LWP::UserAgent;
wenzelm
parents: 73421
diff changeset
   105
      val connection = open_connection(url, timeout = timeout, user_agent = user_agent)
73421
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   106
      connection.setRequestMethod("POST")
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   107
      connection.setDoOutput(true)
73417
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
   108
74094
6113f1db4342 clarified signature;
wenzelm
parents: 73716
diff changeset
   109
      val boundary = UUID.random().toString
73421
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   110
      connection.setRequestProperty(
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   111
        "Content-Type", "multipart/form-data; boundary=" + quote(boundary))
73417
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
   112
73421
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   113
      using(connection.getOutputStream)(out =>
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   114
      {
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   115
        def output(s: String): Unit = out.write(UTF8.bytes(s))
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   116
        def output_newline(n: Int = 1): Unit = (1 to n).foreach(_ => output(NEWLINE))
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   117
        def output_boundary(end: Boolean = false): Unit =
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   118
          output("--" + boundary + (if (end) "--" else "") + NEWLINE)
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   119
        def output_name(name: String): Unit =
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   120
          output("Content-Disposition: form-data; name=" + quote(name))
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   121
        def output_value(value: Any): Unit =
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   122
        {
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   123
          output_newline(2)
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   124
          output(value.toString)
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   125
        }
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   126
        def output_content(content: Content): Unit =
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   127
        {
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   128
          proper_string(content.file_name).foreach(s => output("; filename=" + quote(s)))
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   129
          output_newline()
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   130
          proper_string(content.mime_type).foreach(s => output("Content-Type: " + s))
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   131
          output_newline(2)
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   132
          content.bytes.write_stream(out)
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   133
        }
73417
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
   134
73421
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   135
        output_newline(2)
73417
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
   136
73421
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   137
        for { (name, value) <- parameters } {
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   138
          output_boundary()
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   139
          output_name(name)
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   140
          value match {
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   141
            case content: Content => output_content(content)
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   142
            case file: JFile => output_content(read_content(file))
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   143
            case path: Path => output_content(read_content(path))
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   144
            case _ => output_value(value)
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   145
          }
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   146
          output_newline()
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   147
        }
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   148
        output_boundary(end = true)
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   149
        out.flush()
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   150
      })
73417
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
   151
73421
a114ecd280ca clarified signature;
wenzelm
parents: 73417
diff changeset
   152
      get_content(connection)
73416
08aa4c1ed579 clarified signature: more explicit HTTP operations;
wenzelm
parents: 73413
diff changeset
   153
    }
08aa4c1ed579 clarified signature: more explicit HTTP operations;
wenzelm
parents: 73413
diff changeset
   154
  }
08aa4c1ed579 clarified signature: more explicit HTTP operations;
wenzelm
parents: 73413
diff changeset
   155
08aa4c1ed579 clarified signature: more explicit HTTP operations;
wenzelm
parents: 73413
diff changeset
   156
08aa4c1ed579 clarified signature: more explicit HTTP operations;
wenzelm
parents: 73413
diff changeset
   157
65081
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   158
  /** server **/
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   159
65078
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   160
  /* response */
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   161
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   162
  object Response
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   163
  {
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   164
    def apply(
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   165
        bytes: Bytes = Bytes.empty,
73417
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
   166
        content_type: String = mime_type_bytes): Response =
65078
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   167
      new Response(bytes, content_type)
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   168
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   169
    val empty: Response = apply()
73417
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
   170
    def text(s: String): Response = apply(Bytes(s), mime_type_text)
1dcc2b228b8b clarified HTTP.Content: support encoding;
wenzelm
parents: 73416
diff changeset
   171
    def html(s: String): Response = apply(Bytes(s), mime_type_html)
65078
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   172
  }
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   173
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   174
  class Response private[HTTP](val bytes: Bytes, val content_type: String)
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   175
  {
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   176
    override def toString: String = bytes.toString
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   177
  }
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   178
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   179
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   180
  /* exchange */
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   181
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   182
  class Exchange private[HTTP](val http_exchange: HttpExchange)
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   183
  {
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   184
    def request_method: String = http_exchange.getRequestMethod
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   185
    def request_uri: URI = http_exchange.getRequestURI
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   186
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   187
    def read_request(): Bytes =
69393
ed0824ef337e static type for Library.using: avoid Java 11 warnings on "Illegal reflective access";
wenzelm
parents: 69374
diff changeset
   188
      using(http_exchange.getRequestBody)(Bytes.read_stream(_))
65078
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   189
73340
0ffcad1f6130 tuned --- fewer warnings;
wenzelm
parents: 69463
diff changeset
   190
    def write_response(code: Int, response: Response): Unit =
65078
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   191
    {
73439
cb127ce2c092 tuned --- following hints by IntelliJ;
wenzelm
parents: 73438
diff changeset
   192
      http_exchange.getResponseHeaders.set("Content-Type", response.content_type)
65078
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   193
      http_exchange.sendResponseHeaders(code, response.bytes.length.toLong)
73439
cb127ce2c092 tuned --- following hints by IntelliJ;
wenzelm
parents: 73438
diff changeset
   194
      using(http_exchange.getResponseBody)(response.bytes.write_stream)
65078
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   195
    }
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   196
  }
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   197
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   198
73413
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   199
  /* handler for request method */
66207
8d5cb4ea2b7c support for HTTP/POST method;
wenzelm
parents: 65999
diff changeset
   200
8d5cb4ea2b7c support for HTTP/POST method;
wenzelm
parents: 65999
diff changeset
   201
  sealed case class Arg(method: String, uri: URI, request: Bytes)
8d5cb4ea2b7c support for HTTP/POST method;
wenzelm
parents: 65999
diff changeset
   202
  {
8d5cb4ea2b7c support for HTTP/POST method;
wenzelm
parents: 65999
diff changeset
   203
    def decode_properties: Properties.T =
73716
00ef0f401a29 more uniform use of Properties.Eq.unapply, with slightly changed semantics in boundary cases;
wenzelm
parents: 73547
diff changeset
   204
      space_explode('&', request.text).map(
00ef0f401a29 more uniform use of Properties.Eq.unapply, with slightly changed semantics in boundary cases;
wenzelm
parents: 73547
diff changeset
   205
        {
00ef0f401a29 more uniform use of Properties.Eq.unapply, with slightly changed semantics in boundary cases;
wenzelm
parents: 73547
diff changeset
   206
          case Properties.Eq(a, b) => Url.decode(a) -> Url.decode(b)
00ef0f401a29 more uniform use of Properties.Eq.unapply, with slightly changed semantics in boundary cases;
wenzelm
parents: 73547
diff changeset
   207
          case s => error("Malformed key-value pair in HTTP/POST: " + quote(s))
66207
8d5cb4ea2b7c support for HTTP/POST method;
wenzelm
parents: 65999
diff changeset
   208
        })
8d5cb4ea2b7c support for HTTP/POST method;
wenzelm
parents: 65999
diff changeset
   209
  }
8d5cb4ea2b7c support for HTTP/POST method;
wenzelm
parents: 65999
diff changeset
   210
73413
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   211
  object Handler
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   212
  {
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   213
    def apply(root: String, body: Exchange => Unit): Handler =
73439
cb127ce2c092 tuned --- following hints by IntelliJ;
wenzelm
parents: 73438
diff changeset
   214
      new Handler(root, (x: HttpExchange) => body(new Exchange(x)))
73413
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   215
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   216
    def method(name: String, root: String, body: Arg => Option[Response]): Handler =
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   217
      apply(root, http =>
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   218
        {
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   219
          val request = http.read_request()
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   220
          if (http.request_method == name) {
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   221
            val arg = Arg(name, http.request_uri, request)
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   222
            Exn.capture(body(arg)) match {
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   223
              case Exn.Res(Some(response)) =>
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   224
                http.write_response(200, response)
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   225
              case Exn.Res(None) =>
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   226
                http.write_response(404, Response.empty)
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   227
              case Exn.Exn(ERROR(msg)) =>
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   228
                http.write_response(500, Response.text(Output.error_message_text(msg)))
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   229
              case Exn.Exn(exn) => throw exn
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   230
            }
65078
2339994e8790 more operations;
wenzelm
parents: 65077
diff changeset
   231
          }
73413
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   232
          else http.write_response(400, Response.empty)
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   233
        })
65076
8a96ab58f016 clarified signature;
wenzelm
parents: 63823
diff changeset
   234
73413
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   235
    def get(root: String, body: Arg => Option[Response]): Handler = method("GET", root, body)
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   236
    def post(root: String, body: Arg => Option[Response]): Handler = method("POST", root, body)
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   237
  }
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   238
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   239
  class Handler private(val root: String, val handler: HttpHandler)
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   240
  {
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   241
    override def toString: String = root
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   242
  }
66207
8d5cb4ea2b7c support for HTTP/POST method;
wenzelm
parents: 65999
diff changeset
   243
65077
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
   244
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
   245
  /* server */
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
   246
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
   247
  class Server private[HTTP](val http_server: HttpServer)
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
   248
  {
73340
0ffcad1f6130 tuned --- fewer warnings;
wenzelm
parents: 69463
diff changeset
   249
    def += (handler: Handler): Unit = http_server.createContext(handler.root, handler.handler)
0ffcad1f6130 tuned --- fewer warnings;
wenzelm
parents: 69463
diff changeset
   250
    def -= (handler: Handler): Unit = http_server.removeContext(handler.root)
65077
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
   251
73439
cb127ce2c092 tuned --- following hints by IntelliJ;
wenzelm
parents: 73438
diff changeset
   252
    def start(): Unit = http_server.start()
73367
77ef8bef0593 clarified signature --- fewer warnings;
wenzelm
parents: 73340
diff changeset
   253
    def stop(): Unit = http_server.stop(0)
65077
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
   254
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
   255
    def address: InetSocketAddress = http_server.getAddress
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
   256
    def url: String = "http://" + address.getHostName + ":" + address.getPort
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
   257
    override def toString: String = url
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
   258
  }
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
   259
65081
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   260
  def server(handlers: List[Handler] = isabelle_resources): Server =
63823
ca8b737b08cf minimal HTTP server;
wenzelm
parents:
diff changeset
   261
  {
69463
6439c9024dcc clarified signature;
wenzelm
parents: 69393
diff changeset
   262
    val http_server = HttpServer.create(new InetSocketAddress(isabelle.Server.localhost, 0), 0)
65077
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
   263
    http_server.setExecutor(null)
65076
8a96ab58f016 clarified signature;
wenzelm
parents: 63823
diff changeset
   264
65077
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
   265
    val server = new Server(http_server)
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
   266
    for (handler <- handlers) server += handler
2d6e716c9d6e clarified modules;
wenzelm
parents: 65076
diff changeset
   267
    server
63823
ca8b737b08cf minimal HTTP server;
wenzelm
parents:
diff changeset
   268
  }
65079
8a5c2b86c5f6 handler for Isabelle fonts;
wenzelm
parents: 65078
diff changeset
   269
8a5c2b86c5f6 handler for Isabelle fonts;
wenzelm
parents: 65078
diff changeset
   270
65081
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   271
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   272
  /** Isabelle resources **/
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   273
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   274
  lazy val isabelle_resources: List[Handler] =
73518
c42144d9dde6 more uniform HTTP resources;
wenzelm
parents: 73440
diff changeset
   275
    List(welcome(), fonts())
65081
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   276
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   277
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   278
  /* welcome */
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   279
73518
c42144d9dde6 more uniform HTTP resources;
wenzelm
parents: 73440
diff changeset
   280
  def welcome(root: String = "/"): Handler =
c42144d9dde6 more uniform HTTP resources;
wenzelm
parents: 73440
diff changeset
   281
    Handler.get(root, arg =>
c42144d9dde6 more uniform HTTP resources;
wenzelm
parents: 73440
diff changeset
   282
      if (arg.uri.toString == root) {
73547
a7aabdf889b7 clarified signature;
wenzelm
parents: 73523
diff changeset
   283
        Some(Response.text("Welcome to " + Isabelle_System.identification()))
65081
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   284
      }
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   285
      else None)
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   286
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   287
c20905a5bc8e handler for Isabelle version;
wenzelm
parents: 65080
diff changeset
   288
  /* fonts */
65079
8a5c2b86c5f6 handler for Isabelle fonts;
wenzelm
parents: 65078
diff changeset
   289
69374
ab66951166f3 clarified "hidden" terminology;
wenzelm
parents: 69366
diff changeset
   290
  private lazy val html_fonts: List[Isabelle_Fonts.Entry] =
ab66951166f3 clarified "hidden" terminology;
wenzelm
parents: 69366
diff changeset
   291
    Isabelle_Fonts.fonts(hidden = true)
65079
8a5c2b86c5f6 handler for Isabelle fonts;
wenzelm
parents: 65078
diff changeset
   292
8a5c2b86c5f6 handler for Isabelle fonts;
wenzelm
parents: 65078
diff changeset
   293
  def fonts(root: String = "/fonts"): Handler =
73413
56c0a793cd8b clarified signature;
wenzelm
parents: 73367
diff changeset
   294
    Handler.get(root, arg =>
65079
8a5c2b86c5f6 handler for Isabelle fonts;
wenzelm
parents: 65078
diff changeset
   295
      {
66479
5c0a3f63057d proper argument type (amending 8d5cb4ea2b7c);
wenzelm
parents: 66207
diff changeset
   296
        val uri_name = arg.uri.toString
69364
91dbade9a5fa proper font file name for HTTP (amending dc9a39c3f75d);
wenzelm
parents: 69363
diff changeset
   297
        if (uri_name == root) {
69366
b6dacf6eabe3 clarified signature;
wenzelm
parents: 69364
diff changeset
   298
          Some(Response.text(cat_lines(html_fonts.map(entry => entry.path.file_name))))
69364
91dbade9a5fa proper font file name for HTTP (amending dc9a39c3f75d);
wenzelm
parents: 69363
diff changeset
   299
        }
91dbade9a5fa proper font file name for HTTP (amending dc9a39c3f75d);
wenzelm
parents: 69363
diff changeset
   300
        else {
91dbade9a5fa proper font file name for HTTP (amending dc9a39c3f75d);
wenzelm
parents: 69363
diff changeset
   301
          html_fonts.collectFirst(
69366
b6dacf6eabe3 clarified signature;
wenzelm
parents: 69364
diff changeset
   302
            { case entry if uri_name == root + "/" + entry.path.file_name => Response(entry.bytes) })
69364
91dbade9a5fa proper font file name for HTTP (amending dc9a39c3f75d);
wenzelm
parents: 69363
diff changeset
   303
        }
65079
8a5c2b86c5f6 handler for Isabelle fonts;
wenzelm
parents: 65078
diff changeset
   304
      })
63823
ca8b737b08cf minimal HTTP server;
wenzelm
parents:
diff changeset
   305
}