* luci/libs/http: implement multi-value support in http.protocol
[project/luci.git] / libs / http / luasrc / http / protocol.lua
1 --[[
2
3 HTTP protocol implementation for LuCI
4 (c) 2008 Freifunk Leipzig / Jo-Philipp Wich <xm@leipzig.freifunk.net>
5
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
9
10         http://www.apache.org/licenses/LICENSE-2.0
11
12 $Id$
13
14 ]]--
15
16 module("luci.http.protocol", package.seeall)
17
18 local ltn12 = require("luci.ltn12")
19
20 HTTP_MAX_CONTENT      = 1024*4          -- 4 kB maximum content size
21 HTTP_URLENC_MAXKEYLEN = 1024            -- maximum allowd size of urlencoded parameter names
22
23
24 -- Decode an urlencoded string.
25 -- Returns the decoded value.
26 function urldecode( str, no_plus )
27
28         local function __chrdec( hex )
29                 return string.char( tonumber( hex, 16 ) )
30         end
31
32         if type(str) == "string" then
33                 if not no_plus then
34                         str = str:gsub( "+", " " )
35                 end
36
37                 str = str:gsub( "%%([a-fA-F0-9][a-fA-F0-9])", __chrdec )
38         end
39
40         return str
41 end
42
43
44 -- Extract and split urlencoded data pairs, separated bei either "&" or ";" from given url.
45 -- Returns a table value with urldecoded values.
46 function urldecode_params( url, tbl )
47
48         local params = tbl or { }
49
50         if url:find("?") then
51                 url = url:gsub( "^.+%?([^?]+)", "%1" )
52         end
53
54         for pair in url:gmatch( "[^&;]+" ) do
55
56                 -- find key and value
57                 local key = urldecode( pair:match("^([^=]+)")     )
58                 local val = urldecode( pair:match("^[^=]+=(.+)$") )
59
60                 -- store
61                 if type(key) == "string" and key:len() > 0 then
62                         if type(val) ~= "string" then val = "" end
63
64                         if not params[key] then
65                                 params[key] = val
66                         elseif type(params[key]) ~= "table" then
67                                 params[key] = { params[key], val }
68                         else
69                                 table.insert( params[key], val )
70                         end
71                 end
72         end
73
74         return params
75 end
76
77
78 -- Encode given string in urlencoded format.
79 -- Returns the encoded string.
80 function urlencode( str )
81
82         local function __chrenc( chr )
83                 return string.format(
84                         "%%%02x", string.byte( chr )
85                 )
86         end
87
88         if type(str) == "string" then
89                 str = str:gsub(
90                         "([^a-zA-Z0-9$_%-%.%+!*'(),])",
91                         __chrenc
92                 )
93         end
94
95         return str
96 end
97
98
99 -- Encode given table to urlencoded string.
100 -- Returns the encoded string.
101 function urlencode_params( tbl )
102         local enc = ""
103
104         for k, v in pairs(tbl) do
105                 enc = enc .. ( enc and "&" or "" ) ..
106                         urlencode(k) .. "="  ..
107                         urlencode(v)
108         end
109
110         return enc
111 end
112
113
114 -- Parameter helper
115 local function __initval( tbl, key )
116         local multival = ( key:sub( #key - 1, #key ) == "[]" )
117
118         if multival then
119                 if type(tbl[key]) == "table" then
120                         table.insert( tbl[key], "" )
121                 else
122                         tbl[key] = { "" }
123                 end
124         else
125                 tbl[key] = ""
126         end
127
128         return multival
129 end
130
131 local function __appendval( tbl, key, multival, chunk )
132         if multival then
133                 tbl[key][#tbl[key]] = tbl[key][#tbl[key]] .. chunk
134         else
135                 tbl[key] = tbl[key] .. chunk
136         end
137 end
138
139 local function __finishval( tbl, key, multival, handler )
140         if handler then
141                 if multival then
142                         tbl[key][#tbl[key]] = handler( tbl[key][#tbl[key]] )
143                 else
144                         tbl[key] = handler( tbl[key] )
145                 end
146         end
147 end
148
149
150 -- Table of our process states
151 local process_states = { }
152
153 -- Extract "magic", the first line of a http message.
154 -- Extracts the message type ("get", "post" or "response"), the requested uri
155 -- or the status code if the line descripes a http response.
156 process_states['magic'] = function( msg, chunk, err )
157
158         if chunk ~= nil then
159                 -- ignore empty lines before request
160                 if #chunk == 0 then
161                         return true, nil
162                 end
163
164                 -- Is it a request?
165                 local method, uri, http_ver = chunk:match("^([A-Z]+) ([^ ]+) HTTP/([01]%.[019])$")
166
167                 -- Yup, it is
168                 if method then
169
170                         msg.type           = "request"
171                         msg.request_method = method:lower()
172                         msg.request_uri    = uri
173                         msg.http_version   = tonumber( http_ver )
174                         msg.headers        = { }
175
176                         -- We're done, next state is header parsing
177                         return true, function( chunk )
178                                 return process_states['headers']( msg, chunk )
179                         end
180
181                 -- Is it a response?
182                 else
183
184                         local http_ver, code, message = chunk:match("^HTTP/([01]%.[019]) ([0-9]+) ([^\r\n]+)$")
185
186                         -- Is a response
187                         if code then
188
189                                 msg.type           = "response"
190                                 msg.status_code    = code
191                                 msg.status_message = message
192                                 msg.http_version   = tonumber( http_ver )
193                                 msg.headers        = { }
194
195                                 -- We're done, next state is header parsing
196                                 return true, function( chunk )
197                                         return process_states['headers']( msg, chunk )
198                                 end
199                         end
200                 end
201         end
202
203         -- Can't handle it
204         return nil, "Invalid HTTP message magic"
205 end
206
207
208 -- Extract headers from given string.
209 process_states['headers'] = function( msg, chunk )
210
211         if chunk ~= nil then
212
213                 -- Look for a valid header format
214                 local hdr, val = chunk:match( "^([A-Z][A-Za-z0-9%-_]+): +(.+)$" )
215
216                 if type(hdr) == "string" and hdr:len() > 0 and
217                    type(val) == "string" and val:len() > 0
218                 then
219                         msg.headers[hdr] = val
220
221                         -- Valid header line, proceed
222                         return true, nil
223
224                 elseif #chunk == 0 then
225                         -- Empty line, we won't accept data anymore
226                         return false, nil
227                 else
228                         -- Junk data
229                         return nil, "Invalid HTTP header received"
230                 end
231         else
232                 return nil, "Unexpected EOF"
233         end
234 end
235
236
237 -- Find first MIME boundary
238 process_states['mime-init'] = function( msg, chunk, filecb )
239
240         if chunk ~= nil then
241                 if #chunk >= #msg.mime_boundary + 2 then
242                         local boundary = chunk:sub( 1, #msg.mime_boundary + 4 )
243
244                         if boundary == "--" .. msg.mime_boundary .. "\r\n" then
245
246                                 -- Store remaining data in buffer
247                                 msg._mimebuffer = chunk:sub( #msg.mime_boundary + 5, #chunk )
248
249                                 -- Switch to header processing state
250                                 return true, function( chunk )
251                                         return process_states['mime-headers']( msg, chunk, filecb )
252                                 end
253                         else
254                                 return nil, "Invalid MIME boundary"
255                         end
256                 else
257                         return true
258                 end
259         else
260                 return nil, "Unexpected EOF"
261         end
262 end
263
264
265 -- Read MIME part headers
266 process_states['mime-headers'] = function( msg, chunk, filecb )
267
268         if chunk ~= nil then
269
270                 -- Combine look-behind buffer with current chunk
271                 chunk = msg._mimebuffer .. chunk
272
273                 if not msg._mimeheaders then
274                         msg._mimeheaders = { }
275                 end
276
277                 local function __storehdr( k, v )
278                         msg._mimeheaders[k] = v
279                         return ""
280                 end
281
282                 -- Read all header lines
283                 local ok, count = 1, 0
284                 while ok > 0 do
285                         chunk, ok = chunk:gsub( "^([A-Z][A-Za-z0-9%-_]+): +([^\r\n]+)\r\n", __storehdr )
286                         count = count + ok
287                 end
288
289                 -- Headers processed, check for empty line
290                 chunk, ok = chunk:gsub( "^\r\n", "" )
291
292                 -- Store remaining buffer contents
293                 msg._mimebuffer = chunk
294
295                 -- End of headers
296                 if ok > 0 then
297
298                         -- When no Content-Type header is given assume text/plain
299                         if not msg._mimeheaders['Content-Type'] then
300                                 msg._mimeheaders['Content-Type'] = 'text/plain'
301                         end
302
303                         -- Check Content-Disposition
304                         if msg._mimeheaders['Content-Disposition'] then
305                                 -- Check for "form-data" token
306                                 if msg._mimeheaders['Content-Disposition']:match("^form%-data; ") then
307                                         -- Check for field name, filename
308                                         local field = msg._mimeheaders['Content-Disposition']:match('name="(.-)"')
309                                         local file  = msg._mimeheaders['Content-Disposition']:match('filename="(.+)"$')
310
311                                         -- Is a file field and we have a callback
312                                         if file and filecb then
313                                                 msg.params[field] = file
314                                                 msg._mimecallback = function(chunk,eof)
315                                                         filecb( {
316                                                                 name    = field;
317                                                                 file    = file;
318                                                                 headers = msg._mimeheaders
319                                                         }, chunk, eof )
320                                                 end
321
322                                         -- Treat as form field
323                                         else
324                                                 local mv = __initval( msg.params, field )
325                                                 msg._mimecallback = function(chunk,eof)
326                                                         __appendval( msg.params, field, mv, chunk )
327                                                 end
328                                         end
329
330                                         -- Header was valid, continue with mime-data
331                                         return true, function( chunk )
332                                                 return process_states['mime-data']( msg, chunk, filecb )
333                                         end
334                                 else
335                                         -- Unknown Content-Disposition, abort
336                                         return nil, "Unexpected Content-Disposition MIME section header"
337                                 end
338                         else
339                                 -- Content-Disposition is required, abort without
340                                 return nil, "Missing Content-Disposition MIME section header"
341                         end
342
343                 -- We parsed no headers yet and buffer is almost empty
344                 elseif count > 0 or #chunk < 128 then
345                         -- Keep feeding me with chunks
346                         return true, nil
347                 end
348
349                 -- Buffer looks like garbage
350                 return nil, "Malformed MIME section header"
351         else
352                 return nil, "Unexpected EOF"
353         end
354 end
355
356
357 -- Read MIME part data
358 process_states['mime-data'] = function( msg, chunk, filecb )
359
360         if chunk ~= nil then
361
362                 -- Combine look-behind buffer with current chunk
363                 local buffer = msg._mimebuffer .. chunk
364
365                 -- Look for MIME boundary
366                 local spos, epos = buffer:find( "\r\n--" .. msg.mime_boundary .. "\r\n", 1, true )
367
368                 if spos then
369                         -- Content data
370                         msg._mimecallback( buffer:sub( 1, spos - 1 ), true )
371
372                         -- Store remainder
373                         msg._mimebuffer = buffer:sub( epos + 1, #buffer )
374
375                         -- Next state is mime-header processing
376                         return true, function( chunk )
377                                 return process_states['mime-headers']( msg, chunk, filecb )
378                         end
379                 else
380                         -- Look for EOF?
381                         local spos, epos = buffer:find( "\r\n--" .. msg.mime_boundary .. "--\r\n", 1, true )
382
383                         if spos then
384                                 -- Content data
385                                 msg._mimecallback( buffer:sub( 1, spos - 1 ), true )
386
387                                 -- We processed the final MIME boundary, cleanup
388                                 msg._mimebuffer   = nil
389                                 msg._mimeheaders  = nil
390                                 msg._mimecallback = nil
391
392                                 -- We won't accept data anymore
393                                 return false
394                         else
395                                 -- We're somewhere within a data section and our buffer is full
396                                 if #buffer > #chunk then
397                                         -- Flush buffered data
398                                         msg._mimecallback( buffer:sub( 1, #buffer - #chunk ), false )
399
400                                         -- Store new data
401                                         msg._mimebuffer = buffer:sub( #buffer - #chunk + 1, #buffer )
402
403                                 -- Buffer is not full yet, append new data
404                                 else
405                                         msg._mimebuffer = buffer
406                                 end
407
408                                 -- Keep feeding me
409                                 return true
410                         end
411                 end
412         else
413                 return nil, "Unexpected EOF"
414         end
415 end
416
417
418 -- Init urldecoding stream
419 process_states['urldecode-init'] = function( msg, chunk, filecb )
420
421         if chunk ~= nil then
422
423                 -- Check for Content-Length
424                 if msg.env.CONTENT_LENGTH then
425                         msg.content_length = tonumber(msg.env.CONTENT_LENGTH)
426
427                         if msg.content_length <= HTTP_MAX_CONTENT then
428                                 -- Initialize buffer
429                                 msg._urldecbuffer = chunk
430                                 msg._urldeclength = 0
431
432                                 -- Switch to urldecode-key state
433                                 return true, function(chunk)
434                                         return process_states['urldecode-key']( msg, chunk, filecb )
435                                 end
436                         else
437                                 return nil, "Request exceeds maximum allowed size"
438                         end
439                 else
440                         return nil, "Missing Content-Length header"
441                 end
442         else
443                 return nil, "Unexpected EOF"
444         end
445 end
446
447
448 -- Process urldecoding stream, read and validate parameter key
449 process_states['urldecode-key'] = function( msg, chunk, filecb )
450         if chunk ~= nil then
451
452                 -- Prevent oversized requests
453                 if msg._urldeclength >= msg.content_length then
454                         return nil, "Request exceeds maximum allowed size"
455                 end
456
457                 -- Combine look-behind buffer with current chunk
458                 local buffer = msg._urldecbuffer .. chunk
459                 local spos, epos = buffer:find("=")
460
461                 -- Found param
462                 if spos then
463
464                         -- Check that key doesn't exceed maximum allowed key length
465                         if ( spos - 1 ) <= HTTP_URLENC_MAXKEYLEN then
466                                 local key = urldecode( buffer:sub( 1, spos - 1 ) )
467
468                                 -- Prepare buffers
469                                 msg.params[key]         = ""
470                                 msg._urldeclength   = msg._urldeclength + epos
471                                 msg._urldecbuffer   = buffer:sub( epos + 1, #buffer )
472
473                                 -- Use file callback or store values inside msg.params
474                                 if filecb then
475                                         msg._urldeccallback = function( chunk, eof )
476                                                 filecb( field, chunk, eof )
477                                         end
478                                 else
479                                         local mv = __initval( msg.params, key )
480                                         msg._urldeccallback = function( chunk, eof )
481                                                 __appendval( msg.params, key, mv, chunk )
482
483                                                 -- FIXME: Use a filter
484                                                 if eof then
485                                                         __finishval( msg.params, key, mv, urldecode )
486                                                 end
487                                         end
488                                 end
489
490                                 -- Proceed with urldecode-value state
491                                 return true, function( chunk )
492                                         return process_states['urldecode-value']( msg, chunk, filecb )
493                                 end
494                         else
495                                 return nil, "POST parameter exceeds maximum allowed length"
496                         end
497                 else
498                         return nil, "POST data exceeds maximum allowed length"
499                 end
500         else
501                 return nil, "Unexpected EOF"
502         end
503 end
504
505
506 -- Process urldecoding stream, read parameter value
507 process_states['urldecode-value'] = function( msg, chunk, filecb )
508
509         if chunk ~= nil then
510
511                 -- Combine look-behind buffer with current chunk
512                 local buffer = msg._urldecbuffer .. chunk
513
514                 -- Check for EOF
515                 if #buffer == 0 then
516                         -- Compare processed length
517                         if msg._urldeclength == msg.content_length then
518                                 -- Cleanup
519                                 msg._urldeclength   = nil
520                                 msg._urldecbuffer   = nil
521                                 msg._urldeccallback = nil
522
523                                 -- We won't accept data anymore
524                                 return false
525                         else
526                                 return nil, "Content-Length mismatch"
527                         end
528                 end
529
530                 -- Check for end of value
531                 local spos, epos = buffer:find("[&;]")
532                 if spos then
533
534                         -- Flush buffer, send eof
535                         msg._urldeccallback( buffer:sub( 1, spos - 1 ), true )
536                         msg._urldecbuffer = buffer:sub( epos + 1, #buffer )
537                         msg._urldeclength = msg._urldeclength + epos
538
539                         -- Back to urldecode-key state
540                         return true, function( chunk )
541                                 return process_states['urldecode-key']( msg, chunk, filecb )
542                         end
543                 else
544                         -- We're somewhere within a data section and our buffer is full
545                         if #buffer > #chunk then
546                                 -- Flush buffered data
547                                 msg._urldeccallback( buffer:sub( 1, #buffer - #chunk ), false )
548
549                                 -- Store new data
550                                 msg._urldeclength = msg._urldeclength + #buffer - #chunk
551                                 msg._urldecbuffer = buffer:sub( #buffer - #chunk + 1, #buffer )
552
553                         -- Buffer is not full yet, append new data
554                         else
555                                 msg._urldecbuffer = buffer
556                         end
557
558                         -- Keep feeding me
559                         return true
560                 end
561         else
562                 -- Send EOF
563                 msg._urldeccallback( "", true )
564                 return false
565         end
566 end
567
568
569 -- Creates a header source from a given socket
570 function header_source( sock )
571         return ltn12.source.simplify( function()
572
573                 local chunk, err, part = sock:receive("*l")
574
575                 -- Line too long
576                 if chunk == nil then
577                         if err ~= "timeout" then
578                                 return nil, part
579                                         and "Line exceeds maximum allowed length"
580                                         or  "Unexpected EOF"
581                         else
582                                 return nil, err
583                         end
584
585                 -- Line ok
586                 elseif chunk ~= nil then
587
588                         -- Strip trailing CR
589                         chunk = chunk:gsub("\r$","")
590
591                         return chunk, nil
592                 end
593         end )
594 end
595
596
597 -- Decode MIME encoded data.
598 function mimedecode_message_body( source, msg, filecb )
599
600         -- Find mime boundary
601         if msg and msg.env.CONTENT_TYPE then
602
603                 local bound = msg.env.CONTENT_TYPE:match("^multipart/form%-data; boundary=(.+)")
604
605                 if bound then
606                         msg.mime_boundary = bound
607                 else
608                         return nil, "No MIME boundary found or invalid content type given"
609                 end
610         end
611
612         -- Create an initial LTN12 sink
613         -- The whole MIME parsing process is implemented as fancy sink, sinks replace themself
614         -- depending on current processing state (init, header, data). Return the initial state.
615         local sink = ltn12.sink.simplify(
616                 function( chunk )
617                         return process_states['mime-init']( msg, chunk, filecb )
618                 end
619         )
620
621         -- Create a throttling LTN12 source
622         -- Frequent state switching in the mime parsing process leads to unwanted buffer aggregation.
623         -- This source checks wheather there's still data in our internal read buffer and returns an
624         -- empty string if there's already enough data in the processing queue. If the internal buffer
625         -- runs empty we're calling the original source to get the next chunk of data.
626         local tsrc = function()
627
628                 -- XXX: we schould propably keep the maximum buffer size in sync with
629                 --      the blocksize of our original source... but doesn't really matter
630                 if msg._mimebuffer ~= null and #msg._mimebuffer > 256 then
631                         return ""
632                 else
633                         return source()
634                 end
635         end
636
637         -- Pump input data...
638         while true do
639                 -- get data
640                 local ok, err = ltn12.pump.step( tsrc, sink )
641
642                 -- error
643                 if not ok and err then
644                         return nil, err
645
646                 -- eof
647                 elseif not ok then
648                         return true
649                 end
650         end
651 end
652
653
654 -- Decode urlencoded data.
655 function urldecode_message_body( source, msg )
656
657         -- Create an initial LTN12 sink
658         -- Return the initial state.
659         local sink = ltn12.sink.simplify(
660                 function( chunk )
661                         return process_states['urldecode-init']( msg, chunk )
662                 end
663         )
664
665         -- Create a throttling LTN12 source
666         -- See explaination in mimedecode_message_body().
667         local tsrc = function()
668                 if msg._urldecbuffer ~= null and #msg._urldecbuffer > 0 then
669                         return ""
670                 else
671                         return source()
672                 end
673         end
674
675         -- Pump input data...
676         while true do
677                 -- get data
678                 local ok, err = ltn12.pump.step( tsrc, sink )
679
680                 -- step
681                 if not ok and err then
682                         return nil, err
683
684                 -- eof
685                 elseif not ok then
686                         return true
687                 end
688         end
689 end
690
691
692 -- Parse a http message header
693 function parse_message_header( source )
694
695         local ok   = true
696         local msg  = { }
697
698         local sink = ltn12.sink.simplify(
699                 function( chunk )
700                         return process_states['magic']( msg, chunk )
701                 end
702         )
703
704         -- Pump input data...
705         while ok do
706
707                 -- get data
708                 ok, err = ltn12.pump.step( source, sink )
709
710                 -- error
711                 if not ok and err then
712                         return nil, err
713
714                 -- eof
715                 elseif not ok then
716
717                         -- Process get parameters
718                         if ( msg.request_method == "get" or msg.request_method == "post" ) and
719                            msg.request_uri:match("?")
720                         then
721                                 msg.params = urldecode_params( msg.request_uri )
722                         else
723                                 msg.params = { }
724                         end
725
726                         -- Populate common environment variables
727                         msg.env = {
728                                 CONTENT_LENGTH    = msg.headers['Content-Length'];
729                                 CONTENT_TYPE      = msg.headers['Content-Type'];
730                                 REQUEST_METHOD    = msg.request_method:upper();
731                                 REQUEST_URI       = msg.request_uri;
732                                 SCRIPT_NAME       = msg.request_uri:gsub("?.+$","");
733                                 SCRIPT_FILENAME   = "";         -- XXX implement me
734                                 SERVER_PROTOCOL   = "HTTP/" .. string.format("%.1f", msg.http_version)
735                         }
736
737                         -- Populate HTTP_* environment variables
738                         for i, hdr in ipairs( {
739                                 'Accept',
740                                 'Accept-Charset',
741                                 'Accept-Encoding',
742                                 'Accept-Language',
743                                 'Connection',
744                                 'Cookie',
745                                 'Host',
746                                 'Referer',
747                                 'User-Agent',
748                         } ) do
749                                 local var = 'HTTP_' .. hdr:upper():gsub("%-","_")
750                                 local val = msg.headers[hdr]
751
752                                 msg.env[var] = val
753                         end
754                 end
755         end
756
757         return msg
758 end
759
760
761 -- Parse a http message body
762 function parse_message_body( source, msg, filecb )
763         -- Is it multipart/mime ?
764         if msg.env.REQUEST_METHOD == "POST" and msg.env.CONTENT_TYPE and
765            msg.env.CONTENT_TYPE:match("^multipart/form%-data")
766         then
767
768                 return mimedecode_message_body( source, msg, filecb )
769
770         -- Is it application/x-www-form-urlencoded ?
771         elseif msg.env.REQUEST_METHOD == "POST" and msg.env.CONTENT_TYPE and
772                msg.env.CONTENT_TYPE == "application/x-www-form-urlencoded"
773         then
774                 return urldecode_message_body( source, msg, filecb )
775
776
777         -- Unhandled encoding
778         -- If a file callback is given then feed it chunk by chunk, else
779         -- store whole buffer in message.content
780         else
781
782                 local sink
783
784                 -- If we have a file callback then feed it
785                 if type(filecb) == "function" then
786                         sink = filecb
787
788                 -- ... else append to .content
789                 else
790                         msg.content = ""
791                         msg.content_length = 0
792
793                         sink = function( chunk )
794                                 if ( msg.content_length + #chunk ) <= HTTP_MAX_CONTENT then
795
796                                         msg.content        = msg.content        .. chunk
797                                         msg.content_length = msg.content_length + #chunk
798
799                                         return true
800                                 else
801                                         return nil, "POST data exceeds maximum allowed length"
802                                 end
803                         end
804                 end
805
806                 -- Pump data...
807                 while true do
808                         local ok, err = ltn12.pump.step( source, sink )
809
810                         if not ok and err then
811                                 return nil, err
812                         elseif not err then
813                                 return true
814                         end
815                 end
816         end
817 end
818
819 -- Status codes
820 statusmsg = {
821         [200] = "OK",
822         [301] = "Moved Permanently",
823         [304] = "Not Modified",
824         [400] = "Bad Request",
825         [403] = "Forbidden",
826         [404] = "Not Found",
827         [405] = "Method Not Allowed",
828         [411] = "Length Required",
829         [412] = "Precondition Failed",
830         [500] = "Internal Server Error",
831         [503] = "Server Unavailable",
832 }