From 7414f1034d32c249294a081f1e176a9266fc92ac Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Thu, 8 Feb 2018 22:06:08 +0300 Subject: reorganize project structure --- .../java/com/juick/server/WebsocketManager.java | 163 +++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 juick-server/src/main/java/com/juick/server/WebsocketManager.java (limited to 'juick-server/src/main/java/com/juick/server/WebsocketManager.java') diff --git a/juick-server/src/main/java/com/juick/server/WebsocketManager.java b/juick-server/src/main/java/com/juick/server/WebsocketManager.java new file mode 100644 index 00000000..6e3fbea2 --- /dev/null +++ b/juick-server/src/main/java/com/juick/server/WebsocketManager.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2008-2017, Juick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.juick.server; + +import com.juick.User; +import com.juick.server.helpers.AnonymousUser; +import com.juick.server.util.HttpBadRequestException; +import com.juick.server.util.HttpForbiddenException; +import com.juick.server.util.HttpNotFoundException; +import com.juick.service.MessagesService; +import com.juick.service.SubscriptionService; +import com.juick.service.UserService; +import org.apache.commons.lang3.math.NumberUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.web.socket.CloseStatus; +import org.springframework.web.socket.PingMessage; +import org.springframework.web.socket.WebSocketSession; +import org.springframework.web.socket.handler.TextWebSocketHandler; +import org.springframework.web.util.UriComponents; +import org.springframework.web.util.UriComponentsBuilder; + +import javax.inject.Inject; +import java.io.IOException; +import java.net.URI; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * Created by vitalyster on 28.06.2016. + */ +public class WebsocketManager extends TextWebSocketHandler { + private static final Logger logger = LoggerFactory.getLogger(WebsocketManager.class); + + private final List clients = Collections.synchronizedList(new LinkedList<>()); + + @Inject + private UserService userService; + @Inject + private MessagesService messagesService; + @Inject + private SubscriptionService subscriptionService; + + + @Override + public void afterConnectionEstablished(WebSocketSession session) throws Exception { + URI hLocation; + String hXRealIP; + + hLocation = session.getUri(); + HttpHeaders headers = session.getHandshakeHeaders(); + hXRealIP = headers.getOrDefault("X-Real-IP", + Collections.singletonList(session.getRemoteAddress().toString())).get(0); + + // Auth + User visitor = AnonymousUser.INSTANCE; + UriComponents uriComponents = UriComponentsBuilder.fromUri(hLocation).build(); + List hash = uriComponents.getQueryParams().get("hash"); + if (hash != null && hash.get(0).length() == 16) { + visitor = userService.getUserByHash(hash.get(0)); + } else { + logger.debug("wrong hash for {} from {}", visitor.getUid(), hXRealIP); + } + + int MID = 0; + SocketSubscribed sockSubscr = null; + if (hLocation.getPath().equals("/ws/")) { + logger.debug("user {} connected", visitor.getUid()); + sockSubscr = new SocketSubscribed(session, hXRealIP, visitor, false); + } else if (hLocation.getPath().equals("/ws/_all")) { + logger.debug("user {} connected to legacy _all ({})", visitor.getUid(), hLocation.getPath()); + sockSubscr = new SocketSubscribed(session, hXRealIP, visitor, true); + sockSubscr.allMessages = true; + } else if (hLocation.getPath().equals("/ws/_replies")) { + logger.debug("user {} connected to legacy _replies ({})", visitor.getUid(), hLocation.getPath()); + sockSubscr = new SocketSubscribed(session, hXRealIP, visitor, true); + sockSubscr.allReplies = true; + } else if (hLocation.getPath().matches("^/ws/(\\d)+$")) { + MID = NumberUtils.toInt(hLocation.getPath().substring(4), 0); + if (MID > 0) { + if (messagesService.canViewThread(MID, visitor.getUid())) { + logger.debug("user {} connected to legacy thread ({}) from {}", visitor.getUid(), MID, hXRealIP); + sockSubscr = new SocketSubscribed(session, hXRealIP, visitor, true); + sockSubscr.MID = MID; + } else { + throw new HttpForbiddenException(); + } + } + } else { + throw new HttpNotFoundException(); + } + if (sockSubscr != null) { + synchronized (clients) { + clients.add(sockSubscr); + logger.debug("{} clients connected", clients.size()); + } + } else { + throw new HttpBadRequestException(); + } + } + + @Override + public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { + synchronized (clients) { + logger.debug("session closed with status {}: {}", status.getCode(), status.getReason()); + clients.removeIf(c -> c.session.getId().equals(session.getId())); + logger.debug("{} clients connected", clients.size()); + } + + } + + @Scheduled(fixedRate = 30000) + public void ping() { + clients.forEach(c -> { + try { + c.session.sendMessage(new PingMessage()); + } catch (IOException e) { + logger.error("WebSocket PING exception", e); + } + }); + } + public List getClients() { + return clients; + } + + class SocketSubscribed { + WebSocketSession session; + String clientName; + User visitor; + int MID; + boolean allMessages; + boolean allReplies; + long tsConnected; + long tsLastData; + boolean legacy; + + public SocketSubscribed(WebSocketSession session, String clientName, User visitor, boolean legacy) { + this.session = session; + this.clientName = clientName; + this.visitor = visitor; + tsConnected = tsLastData = System.currentTimeMillis(); + this.legacy = legacy; + } + } +} -- cgit v1.2.3