Giter Club home page Giter Club logo

compojure's Introduction

Compojure

Build Status

Compojure is a small routing library for Ring that allows web applications to be composed of small, independent parts.

Installation

Add the following dependency to your project.clj file:

[compojure "1.7.1"]

Documentation

Community

Usage

This small Compojure application demonstrates creating a Ring handler from two routes:

(ns hello-world.core
  (:require [compojure.core :refer :all]
            [compojure.route :as route]))

(defroutes app
  (GET "/" [] "<h1>Hello World</h1>")
  (route/not-found "<h1>Page not found</h1>"))

Also refer to the Getting Started page on the wiki.

License

Copyright © 2024 James Reeves

Distributed under the Eclipse Public License, the same as Clojure.

compojure's People

Contributors

abedra avatar abp avatar arohner avatar atroche avatar budu avatar cemerick avatar chrisvest avatar deraen avatar joephayes avatar josephwilk avatar liamchzh avatar lrenn avatar misfo avatar mjroghelia avatar mtnygard avatar mvitz avatar paraseba avatar paulbutcher avatar ptaoussanis avatar pupeno avatar rnewman avatar rodnaph avatar semsorock avatar shenfeng avatar stevejmp avatar svdm avatar tcrayford avatar toofuu avatar weavejester avatar zuzkins avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

compojure's Issues

Exception in thread main, no such var: clojure.core/quote

When following exactly the instructions on the "Getting Started" page of the project's wiki I get the following error after starting the server with the command "lein ring server":

Exception in thread "main" java.lang.Exception: No such var: clojure.core/quote (core.clj:1)
at clojure.lang.Compiler.analyze(Compiler.java:5205)
at clojure.lang.Compiler.analyze(Compiler.java:5151)
at clojure.lang.Compiler$InvokeExpr.parse(Compiler.java:3036)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:5371)
at clojure.lang.Compiler.analyze(Compiler.java:5190)
at clojure.lang.Compiler.analyze(Compiler.java:5151)
at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:4670)
at clojure.lang.Compiler$TryExpr$Parser.parse(Compiler.java:1833)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:5369)
at clojure.lang.Compiler.analyze(Compiler.java:5190)
at clojure.lang.Compiler.analyze(Compiler.java:5151)
at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:4670)
at clojure.lang.Compiler$FnMethod.parse(Compiler.java:4328)
at clojure.lang.Compiler$FnExpr.parse(Compiler.java:3173)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:5367)
at clojure.lang.Compiler.analyze(Compiler.java:5190)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:5357)
at clojure.lang.Compiler.analyze(Compiler.java:5190)
at clojure.lang.Compiler.analyze(Compiler.java:5151)
at clojure.lang.Compiler$InvokeExpr.parse(Compiler.java:3036)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:5371)
at clojure.lang.Compiler.analyze(Compiler.java:5190)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:5357)
at clojure.lang.Compiler.analyze(Compiler.java:5190)
at clojure.lang.Compiler.analyze(Compiler.java:5151)
at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:4670)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:5369)
at clojure.lang.Compiler.analyze(Compiler.java:5190)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:5357)
at clojure.lang.Compiler.analyze(Compiler.java:5190)
at clojure.lang.Compiler.analyze(Compiler.java:5151)
at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:4670)
at clojure.lang.Compiler$TryExpr$Parser.parse(Compiler.java:1833)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:5369)
at clojure.lang.Compiler.analyze(Compiler.java:5190)
at clojure.lang.Compiler.analyze(Compiler.java:5151)
at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:4670)
at clojure.lang.Compiler$FnMethod.parse(Compiler.java:4328)
at clojure.lang.Compiler$FnExpr.parse(Compiler.java:3173)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:5367)
at clojure.lang.Compiler.analyze(Compiler.java:5190)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:5357)
at clojure.lang.Compiler.analyze(Compiler.java:5190)
at clojure.lang.Compiler.analyze(Compiler.java:5151)
at clojure.lang.Compiler$InvokeExpr.parse(Compiler.java:3036)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:5371)
at clojure.lang.Compiler.analyze(Compiler.java:5190)
at clojure.lang.Compiler.analyze(Compiler.java:5151)
at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:4670)
at clojure.lang.Compiler$FnMethod.parse(Compiler.java:4328)
at clojure.lang.Compiler$FnExpr.parse(Compiler.java:3173)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:5367)
at clojure.lang.Compiler.analyze(Compiler.java:5190)
at clojure.lang.Compiler.eval(Compiler.java:5421)
at clojure.lang.Compiler.eval(Compiler.java:5415)
at clojure.lang.Compiler.load(Compiler.java:5857)
at clojure.lang.RT.loadResourceScript(RT.java:340)
at clojure.lang.RT.loadResourceScript(RT.java:331)
at clojure.lang.RT.load(RT.java:409)
at clojure.lang.RT.load(RT.java:381)
at clojure.core$load$fn__4511.invoke(core.clj:4905)
at clojure.core$load.doInvoke(core.clj:4904)
at clojure.lang.RestFn.invoke(RestFn.java:409)
at clojure.core$load_one.invoke(core.clj:4729)
at clojure.core$load_lib.doInvoke(core.clj:4766)
at clojure.lang.RestFn.applyTo(RestFn.java:143)
at clojure.core$apply.invoke(core.clj:542)
at clojure.core$load_libs.doInvoke(core.clj:4800)
at clojure.lang.RestFn.applyTo(RestFn.java:138)
at clojure.core$apply.invoke(core.clj:542)
at clojure.core$require.doInvoke(core.clj:4869)
at clojure.lang.RestFn.invoke(RestFn.java:458)
at user$eval3.invoke(NO_SOURCE_FILE:1)
at clojure.lang.Compiler.eval(Compiler.java:5424)
at clojure.lang.Compiler.eval(Compiler.java:5414)
at clojure.lang.Compiler.eval(Compiler.java:5415)
at clojure.lang.Compiler.eval(Compiler.java:5391)
at clojure.core$eval.invoke(core.clj:2382)
at clojure.main$eval_opt.invoke(main.clj:235)
at clojure.main$initialize.invoke(main.clj:254)
at clojure.main$null_opt.invoke(main.clj:279)
at clojure.main$main.doInvoke(main.clj:354)
at clojure.lang.RestFn.invoke(RestFn.java:422)
at clojure.lang.Var.invoke(Var.java:369)
at clojure.lang.AFn.applyToHelper(AFn.java:165)
at clojure.lang.Var.applyTo(Var.java:482)
at clojure.main.main(main.java:37)
Caused by: java.lang.Exception: No such var: clojure.core/quote
at clojure.lang.Compiler.resolveIn(Compiler.java:5651)
at clojure.lang.Compiler.resolve(Compiler.java:5621)
at clojure.lang.Compiler.analyzeSymbol(Compiler.java:5584)
at clojure.lang.Compiler.analyze(Compiler.java:5172)
... 86 more

I am using JRE version 1.6.0 on Mac OS X 10.6.8. Any hints?

Thanks and Best Regards,
Otto

Would love to have a way of obtaining URLs from controllers

(Feature request)

It would be wonderful if Compojure had a way of returning URLs from functions, similar to Django's reverse(). In a perfect world, I'd like to be able to define some routes:

(defroutes routes
  (GET  "/show_bananas" [] (bananas-controller)))

then in my template:

