Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Server-initiated requests

Tags:

http

go

http2

I know that HTTP is a request-response protocol. My problem, in short, is that a client makes a request to the server to start a long running-process, and I want to inform the client of the progress with a simple JSON message containing progress info.

In HTTP/1.1 I know that I could use a WebSocket or server-sent events (SSE) or long polling.

Now I know that HTTP/2 does not support WebSocket yet.

My question is, what is the optimal way to handle such things over HTTP/2?

Are there any new things that I am not aware of to handle server-initiated requests in HTTP/2?

I am using the Go language, if that matters.

like image 588
FPGA Avatar asked Jun 15 '15 00:06

FPGA


People also ask

What are server requests?

The messages sent by the client, usually a Web browser, are called requests and the messages sent by the server as an answer are called responses.

Can a server initiate a HTTP request?

Server Push allows an HTTP/2 server to send any kind of file to the client on its own initiative. It's called a Server Push "response" even though it's sent before the client asks for it.

How is a server request done?

The client (usually a browser) opens a connection to the server and sends a request. The server processes the request, generates a response, and closes the connection if it finds a Connection: Close header.

What happens when you send a request to a server?

The URL you are requesting is the address that belongs to the server. Once the TCP connection is established, the client sends a HTTP GET request to the server to retrieve the webpage it should display. After the server has sent the response, it closes the TCP connection.


1 Answers

Before websockets we had polling. This literally means having the client periodically (every few seconds, or whatever time period makes sense for your application), make a request to the server to find out the status of the job.

An optimization many people use is "long" polling. This involves having the server accept the request, and internally to the server, check for changes, and sleep while there are none, until either a specific timeout is reached or the desired event occurs, which is then messaged back to the client.

If a timeout is reached, the connection is closed and the client needs to make another request. The server code would look something like the following, assume the functions do sensible things based on their names and signatures:

import (
    "net/http"
    "time"
)

func PollingHandler(w http.ResponseWriter, r *http.Request) {
    jobID := getJobID(r)
    for finish := 60; finish > 0; finish-- { // iterate for ~1 minute
        status, err := checkStatus(jobID)
        if err != nil {
            writeError(w, err)
            return
        }
        if status != nil {
            writeStatus(w, status)
            return
        }
        time.Sleep(time.Second) // sleep 1 second
    }
    writeNil(w) // specific response telling client to request again.
}

A better way to handle the timeout would be to use the context package and create a context with a timeout. That would look something like:

import (
    "net/http"
    "time"
    "golang.org/x/net/context"
)

func PollingHandler(w http.ResponseWriter, r *http.Request) {
    jobID := getJobID(r)
    ctx := context.WithTimeout(context.Background(), time.Second * 60)
    for {
        select{
        case <-ctx.Done():
            writeNil(w)
        default: 
            status, err := checkStatus(jobID)
            if err != nil {
                writeError(w, err)
                return
            }
            if status != nil {
                writeStatus(w, status)
                return
            }
            time.Sleep(time.Second) // sleep 1 second
        }
    }

}

This second version is just going to return in a more reliable amount of time, especially in the case where checkStatus may be a slower call.

like image 124
Endophage Avatar answered Oct 08 '22 07:10

Endophage