Blockchain Websocket API with Python gets Bad Request 400 - api

I am trying to establish a websocket to blockchain over blockchain exchange api, but I get a response like HTTP/1.1 400 Bad Request.
Code example taken from https://github.com/websocket-client/websocket-client
CODE I
import websocket
from websocket import create_connection
try:
import thread
except ImportError:
import _thread as thread
import time
def on_message(ws, message):
print(message)
def on_error(ws, error):
print(error)
def on_close(ws):
print("### closed ###")
def on_open(ws):
msg = '{"token": "", "action": "subscribe", "channel": "auth"}'
ws.send(msg)
if __name__ == "__main__":
# options = {'origin': 'https://exchange.blockchain.com'}
url = "wss://ws.prod.blockchain.info/mercury-gateway/v1/ws"
websocket.enableTrace(True)
ws = websocket.WebSocketApp(url,
on_message=on_message,
on_error=on_error,
on_close=on_close)
ws.on_open = on_open
ws.run_forever()
--- request header ---
GET /mercury-gateway/v1/ws HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: ws.prod.blockchain.info
Origin: http://ws.prod.blockchain.info
Sec-WebSocket-Key: ldRVrsrxBUnvokDHvzNICw==
Sec-WebSocket-Version: 13
--- response header ---
HTTP/1.1 400 Bad Request
Server: nginx
Date: Mon, 08 Feb 2021 23:20:44 GMT
Content-Type: text/plain; charset=UTF-8
Content-Length: 70
X-Cache-Status: 9b46f4091784da7cbae64c3e66446707
X-Blockchain-Language: en
X-Blockchain-Language-ID: 0:0:0 (en:en:en)
X-Request-ID: 5ef71341f43f1d9ec5d37c25164676dc
X-Original-Host: ws.prod.blockchain.info
X-Blockchain-Server: BlockchainFE/1.0
X-Blockchain-CP-F: j1vn 0.012 - 5ef71341f43f1d9ec5d37c25164676dc
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Via: 1.1 google
Alt-Svc: clear
Connection: close
I tried with and without the options dictionary, but without any success.
Although that code works, but I need the event methods on_message, on_error, on_close and on_open.
Either there is a way to get those events working for CODE II or there is a way to establish the websocket connection for CODE I.
Help is much appreciated. Thank you!
CODE II
from websocket import create_connection
options = {}
options['origin'] = 'https://exchange.blockchain.com'
url = "wss://ws.prod.blockchain.info/mercury-gateway/v1/ws"
ws = create_connection(url, **options)
print(ws.sock_opt)
msg = '{"token": "", "action": "subscribe", "channel": "auth"}'
ws.send(msg)
result = ws.recv()
print(result)
msg = '{"action": "subscribe", "channel": "balances"}'
ws.send(msg)
result = ws.recv()
print(result)

Actually, the answer is, setting the origin-Header to the right value, like this:
import websocket
try:
import thread
except ImportError:
import _thread as thread
import time
def on_message(ws, message):
print("MSG:", message)
def on_error(ws, error):
print("### error ###")
print(error)
def on_close(ws):
print("### closed ###")
def on_open(ws):
msg = '{"action": "subscribe", "channel": "prices", "symbol": "BTC-EUR", "granularity": "60" }'
ws.send(msg)
if __name__ == "__main__":
url = "wss://ws.prod.blockchain.info/mercury-gateway/v1/ws"
websocket.enableTrace(True)
ws = websocket.WebSocketApp(url,
on_message=on_message,
on_error=on_error,
on_close=on_close)
ws.on_open = on_open
options = {'origin': 'https://exchange.blockchain.com'}
ws.run_forever(origin=options['origin'])

Related

CORS with Flask, axios and https not working (response header sends origin as http instead of https)

My frontend (Expo Go web) is running at http://localhost:19006/ but when it receives a response from the backend, it somehow believes it runs under https://localhost:19006/
Also, the iOS version of Expo Go logs the following error:
LOG [AxiosError: Network Error]
I'm using Flask in the backend with CORS set as follows:
app = Flask(__name__)
app.config['DEBUG'] = True
app.config['CORS_HEADERS'] = 'Content-Type'
app.config['SECRET_KEY'] = os.environ.get("SECRET_KEY", 'xxx')
cors = CORS(app, resources={r"/*": {"origins": "*", "allow_headers": "*", "expose_headers": "*", "Access-Control-Allow-Origin": "*"}})
and a simple return function:
#app.route("/matches", methods=["GET"])
def getMatches():
print('request for matches')
response = matches.getMatches()
return response
if __name__ == '__main__':
app.run(ssl_context=('certs/cert.pem', 'certs/key.pem'))
My frontend part is using react native with Expo Go. The query to the backend is done this way:
export default function App() {
const axiosApiCall = () => {
const config = {
headers:{
'origin': 'https://localhost:19006' #<- Here also tried http but no change
}
};
axios
.get("https://127.0.0.1:5000/matches", config)
.then((response) => {
setState({quote : 'yes'});
console.log(response.data);
})
.catch((error) => {
console.log(error);
})
}
The backend works properly fine as I can see in Postman. The result is technically showing up in the response of the web-version of Expo Go, however, it appears that there's an issue with CORS:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://127.0.0.1:5000/matches. (Reason: CORS header ‘Access-Control-Allow-Origin’ does not match ‘https://localhost:19006’).
And here's the response header:
HTTP/1.1 200 OK
Server: Werkzeug/2.2.2 Python/3.9.16
Date: Thu, 05 Jan 2023 10:16:42 GMT
Content-Type: application/json
Content-Length: 274552
Access-Control-Allow-Origin: http://localhost:19006
Access-Control-Expose-Headers: *
Vary: Origin
Connection: close
GET /matches HTTP/1.1
Host: 127.0.0.1:5000
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:108.0) Gecko/20100101 Firefox/108.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Origin: http://localhost:19006
DNT: 1
Connection: keep-alive
Referer: http://localhost:19006/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
Sec-GPC: 1
Anyone any idea? Thanks!!!
Hardcoding the CORS origins: no change
Changing the query from axios to async fetch calls: no change
Including/modifying the header in the axios config: no change
Other browsers: no change
Deactivating SSL in the backend: caused other problems related to react native

Can't get Twisted to return application/json content-type

I have a server which is supposed to return JSON, I set the request response 'Content-Type' to 'application/json', yet from client side I always get text/html
this is the server printing response headers:
b'Server' = [b'TwistedWeb/22.4.0']
b'Date' = [b'Fri, 08 Jul 2022 18:35:59 GMT']
b'Content-Type' = [b'application/json']
this is the client printing the reply headers:
b'Transfer-Encoding' = b'chunked'
b'Server' = b'TwistedWeb/22.4.0'
b'Date' = b'Fri, 08 Jul 2022 18:35:59 GMT'
b'Content-Type' = b'text/html'
I print headers in the server just before calling request.finish(), is there something happening after that that I should know about ?
this is the server code (simplified):
import json
from twisted.web import server, resource
from twisted.internet import reactor
class Resource(resource.Resource):
def render(self, request):
def return_json(data):
request.write(json.dumps(data, separators=(',', ':')).encode())
request.setHeader(b'content-type', b'application/json')
request.finish()
deferred = someFunctionReturningDeferred()
deferred.addCallback(return_json)
return server.NOT_DONE_YET
site = server.Site(Resource())
reactor.listenTCP(PORT, site)
reactor.run()
What am I doing wrong ?
Many thanks,
Paul
"Hey me, have you tried to put request.setHeader() before request.write()?"
"Why would I do that ? Why would headers data and actual body data depends on each other ? ok, let's try..."
"so ?"
"it works -__-"
so, the working solution :
class Resource(resource.Resource):
isLeaf = True
def render(self, request):
def return_json(data):
# FIRST, set content-type
request.setHeader(b'content-type', b'application/json')
# THEN, write data
request.write(data)
request.finish()
# makes defer and adds callback in single function, for the example
task.deferLater(reactor, 2, return_json, b'{"hello": "dummy"}')
return server.NOT_DONE_YET

#QueryMap pass parameters in the body