[:a {:href (reverse-url 'bananas-controller)} "all about our fruit"]

This would then then save me from hard-coding my URLs in lots of places.

Is this possible at the moment? Would it be possible to add?

Thanks for a great framework :)

Exception when %5C is used in a url with route/resources

Hey James,

Running this code:

(use 'compojure.core 'compojure.route)
(defroutes x (GET "/" [] "blah") (resources "/"))
(x {:uri "/%5C" :request-method :get})

I get the following exception:

java.lang.StringIndexOutOfBoundsException: String index out of range: 1
nil
    at java.lang.String.charAt(String.java:686)
    at java.util.regex.Matcher.appendReplacement(Matcher.java:703)
    at clojure.string$replace_by.invoke(string.clj:58)
    at clojure.string$replace.invoke(string.clj:82)
    at clout.core$path_decode.invoke(core.clj:33)
    at clout.core$path_decode.invoke(core.clj:31)
    at clojure.core$map$fn__3811.invoke(core.clj:2430)
    at clojure.lang.LazySeq.sval(LazySeq.java:42)
    at clojure.lang.LazySeq.seq(LazySeq.java:60)
    at clojure.lang.RT.seq(RT.java:466)
    at clojure.core$seq.invoke(core.clj:133)
    at clojure.core$map$fn__3815.invoke(core.clj:2435)
    at clojure.lang.LazySeq.sval(LazySeq.java:42)
    at clojure.lang.LazySeq.seq(LazySeq.java:60)
    at clojure.lang.RT.seq(RT.java:466)
    at clojure.core$seq.invoke(core.clj:133)
    at clojure.core$reduce.invoke(core.clj:5994)
    at clout.core$assoc_keys_with_groups.invoke(core.clj:56)
    at clout.core.CompiledRoute.route_matches(core.clj:86)
    at compojure.core$if_route$fn__437.invoke(core.clj:38)
    at compojure.core$if_method$fn_user=> _430.invoke(core.clj:24)
    at ring.middleware.file_info$wrap_file_info$fn__941.invoke(file_info.clj:40)
    at ring.middleware.content_type$wrap_content_type$fn__921.invoke(content_type.clj:18)
    at compojure.core$routing$fn__456.invoke(core.clj:98)
    at clojure.core$some.invoke(core.clj:2388)
    at compojure.core$routing.doInvoke(core.clj:98)
    at clojure.lang.RestFn.applyTo(RestFn.java:139)
    at clojure.core$apply.invoke(core.clj:602)
    at compojure.core$routes$fn__460.invoke(core.clj:103)
    at user$eval968.invoke(NO_SOURCE_FILE:15)
    at clojure.lang.Compiler.eval(Compiler.java:6465)
    at clojure.lang.Compiler.eval(Compiler.java:6431)
    at clojure.core$eval.invoke(core.clj:2795)
    at clojure.main$repl$read_eval_print__5967.invoke(main.clj:244)
    at clojure.main$repl$fn__5972.invoke(main.clj:265)
    at clojure.main$repl.doInvoke(main.clj:265)
    at clojure.lang.RestFn.invoke(RestFn.java:512)
    at user$eval27$acc__3869__auto____30$fn__32.invoke(NO_SOURCE_FILE:1)
    at clojure.lang.AFn.run(AFn.java:24)
    at java.lang.Thread.run(Thread.java:680)

POST with body but no content-type header throws NPE

with a route like (POST "/foo" [:as req] (do-something req)), the server throws NPE before do-something is called if I do:

curl -d @file.name http://localhost/foo

but is fine with

curl -H "Content-type: appliction/octet-stream" -d @file.name http://localhost/foo

or even

curl -H "Content-type: " -d @file.name http://localhost/foo

New release please?

It would be great to have the most recent changes released on clojars, perhaps a SNAPSHOT... The removal wrap-cookies from defroutes is the key change I want. I've built a jar myself of course but using it with leiningen is a pain.

route/resources throws exception when user enters directory into address bar

When a user tries to visit /img/ instead of /img/dog.jpg the browser hangs and an exception shows up under lein ring. Shouldn't route/resource return nil in this case and let some other handler take care of this? This would allow the default handler to send back a 404 http reply.

Here is the project.clj file:

(defproject compojure-experiment "1.0.0-SNAPSHOT"
:description "FIXME: write description"
:dependencies [[org.clojure/clojure "1.2.1"]
[compojure "0.6.5"]]
:dev-dependencies [[lein-ring "0.4.5"]]
:ring {:handler compojure-experiment.core/app})

Here is compojure-experiment/core.clj:
(ns compojure-experiment.core
(:use compojure.core)
(:require [compojure.route :as route]
[compojure.handler :as handler]))

(defroutes main-routes
(GET "/" [] "<h1>Fee fi fo fum</h1>")
(route/resources "/img/")
(route/resources "/css/")
(route/not-found "<h1>No such page, fool.</h1>"))

(def app
(handler/site main-routes))

Here is the url:
http://127.0.0.1:3000/img/

Here is the stack trace:

2011-10-05 09:40:01.600:WARN::/img/
java.io.FileNotFoundException: /home/afonso/clojure/resume/tin-can/compojure-experiment/resources/public (Is a directory)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.(FileInputStream.java:137)
at ring.util.servlet$set_body.invoke(servlet.clj:78)
at ring.util.servlet$update_servlet_response.invoke(servlet.clj:108)
at ring.adapter.jetty$proxy_handler$fn__77.invoke(jetty.clj:18)
at ring.adapter.jetty.proxy$org.mortbay.jetty.handler.AbstractHandler$0.handle(Unknown Source)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.Server.handle(Server.java:326)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:928)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:549)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
at org.mortbay.jetty.bio.SocketConnector$Connection.run(SocketConnector.java:228)
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)

