                (swap! state #(assoc % :upstream next-up))
                (next-up evt val)))
 
+           (= :close evt)
+           (when next-up
+             (throw (IOException. "Connection reset by peer")))
+
            next-up
            (next-up evt val)
 
-           :else
+           (not (#{:open :close} evt))
            (throw (Exception. "Not expecting a message right now."))))))))

(ns picard.http.server
  (:use picard.http.core))

(declare
 handle-request


(defn- awaiting-100-continue?
  [current-state]
  (= awaiting-100-continue (.next-up-fn current-state)))

(defn- awaiting-response?
  [current-state]
  (= waiting-for-response (.next-up-fn current-state)))

(defn- exchange-finished?
  [current-state]
  (and (.responded? current-state)
       (or (awaiting-response? current-state)
           (awaiting-100-continue? current-state))))

(defn- maybe-finalize-exchange
  [current-state]
  (when (exchange-finished? current-state)
    (let [conn     (.conn current-state)
          upstream (.upstream current-state)]

      ;; The current exchange is complete, so remove it from
      ;; the connection state.
      (swap! conn #(dissoc % :upstream))

      ;; Send an upstream event that indicates the HTTP exchange
      ;; is complete.
      (upstream :done nil))))

(defn- maybe-close-connection
  [current-state]
  (when-not (.keepalive? current-state)
    ((.downstream current-state) :close nil)))

(defn- stream-or-finalize-response
  [state evt chunk current-state]
  (when-not (= :body evt)
    (throw (Exception. "Expecting a :body event")))

  (swap-then!
   state
   (fn [current-state]
     (if chunk
       (let [bytes-sent     (.bytes-to-send current-state)
             bytes-expected (.bytes-expected current-state)
             bytes-to-send  (+ bytes-sent (chunk-size chunk))
             responded?     (= bytes-expected bytes-to-send)]
         (assoc current-state
           :bytes-to-send bytes-to-send
           :responded?    responded?
           :next-dn-fn    (when-not responded? stream-or-finalize-request)))
       ;; This is the final chunk
       ;; TODO: Check that the content-length is correct
       (assoc current-state
         :responded? true
         :next-dn-fn nil)))
   (fn [current-state]
     (let [responded? (.responded? current-state)
           chunked?   (.chunked? current-state)
           downstream (.downstream current-state)]
       ;; It only makes sense to check if the exchange needs to be finalized
       ;; when the response is complete.
       (when responded?
         (maybe-finalize-exchange current-state))

       (cond
        chunk    (downstream :message (chunk->netty-chunk chunk))
        chunked? (downstream :message HttpChunk/LAST_CHUNK))

       (maybe-close-connection current-state)))))

(defn- handle-response
  [state evt val current-state]
  (when-not (= :response evt)
    (throw (Exception. "Expecting a :response event")))

