<commit_before>;; Copyright 2014 Red Hat, Inc, and individual contributors.
;; 
;; Licensed under the Apache License, Version 2.0 (the "License");
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;; 
;; http://www.apache.org/licenses/LICENSE-2.0
;; 
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.

(ns ^{:no-doc true} immutant.web.undertow.http
  (:require [clojure.string :as str]
            [clojure.java.io :as io]
            [potemkin :refer [def-map-type]])
  (:import [io.undertow.server HttpHandler HttpServerExchange]
           [io.undertow.util HeaderMap Headers HttpString]
           [java.io File InputStream OutputStream]
           java.nio.channels.FileChannel
           clojure.lang.ISeq))

(def-map-type LazyMap [m]   
  (get [_ k default-value]
    (if (contains? m k)
      (let [v (get m k)]
        (if (delay? v)
          @v
          v))
      default-value))
  (assoc [_ k v]
    (LazyMap. (assoc m k v))
  (dissoc [_ k]
     (LazyMap. (dissoc m k)))
  (keys [_]
    (keys m))))

(defn- headers->map [^HeaderMap headers]
  (reduce
    (fn [accum ^HttpString header-name]
      (assoc accum
        (-> header-name .toString .toLowerCase)
        (->> header-name
          (.get headers)
          (str/join ","))))
    {}
    (.getHeaderNames headers)))

(defn- ring-request [^HttpServerExchange exchange]
  (let [headers (delay (.getRequestHeaders exchange))
        content-type (delay (.getFirst @headers Headers/CONTENT_TYPE))]
    ;; TODO: context, path-info ?
    (LazyMap. {:server-port (delay (-> exchange .getDestinationAddress .getPort))
               :server-name (delay (.getHostName exchange))
               :remote-addr (delay (-> exchange .getSourceAddress .getAddress .getHostAddress))
               :uri (delay (.getRequestURI exchange))
               :query-string (delay (.getQueryString exchange))
               :scheme (delay (-> exchange .getRequestScheme keyword))
               :request-method (delay (-> exchange .getRequestMethod .toString .toLowerCase keyword))
               :content-type @content-type
               :content-length (delay (.getRequestContentLength exchange))
               :character-encoding (delay (if @content-type
                                            (Headers/extractTokenFromHeader @content-type "charset")))
               ;; TODO: :ssl-client-cert
               :headers (delay (headers->map @headers))
               :body (delay (.getInputStream exchange))})))

(defn- merge-headers [^HeaderMap to-headers from-headers]
  (doseq [[k v] from-headers]
    (let [^HttpString k (HttpString. k)]
      (if (coll? v)
        (.addAll to-headers k v)
        (.add to-headers k v)))))

(defprotocol BodyWriter
  (write-body [body exchange]))

(extend-protocol BodyWriter

  Object
  (write-body [body _]
    (throw (IllegalStateException. (str "Can't coerce body of type " (class body)))))

  nil
  (write-body [_ _]
    (throw (IllegalStateException. "Can't coerce nil body")))

  String
  (write-body [body ^OutputStream os]
    (.write os (.getBytes body)))
<commit_msg>More type hints<commit_after>;; Copyright 2014 Red Hat, Inc, and individual contributors.
;; 
;; Licensed under the Apache License, Version 2.0 (the "License");
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;; 
;; http://www.apache.org/licenses/LICENSE-2.0
;; 
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.

(ns ^{:no-doc true} immutant.web.undertow.http
  (:require [clojure.string :as str]
            [clojure.java.io :as io]
            [potemkin :refer [def-map-type]])
  (:import [io.undertow.server HttpHandler HttpServerExchange]
           [io.undertow.util HeaderMap Headers HttpString]
           [java.io File InputStream OutputStream]
           java.nio.channels.FileChannel
           clojure.lang.ISeq))

(def-map-type LazyMap [m]   
  (get [_ k default-value]
    (if (contains? m k)
      (let [v (get m k)]
        (if (delay? v)
          @v
          v))
      default-value))
  (assoc [_ k v]
    (LazyMap. (assoc m k v))
  (dissoc [_ k]
     (LazyMap. (dissoc m k)))
  (keys [_]
    (keys m))))

(defn- headers->map [^HeaderMap headers]
  (reduce
    (fn [accum ^HttpString header-name]
      (assoc accum
        (-> header-name .toString .toLowerCase)
        (->> header-name
          (.get headers)
          (str/join ","))))
    {}
    (.getHeaderNames headers)))

(defn- ring-request [^HttpServerExchange exchange]
  (let [headers (delay (.getRequestHeaders exchange))
        content-type (delay (.getFirst ^HeaderMap @headers Headers/CONTENT_TYPE))]
    ;; TODO: context, path-info ?
    (LazyMap. {:server-port (delay (-> exchange .getDestinationAddress .getPort))
               :server-name (delay (.getHostName exchange))
               :remote-addr (delay (-> exchange .getSourceAddress .getAddress .getHostAddress))
               :uri (delay (.getRequestURI exchange))
               :query-string (delay (.getQueryString exchange))
               :scheme (delay (-> exchange .getRequestScheme keyword))
               :request-method (delay (-> exchange .getRequestMethod .toString .toLowerCase keyword))
               :content-type @content-type
               :content-length (delay (.getRequestContentLength exchange))
               :character-encoding (delay (if @content-type
                                            (Headers/extractTokenFromHeader @content-type "charset")))
               ;; TODO: :ssl-client-cert
               :headers (delay (headers->map @headers))
               :body (delay (.getInputStream exchange))})))

(defn- merge-headers [^HeaderMap to-headers from-headers]
  (doseq [[^String k v] from-headers]
    (let [^HttpString k (HttpString. k)]
      (if (coll? v)
        (.addAll to-headers k v)
        (.add to-headers k ^String v)))))

(defprotocol BodyWriter
  (write-body [body exchange]))

(extend-protocol BodyWriter

  Object
  (write-body [body _]
    (throw (IllegalStateException. (str "Can't coerce body of type " (class body)))))

  nil
  (write-body [_ _]
    (throw (IllegalStateException. "Can't coerce nil body")))

  String
  (write-body [body ^OutputStream os]
    (.write os (.getBytes body)))

{'fraction_non_alphanumeric': 0.12651747842621033, 'fraction_numerical': 0.002340207693432792, 'mean_word_length': 2.5614583333333334, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5549341', 'n_tokens_mistral': 2037, 'n_tokens_neox': 1951, 'n_words': 627}