All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alipay.api.java_websocket.drafts.Draft Maven / Gradle / Ivy

/*
 * Copyright (c) 2010-2018 Nathan Rajlich
 *
 *  Permission is hereby granted, free of charge, to any person
 *  obtaining a copy of this software and associated documentation
 *  files (the "Software"), to deal in the Software without
 *  restriction, including without limitation the rights to use,
 *  copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the
 *  Software is furnished to do so, subject to the following
 *  conditions:
 *
 *  The above copyright notice and this permission notice shall be
 *  included in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 *  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 *  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *  OTHER DEALINGS IN THE SOFTWARE.
 */

package com.alipay.api.java_websocket.drafts;

import com.alipay.api.java_websocket.WebSocketImpl;
import com.alipay.api.java_websocket.enums.CloseHandshakeType;
import com.alipay.api.java_websocket.enums.HandshakeState;
import com.alipay.api.java_websocket.enums.Opcode;
import com.alipay.api.java_websocket.enums.Role;
import com.alipay.api.java_websocket.exceptions.IncompleteHandshakeException;
import com.alipay.api.java_websocket.exceptions.InvalidDataException;
import com.alipay.api.java_websocket.exceptions.InvalidHandshakeException;
import com.alipay.api.java_websocket.exceptions.LimitExedeedException;
import com.alipay.api.java_websocket.framing.*;
import com.alipay.api.java_websocket.handshake.*;
import com.alipay.api.java_websocket.util.Charsetfunctions;

import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

/**
 * Base class for everything of a websocket specification which is not common such as the way the handshake is read or frames are
 * transfered.
 **/
public abstract class Draft {

    /**
     * In some cases the handshake will be parsed different depending on whether
     */
    protected Role role = null;

    protected Opcode continuousFrameType = null;

    private String charset = "UTF8";

    public static ByteBuffer readLine(ByteBuffer buf) {
        ByteBuffer sbuf = ByteBuffer.allocate(buf.remaining());
        byte prev;
        byte cur = '0';
        while (buf.hasRemaining()) {
            prev = cur;
            cur = buf.get();
            sbuf.put(cur);
            if (prev == (byte) '\r' && cur == (byte) '\n') {
                sbuf.limit(sbuf.position() - 2);
                sbuf.position(0);
                return sbuf;

            }
        }
        // ensure that there wont be any bytes skipped
        buf.position(buf.position() - sbuf.position());
        return null;
    }

    public static String readStringLine(ByteBuffer buf) {
        ByteBuffer b = readLine(buf);
        return b == null ? null : Charsetfunctions.stringAscii(b.array(), 0, b.limit());
    }

    public static HandshakeBuilder translateHandshakeHttp(ByteBuffer buf, Role role)
            throws InvalidHandshakeException, IncompleteHandshakeException {
        HandshakeBuilder handshake;

        String line = readStringLine(buf);
        if (line == null) { throw new IncompleteHandshakeException(buf.capacity() + 128); }

        String[] firstLineTokens = line.split(" ", 3);// eg. HTTP/1.1 101 Switching the Protocols
        if (firstLineTokens.length != 3) {
            throw new InvalidHandshakeException();
        }

        if (role == Role.CLIENT) {
            // translating/parsing the response from the SERVER
            if (!"101".equals(firstLineTokens[1])) {
                throw new InvalidHandshakeException("Invalid status code received: " + firstLineTokens[1] + " Status line: " + line);
            }
            if (!"HTTP/1.1".equalsIgnoreCase(firstLineTokens[0])) {
                throw new InvalidHandshakeException("Invalid status line received: " + firstLineTokens[0] + " Status line: " + line);
            }

            handshake = new HandshakeImpl1Server();
            ServerHandshakeBuilder serverhandshake = (ServerHandshakeBuilder) handshake;
            serverhandshake.setHttpStatus(Short.parseShort(firstLineTokens[1]));
            serverhandshake.setHttpStatusMessage(firstLineTokens[2]);
        } else {
            // translating/parsing the request from the CLIENT
            if (!"GET".equalsIgnoreCase(firstLineTokens[0])) {
                throw new InvalidHandshakeException("Invalid request method received: " + firstLineTokens[0] + " Status line: " + line);
            }
            if (!"HTTP/1.1".equalsIgnoreCase(firstLineTokens[2])) {
                throw new InvalidHandshakeException("Invalid status line received: " + firstLineTokens[2] + " Status line: " + line);
            }
            ClientHandshakeBuilder clienthandshake = new HandshakeImpl1Client();
            clienthandshake.setResourceDescriptor(firstLineTokens[1]);
            handshake = clienthandshake;
        }

        line = readStringLine(buf);
        while (line != null && line.length() > 0) {
            String[] pair = line.split(":", 2);
            if (pair.length != 2) { throw new InvalidHandshakeException("not an http header"); }
            // If the handshake contains already a specific key, append the new value
            if (handshake.hasFieldValue(pair[0])) {
                handshake.put(pair[0], handshake.getFieldValue(pair[0]) + "; " + pair[1].replaceFirst("^ +", ""));
            } else {
                handshake.put(pair[0], pair[1].replaceFirst("^ +", ""));
            }
            line = readStringLine(buf);
        }
        if (line == null) { throw new IncompleteHandshakeException(); }
        return handshake;
    }

    public abstract HandshakeState acceptHandshakeAsClient(ClientHandshake request, ServerHandshake response) throws
            InvalidHandshakeException;