I have a simple POJO:
class FilterDTO(
open var page: Int = 0,
open var size: Int = 20
)
And a simple Feign Client:
#FeignClient(name = "feign-client", url = "\${feign.url.client}")
interface FeignClient{
#GetMapping("get/{id}")
fun getById(#PathVariable("id") id: String, #QueryMap(encoded = true) filter: FilterDTO): Any
}
According to the Pull Request #667, I was expecting this to be translated as:
---> GET http://my.service.com/get/123?page=0&size=20 HTTP/1.1
Content-Length: 64
Content-Type: application/json
---> END HTTP (64-byte body)
<--- HTTP/1.1 200 Ok (807ms)
allow: GET
// ...
{"response": "foo"}
<--- END HTTP (108-byte body)
But instead I'm getting:
---> GET http://my.service.com/get/123 HTTP/1.1
Content-Length: 64
Content-Type: application/json
{"page":0,"size":2}
---> END HTTP (64-byte body)
<--- HTTP/1.1 405 Method Not Allowed (807ms)
allow: GET
cache-control: no-cache, no-store, max-age=0, must-revalidate
//..
{"timestamp":1628783721302,"status":405,"error":"Method Not Allowed","path":"/get/123"}
<--- END HTTP (108-byte body)
Notice the #QueryMap parameter is being passed in the body of the request instead being passed as queryString.
The endpoint it is trying to call is defined as:
#GetMapping("/get/{id}")
fun getById(
#PathVariable("id")
id: String,
filter: FilterDTO
)
What am I missing? How can I use #QueryMap to pass it as query parameter?
Found out that using spring-cloud-openfeign I should use #SpringQueryMap instead of #QueryMap as stated in the docs

Fastify & NestJS - How to set response headers in interceptor

I'm trying to set the response headers in my interceptor, and have had no luck with any method I've found yet. I've tried:
const request = context.switchToHttp().getRequest();
const response = context.switchToHttp().getResponse();
<snippet of code from below>
return next.handle();
request.res.headers['my-header'] = 'xyz'
response.header('my-header', 'xyz')
response.headers['my-header'] = 'xyz'
response.header['my-header'] = 'xyz'
with no luck. The first option says that res is undefined, the second "Cannot read property 'Symbol(fastify.reply.headers)' of undefined", and the others just do nothing.
I have the following working for me with the FastifyAdapter in my main.ts:
HeaderInterceptor
#Injectable()
export class HeaderInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
tap(() => {
const res = context.switchToHttp().getResponse<FastifyReply<ServerResponse>>();
res.header('foo', 'bar');
})
);
}
}
Using .getResponse<FastifyReply<ServerResponse>>() gives us the correct typings to work with.
AppModule
#Module({
imports: [],
controllers: [AppController],
providers: [
AppService,
{
provide: APP_INTERCEPTOR,
useClass: HeaderInterceptor,
},
],
})
export class AppModule {}
Bind the interceptor to the entire server
curl Command
▶ curl http://localhost:3000 -v
* Rebuilt URL to: http://localhost:3000/
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 3000 (#0)
> GET / HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< foo: bar
< content-type: text/plain; charset=utf-8
< content-length: 12
< Date: Thu, 14 May 2020 14:09:22 GMT
< Connection: keep-alive
<
* Connection #0 to host localhost left intact
Hello World!%
As you can see, the response comes back with the header foo: bar meaning the interceptor added what was expected.
Looking at your error, it looks like your second attempt may have actually been response.headers('my-header', 'xyz). Whatever the case, the above is working for me on a nest new application, and on the latest version of Nest's packages.

How to HTTP 'Keep-Alive' in Python3.2 with urllib

I try to keep a HTTP Connection with urllib.request in Python 3.2.3 alive with this code:
handler = urllib.request.HTTPHandler()
opener = urllib.request.build_opener(handler)
opener.addheaders = [("connection", "keep-alive"), ("Cookie", cookie_value)]
r = opener.open(url)
But if I listen to the connection with Wireshark I get an Header with "Connection: closed" but set Cookie.
Host: url
Cookie: cookie-value
Connection: close
What do I have to do to set Headerinfo to Connection: keep-alive?
If you need something more automatic than plain http.client, this might help, though it's not threadsafe.
from http.client import HTTPConnection, HTTPSConnection
import select
connections = {}
def request(method, url, body=None, headers={}, **kwargs):
scheme, _, host, path = url.split('/', 3)
h = connections.get((scheme, host))
if h and select.select([h.sock], [], [], 0)[0]:
h.close()
h = None
if not h:
Connection = HTTPConnection if scheme == 'http:' else HTTPSConnection
h = connections[(scheme, host)] = Connection(host, **kwargs)
h.request(method, '/' + path, body, headers)
return h.getresponse()
def urlopen(url, data=None, *args, **kwargs):
resp = request('POST' if data else 'GET', url, data, *args, **kwargs)
assert resp.status < 400, (resp.status, resp.reason, resp.read())
return resp
I keep connection alive by use http-client
import http.client
conn = http.client.HTTPConnection(host, port)
conn.request(method, url, body, headers)
the headers just give dict and body still can use urllib.parse.urlencode.
so, you can make Cookie header by http client.
reference:
official reference