1 # httpsrv library routines for python.
2 # Copyright (C) 2018 rofl0r
4 # This library is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU Lesser General Public
6 # License as published by the Free Software Foundation; either
7 # version 2.1 of the License, or (at your option) any later version.
9 # This library is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # Lesser General Public License for more details.
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 # you can find the full license text at
19 # https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
36 return method
, url
, ver
39 def __init__(self
, addr
, conn
):
45 def _send_i(self
, data
):
47 if self
.debugreq
and len(data
): print ">>>\n", data
49 def send(self
, code
, msg
, response
, headers
=None):
51 r
+= "HTTP/1.1 %d %s\r\n"%(code
, msg
)
54 r
+= "%s: %s\r\n"%(h
, headers
[h
])
55 r
+= "Content-Length: %d\r\n" % len(response
)
59 self
._send
_i
(response
)
63 def serve_file(self
, filename
):
64 self
.send(200, "OK", open(filename
, 'r').read())
66 def redirect(self
, url
, headers
=None):
67 h
= dict() if not headers
else headers
.copy()
69 self
.send(301, "Moved Permanently", "", headers
=h
)
71 def _url_decode(self
, s
): return urllib
.unquote_plus(s
)
73 def read_request(self
):
77 rnrn
= s
.find('\r\n\r\n')
79 r
= self
.conn
.recv(CHUNKSIZE
)
80 if len(r
) == 0: return None
84 for line
in s
.split('\n'):
85 if line
.lower().startswith('content-length:'):
86 try: cl
= int(line
.split(':', 1)[1].strip())
89 while len(s
) < rnrn
+ 4 + cl
: # 4 == len('\r\n\r\n')
90 r
= self
.conn
.recv(CHUNKSIZE
)
91 if len(r
) == 0: return None
101 if self
.debugreq
: print "<<<\n", s
104 if n
== -1: err
= True
108 meth
, url
, ver
= _parse_req(line
)
109 if not (ver
== "HTTP/1.0" or ver
== "HTTP/1.1"):
111 if not (meth
== 'GET' or meth
== 'POST'):
114 self
.send(500, "error", "client sent invalid request")
119 result
['method'] = meth
121 for x
in a
.split('\r\n'):
123 y
,z
= x
.split(':', 1)
124 result
[y
] = z
.strip()
126 result
['postdata'] = dict()
127 postdata
= s
[rnrn
:].split('&')
128 for line
in '\n'.join( postdata
).split('\n'):
130 k
,v
= line
.split('=', 1)
131 result
['postdata'][k
] = self
._url
_decode
(v
.strip())
134 def disconnect(self
):
135 if self
.active
: self
.conn
.close()
141 def _isnumericipv4(self
, ip
):
143 a
,b
,c
,d
= ip
.split('.')
144 if int(a
) < 256 and int(b
) < 256 and int(c
) < 256 and int(d
) < 256:
150 def _resolve(self
, host
, port
, want_v4
=True):
151 if self
._isnumericipv
4(host
):
152 return socket
.AF_INET
, (host
, port
)
153 for res
in socket
.getaddrinfo(host
, port
, \
154 socket
.AF_UNSPEC
, socket
.SOCK_STREAM
, 0, socket
.AI_PASSIVE
):
155 af
, socktype
, proto
, canonname
, sa
= res
156 if want_v4
and af
!= socket
.AF_INET
: continue
157 if af
!= socket
.AF_INET
and af
!= socket
.AF_INET6
: continue
162 def __init__(self
, listenip
, port
):
164 self
.listenip
= listenip
168 af
, sa
= self
._resolve
(self
.listenip
, self
.port
)
169 s
= socket
.socket(af
, socket
.SOCK_STREAM
)
170 s
.setsockopt(socket
.SOL_SOCKET
, socket
.SO_REUSEADDR
, 1)
171 s
.bind((sa
[0], sa
[1]))
175 def wait_client(self
):
176 conn
, addr
= self
.s
.accept()
177 c
= HttpClient(addr
, conn
)
181 if __name__
== "__main__":
182 hs
= HttpSrv('0.0.0.0', 8080)
187 req
= c
.read_request()
190 testdomain
= 'foobar.corps'
192 c
.send(200, "OK", "<html><body>hello world</body></html>")
193 elif url
.endswith("/redir") or url
.endswith("/redir/"):
194 c
.redirect("http://www.%s:%d/"%(testdomain
, hs
.port
), headers
={'Set-Cookie':'foo=bar; Path=/; HttpOnly; Domain=%s;'%testdomain
})
195 elif url
== '/post.html':
197 c
.send(200, "OK", '<html><body><pre>%s</pre></body></html>'%s)
199 c
.send(404, "Not Found", "404: The requested resource was not found")