-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
installSubscriptionHandlers
can't coexist with other websockets on the same port
#134
Comments
installSubscriptionHandlers
can't coexist with other websocketsinstallSubscriptionHandlers
can't coexist with other websockets on the same port
same issue |
When can we expect the fix for this? We are incrementally incorporating subscriptions into our setup which already has websockets using SocketIO and ran into this problem. |
Edit: I did more research on this issue, and it’s more complex than it seems on the surface - this library doesn’t have much control over it as the cause is coming from the |
running into this as well after fiddling with old solutions etc I came up with a working workaround that isn't too bad: import { WebApp } from "meteor/webapp";
import { Meteor } from "meteor/meteor";
import * as http from 'http';
import * as net from 'net';
import * as WebSocket from 'ws'
import mongoose from "mongoose";
import url from "url";
import { ApolloServer } from "apollo-server-express";
import { ExpressContext } from "apollo-server-express/dist/ApolloServer";
import { buildSchema } from "type-graphql";
import { PubSub as ApolloPubSub } from "apollo-server-express";
import { RedisPubSub } from "graphql-redis-subscriptions";
import Redis from "ioredis";
import { ObjectId } from "mongodb";
import { TypegooseMiddleware } from "./helper/typegooseMiddleware";
import { ObjectIdScalar } from "./helper/scalarObjectID";
import { getUser } from "./helper/getUser";
import { authChecker } from "./helper/authChecker";
import { UserResolver } from "./resolver/user.resolvers";
import { SubscriptionServer } from "subscriptions-transport-ws";
import { execute, subscribe } from "graphql";
export interface SubscriptionParams {
authorization?: string
}
export interface GraphqlContext {
userId?: string | null;
user?: Meteor.User | null;
}
export const createMongoConnection = async (): Promise<typeof mongoose> => {
const mongoUrl = process.env.MONGO_URL ?? "mongodb://localhost:3001/meteor";
const mongo = await mongoose.connect(mongoUrl, {
useUnifiedTopology: true,
useNewUrlParser: true,
useCreateIndex: true,
useFindAndModify: false,
});
return mongo;
};
const createRedisPubSub = (redisUrl: string) => {
return new RedisPubSub({
publisher: new Redis(redisUrl),
subscriber: new Redis(redisUrl),
});
};
const createApolloPubSub = () => new ApolloPubSub();
export const createApolloServer = async () => {
await createMongoConnection();
const useRedis: string | undefined = process.env?.REDIS_URL ?? undefined;
const pubSub = useRedis ? createRedisPubSub(process.env.REDIS_URL!) : createApolloPubSub();
const schema = await buildSchema({
resolvers: [UserResolver],
pubSub,
authChecker,
scalarsMap: [{ type: ObjectId, scalar: ObjectIdScalar }],
globalMiddlewares: [TypegooseMiddleware],
validate: true,
});
const server = new ApolloServer({
schema: schema,
playground: true,
tracing: true,
context: async (context: ExpressContext): Promise<GraphqlContext> => {
if (!context?.req?.headers) return {};
if (!context?.req?.headers.authorization) return {};
const { authorization } = context.req.headers;
const user = await getUser(authorization);
return user ? {
user,
userId: user?._id,
} : {};
},
});
const subscriptionServer = new SubscriptionServer({
schema,
execute,
subscribe,
onConnect: async (params: SubscriptionParams) => {
if (params.authorization) {
const user = await getUser(params.authorization);
return user ? {
user,
userId: user?._id,
} : {};
}
}
}, {
noServer: true,
});
server.applyMiddleware({
//@ts-ignore this is compatible and stated in the docs of apollo-server-express to be compatible
app: WebApp.connectHandlers,
path: "/graphql",
});
WebApp.connectHandlers.use("/graphql", (req: http.IncomingMessage, res: http.ServerResponse) => {
if (req.method === "GET") res.end();
});
//@ts-ignore we need to access the private field which typescript does not like
const wsServer: WebSocket.Server = subscriptionServer.wsServer;
const upgradeHandler = (req: http.IncomingMessage, socket: net.Socket, head: Buffer) => {
if (!req.url) return;
const pathname = url.parse(req.url).pathname;
if (!pathname) return
if (pathname.startsWith("/graphql")) {
wsServer.handleUpgrade(req, socket, head, (ws) => {
wsServer.emit("connection", ws, req);
});
}
};
WebApp.httpServer.on("upgrade", upgradeHandler);
}; hope this helps someone. repository using it: ruohki/meteor-graphql-mongoose-starter |
Subscriptions over websockets are no longer an Apollo Server concern as of v4, so any outstanding issues with using them would need to be taken up with the respective libraries (apparently Apollo Server remains compatible with subscriptions, but the functionality is now adjacent rather than internal. |
I'm trying to add a websocket for GraphQL subscriptions alongside a Meteor DDP connection.
Unfortunately,
WebSocket.Server
aborts the handshake for anyupgrade
request it receives for a different path than the one configured in myApolloServer
, so it breaks Meteor's DDP websocket.It's possible to support two websockets on the same port/different paths by writing an
upgrade
listener that decides whether to forward the event to theWebSocket.Server
for Apollo or ignore it and let Meteor DDP handle it. However, to do that I have to copy code out ofApolloServer.installSubscriptionHandlers
(which could change in the future) so that I can pass my ownWebSocket.Server
instance toSubscriptionServer.create
.This is an unhappy workaround. Could we at least make it possible to pass our own
WebSocket.Server
toinstallSubscriptionHandlers
? Or is there another way to makeApolloServer
more flexible? I didn't have this problem withapollo-server-express
v1 because it was a much less monolithic API.I also asked for an option to ignore
upgrade
requests for other paths inWebSocket.Server
: websockets/ws#1193 (comment). If that becomes a reality we would simply need a way to pass that option through.The text was updated successfully, but these errors were encountered: