fukamachi / ningle

Super micro framework for Common Lisp
http://8arrow.org/ningle/
273 stars 25 forks source link

Body parameters containing an object with an array only keeps the first item. #24

Open StephenWakely opened 7 years ago

StephenWakely commented 7 years ago

Create a simple Ningle server -

(defvar *app* (make-instance 'ningle:<app>))

(setf (ningle:route *app* "/" :method :POST)
      (lambda (params)
        (format t "handler: ~A" ningle:*request*)))

(clack:clackup *app*)

Then send some json to it -

curl -H "Content-Type: application/json" -X POST -d '{"items":[{"id": 0, "shnorp": "incplork"}, {"id": 1, "shnorp": "ookwonk"}, {"id": 3, "shnorp": "erk"}]}' http://localhost:5000/

The only item that comes into BODY-PARAMETERS is the first item in that array

:BODY-PARAMETERS ((items (shnorp . incplork) (id . 0)))

The parameters passed into lack.request:make-request does contain the full array, so somewhere between there and my handler being called it gets truncated. I cannot work out where.

anquegi commented 7 years ago

I also have the same problem, i 'm trying to build a Bot following the 10 minute tutorial from facebook: code:

(ql:quickload '(ningle clack woo yason) :silent t)

(defvar *10-tutorial* (make-instance 'ningle:<app>))

;; /
(setf (ningle:route *10-tutorial* "/")
      "BoTE BoTE!")

;; /webhook?hubmode=subscribe&
;;          hubverify_token=este_es_un_token_muy_seguro_y_ademas_tiene_un_numer0&
;;          hubchallenge=numero 
(setf (ningle:route *10-tutorial* "/webhook")
      #'(lambda (params)
          (let ((mode (cdr (assoc "hub.mode" params :test #'string=)))
                (token "este_es_un_token_muy_seguro_y_ademas_tiene_un_numer0")
                (verify-token (cdr (assoc "hub.verify_token" params :test #'string=) ))
                (challenge (cdr (assoc "hub.challenge" params :test #'string=))))
            (cond
              ((and (string= mode "subscribe")
                    (string= token verify-token))
                `(200 (:content-type "text/plain") (,challenge)))
              (t
                (format t "Failed Validation: ~{~s~^~%~}~%" params)
               '(403 (:content-type "text/plain")
                 ("Failed validation. Make sure the validation tokens match.")))))))

(setf (ningle:route *10-tutorial* "/webhook" :method :post)
      #'(lambda (params)
          (format t "~a~%" ningle:*request*)))

*10-tutorial*

Then I start it using this:

clackup 10-minute-tutorial/10-tutorial.lisp --server woo --debug t

I use emacs restclient:

# test post webhook
POST http://:host::port/webhook
Content-Type: application/json
{
    "jql": "project = HSP",
    "startAt": 0,
    "maxResults": 15,
    "fields": [ "summary", "status", "assignee" ]
}

and the response is correct:

// POST http://localhost:5000/webhook
// HTTP/1.1 200 OK
// Date: Fri, 14 Apr 2017 06:17:10 GMT
// Connection: keep-alive
// Content-Length: 0
// Request duration: 0.147316s

but when I look the request object I get this:

Woo server is going to start.
Listening on localhost:5000.
#S(LACK.REQUEST:REQUEST
   :ENV (RAW-BODY #<VECTOR-INPUT-STREAM {10033DEAA3}> REQUEST-METHOD POST
         SCRIPT-NAME  SERVER-NAME localhost SERVER-PORT 5000 SERVER-PROTOCOL
         HTTP/1.1 PATH-INFO /webhook QUERY-STRING NIL URL-SCHEME http
         REMOTE-ADDR 127.0.0.1 REMOTE-PORT 45760 REQUEST-URI /webhook
         CLACK.STREAMING T CLACK.NONBLOCKING T CLACK.IO
         #S(WOO.EV.SOCKET:SOCKET
            :WATCHERS #(#.(SB-SYS:INT-SAP #X00300F10)
                        #.(SB-SYS:INT-SAP #X00300F40)
                        #.(SB-SYS:INT-SAP #X00300F70))
            :LAST-ACTIVITY 1.492150630601952d9
            :FD 11
            :REMOTE-ADDR 127.0.0.1
            :REMOTE-PORT 45760
            :DATA #<CLOSURE (LAMBDA
                                (FAST-HTTP::DATA
                                 &KEY (FAST-HTTP::START 0) FAST-HTTP::END)
                              :IN
                              FAST-HTTP:MAKE-PARSER)
                    {100323E19B}>
            :TCP-READ-CB TCP-READ-CB
            :READ-CB #<FUNCTION WOO::READ-CB>
            :WRITE-CB NIL
            :OPEN-P T
            :BUFFER #S(FAST-IO::OUTPUT-BUFFER
                       :VECTOR #(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
                       :FILL 0
                       :LEN 0
                       :QUEUE NIL
                       :LAST NIL
                       :OUTPUT NIL)
            :SENDFILE-FD NIL
            :SENDFILE-SIZE NIL
            :SENDFILE-OFFSET 0)
         CONTENT-LENGTH 122 CONTENT-TYPE application/json HEADERS
         #<HASH-TABLE :TEST EQUAL :COUNT 8 {100323E5C3}> BODY-PARAMETERS
         ((fields . summary) (maxResults . 15) (startAt . 0)
          (jql . project = HSP)))
   :METHOD POST
   :SCRIPT-NAME
   :PATH-INFO /webhook
   :SERVER-NAME localhost
   :SERVER-PORT 5000
   :SERVER-PROTOCOL HTTP/1.1
   :URI /webhook
   :REMOTE-ADDR 127.0.0.1
   :REMOTE-PORT 45760
   :QUERY-STRING NIL
   :RAW-BODY #<CIRCULAR-INPUT-STREAM {100345F073}>
   :CONTENT-LENGTH 122
   :CONTENT-TYPE application/json
   :HEADERS #<HASH-TABLE :TEST EQUAL :COUNT 8 {100323E5C3}>
   :COOKIES NIL
   :BODY-PARAMETERS ((fields . summary) (maxResults . 15) (startAt . 0)
                     (jql . project = HSP))
   :QUERY-PARAMETERS NIL)

So I cannot get the other parameters. I suppose that there is workaround for this, but I cannot figure out what to do.

It seems that it is an error getting the body-parameters, because the raw-body circular stream is correct:

CL-USER> my-request
#S(LACK.REQUEST:REQUEST
   :ENV (:REQUEST-METHOD :POST :SCRIPT-NAME "" :PATH-INFO "/webhook"
         :SERVER-NAME "localhost" :SERVER-PORT 5000 :SERVER-PROTOCOL :HTTP/1.1
         :REQUEST-URI "/webhook" :URL-SCHEME "http" :REMOTE-ADDR "127.0.0.1"
         :REMOTE-PORT 49780 :QUERY-STRING NIL :RAW-BODY
         #<FLEXI-STREAMS:FLEXI-IO-STREAM {1007CDCB43}> :CONTENT-LENGTH 122
         :CONTENT-TYPE "application/json;utf=8" :CLACK.STREAMING T :CLACK.IO
         #<CLACK.HANDLER.HUNCHENTOOT::CLIENT {1007CE0AC3}> :HEADERS
         #<HASH-TABLE :TEST EQUAL :COUNT 6 {1007CE0EB3}> :BODY-PARAMETERS
         (("fields" . "summary") ("maxResults" . 15) ("startAt" . 0)
          ("jql" . "project = HSP")))
   :METHOD :POST
   :SCRIPT-NAME ""
   :PATH-INFO "/webhook"
   :SERVER-NAME "localhost"
   :SERVER-PORT 5000
   :SERVER-PROTOCOL :HTTP/1.1
   :URI "/webhook"
   :REMOTE-ADDR "127.0.0.1"
   :REMOTE-PORT 49780
   :QUERY-STRING NIL
   :RAW-BODY #<CIRCULAR-STREAMS:CIRCULAR-INPUT-STREAM {1007D67E83}>
   :CONTENT-LENGTH 122
   :CONTENT-TYPE "application/json;utf=8"
   :HEADERS #<HASH-TABLE :TEST EQUAL :COUNT 6 {1007CE0EB3}>
   :COOKIES NIL
   :BODY-PARAMETERS (("fields" . "summary") ("maxResults" . 15) ("startAt" . 0)
                     ("jql" . "project = HSP"))
   :QUERY-PARAMETERS NIL)
CL-USER> (lack.request:request-method my-request)
:POST
CL-USER> (lack.request:request-headers my-request)
#<HASH-TABLE :TEST EQUAL :COUNT 6 {1007CE0EB3}>
CL-USER> (circular-streams:circular-stream-buffer (lack.request:request-raw-body my-request))
#S(FAST-IO::OUTPUT-BUFFER
   :VECTOR #(111 106 101 99 116 32 61 32 72 83 80 34 44 10 32 32 32 32 34 115
             116 97 114 116 65 116 34 58 32 48 44 10 32 32 32 32 34 109 97 120
             82 101 115 117 108 116 115 34 58 32 49 53 44 10 32 32 32 32 34 102
             105 101 108 100 115 34 58 32 91 32 34 115 117 109 109 97 114 121
             34 44 32 34 115 116 97 116 117 115 34 44 32 34 97 115 115 105 103
             110 101 101 34 32 93 10 125 10)
   :FILL 106
   :LEN 122
   :QUEUE (#(123 10 32 32 32 32 34 106 113 108 34 58 32 34 112 114))
   :LAST (#(123 10 32 32 32 32 34 106 113 108 34 58 32 34 112 114))
   :OUTPUT NIL)
CL-USER> (fast-io::output-buffer-vector *)
#(111 106 101 99 116 32 61 32 72 83 80 34 44 10 32 32 32 32 34 115 116 97 114
  116 65 116 34 58 32 48 44 10 32 32 32 32 34 109 97 120 82 101 115 117 108 116
  115 34 58 32 49 53 44 10 32 32 32 32 34 102 105 101 108 100 115 34 58 32 91
  32 34 115 117 109 109 97 114 121 34 44 32 34 115 116 97 116 117 115 34 44 32
  34 97 115 115 105 103 110 101 101 34 32 93 10 125 10)
CL-USER> (flexi-streams:octets-to-string *)
"oject = HSP\",
    \"startAt\": 0,
    \"maxResults\": 15,
    \"fields\": [ \"summary\", \"status\", \"assignee\" ]
}
"