Commits

Ginés Martínez Sánchez committed d76fd2a Draft

remove unused code of sockjs

Comments (0)

Files changed (11)

ginsfsm/protocols/sockjs/server/transports/__init__.py

-# -*- coding: utf-8 -*-
-
-import ginsfsm.protocols.sockjs.server.transports.pollingbase
-
-from .xhr import XhrPollingTransport, XhrSendHandler
-from .jsonp import JSONPTransport, JSONPSendHandler
-from .websocket import WebSocketTransport
-from .eventsource import EventSourceTransport
-from .htmlfile import HtmlFileTransport
-from .rawwebsocket import RawWebSocketTransport

ginsfsm/protocols/sockjs/server/transports/base.py

-from ginsfsm.protocols.sockjs.server import session
-
-
-class BaseTransportMixin(object):
-    """Base transport.
-
-    Implements few methods that session expects to see in each transport.
-    """
-
-    name = 'override_me_please'
-
-    def get_conn_info(self):
-        """Return `ConnectionInfo` object from current transport"""
-        return session.ConnectionInfo(self.request.remote_addr,  # remote_ip
-                                      self.request.cookies,
-                                      self.request.params,  # arguments
-                                      self.request.headers,
-                                      self.request.path)
-
-    def session_closed(self):
-        """Called by the session, when it gets closed"""
-        pass

ginsfsm/protocols/sockjs/server/transports/eventsource.py

-# -*- coding: utf-8 -*-
-"""
-    ginsfsm.protocols.sockjs.server.transports.eventsource
-    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    EventSource transport implementation.
-"""
-
-from tornado.web import asynchronous
-
-from ginsfsm.protocols.sockjs.server.transports import streamingbase
-
-
-class EventSourceTransport(streamingbase.StreamingTransportBase):
-    name = 'eventsource'
-
-    @asynchronous
-    def get(self, session_id):
-        # Start response
-        self.preflight()
-        self.handle_session_cookie()
-        self.disable_cache()
-
-        self.set_header('Content-Type', 'text/event-stream; charset=UTF-8')
-        self.write('\r\n')
-        self.flush()
-
-        if not self._attach_session(session_id):
-            self.finish()
-            return
-
-        if self.session:
-            self.session.flush()
-
-    def send_pack(self, message, binary=False):
-        if binary:
-            raise Exception('binary not supported for EventSourceTransport')
-
-        msg = 'data: %s\r\n\r\n' % message
-
-        self.active = False
-
-        try:
-            self.notify_sent(len(msg))
-
-            self.write(msg)
-            self.flush(callback=self.send_complete)
-        except IOError:
-            # If connection dropped, make sure we close offending session instead
-            # of propagating error all way up.
-            self.session.delayed_close()
-            self._detach()

ginsfsm/protocols/sockjs/server/transports/htmlfile.py

-# -*- coding: utf-8 -*-
-"""
-    ginsfsm.protocols.sockjs.server.transports.htmlfile
-    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    HtmlFile transport implementation.
-"""
-
-from tornado.web import asynchronous
-
-from ginsfsm.protocols.sockjs.server import proto
-from ginsfsm.protocols.sockjs.server.transports import streamingbase
-
-# HTMLFILE template
-HTMLFILE_HEAD = r'''
-<!doctype html>
-<html><head>
-  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
-  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
-</head><body><h2>Don't panic!</h2>
-  <script>
-    document.domain = document.domain;
-    var c = parent.%s;
-    c.start();
-    function p(d) {c.message(d);};
-    window.onload = function() {c.stop();};
-  </script>
-'''.strip()
-HTMLFILE_HEAD += ' ' * (1024 - len(HTMLFILE_HEAD) + 14)
-HTMLFILE_HEAD += '\r\n\r\n'
-
-
-class HtmlFileTransport(streamingbase.StreamingTransportBase):
-    name = 'htmlfile'
-
-    def initialize(self, server):
-        super(HtmlFileTransport, self).initialize(server)
-
-    @asynchronous
-    def get(self, session_id):
-        # Start response
-        self.preflight()
-        self.handle_session_cookie()
-        self.disable_cache()
-        self.set_header('Content-Type', 'text/html; charset=UTF-8')
-
-        # Grab callback parameter
-        callback = self.get_argument('c', None)
-        if not callback:
-            self.write('"callback" parameter required')
-            self.set_status(500)
-            self.finish()
-            return
-
-        # TODO: Fix me - use parameter
-        self.write(HTMLFILE_HEAD % callback)
-        self.flush()
-
-        # Now try to attach to session
-        if not self._attach_session(session_id):
-            self.finish()
-            return
-
-        # Flush any pending messages
-        if self.session:
-            self.session.flush()
-
-    def send_pack(self, message, binary=False):
-        if binary:
-            raise Exception('binary not supported for HtmlFileTransport')
-
-        # TODO: Just do escaping
-        msg = '<script>\np(%s);\n</script>\r\n' % proto.json_encode(message)
-
-        self.active = False
-
-        try:
-            self.notify_sent(len(message))
-
-            self.write(msg)
-            self.flush(callback=self.send_complete)
-        except IOError:
-            # If connection dropped, make sure we close offending session instead
-            # of propagating error all way up.
-            self.session.delayed_close()
-            self._detach()

ginsfsm/protocols/sockjs/server/transports/jsonp.py

-# -*- coding: utf-8 -*-
-"""
-    ginsfsm.protocols.sockjs.server.transports.jsonp
-    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    JSONP transport implementation.
-"""
-import urllib
-import logging
-
-from tornado.web import asynchronous
-
-from ginsfsm.protocols.sockjs.server import proto
-from ginsfsm.protocols.sockjs.server.transports import pollingbase
-
-
-class JSONPTransport(pollingbase.PollingTransportBase):
-    name = 'jsonp'
-
-    @asynchronous
-    def get(self, session_id):
-        # Start response
-        self.handle_session_cookie()
-        self.disable_cache()
-
-        # Grab callback parameter
-        self.callback = self.get_argument('c', None)
-        if not self.callback:
-            self.write('"callback" parameter required')
-            self.set_status(500)
-            self.finish()
-            return
-
-        # Get or create session without starting heartbeat
-        if not self._attach_session(session_id, False):
-            return
-
-        # Might get already detached because connection was closed in on_open
-        if not self.session:
-            return
-
-        if not self.session.send_queue:
-            self.session.start_heartbeat()
-        else:
-            self.session.flush()
-
-    def send_pack(self, message, binary=False):
-        if binary:
-            raise Exception('binary not supported for JSONPTransport')
-
-        self.active = False
-
-        try:
-            # TODO: Just escape
-            msg = '%s(%s);\r\n' % (self.callback, proto.json_encode(message))
-
-            self.set_header('Content-Type', 'application/javascript; charset=UTF-8')
-            self.set_header('Content-Length', len(msg))
-
-            # TODO: Fix me
-            self.set_header('Etag', 'dummy')
-
-            self.write(msg)
-            self.flush(callback=self.send_complete)
-        except IOError:
-            # If connection dropped, make sure we close offending session instead
-            # of propagating error all way up.
-            self.session.delayed_close()
-
-
-class JSONPSendHandler(pollingbase.PollingTransportBase):
-    def post(self, session_id):
-        self.preflight()
-        self.handle_session_cookie()
-        self.disable_cache()
-
-        session = self._get_session(session_id)
-
-        if session is None:
-            self.set_status(404)
-            return
-
-        #data = self.request.body.decode('utf-8')
-        data = self.request.body
-
-        ctype = self.request.headers.get('Content-Type', '').lower()
-        if ctype == 'application/x-www-form-urlencoded':
-            if not data.startswith('d='):
-                logging.exception('jsonp_send: Invalid payload.')
-
-                self.write("Payload expected.")
-                self.set_status(500)
-                return
-
-            data = urllib.unquote_plus(data[2:])
-
-        if not data:
-            logging.debug('jsonp_send: Payload expected.')
-
-            self.write("Payload expected.")
-            self.set_status(500)
-            return
-
-        try:
-            messages = proto.json_decode(data)
-        except:
-            # TODO: Proper error handling
-            logging.debug('jsonp_send: Invalid json encoding')
-
-            self.write("Broken JSON encoding.")
-            self.set_status(500)
-            return
-
-        try:
-            session.on_messages(messages)
-        except Exception:
-            logging.exception('jsonp_send: on_message() failed')
-
-            session.close()
-
-            self.write('Message handler failed.')
-            self.set_status(500)
-            return
-
-        self.write('ok')
-        self.set_header('Content-Type', 'text/plain; charset=UTF-8')
-        self.set_status(200)

ginsfsm/protocols/sockjs/server/transports/pollingbase.py

-# -*- coding: utf-8 -*-
-"""
-    Polling transports base
-"""
-
-from ginsfsm.protocols.sockjs.server import basehandler
-from ginsfsm.protocols.sockjs.server.transports import base
-
-
-class PollingTransportBase(basehandler.PreflightHandler,
-                           base.BaseTransportMixin):
-    """Polling transport handler base class"""
-    def __init__(self, context, request, async_response=False):
-        super(PollingTransportBase, self).__init__(
-            context,
-            request,
-            async_response
-        )
-        self.session = None
-        self.active = True
-
-    def _get_session(self, session_id):
-        return self.context.sockjs_server.get_session(session_id)
-
-    def _attach_session(self, session_id, start_heartbeat=False):
-        session = self._get_session(session_id)
-
-        if session is None:
-            session = self.context.sockjs_server.create_session(session_id)
-
-        # Try to attach to the session
-        if not session.set_handler(self, start_heartbeat):
-            return False
-
-        self.session = session
-
-        # Verify if session is properly opened
-        session.verify_state()
-
-        return True
-
-    def _detach(self):
-        """Detach from the session"""
-        if self.session:
-            self.session.remove_handler(self)
-            self.session = None
-
-    def check_xsrf_cookie(self):
-        pass
-
-    def session_closed(self):
-        """Called by the session when it was closed"""
-        self._detach()
-        self.safe_finish()
-
-    def on_connection_close(self):
-        # If connection was dropped by the client, close session.
-        # In all other cases, connection will be closed by the server.
-        if self.session is not None:
-            self.session.close(1002, 'Connection interrupted')
-
-        super(PollingTransportBase, self).on_connection_close()
-
-    def send_complete(self):
-        self._detach()
-        self.safe_finish()

ginsfsm/protocols/sockjs/server/transports/rawwebsocket.py

-# -*- coding: utf-8 -*-
-"""
-    Raw websocket transport implementation
-"""
-import logging
-import socket
-
-from ginsfsm.protocols.sockjs.server import websocket, session
-from ginsfsm.protocols.sockjs.server.transports import base
-
-
-class RawSession(session.BaseSession):
-    """Raw session without any sockjs protocol encoding/decoding. Simply
-    works as a proxy between `SockJSConnection` class and `RawWebSocketTransport`."""
-    def send_message(self, msg, stats=True, binary=False):
-        self.handler.send_pack(msg, binary)
-
-    def on_message(self, msg):
-        self.conn.on_message(msg)
-
-
-class RawWebSocketTransport(websocket.WebSocketHandler, base.BaseTransportMixin):
-    """Raw Websocket transport"""
-    name = 'rawwebsocket'
-
-    def initialize(self, server):
-        self.server = server
-        self.session = None
-        self.active = True
-
-    def open(self):
-        # Stats
-        self.server.stats.on_conn_opened()
-
-        # Disable nagle if needed
-        if self.server.settings['disable_nagle']:
-            self.stream.socket.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
-
-        # Create and attach to session
-        self.session = RawSession(self.server.get_connection_class(), self.server)
-        self.session.set_handler(self)
-        self.session.verify_state()
-
-    def _detach(self):
-        if self.session is not None:
-            self.session.remove_handler(self)
-            self.session = None
-
-    def on_message(self, message):
-        # SockJS requires that empty messages should be ignored
-        if not message or not self.session:
-            return
-
-        try:
-            self.session.on_message(message)
-        except Exception:
-            logging.exception('RawWebSocket')
-
-            # Close running connection
-            self._detach()
-            self.abort_connection()
-
-    def on_close(self):
-        # Close session if websocket connection was closed
-        if self.session is not None:
-            # Stats
-            self.server.stats.on_conn_closed()
-
-            session = self.session
-            self._detach()
-            session.close()
-
-    def send_pack(self, message, binary=False):
-        # Send message
-        try:
-            self.write_message(message, binary)
-        except IOError:
-            self.server.io_loop.add_callback(self.on_close)
-
-    def session_closed(self):
-        try:
-            self.close()
-        except IOError:
-            pass
-        finally:
-            self._detach()
-
-    # Websocket overrides
-    def allow_draft76(self):
-        return True

ginsfsm/protocols/sockjs/server/transports/streamingbase.py

-from ginsfsm.protocols.sockjs.server.transports import pollingbase
-
-
-class StreamingTransportBase(pollingbase.PollingTransportBase):
-    def __init__(self, context, request):
-        super(StreamingTransportBase, self).__init__(context, request, True)
-
-        self.amount_limit = self.context.sockjs_server.response_limit
-
-        # HTTP 1.0 client might send keep-alive
-        if 'Connection' in self.request.headers:
-            # TODO: busca el equivalente!!
-            if not self.request.supports_http_1_1():
-                self.request.connection.no_keep_alive = True
-
-    def notify_sent(self, data_len):
-        """
-            Update amount of data sent
-        """
-        self.amount_limit -= data_len
-
-    def should_finish(self):
-        """
-            Check if transport should close long running connection after
-            sending X bytes to the client.
-
-            `data_len`
-                Amount of data that was sent
-        """
-        if self.amount_limit <= 0:
-            return True
-
-        return False
-
-    def send_complete(self):
-        """
-            Verify if connection should be closed based on amount of data
-            that was sent.
-        """
-        self.active = True
-
-        if self.should_finish():
-            self._detach()
-            self.safe_finish()
-        else:
-            if self.session:
-                self.session.flush()

ginsfsm/protocols/sockjs/server/transports/websocket.py

-# -*- coding: utf-8 -*-
-"""
-    Websocket transport implementation
-"""
-import logging
-import socket
-
-from ginsfsm.protocols.sockjs.server import proto, websocket
-from ginsfsm.protocols.sockjs.server.transports import base
-
-
-class WebSocketTransport(websocket.WebSocketHandler, base.BaseTransportMixin):
-    """Websocket transport"""
-    name = 'websocket'
-
-    def initialize(self, server):
-        self.server = server
-        self.session = None
-        self.active = True
-
-    def open(self, session_id):
-        # Stats
-        self.server.stats.on_conn_opened()
-
-        # Disable nagle
-        if self.server.settings['disable_nagle']:
-            self.stream.socket.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
-
-        # Handle session
-        self.session = self.server.create_session(session_id, register=False)
-
-        if not self.session.set_handler(self):
-            self.close()
-            return
-
-        self.session.verify_state()
-
-        if self.session:
-            self.session.flush()
-
-    def _detach(self):
-        if self.session is not None:
-            self.session.remove_handler(self)
-            self.session = None
-
-    def on_message(self, message):
-        # SockJS requires that empty messages should be ignored
-        if not message or not self.session:
-            return
-
-        try:
-            msg = proto.json_decode(message)
-
-            if isinstance(msg, list):
-                self.session.on_messages(msg)
-            else:
-                self.session.on_messages((msg,))
-        except Exception:
-            logging.exception('WebSocket')
-
-            # Close session on exception
-            #self.session.close()
-
-            # Close running connection
-            self.abort_connection()
-
-    def on_close(self):
-        # Close session if websocket connection was closed
-        if self.session is not None:
-            # Stats
-            self.server.stats.on_conn_closed()
-
-            # Detach before closing session
-            session = self.session
-            self._detach()
-            session.close()
-
-    def send_pack(self, message, binary=False):
-        # Send message
-        try:
-            self.write_message(message, binary)
-        except IOError:
-            self.server.io_loop.add_callback(self.on_close)
-
-    def session_closed(self):
-        # If session was closed by the application, terminate websocket
-        # connection as well.
-        try:
-            self.close()
-        except IOError:
-            pass
-        finally:
-            self._detach()
-
-    # Websocket overrides
-    def allow_draft76(self):
-        return True
-
-    def auto_decode(self):
-        return False

ginsfsm/protocols/sockjs/server/transports/xhr.py

-# -*- coding: utf-8 -*-
-"""
-    Xhr-Polling transport implementation
-"""
-import logging
-
-from pyramid.view import view_config
-from pyramid.security import authenticated_userid
-from pyramid.httpexceptions import HTTPNotFound, HTTPServerError
-
-from ginsfsm.gobj import GObj
-from ginsfsm.protocols.sockjs.server import proto
-from ginsfsm.protocols.sockjs.server.transports import pollingbase
-
-#----------------------------------------------------------------#
-#                   GXhrPolling GClass
-#----------------------------------------------------------------#
-GXHRPOLLING_GCONFIG = {
-    'sockjs_server': [None, None, 0, None, ""],
-}
-
-
-class GXhrPolling(GObj):
-    """  GXhrPolling GObj.
-    """
-    def __init__(self):
-        GObj.__init__(self, {}, GXHRPOLLING_GCONFIG)
-
-    def start_up(self):
-        """ Initialization zone.
-        """
-
-
-#----------------------------------------------------------------#
-#                   GXhrPolling Views
-#----------------------------------------------------------------#
-@view_config(
-    context=GXhrPolling,
-    name='',
-    attr='options',
-    request_method='OPTIONS',
-)
-@view_config(
-    context=GXhrPolling,
-    name='',
-    attr='post',
-    request_method='POST',
-)
-class XhrPollingTransport(pollingbase.PollingTransportBase):
-    """xhr-polling transport implementation"""
-    name = 'xhr'
-
-    def post(self):
-        response = self.response
-        self.preflight()
-        self.handle_session_cookie()
-        self.disable_cache()
-
-        self.sid = self.context.parent.re_matched_name  # session_id
-        if self.context.sockjs_server.per_user:
-            self.sid = (authenticated_userid(self.request), self.sid)
-
-        # Get or create session without starting heartbeat
-        if not self._attach_session(self.sid, False):
-            return response  # HTTPServerError("Error in attach_session.")
-
-        # Might get already detached because connection was closed in on_open
-        if not self.session:
-            return response  # HTTPServerError("Error, no session.")
-
-        if not self.session.send_queue:
-            print ">>>>>>>SEND_QUEUE"
-            self.session.start_heartbeat()
-        else:
-            print ">>>>>>>FLUSHHH"
-            self.session.flush()
-
-        return response
-
-    def send_pack(self, message, binary=False):
-        if binary:
-            raise Exception('binary not supported for XhrPollingTransport')
-
-        if not self.active:
-            return
-
-        self.active = False
-
-        try:
-            self.response.content_type = 'application/javascript'
-            self.response.charset = 'UTF-8'
-
-            print "zzzzz", message
-            self.set_body(message + '\n')
-            self.context.gaplic.add_callback(self.send_complete)
-            #self.flush(callback=self.send_complete)
-        except:
-            # If connection dropped, make sure we close offending session
-            # instead of propagating error all way up.
-
-            # TODO: ERROR self.session.delayed_close()
-            raise
-
-#----------------------------------------------------------------#
-#                   GXhrSend GClass
-#----------------------------------------------------------------#
-GXHRSEND_GCONFIG = {
-    'sockjs_server': [None, None, 0, None, ""],
-}
-
-
-class GXhrSend(GObj):
-    """  GXhr GObj.
-    """
-    def __init__(self):
-        GObj.__init__(self, {}, GXHRSEND_GCONFIG)
-
-    def start_up(self):
-        """ Initialization zone.
-        """
-
-
-#----------------------------------------------------------------#
-#                   GXhrSend Views
-#----------------------------------------------------------------#
-@view_config(
-    context=GXhrSend,
-    name='',
-    attr='options',
-    request_method='OPTIONS',
-)
-@view_config(
-    context=GXhrSend,
-    name='',
-    attr='post',
-    request_method='POST',
-)
-class XhrSendHandler(pollingbase.PollingTransportBase):
-    def post(self):
-        response = self.response
-
-        self.sid = self.context.parent.re_matched_name  # session_id
-        if self.context.sockjs_server.per_user:
-            self.sid = (authenticated_userid(self.request), self.sid)
-
-        # Start response
-        self.preflight()
-        self.handle_session_cookie()
-        self.disable_cache()
-
-        session = self._get_session(self.sid)
-
-        if session is None:
-            return HTTPNotFound('Session not found')
-
-        data = self.request.body_file.read()
-        if not data:
-            return HTTPServerError("Payload expected.")
-
-        try:
-            messages = proto.json_decode(data)
-        except:
-            return HTTPServerError("Broken JSON encoding.")
-
-        try:
-            session.on_messages(messages)
-        except Exception:
-            logging.exception('XHR incoming')
-            session.close()
-            return HTTPServerError('XHR incoming')
-
-        self.set_status(204)
-        self.response.content_type = 'text/plain'
-        self.response.charset = 'UTF-8'
-        return response

ginsfsm/protocols/sockjs/server/transports/xhrstreaming.py

-# -*- coding: utf-8 -*-
-"""
-    Xhr-Streaming transport implementation
-"""
-from pyramid.view import view_config
-from pyramid.security import authenticated_userid
-
-from ginsfsm.gobj import GObj
-from ginsfsm.protocols.sockjs.server.transports import streamingbase
-
-#----------------------------------------------------------------#
-#                   GXhrStreaming GClass
-#----------------------------------------------------------------#
-GXHRSTREAMING_GCONFIG = {
-    'sockjs_server': [None, None, 0, None, ""],
-}
-
-
-class GXhrStreaming(GObj):
-    """  GStreaming GObj.
-    """
-    def __init__(self):
-        GObj.__init__(self, {}, GXHRSTREAMING_GCONFIG)
-
-    def start_up(self):
-        """ Initialization zone.
-        """
-
-
-#----------------------------------------------------------------#
-#                   GXhrStreaming Views
-#----------------------------------------------------------------#
-@view_config(
-    context=GXhrStreaming,
-    name='',
-    attr='options',
-    request_method='OPTIONS',
-)
-@view_config(
-    context=GXhrStreaming,
-    name='',
-    attr='post',
-    request_method='POST',
-)
-class XhrStreamingTransport(streamingbase.StreamingTransportBase):
-    name = 'xhr_streaming'
-
-    def post(self):
-        response = self.response
-
-        self.sid = self.context.parent.re_matched_name  # session_id
-        if self.context.sockjs_server.per_user:
-            self.sid = (authenticated_userid(self.request), self.sid)
-
-        #try: esto es de pyramid_sockjs
-        #    session = manager.get(sid, create, request=request)
-        #except KeyError:
-        #    return HTTPNotFound(headers=(session_cookie(request),))
-        #request.environ['wsgi.sockjs_session'] = session
-
-        # Start response
-        self.preflight()
-        self.handle_session_cookie()
-        self.disable_cache()
-        response.content_type = 'application/javascript'
-        response.charset = 'UTF-8'
-        response.write('h' * 2048 + '\n')
-
-        if not self._attach_session(self.sid, False):
-            self.finish()
-            return ()
-
-        """
-
-        if self.session:
-            self.ginsfsm_channel.flush()
-            #self.session.flush()
-        """
-
-        return response
-
-    def send_pack(self, message, binary=False):
-        if binary:
-            raise Exception('binary not supported for XhrStreamingTransport')
-
-        print ">>>>>>>>>>>>>>>>>>>>>>> SEND_PACK:", message
-        # self.active = False  # TODO si chequeo active fallan los tests
-        # se vuelve a poner active a true al ejecutar send_complete
-        # parece como un flow control
-
-        try:
-            self.notify_sent(len(message))
-            self.write(message + '\n')
-            self.flush(callback=self.send_complete)
-        except:
-            raise
-            # If connection dropped, make sure we close offending session instead
-            # of propagating error all way up.
-            print "??????????????????????????'"
-            self.session.delayed_close()
-            self._detach()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.