http.bi 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. ' http.bi is part of Bliss (track editor for Stunts)
  2. ' Copyright (C) 2016-2018 Lucas Pedrosa
  3. ' Bliss is free software: you can redistribute it and/or modify
  4. ' it under the terms of the GNU General Public License as published by
  5. ' the Free Software Foundation, version 3 of the License.
  6. ' This program is distributed in the hope that it will be useful,
  7. ' but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. ' GNU General Public License for more details.
  10. ' You should have received a copy of the GNU General Public License
  11. ' along with this program. If not, see <http://www.gnu.org/licenses/>.
  12. #ifdef __FB_WIN32__
  13. #include "windows.bi"
  14. #include "win/windef.bi"
  15. #include "win/winsock2.bi"
  16. #ifndef opensocket
  17. #define opensocket socket
  18. #endif
  19. #else
  20. #include "crt/unistd.bi"
  21. #include "crt/netinet/in.bi"
  22. #include "crt/sys/select.bi"
  23. #include "crt/netdb.bi"
  24. #include "crt/arpa/inet.bi"
  25. #endif
  26. #ifndef TCP_NODELAY
  27. #define TCP_NODELAY &H01
  28. #endif
  29. Declare Sub HTTP_Start
  30. Declare Sub HTTP_End
  31. Declare Function HTTP_StartServer(ByVal port As UShort, max_connections As Integer) As Byte
  32. Declare Function HTTP_GetClient As Long
  33. Declare Function HTTP_Connect(address As String, port As UShort) As Byte
  34. Declare Function HTTP_BuildGet(host As String, path As String, mimetype As String = "") As String
  35. Declare Function HTTP_BuildHead(host As String, path As String, mimetype As String = "") As String
  36. Declare Function HTTP_BuildPost(host As String, path As String, query As String, refer As Byte) As String
  37. Declare Function HTTP_ReadyToGet As Byte
  38. Declare Function HTTP_ReadyToPut As Byte
  39. Declare Function HTTP_GetData(ByRef dbuffer As Any Ptr) As Long
  40. Declare Function HTTP_PutData(ByVal dbuffer As Any Ptr, ByVal datasize As Long) As Long
  41. Dim Shared HTTP_Socket As Long, HTTP_addr As sockaddr_in
  42. Dim Shared As fd_set HTTP_writefds, HTTP_readfds, HTTP_readfd
  43. Dim Shared HTTP_timeout As timeval
  44. Dim Shared use_curl As Byte = -1
  45. Function HTTP_BuildGet(host As String, path As String, mimetype As String = "") As String
  46. Dim s As String
  47. s = "GET " + path + " HTTP/1.1" + Chr(13, 10)
  48. s += "Host: " + host + Chr(13, 10)
  49. If Len(mimetype) Then s += "mime: " + mimetype + Chr(13, 10)
  50. s += Chr(13, 10)
  51. Return s
  52. End Function
  53. Function HTTP_BuildHead(host As String, path As String, mimetype As String = "") As String
  54. Dim s As String
  55. s = "HEAD " + path + " HTTP/1.1" + Chr(13, 10)
  56. s += "Host: " + host + Chr(13, 10)
  57. If Len(mimetype) Then s += "mime: " + mimetype + Chr(13, 10)
  58. s += Chr(13, 10)
  59. Return s
  60. End Function
  61. Function HTTP_BuildPost(host As String, path As String, query As String, refer As Byte) As String
  62. Dim s As String
  63. s = "POST " + path + " HTTP/1.1" + Chr(13, 10)
  64. s += "Host: " + host + Chr(13, 10)
  65. If refer Then s += "Referer: http://" + host + path + "?" + Chr(13, 10)
  66. s += "Content-type: application/x-www-form-urlencoded" + Chr(13, 10)
  67. s += "Content-length: " + Str(Len(query)) + Chr(13, 10)
  68. s += "Connection: close" + Chr(13, 10, 13, 10)
  69. s += query
  70. Return s
  71. End Function
  72. Function HTTP_Connect(address As String, port As UShort) As Byte
  73. HTTP_Start
  74. HTTP_Socket = opensocket(AF_INET, SOCK_STREAM, 0)
  75. If HTTP_Socket = -1 Then Return -1
  76. Dim he As hostent Ptr
  77. he = gethostbyname(StrPtr(address))
  78. If he = 0 Then Return -2
  79. HTTP_addr.sin_family = AF_INET
  80. HTTP_addr.sin_port = htons(port)
  81. HTTP_addr.sin_addr = *CPtr(in_addr Ptr, he->h_addr_list[0])
  82. If connect(HTTP_Socket, CPtr(sockaddr Ptr, @HTTP_addr), SizeOf(sockaddr)) < 0 Then
  83. Return -3
  84. End if
  85. Return 0
  86. End Function
  87. Function HTTP_Download_via_curl(address As String, ByRef buffer As UByte Ptr, isawebpage As Byte = 0, tempdir As String = "") As Long
  88. Dim ad As String, f As Integer
  89. ad = Trim(address)
  90. If LCase(Left(ad, 5)) = "http:" OrElse LCase(Left(ad, 6)) = "https:" Then
  91. Shell "curl --connect-timeout 8 " & ad & " > " & tempdir & "tfile.tmp"
  92. Else
  93. Shell "curl --connect-timeout 8 https://" & ad & " > " & tempdir & "tfile.tmp"
  94. If FileLen(tempdir & "tfile.tmp") = 0 Then
  95. Shell "curl --connect-timeout 10 http://" & ad & " > " & tempdir & "tfile.tmp"
  96. End If
  97. End If
  98. If FileLen(tempdir & "tfile.tmp") = 0 Then
  99. Return 0
  100. Else
  101. Dim l As Long
  102. f = FreeFile
  103. Open tempdir & "tfile.tmp" For Binary Access Read As f
  104. l = LoF(f)
  105. If buffer Then Deallocate buffer : buffer = 0
  106. buffer = Allocate(LoF(f))
  107. Get #f, , *buffer, LoF(f)
  108. Close f
  109. Kill tempdir & "tfile.tmp"
  110. Return l
  111. End If
  112. End Function
  113. Function HTTP_Download(address As String, ByRef buffer As UByte Ptr, isawebpage As Byte = 0, tempdir As String = "") As Long
  114. If use_curl Then
  115. Return HTTP_Download_via_curl(address, buffer, isawebpage, tempdir)
  116. End If
  117. Dim request As String, n As Short
  118. Dim As String host, path, mime
  119. Dim t As Double, bytes As Long, newbuffer As UByte Ptr
  120. Dim header As ZString Ptr
  121. If isawebpage Then mime = "text/html;text/plain" Else mime = ""
  122. n = InStr(address, "/")
  123. If n Then
  124. host = Left(address, n - 1)
  125. path = Mid(address, n)
  126. Else
  127. host = address
  128. path = "/"
  129. End If
  130. If HTTP_Connect(host, 80) Then Return -1 'Could not connect
  131. request = HTTP_BuildGet(host, path, mime)
  132. t = Timer
  133. Do
  134. If HTTP_ReadyToPut Then Exit Do
  135. If Timer > t + 3 Then Return -2 'Time-out 3 seconds
  136. Sleep 100
  137. Loop
  138. HTTP_PutData StrPtr(request), Len(request)
  139. t = Timer
  140. Do
  141. If HTTP_ReadyToGet Then Exit Do
  142. If Timer > t + 3 Then Return -3 'Time-out 3 seconds
  143. Sleep 100
  144. Loop
  145. bytes = HTTP_GetData(newbuffer)
  146. header = newbuffer
  147. n = InStr(*header, Chr(13, 10, 13, 10)) + 3
  148. If buffer Then Deallocate buffer : buffer = 0
  149. If bytes - n >= 1 Then
  150. buffer = Allocate(bytes - n)
  151. For i As Long = 0 To bytes - n - 1
  152. buffer[i] = newbuffer[i + n]
  153. Next i
  154. End If
  155. Deallocate newbuffer
  156. Return bytes - n
  157. End Function
  158. Sub HTTP_End
  159. If HTTP_Socket >= 1 Then closesocket(HTTP_Socket)
  160. #ifdef __FB_WIN32__
  161. WSACleanup()
  162. #endif
  163. End Sub
  164. Function HTTP_GetData(ByRef dbuffer As Any Ptr) As Long
  165. Static chunk(8191) As UByte
  166. If HTTP_Socket < 0 Then Return -1
  167. Dim dsize As Long, bytes As Long, pwrite As UByte Ptr, i As Long
  168. If dbuffer Then Deallocate dbuffer : dbuffer = 0
  169. Do
  170. bytes = recv(HTTP_Socket, @chunk(0), 8192, 0)
  171. If bytes = 0 Then
  172. If dsize = 0 Then Return -1
  173. Exit Do
  174. End If
  175. If bytes < 0 Then Return -1
  176. dbuffer = Reallocate(dbuffer, dsize + bytes)
  177. pwrite = dbuffer + dsize
  178. For i = 0 To bytes - 1
  179. pwrite[i] = chunk(i)
  180. Next i
  181. dsize += bytes
  182. Sleep 20
  183. For i = 1 To 100
  184. If HTTP_ReadyToGet Then Exit For
  185. Sleep 10
  186. Next i
  187. Loop Until i > 100
  188. Return dsize
  189. End Function
  190. Function HTTP_PutData(ByVal dbuffer As Any Ptr, ByVal datasize As Long) As Long
  191. If HTTP_Socket < 0 Or dbuffer = 0 Or datasize < 1 Then Return -1
  192. Dim size As Long, dsize As Integer, bytes As Long
  193. dsize = datasize
  194. While (size < datasize)
  195. bytes = send(HTTP_Socket, dbuffer, dsize, 0)
  196. If bytes < 0 Then Return -1
  197. dbuffer += bytes
  198. dsize -= bytes
  199. size += bytes
  200. WEnd
  201. Return size
  202. End Function
  203. Function HTTP_ReadyToGet As Byte
  204. If HTTP_Socket < 0 Then Return -1
  205. FD_ZERO(@HTTP_readfds)
  206. FD_SET_(HTTP_Socket, @HTTP_readfds)
  207. If select_(HTTP_Socket + 1, @HTTP_readfds, 0, 0, @HTTP_timeout) = -1 Then Return -1
  208. Return IIf(FD_ISSET(HTTP_Socket, @HTTP_readfds), -1, 0)
  209. End Function
  210. Function HTTP_ReadyToPut As Byte
  211. If HTTP_Socket < 0 Then Return -1
  212. FD_ZERO(@HTTP_writefds)
  213. FD_SET_(HTTP_Socket, @HTTP_writefds)
  214. If select_(HTTP_Socket + 1, 0, @HTTP_writefds, 0, @HTTP_timeout) = -1 Then Return -1
  215. Return IIf(FD_ISSET(HTTP_Socket, @HTTP_writefds), -1, 0)
  216. End Function
  217. Sub HTTP_Start
  218. Static already_initialised As Byte = 0
  219. #ifdef __FB_WIN32__
  220. If Not already_initialised Then
  221. Dim wdata As WSAData
  222. WSAStartup(WINSOCK_VERSION, @wdata)
  223. already_initialised = -1
  224. End If
  225. #endif
  226. End Sub
  227. Function HTTP_GetClient As Long
  228. Dim ip As Long
  229. FD_ZERO(@HTTP_readfd)
  230. FD_SET_(HTTP_Socket, @HTTP_readfd)
  231. If select_(HTTP_Socket + 1, @HTTP_readfd, 0, 0, @HTTP_timeout) = -1 _
  232. Then Return 0
  233. If FD_ISSET(HTTP_Socket, @HTTP_readfd) Then
  234. ip = accept(HTTP_Socket, 0, 0)
  235. If ip = -1 Then Return 0
  236. Return ip
  237. Else
  238. Return 0
  239. End If
  240. End Function