    public abstract HandshakeState acceptHandshakeAsServer(ClientHandshake handshakedata) throws InvalidHandshakeException;

    protected boolean basicAccept(Handshakedata handshakedata) {
        return handshakedata.getFieldValue("Upgrade").equalsIgnoreCase("websocket") && handshakedata.getFieldValue("Connection")
                .toLowerCase(Locale.ENGLISH).contains("upgrade");
    }

    public abstract ByteBuffer createBinaryFrame(Framedata framedata); // TODO Allow to send data on the base of an Iterator or InputStream

    public abstract List createFrames(ByteBuffer binary, boolean mask);

    public abstract List createFrames(String text, boolean mask);

    /**
     * Handle the frame specific to the draft
     *
     * @param webSocketImpl the websocketimpl used for this draft
     * @param frame         the frame which is supposed to be handled
     * @throws InvalidDataException will be thrown on invalid data
     */
    public abstract void processFrame(WebSocketImpl webSocketImpl, Framedata frame) throws InvalidDataException;

    public List continuousFrame(Opcode op, ByteBuffer buffer, boolean fin) {
        if (op != Opcode.BINARY && op != Opcode.TEXT) {
            throw new IllegalArgumentException("Only Opcode.BINARY or  Opcode.TEXT are allowed");
        }
        DataFrame bui = null;
        if (continuousFrameType != null) {
            bui = new ContinuousFrame();
        } else {
            continuousFrameType = op;
            if (op == Opcode.BINARY) {
                bui = new BinaryFrame();
            } else if (op == Opcode.TEXT) {
                bui = new TextFrame();
            }
        }
        bui.setPayload(buffer);
        bui.setFin(fin);
        try {
            bui.isValid();
        } catch (InvalidDataException e) {
            throw new IllegalArgumentException(e); // can only happen when one builds close frames(Opcode.Close)
        }
        if (fin) {
            continuousFrameType = null;
        } else {
            continuousFrameType = op;
        }
        return Collections.singletonList((Framedata) bui);
    }

    public abstract void reset();

    public List createHandshake(Handshakedata handshakedata, Role ownrole) {
        return createHandshake(handshakedata, ownrole, true);
    }

    public List createHandshake(Handshakedata handshakedata, Role ownrole, boolean withcontent) {
        StringBuilder bui = new StringBuilder(100);
        if (handshakedata instanceof ClientHandshake) {
            bui.append("GET ");
            bui.append(((ClientHandshake) handshakedata).getResourceDescriptor());
            bui.append(" HTTP/1.1");
        } else if (handshakedata instanceof ServerHandshake) {
            bui.append("HTTP/1.1 101 ").append(((ServerHandshake) handshakedata).getHttpStatusMessage());
        } else {
            throw new IllegalArgumentException("unknown role");
        }
        bui.append("\r\n");
        Iterator it = handshakedata.iterateHttpFields();
        while (it.hasNext()) {
            String fieldname = it.next();
            String fieldvalue = handshakedata.getFieldValue(fieldname);
            bui.append(fieldname);
            bui.append(": ");
            bui.append(fieldvalue);
            bui.append("\r\n");
        }
        bui.append("\r\n");
        byte[] httpheader = Charsetfunctions.asciiBytes(bui.toString());

        byte[] content = withcontent ? handshakedata.getContent() : null;
        ByteBuffer bytebuffer = ByteBuffer.allocate((content == null ? 0 : content.length) + httpheader.length);
        bytebuffer.put(httpheader);
        if (content != null) { bytebuffer.put(content); }
        bytebuffer.flip();
        return Collections.singletonList(bytebuffer);
    }

    public abstract ClientHandshakeBuilder postProcessHandshakeRequestAsClient(ClientHandshakeBuilder request) throws
            InvalidHandshakeException;

    public abstract HandshakeBuilder postProcessHandshakeResponseAsServer(ClientHandshake request, ServerHandshakeBuilder response) throws
            InvalidHandshakeException;

    public abstract List translateFrame(ByteBuffer buffer) throws InvalidDataException;

    public abstract CloseHandshakeType getCloseHandshakeType();

    /**
     * Drafts must only be by one websocket at all. To prevent drafts to be used more than once the Websocket implementation should call
     * this method in order to create a new usable version of a given draft instance.
The copy can be safely used in conjunction with a * new websocket connection. * * @return a copy of the draft */ public abstract Draft copyInstance(); public Handshakedata translateHandshake(ByteBuffer buf) throws InvalidHandshakeException { return translateHandshakeHttp(buf, role); } public int checkAlloc(int bytecount) throws LimitExedeedException, InvalidDataException { if (bytecount < 0) { throw new InvalidDataException(CloseFrame.PROTOCOL_ERROR, "Negative count"); } return bytecount; } int readVersion(Handshakedata handshakedata) { String vers = handshakedata.getFieldValue("Sec-WebSocket-Version"); if (vers.length() > 0) { int v; try { v = new Integer(vers.trim()); return v; } catch (NumberFormatException e) { return -1; } } return -1; } public void setParseMode(Role role) { this.role = role; } public Role getRole() { return role; } /** * Getter method for property charset. * * @return property value of charset */ public String getCharset() { return charset; } /** * Setter method for property charset. * * @param charset value to be assigned to property charset */ public void setCharset(String charset) { this.charset = charset; } public String toString() { return getClass().getSimpleName(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy