GithubHelp home page GithubHelp logo

oksse's Introduction

Build Status

Introduction

OkSse is an extension library for OkHttp to create a Server-Sent Event (SSE) client

Server-sent events is a standard describing how servers can initiate data transmission towards clients once an initial client connection has been established. They are commonly used to send message updates or continuous data streams to a client.

Integration

Add JitPack repository:

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}

Add OkSse dependency:

dependencies {
    implementation 'com.github.heremaps:oksse:0.9.0'
}

Usage

You can create an OkSse instance either using an already existing OkHttp client instance, or let OkSse create a default one instead.

The following code creats a request that points to our SSE server and uses the OkSse instance to create a new ServerSentEvent connection:

Request request = new Request.Builder().url(path).build();
OkSse okSse = new OkSse();
ServerSentEvent sse = okSse.newServerSentEvent(request, listener);

This implements the ServerSentEvent listener to get notified of the channel status and the received messages or comments:

new ServerSentEvent.Listener() {
    @Override
    public void onOpen(ServerSentEvent sse, Response response) {
        // When the channel is opened
    }

    @Override
    public void onMessage(ServerSentEvent sse, String id, String event, String message) {
        // When a message is received
    }

    @WorkerThread
    @Override
    public void onComment(ServerSentEvent sse, String comment) {
       // When a comment is received
    }

    @WorkerThread
    @Override
    public boolean onRetryTime(ServerSentEvent sse, long milliseconds) {
        return true; // True to use the new retry time received by SSE
    }

    @WorkerThread
    @Override
    public boolean onRetryError(ServerSentEvent sse, Throwable throwable, Response response) {
        return true; // True to retry, false otherwise
    }

    @WorkerThread
    @Override
    public void onClosed(ServerSentEvent sse) {
        // Channel closed
    }

When done listing from SSE, remember to close the channel and clear resources:

sse.close();

Once closed, you will need to create a new instance.

Alternative constructor

You can provide your own OkHttp client when creating a new OkSse instance.

OkHttpClient client = new OkHttpClient.Builder().readTimeout(0, TimeUnit.SECONDS).build();
OkSse oksse = new OkSse(client)

Note the read timeout set to 0, this is required in order to keep the connection alive, if not OkHttp will close the channel after timeout is reached.

License

Copyright (c) 2017 HERE Europe B.V.

Please see the LICENSE file for details.

oksse's People

Contributors

bruestel avatar felipecsl avatar marcelpinto avatar ryanramchandar avatar sschuberth avatar t-spoon avatar yosriz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

oksse's Issues

HTTPS Not secure problem

I'm working on app where url is on https but certificate is not valid. How can i bypass this for now until they fix their https?

java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

Crash app when turn off network

Hi guys, the app crashes when i turn off network. This is the stack trace

E/AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher
Process: com.tmt.tpos, PID: 27083
java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter response
at com.tmt.tpos.Utils.LiveCommentController$registerLiveCommentEvent$1.onRetryError(LiveCommentController.kt)
at com.here.oksse.RealServerSentEvent.retry(RealServerSentEvent.java:104)
at com.here.oksse.RealServerSentEvent.notifyFailure(RealServerSentEvent.java:94)
at com.here.oksse.RealServerSentEvent.access$000(RealServerSentEvent.java:26)
at com.here.oksse.RealServerSentEvent$Reader.read(RealServerSentEvent.java:188)
at com.here.oksse.RealServerSentEvent.openSse(RealServerSentEvent.java:89)
at com.here.oksse.RealServerSentEvent.access$100(RealServerSentEvent.java:26)
at com.here.oksse.RealServerSentEvent$1.onResponse(RealServerSentEvent.java:75)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:153)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)

Spring SSE?

Hey, can you provide any documentation on making this work specifically with Spring? Thanks

onRetryErrror(). Reoccurring EOFException.

I constantly receive an EOFException in the onRetryError handler.
This happens about 60 seconds after the connection is established and onOpen() has been called.
We manage to receive 1-2 messages before we encounter the exception.

I was just wondering if there is something I can do to prevent this behaviour as I often have to reconnect to our SSE server.

We are wondering if this is an issue on our side, or if we should look closer into oksse itself.

The stack trace itself reads:

java.io.EOFException
    at okio.RealBufferedSource.require(RealBufferedSource.java:60)
    at okio.RealBufferedSource.readHexadecimalUnsignedLong(RealBufferedSource.java:294)
    at okhttp3.internal.http1.Http1Codec$ChunkedSource.readChunkSize(Http1Codec.java:458)
    at okhttp3.internal.http1.Http1Codec$ChunkedSource.read(Http1Codec.java:438)
    at okio.RealBufferedSource.indexOf(RealBufferedSource.java:345)
    at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:217)
    at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:211)
    at com.here.oksse.RealServerSentEvent$Reader.read(RealServerSentEvent.java:181)
    at com.here.oksse.RealServerSentEvent.openSse(RealServerSentEvent.java:89)
    at com.here.oksse.RealServerSentEvent.access$100(RealServerSentEvent.java:26)
    at com.here.oksse.RealServerSentEvent$1.onResponse(RealServerSentEvent.java:75)
    at okhttp3.RealCall$AsyncCall.execute(RealCall.java:153)
    at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
    at java.lang.Thread.run(Thread.java:762)

In essence, our client setup in performed in the following way:

OkSse okSse = new OkSse();

Request request = new Request.Builder()
                    .url(url)
                    .addHeader("Authorization", auth.getToken())
                    .build();

sse = okSse.newServerSentEvent(request, callback);

For reference, the SSE Listener callback, is implemented in the following way:

ServerSentEvent.Listener callback = new ServerSentEvent.Listener() {
        @Override
        public void onOpen(ServerSentEvent sse, Response response) {
            Timber.d("onOpen()");
            state = State.CONNECTED;
        }

        @Override
        public void onMessage(ServerSentEvent sse, String id, String event, String message) {
            if (message != null && !message.isEmpty()) {
                    listener.onMessage(event, message);
            }
        }

        @WorkerThread
        @Override
        public void onComment(ServerSentEvent sse, String comment) {
            // When a comment is received
        }

        @WorkerThread
        @Override
        public boolean onRetryTime(ServerSentEvent sse, long milliseconds) {
            return true; // True to use the new retry time received by SSE
        }

        @WorkerThread
        @Override
        public boolean onRetryError(ServerSentEvent sse, Throwable throwable, Response response) {
            Timber.e("onRetryError err = " + throwable + ", res = " + response);

            listener.onError(throwable);

            return true; // True to retry, false otherwise
        }

        @Override
        public Request onPreRetry(ServerSentEvent sse, Request originalRequest) {
            return originalRequest;
        }

        @WorkerThread
        @Override
        public void onClosed(ServerSentEvent sse) {
            Timber.d("onClosed");
            sse = null;

            if (listener != null) {
                listener.onClose();
            }
        }

    };

okhttp3.internal.connection.RealCall.timeoutEarlyExit

Exception java.lang.IllegalStateException: Check failed.
at okhttp3.internal.connection.RealCall.timeoutEarlyExit (RealCall.kt:409)
at okhttp3.internal.sse.RealEventSource.processResponse (RealEventSource.kt:65)
at okhttp3.internal.sse.RealEventSource.onResponse (RealEventSource.kt:46)
at okhttp3.internal.connection.RealCall$AsyncCall.run (RealCall.kt:519)
at java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:641)
at java.lang.Thread.run (Thread.java:923)

StrictMode error: Explicit termination method 'response.body().close()' not called

The following code throws a StrictMode error:

val request = Request.Builder().url(url).build()
val okSse = OkSse(client)
sse = okSse.newServerSentEvent(request, listener())
04-04 14:58:32.269 7907-8257/co.app.debug E/StrictMode: A resource was acquired at attached
 stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
    java.lang.Throwable: Explicit termination method 'response.body().close()' not called
        at dalvik.system.CloseGuard.open(CloseGuard.java:184)
        at java.lang.reflect.Method.invoke(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:372)
        at okhttp3.internal.platform.AndroidPlatform$CloseGuard.createAndOpen(AndroidPlatform.java:340)
        at okhttp3.internal.platform.AndroidPlatform.getStackTraceForCloseable(AndroidPlatform.java:155)
        at okhttp3.RealCall.captureCallStackTrace(RealCall.java:89)
        at okhttp3.RealCall.enqueue(RealCall.java:98)
        at com.here.oksse.RealServerSentEvent.enqueue(RealServerSentEvent.java:66)
        at com.here.oksse.RealServerSentEvent.connect(RealServerSentEvent.java:49)
        at com.here.oksse.OkSse.newServerSentEvent(OkSse.java:90)
        ... app stack

Version:

implementation "com.github.heremaps:oksse:0c60781b9a"

Library Version with HTTP2 support doesn't appear to be published on jitpack

When I bundle the oksse library as part of my android app, I get an issue where the list of protocols available is limited to only http/1.1

However, I've been explicitly passing in an OkHttpClient that has h2 as part of the protocols

Seems like the PR here isn't included in the library that was published to jitpack #25

The library crashes on Android when closeSSEConnection() is called

Hi guys,
The library crashes in closeSSEConnection() call, the stack trace looks like:

Caused by: android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1303)
at java.net.SocketInputStream.read(SocketInputStream.java:150)
at java.net.SocketInputStream.read(SocketInputStream.java:120)
at okio.Okio$2.read(Okio.java:139)
at okio.AsyncTimeout$2.read(AsyncTimeout.java:237)
at okio.RealBufferedSource.indexOf(RealBufferedSource.java:345)
at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:217)
at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:211)
at okhttp3.internal.http1.Http1Codec$ChunkedSource.readChunkSize(Http1Codec.java:463)
at okhttp3.internal.http1.Http1Codec$ChunkedSource.read(Http1Codec.java:446)
at okhttp3.internal.Util.skipAll(Util.java:175)
at okhttp3.internal.Util.discard(Util.java:157)
at okhttp3.internal.http1.Http1Codec$ChunkedSource.close(Http1Codec.java:484)
at okio.RealBufferedSource.close(RealBufferedSource.java:455)
at com.here.oksse.RealServerSentEvent$Reader.close(RealServerSentEvent.java:199)
at com.here.oksse.RealServerSentEvent.close(RealServerSentEvent.java:142)
at xx.xxxxx..controller.EventsController.closeSSEConnection(EventsController.java:140)

The source of trouble that at line #199 of RealServerSentEvent.java the method source.close() is called from the main thread. If the library is designed only for Java, not Android, sorry that I raised this issue. Otherwise the library should take care to correctly handle this situation. Also it should send callback events from the main thread, not the Okio execution (background) thread.

Question: No changes in 2+ years, should we consider moving to okhttp-sse instead?

I'll soon start a SSE project, not sure if I should go with this library that looks dedicated to the problem or pick the experimental project from okhttp main repository https://github.com/square/okhttp/tree/master/okhttp-sse as it has more recent changes (2 months).
On this library I see no recent changes and the latest version is not a 1.0 but a 0.9, so I'm not sure about the confidence I can have on this lib to push this in production. Maybe it's stable and used in production for years, then I understand there is no changes required, in such case thanks for your responses.


Also I have a question about the listeners, the mechanism from okhttp-sse seems simpler, is there differences in the SSE protocol (2 RFCs?) or does this library provides advanced retry mechanism hence the more complex API?
https://github.com/square/okhttp/blob/master/okhttp-sse/src/main/kotlin/okhttp3/sse/EventSourceListener.kt

creating a release

Hi *,

could you tag a version, so we don't use snapshot?
Also would be nice to propagate it to maven central.

Best
Hasan

Cannot add Accept: application/stream+json header

My server guys are requiring me to use

Accept: application/stream+json

header to receive their Spring Flux server sent events. After tracing through the code, it appears that the prepareCall(Request request) method in the RealServerSentEvent class is forcing the request object to always have the same default headers for every call. This is preventing me from adding new headers to my ServerSentEvent object.

Request newRequest = request.newBuilder()
            .header("Accept-Encoding", "")
            .header("Accept", "text/event-stream")
            .header("Cache-Control", "no-cache")
            .build();

    call = client.newCall(newRequest);

I'm wondering if this is by design, since server sent events should be sent with the text/event-stream header. I think the reason they are requiring the application/stream+json header is that works in Google Chrome developer tools, which they are using for debugging.

Error in onPreRetry signature

The onPreRetry documentation mentions being able to return null to cancel the retry attempt and close the channel. The return type is not an optional Request however, which prevents you from actually returning null.

In other words, the return type should be changed to Request?

getting SocketTimeout - curl works

Hi Here Team,

currently, I investigate SSE for our app. I always get a SocketTimeOutException, when I try to connect to my own SSE enabled resource. That the server works was checked with curl on the dev machine, but in the emulator, the error happens. I followed your example, but I used an existing OkHttpClient, which have interceptors and authenticator for JWT.

Could you give me a hint, how to solve that or to dig deeper?

Thanks
Hasan

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.