1+ import usocket , os
2+ class Response :
3+
4+ def __init__ (self , socket , saveToFile = None ):
5+ self ._socket = socket
6+ self ._saveToFile = saveToFile
7+ self ._encoding = 'utf-8'
8+ if saveToFile is not None :
9+ CHUNK_SIZE = 512 # bytes
10+ with open (saveToFile , 'w' ) as outfile :
11+ data = self ._socket .read (CHUNK_SIZE )
12+ while data :
13+ outfile .write (data )
14+ data = self ._socket .read (CHUNK_SIZE )
15+ outfile .close ()
16+
17+ self .close ()
18+
19+ def close (self ):
20+ if self ._socket :
21+ self ._socket .close ()
22+ self ._socket = None
23+
24+ @property
25+ def content (self ):
26+ if self ._saveToFile is not None :
27+ raise SystemError ('You cannot get the content from the response as you decided to save it in {}' .format (self ._saveToFile ))
28+
29+ try :
30+ result = self ._socket .read ()
31+ return result
32+ finally :
33+ self .close ()
34+
35+ @property
36+ def text (self ):
37+ return str (self .content , self ._encoding )
38+
39+ def json (self ):
40+ try :
41+ import ujson
42+ result = ujson .load (self ._socket )
43+ return result
44+ finally :
45+ self .close ()
46+
47+
48+ class HttpClient :
49+
50+ def __init__ (self , headers = {}):
51+ self ._headers = headers
52+
53+ def request (self , method , url , data = None , json = None , file = None , custom = None , saveToFile = None , headers = {}, stream = None ):
54+ def _write_headers (sock , _headers ):
55+ for k in _headers :
56+ sock .write (b'{}: {}\r \n ' .format (k , _headers [k ]))
57+
58+ try :
59+ proto , dummy , host , path = url .split ('/' , 3 )
60+ except ValueError :
61+ proto , dummy , host = url .split ('/' , 2 )
62+ path = ''
63+ if proto == 'http:' :
64+ port = 80
65+ elif proto == 'https:' :
66+ import ussl
67+ port = 443
68+ else :
69+ raise ValueError ('Unsupported protocol: ' + proto )
70+
71+ if ':' in host :
72+ host , port = host .split (':' , 1 )
73+ port = int (port )
74+
75+ ai = usocket .getaddrinfo (host , port , 0 , usocket .SOCK_STREAM )
76+ if len (ai ) < 1 :
77+ raise ValueError ('You are not connected to the internet...' )
78+ ai = ai [0 ]
79+
80+ s = usocket .socket (ai [0 ], ai [1 ], ai [2 ])
81+ try :
82+ s .connect (ai [- 1 ])
83+ if proto == 'https:' :
84+ s = ussl .wrap_socket (s , server_hostname = host )
85+ s .write (b'%s /%s HTTP/1.0\r \n ' % (method , path ))
86+ if not 'Host' in headers :
87+ s .write (b'Host: %s\r \n ' % host )
88+ # Iterate over keys to avoid tuple alloc
89+ _write_headers (s , self ._headers )
90+ _write_headers (s , headers )
91+
92+ # add user agent
93+ s .write (b'User-Agent: MicroPython Client\r \n ' )
94+ if json is not None :
95+ assert data is None
96+ import ujson
97+ data = ujson .dumps (json )
98+ s .write (b'Content-Type: application/json\r \n ' )
99+
100+ if data :
101+ s .write (b'Content-Length: %d\r \n ' % len (data ))
102+ s .write (b'\r \n ' )
103+ s .write (data )
104+ elif file :
105+ s .write (b'Content-Length: %d\r \n ' % os .stat (file )[6 ])
106+ s .write (b'\r \n ' )
107+ with open (file , 'r' ) as file_object :
108+ for line in file_object :
109+ s .write (line + '\n ' )
110+ elif custom :
111+ custom (s )
112+ else :
113+ s .write (b'\r \n ' )
114+
115+ l = s .readline ()
116+ # print(l)
117+ l = l .split (None , 2 )
118+ status = int (l [1 ])
119+ reason = ''
120+ if len (l ) > 2 :
121+ reason = l [2 ].rstrip ()
122+ while True :
123+ l = s .readline ()
124+ if not l or l == b'\r \n ' :
125+ break
126+ # print(l)
127+ if l .startswith (b'Transfer-Encoding:' ):
128+ if b'chunked' in l :
129+ raise ValueError ('Unsupported ' + l )
130+ elif l .startswith (b'Location:' ) and not 200 <= status <= 299 :
131+ raise NotImplementedError ('Redirects not yet supported' )
132+ except OSError :
133+ s .close ()
134+ raise
135+
136+ resp = Response (s , saveToFile )
137+ resp .status_code = status
138+ resp .reason = reason
139+ return resp
140+
141+ def head (self , url , ** kw ):
142+ return self .request ('HEAD' , url , ** kw )
143+
144+ def get (self , url , ** kw ):
145+ return self .request ('GET' , url , ** kw )
146+
147+ def post (self , url , ** kw ):
148+ return self .request ('POST' , url , ** kw )
149+
150+ def put (self , url , ** kw ):
151+ return self .request ('PUT' , url , ** kw )
152+
153+ def patch (self , url , ** kw ):
154+ return self .request ('PATCH' , url , ** kw )
155+
156+ def delete (self , url , ** kw ):
157+ return self .request ('DELETE' , url , ** kw )
0 commit comments