IFn and IDeref responses broken?

In lines 23 and 26 of response.clj, shouldn't the request be passed to render as well as the result of calling the fn or derefing? I get arity exceptions when a route returns a fn.

Functionality to remove current session

When a response is returned where :session is nil, the current session should be removed with the destroy-session method.

Note that this is different from the :session key not existing in the response. If the :session key does not exist in the response map, no change should be made to the session.

Changed url decoding behavior in 1.0.*

The url decoding behavior has changed in version 1.0.* when upgrading from version 0.6.4.

Previously, when calling a route "test/:input", the parameter would get url decoded so that "test/first+second" would get decoded so input would be "first second". In 1.0.* the input is not decoded so input is "first+second".

'Getting Started' guide uses an unavailable version of Clojure?

Sat down to start playing around with Compojure, as it looks interesting, but attempting the example at https://github.com/weavejester/compojure/wiki/Getting-Started throws the following exception:

kaylee:compojure-test (master) $ lein deps
Could not find artifact org.clojure:clojure-contrib:pom:1.2.1 in central (http://repo1.maven.org/maven2)
Could not find artifact org.clojure:clojure-contrib:pom:1.2.1 in clojars (http://clojars.org/repo/)
Could not find artifact org.clojure:clojure-contrib:jar:1.2.1 in central (http://repo1.maven.org/maven2)
Could not find artifact org.clojure:clojure-contrib:jar:1.2.1 in clojars (http://clojars.org/repo/)
Exception in thread "main" java.lang.RuntimeException: org.sonatype.aether.resolution.DependencyResolutionException: Could not find artifact org.clojure:clojure-contrib:jar:1.2.1 in central (http://repo1.maven.org/maven2)

I'm using Leiningen 2.0.0-preview2 on Java 1.7.0_02 Java HotSpot(TM) Client VM

with-prefix middleware

Sometimes it's useful to specify a common URI prefix for a set of routes. The compojure.http.middleware/with-prefix function should strip the supplied prefix from the URI before passing it to the wrapped handler. If the request does not match the prefix, the wrapped handler should return nil. e.g.

(defroutes example
  (GET "/" "Hello World"))

(decorate example
  (with-prefix "/my-site"))

(example {:request-method :get, :uri "/"})
=> nil

(example {:request-method :get, :uri "/my-site/"})
=> {:status 200
     :headers {"Content Type" "text/html"}
     :body "Hello World"}

Wrong namespace for predicates.clj

Looking at the file src/compojure/validation/predicates.clj:

(ns compojure.validation)

(defn blank?
"True if x is nil or an empty string
.....

Should this not be (ns compojure.validation.predicates)? Or an (in-ns...) to append to the namespace?

Cannot get params and query-params in 0.6.0-RC3

Hi, I am new to compojure and I just found this problem when using 0.6.0-RC3

(GET "/" {params :params} (str params))

I got a empty map when I pass some query string from url. And I tested query-params, also got the same result. But when I try query-string, the right data returned.

I downgraded to 0.5.3, the code just worked as expected.

Encrypt cookie sessions before adding HMAC

Cookie sessions have a HMAC to ensure data is not tampered with. However, the content of the session is still visible to the user, even if they cannot change it. To correct this, session data stored in the cookie should be encrypted before the HMAC is calculated, and decrypted when the session is read. It should be sufficient to use the same secret key as used by the SHA256 HMAC.

The encryption algorithm should probably be AES256. This issue requires delving into the javax.crypto package, so difficulty has been set to "medium".

Ring NPE when following the getting started guide

I get this both with leiningen 1.7 and 2.0-preview. Any ideas where I should start looking?

java.lang.NullPointerException
stacktrace.clj:15   ring.middleware.stacktrace/wrap-stacktrace-log[fn]
stacktrace.clj:79   ring.middleware.stacktrace/wrap-stacktrace-web[fn]
reload.clj:18   ring.middleware.reload/wrap-reload[fn]
jetty.clj:18    ring.adapter.jetty/proxy-handler[fn]
(Unknown Source)    ring.adapter.jetty.proxy$org.eclipse.jetty.server.handler.AbstractHandler$0.handle
HandlerWrapper.java:111 org.eclipse.jetty.server.handler.HandlerWrapper.handle
Server.java:349 org.eclipse.jetty.server.Server.handle
AbstractHttpConnection.java:452 org.eclipse.jetty.server.AbstractHttpConnection.handleRequest
AbstractHttpConnection.java:884 org.eclipse.jetty.server.AbstractHttpConnection.headerComplete
AbstractHttpConnection.java:938 org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete
HttpParser.java:634 org.eclipse.jetty.http.HttpParser.parseNext
HttpParser.java:230 org.eclipse.jetty.http.HttpParser.parseAvailable
AsyncHttpConnection.java:76 org.eclipse.jetty.server.AsyncHttpConnection.handle
SelectChannelEndPoint.java:609  org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle
SelectChannelEndPoint.java:45   org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run
QueuedThreadPool.java:599   org.eclipse.jetty.util.thread.QueuedThreadPool.runJob
QueuedThreadPool.java:534   org.eclipse.jetty.util.thread.QueuedThreadPool$3.run
Thread.java:662 java.lang.Thread.run

Unable to set cookie alongside with session

I'm trying to set both cookie and session within the same response, here's what it looks like:

(ns sesscookie.core
  (:use compojure.core
        ring.adapter.jetty
        ring.middleware.session
        ring.middleware.session.cookie))

(defn write-index [req]
  {:status 200
   :body (str ":demo-session " (get-in req [:session :demo-session]) "\n"
              ":demo-cookies " (get-in req [:cookies "demo-cookies"]))})

(defn write-cookie [req]
  {:status 200
   :body "demo-cookies set to: cookie"
   :cookies {:demo-cookies "cookie"}})

(defn write-both [req]
  {:status 200
   :body (str "demo-cookies set to: both" "\n"
              "demo-session set to: both" "\n")
   :cookies {:demo-cookies "both"}
   :session {:demo-session "both"}})

(defroutes site
  (GET "/" [] write-index)
  (GET "/cookie" [] write-cookie)
  (GET "/both" [] write-both))

(wrap! site (:session {:store (cookie-store)}))

(defn start-server
  [] (run-jetty (var site) {:port 8080 :join? false}))

When visiting /both only session cookie will be set. I thought this might be Ring's bug at first, so I wrote another one with only Ring:

(ns ringcookie.core
  (:use ring.adapter.jetty
        ring.middleware.cookies
        ring.middleware.session
        ring.middleware.session.cookie))

(defn app
  [req]
  {:status 200
   :cookies {:test-cookie "foobar"}
   :session {:test-session "foobar"}})

(def site
  (-> app
      (wrap-session {:store (cookie-store)})
      wrap-cookies))

(defn start-server
  [] (run-jetty #'site {:port 8080 :join? false}))

On visiting to /, both cookies were set. However, when I swap around wrap-cookies and wrap-session position, the behavior becomes the same as above Compojure code. (Only session set, no cookie.)

I found that Compojure is already calling the cookie middleware by itself, and wrap-cookies will dissoc :cookies from the response map after the header is written. Session middleware also do the same so the Set-Cookies header get rewritten.

So I think either removing wrap-cookies from Compojure, or make cookies middleware stop dissoc the response map will fix the problem. Not sure where to file this, I posted it here instead.

(Also, the first code snippet will seems to work if cookie-store is not used, this is because session middleware will not try to set the cookie when session cookie is already present.)

route/files returning HTTP HEAD with missing or empty fields

Using compojure 1.01

When I retrieve HTTP HEAD for a static file exported by route/files, the header is:

HTTP/1.1 200 OK
Date: Fri, 20 Jan 2012 17:18:41 GMT
Content-Length: 0
Server: Jetty(6.1.25)

But when I retrieve the same file using HTTP GET, the header is:

HTTP/1.1 200 OK
Date: Fri, 20 Jan 2012 17:19:50 GMT
Content-Length: 168
Last-Modified: Fri, 20 Jan 2012 17:12:31 +0000
Content-Type: application/octet-stream
Server: Jetty(6.1.25)

The first arg to compojure.core/context must be a literal

I try to add a context prefix from an environment variable, using

(def handler (compojure.core/context (System/getenv "RING_CONTEXT") [] #'my-routes))

But it doesn't work. It compiles OK, but when running, it fails with stack trace. If I put a string literal instead of (System...), it works.

If there's no better solution, I would propose you add an eval in the definition of compojure.core/context, replacing

(context-route path)

with

(context-route (eval path))

Cannot set Content-Type header of routes/file and routes/resources

It seems there is currently no obvious way to set the Content-Type response header for 'files' and 'resources' routes according to the extension of their request uris (webkit browsers (e.g. chromium) complain about this issue through the console log). The 'wrap-file-info' middleware does not help in this case because it expects a 'File' object as the response body.
To fix this issue for my purposes I've forked 'mmcgrana/ring' (to 'tscheibl/ring') on github and implemented an additional middleware within 'file_info.clj' named 'wrap-contenttype-by-uriext' which determines the mime-type by mapping the request uri's extension.
It seemed to me that this would be the right place for a fix although the lack of such a middleware primarily limits the usefulness of compojures 'files' and 'resources' routes.

Middleware for mimetypes

Create a compojure.http.middleware/with-mimetypes function that sets the content type of the response from the wrapped handler depending on the file extension. The function should take a map with extensions for keys and mimetypes as values.

For example:

(decorate your-routes
  (with-mimetypes
    {"txt" "text/plain"
     "png" "image/png"
     "js"  "text/javascript"})) 

Middleware for Cache-Control header

There should be "with-cache-control" middleware in compojure.http.helpers that enables the user to set the Cache-Control header for a set of routes. The syntax might look something like this:

(decorate my-routes
  (with-cache-control {:max-age 3600, :must-revalidate true}))

This would add the following header to the response map:

{"Cache-Control" "max-age=3600, must-revalidate"}

Note that if the value in the map in true, then only the key is serialized. Otherwise it is serialized as "{key}={value}". Options are separated by ", ".

Please see the Tutorial Draft for an example of how to create middleware that adds a header. In fact, it might also be useful to add in a "with-header" function as well.

Compojure files should use docstrings in namespaces

Currently namespace documentation is written in comments, like this:

;; compojure.str-utils:
;;
;; Utility functions for manipulating strings

(ns compojure.str-utils
  (:use clojure.contrib.seq-utils)
  (:use clojure.contrib.str-utils)
  (:import clojure.lang.Named))

Compojure should take advantage of Clojure's namespace docstrings and rewrite the above to be:

(ns compojure.str-utils
  "Utility functions for manipulating strings"
  (:use clojure.contrib.seq-utils)
  (:use clojure.contrib.str-utils)
  (:import clojure.lang.Named))

This should be applied to all Compojure files.

wrap! macro behavior

I think wrap! macro should iterate over the wrapper funcs, transforming each one from kw to handler as needed. Also, function keywords->middleware should probably be named keyword->middleware (singular).

I'm sending a pull request, with a failing test and the proposed fix.

Thanks.

Path parameters containing . don't match routes

If you define a route (e.g. (GET "/somepath/:user" _ dostuff)) it will not match /somepath/example.user
and if I'm not mistaken dots are allowed in path parameters.
I am not sure if there is a reason for this but I would really like to know if it was an oversight or if there is some reasoning behinder it.

First Example in README needs update

(ns hello-world
 (:use compojure.core, ring.adapter.jetty)
  (:require [compojure.route :as route])

(defroutes main-routes
  (GET "/" [] "<h1>Hello World</h1>")
  (route/not-found "<h1>Page not found</h1>")))

(run-jetty main-routes {:port 8080})

gives an error

Exception in thread "main" java.lang.Exception: No such var: clojure.core/defroutes (1.clj:1)

It should be changed to

(ns hello-world
 (:use compojure.core, ring.adapter.jetty)
  (:require [compojure.route :as route]))

(defroutes main-routes
 (GET "/" [] "<h1>Hello World</h1>")
 (route/not-found "<h1>Page not found</h1>"))

(run-jetty main-routes {:port 8080})

thread-resource-stream has a problem with large resources

Your thread-resource-stream implementation in response.clj has a problem with large resources when reading from within a jar (at least on my configuration: Linux x64, sun-jdk-1.6.0.24 and OpenJDK-1.6) e.g. when reading jquery-1.5.1-min.js using route/resource.
It reads about 2000 bytes correctly and fills the rest with zeros (or something).
I replaced it with this Implementation which works as it should:
(defn- thread-resource-stream [path](clojure.java.io/input-stream
%28clojure.java.io/resource path%29))

note: I'm using compojure wrapped in aleph but that shouldn't have an impact on this?

Should be able to set an expiry time on sessions

Users should be able to set an expiry time on the :expiry key of the current session. Because sessions may be serialized, this should be an object serializable by pr-str, such as an integer that represents a unix timestamp.

If a session is read and it is expired, it should be deleted with destroy-session and a new session created with create-session.

route/not-found should be a macro

Since route/not-found works as a shortcut for writing a catch-all route, it should behave like all other defined routes. Currently, it evaluates the content immediately, meaning that middleware will not be evaluated and odd errors arise as a result.

Add support for Ring adapters

Currently in Compojure you attach a handler to a server like:

(use 'compojure.server.jetty)

(run-server {:port 8080}
  "/*" (servlet your-handler))

This allows integration with other Java servlets, which is sometimes quite useful. However, most of the time, people won't need this functionality. The Ring spec defines adapters which provide a more concise way of attaching a handler to a web server. In Compojure's case, we could create a jetty and grizzly adapter.

(use 'compojure.adapter)

(jetty {:port 8080} your-handler)

The options map should be optional, in the same way it is for run-server. The above code can just be a convenient wrapper around the normal Compojure servers.

Trivial enhancement to route destructuring would vastly improve my quality of life

In clojure it is legal to use :as when destructuring a vector:

(let [[foo :as req] [1]] req)

However, it is not legal to do this when destructuring a compojure route:

(GET "/foos/:foo" [foo :as req] ...)

Seems like this would be a trivial, clean, safe, and very useful ability.

Thanks for all your wonderful work on compojure!

-Conrad Barski

Allow (context) to be used with a symbol as the path

This doesn't work:

(def foo "/foo")

(defroutes test
  (context foo [] ...))

But inlining the string does:

(defroutes test
  (context "/foo" [] ...))

It would be helpful to be able to define these at runtime to allow for more "pluggable" apps written as middleware.

hyphens converted to underscores for namespaces?

I tried out your testcase, exactly as you have in your README (using a latest git checkout):

I get this error:
"REPL started; server listening on localhost:28412."
user=> (use 'hello-www.core)
java.io.FileNotFoundException: Could not locate hello_www/core__init.class or hello_www/core.clj on classpath: (NO_SOURCE_FILE:0)
user=> %

note the hello_www instead of hello-www. If I change everything to have underscores, it works properly.

I'm using Ubuntu 10.10
Leiningen 1.3.1 on Java 1.6.0_20 OpenJDK 64-Bit Server VM

wrap! doesn't seems to work with Jetty.

I'm very confused by this one. I can use the wrap! macro on my routes and it seems to work correctly if I call them manually. But, when used with run-jetty, it has no effect. There's a repl session below that demonstrates my problem. Note that calling my-routes directly yields keyword params (:name) as expected. When accessed through Jetty with curl, :params is unaffected. I have the same problem if I use (wrap-keyword-params) directly as well.

Hopefully I just don't understand how to use wrap! or run-jetty correctly.

Thanks!

(compojure.core, ring.adapter.jetty, and ring.middleware.keyword-params are all used in the current ns)

rhymetime.web=> (defroutes my-routes (GET "/" request (str request)))
#'rhymetime.web/my-routes

rhymetime.web=> my-routes
#<cookies$wrap_cookies$fn__614 ring.middleware.cookies$wrap_cookies$fn__614@167acf2>

rhymetime.web=> (wrap! my-routes (:keyword-params))
#<keyword_params$wrap_keyword_params$fn__995 ring.middleware.keyword_params$wrap_keyword_params$fn__995@6b6ac8>

rhymetime.web=> my-routes
#<keyword_params$wrap_keyword_params$fn__995 ring.middleware.keyword_params$wrap_keyword_params$fn__995@6b6ac8>

rhymetime.web=> (my-routes { :request-method :get :params {"name" "value"}})
{:status 200, 
 :headers {"Content-Type" "text/html"}, 
 :body "{:route-params {}, 
         :query-params {}, 
         :form-params {}, 
         :cookies {}, 
         :request-method :get, 
         :params {:name \"value\"}}"}

rhymetime.web=> (run-jetty my-routes {:port 8080})
2010-11-12 20:15:14.626::INFO:  Logging to STDERR via org.mortbay.log.StdErrLog
2010-11-12 20:15:14.632::INFO:  jetty-6.1.14
2010-11-12 20:15:14.751::INFO:  Started [email protected]:8080

$ curl http://localhost:8080/?name=value
{:remote-addr "0:0:0:0:0:0:0:1", 
 :scheme :http, 
 :query-params {"name" "value"}, 
 :form-params {}, 
 :request-method :get, 
 :query-string "name=value", 
 :route-params {}, 
 :content-type nil, 
 :cookies {}, 
 :uri "/", 
 :server-name "localhost", 
 :params {"name" "value"}, 
 :headers {"accept" "*/*", 
           "host" "localhost:8080", 
           "user-agent" "curl/7.18.2 (i486-pc-linux-gnu) libcurl/7.18.2 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.10"}, 
 :content-length nil, 
 :server-port 8080, 
 :character-encoding nil, 
 :body #<Input org.mortbay.jetty.HttpParser$Input@16f4ce0>}

The with-headers associates headers to a nil response

Problem:

The with-headers of http/middleware.clj associates headers to a nil response.

Demonstration:

If response is nil

user> (assoc nil :headers (merge (:headers nil) {"Content-Type" "text/html"}))
{:headers {"Content-Type" "text/html"}}

So what happens when response is nil and gets decorated by a with-headers directly, or indirectly (e.g. via with-mimetypes), is that it becomes a map with a single key, value pair named :headers.
This is also the reason why NPE is thrown on .setStatus of the HttpServletResponse method as the status is nil.

Static files with space characters aren't routed properly

I first ran into this in noir which has Issue #36 filed against this.

I suspected an upstream bug so created a new compojure app using the wiki example. I placed a pdf in "resources/public/file.pdf" which is found and parsed correctly but "resources/public/spaced%20file.pdf" (in the url) is not.

2011-12-15 21:15:37.305:INFO::Logging to STDERR via org.mortbay.log.StdErrLog
Started server on port 3000
2011-12-15 21:15:37.306:INFO::jetty-6.1.26
2011-12-15 21:15:37.322:INFO::Started [email protected]:3000
2011-12-15 21:16:05.110:WARN::/2011-11-11%20gas.pdf
java.io.FileNotFoundException: /Users/gberenfield/clojure/web/resources/public/spaced%20file.pdf (No such file or directory)
    at java.io.FileInputStream.open(Native Method)
    at java.io.FileInputStream.<init>(FileInputStream.java:120)
    at ring.util.servlet$set_body.invoke(servlet.clj:78)
    at ring.util.servlet$update_servlet_response.invoke(servlet.clj:108)
    at ring.adapter.jetty$proxy_handler$fn__77.invoke(jetty.clj:18)
    at ring.adapter.jetty.proxy$org.mortbay.jetty.handler.AbstractHandler$0.handle(Unknown Source)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at org.mortbay.jetty.Server.handle(Server.java:326)
    at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
    at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:928)
    at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:549)
    at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
    at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
    at org.mortbay.jetty.bio.SocketConnector$Connection.run(SocketConnector.java:228)
    at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)

strict= required to prevent timing attacks

In Clojure, the = function checks the equality of strings lazily. This is good for efficiency, but bad for cryptographic security, as it gives an attacker potentially useful information.

A strict-seq= function is needed in the compojure.crypto namespace. Whilst = returns false the moment it finds an item in the sequence that does not match, the strict-seq function will always check every value in a sequence.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.