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

org.cometd.javascript.XMLHttpRequestExchange Maven / Gradle / Ivy

There is a newer version: 4.0.9
Show newest version
/*
 * Copyright (c) 2010 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.cometd.javascript;

import java.io.EOFException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;

import org.eclipse.jetty.client.ContentExchange;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.http.HttpHeaders;
import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class XMLHttpRequestExchange extends ScriptableObject
{
    private CometDExchange exchange;

    public XMLHttpRequestExchange()
    {
    }

    public void jsConstructor(Object cookieStore, Object threadModel, Scriptable thiz, String method, String url, boolean async)
    {
        exchange = new CometDExchange((HttpCookieStore)cookieStore, (ThreadModel)threadModel, thiz, method, url, async);
    }

    public String getClassName()
    {
        return "XMLHttpRequestExchange";
    }

    public HttpExchange getHttpExchange()
    {
        return exchange;
    }

    public boolean isAsynchronous()
    {
        return exchange.isAsynchronous();
    }

    public void await() throws InterruptedException
    {
        exchange.waitForDone();
        exchange.notifyReadyStateChange(false);
    }

    public void jsFunction_addRequestHeader(String name, String value)
    {
        exchange.addRequestHeader(name, value);
    }

    public String jsGet_method()
    {
        return exchange.getMethod();
    }

    public void jsFunction_setRequestContent(String data) throws UnsupportedEncodingException
    {
        exchange.setRequestContent(data);
    }

    public int jsGet_readyState()
    {
        return exchange.getReadyState();
    }

    public String jsGet_responseText()
    {
        return exchange.getResponseText();
    }

    public int jsGet_responseStatus()
    {
        return exchange.getResponseStatus();
    }

    public String jsGet_responseStatusText()
    {
        return exchange.getResponseStatusText();
    }

    public void jsFunction_abort()
    {
        exchange.cancel();
    }

    public String jsFunction_getAllResponseHeaders()
    {
        return exchange.getAllResponseHeaders();
    }

    public String jsFunction_getResponseHeader(String name)
    {
        return exchange.getResponseHeader(name);
    }

    public void send(HttpClient httpClient) throws Exception
    {
        exchange.send(httpClient);
    }

    public static class CometDExchange extends ContentExchange
    {
        public enum ReadyState
        {
            UNSENT, OPENED, HEADERS_RECEIVED, LOADING, DONE
        }

        private final Logger logger = LoggerFactory.getLogger(getClass().getName());
        private final HttpCookieStore cookieStore;
        private final ThreadModel threads;
        private final Scriptable thiz;
        private final boolean async;
        private volatile boolean aborted;
        private volatile ReadyState readyState = ReadyState.UNSENT;
        private volatile String responseText;
        private volatile String responseStatusText;

        public CometDExchange(HttpCookieStore cookieStore, ThreadModel threads, Scriptable thiz, String method, String url, boolean async)
        {
            super(true);
            this.cookieStore = cookieStore;
            this.threads = threads;
            this.thiz = thiz;
            setMethod(method == null ? "GET" : method.toUpperCase());
            setURL(url);
            this.async = async;
            aborted = false;
            readyState = ReadyState.OPENED;
            responseStatusText = null;
            getRequestFields().clear();
            if (async)
                notifyReadyStateChange(false);
        }

        public boolean isAsynchronous()
        {
            return async;
        }

        /**
         * If this method is invoked in the same stack of a JavaScript call,
         * then it must be asynchronous.
         * The reason is that the JavaScript may modify the onreadystatechange
         * function afterwards, and we would be notifying the wrong function.
         *
         * @param sync whether the call should be synchronous
         */
        private void notifyReadyStateChange(boolean sync)
        {
            threads.invoke(sync, thiz, thiz, "onreadystatechange");
        }

        public void send(HttpClient httpClient) throws Exception
        {
            String cookies = cookieStore.jsFunction_get(getScheme().toString("UTF-8"), getAddress().toString(), "");
            if (cookies.length() > 0)
                setRequestHeader(HttpHeaders.COOKIE, cookies);
            log("Submitted {}", this);
            httpClient.send(this);
        }

        @Override
        public void cancel()
        {
            super.cancel();
            log("Aborted {}", this);
            aborted = true;
            responseText = null;
            getRequestFields().clear();
            if (!async || readyState == ReadyState.HEADERS_RECEIVED || readyState == ReadyState.LOADING)
            {
                readyState = ReadyState.DONE;
                notifyReadyStateChange(false);
            }
            readyState = ReadyState.UNSENT;
        }

        public int getReadyState()
        {
            return readyState.ordinal();
        }

        public String getResponseText()
        {
            return responseText;
        }

        public String getResponseStatusText()
        {
            return responseStatusText;
        }

        public void setRequestContent(String content) throws UnsupportedEncodingException
        {
            setRequestContent(new ByteArrayBuffer(content, "UTF-8"));
        }

        public String getAllResponseHeaders()
        {
            return getResponseFields().toString();
        }

        public String getResponseHeader(String name)
        {
            return getResponseFields().getStringField(name);
        }

        @Override
        protected void onResponseStatus(Buffer version, int status, Buffer statusText) throws IOException
        {
            super.onResponseStatus(version, status, statusText);
            this.responseStatusText = new String(statusText.asArray(), "UTF-8");
        }

        @Override
        protected void onResponseHeader(Buffer name, Buffer value) throws IOException
        {
            super.onResponseHeader(name, value);
            int headerName = HttpHeaders.CACHE.getOrdinal(name);
            if (headerName == HttpHeaders.SET_COOKIE_ORDINAL)
            {
                try
                {
                    cookieStore.jsFunction_set(getScheme().toString("UTF-8"), getAddress().toString(), "", value.toString("UTF-8"));
                }
                catch (Exception x)
                {
                    throw (IOException)new IOException().initCause(x);
                }
            }
        }

        @Override
        protected void onResponseHeaderComplete() throws IOException
        {
            if (!aborted)
            {
                if (async)
                {
                    readyState = ReadyState.HEADERS_RECEIVED;
                    notifyReadyStateChange(true);
                }
            }
        }

        @Override
        protected void onResponseContent(Buffer buffer) throws IOException
        {
            super.onResponseContent(buffer);
            if (!aborted)
            {
                if (async)
                {
                    if (readyState != ReadyState.LOADING)
                    {
                        readyState = ReadyState.LOADING;
                        notifyReadyStateChange(true);
                    }
                }
            }
        }

        @Override
        protected void onResponseComplete() throws IOException
        {
            if (!aborted)
            {
                log("Completed {}", this);
                responseText = getResponseContent();
                readyState = ReadyState.DONE;
                if (async)
                    notifyReadyStateChange(true);
            }
        }

        @Override
        protected void onException(Throwable x)
        {
            if (!(x instanceof EOFException))
                super.onException(x);
        }

        @Override
        protected void onRequestCommitted() throws IOException
        {
            super.onRequestCommitted();
            log("Committed {}", this);
        }

        @Override
        protected void onRequestComplete() throws IOException
        {
            super.onRequestComplete();
            log("Sent {}", this);
        }

        private void log(String message, Object... args)
        {
            if (Boolean.getBoolean("debugTests"))
                logger.info(message, args);
            else
                logger.debug(message, args);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy