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