org.opensilk.music.renderer.googlecast.server.ProxyHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.opensilk.music.renderer.googlecast.server.ProxyHandler.java

Source

/*
 * Copyright (c) 2015 OpenSilk Productions LLC
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.opensilk.music.renderer.googlecast.server;

import android.net.Uri;

import com.squareup.okhttp.CacheControl;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Response;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jetty.http.HttpMethods;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.opensilk.music.model.Track;
import org.opensilk.music.renderer.googlecast.TrackResCache;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Map;

import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import timber.log.Timber;

/**
 * Handles requests that require auth by proxying them through
 * the device (which has been previously authenticated)
 *
 * Note that this in not a true proxy, we merely make the request on their
 * behalf and feed them the input stream.
 *
 * Created by drew on 10/30/15.
 */
public class ProxyHandler extends AbstractHandler {
    final OkHttpClient mOkClient;
    final TrackResCache mTrackResCache;

    @Inject
    public ProxyHandler(OkHttpClient mOkClient, TrackResCache mTrackResCache) {
        this.mOkClient = mOkClient;
        this.mTrackResCache = mTrackResCache;
    }

    @Override
    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        if (!HttpMethods.GET.equals(request.getMethod())) {
            response.sendError(HttpStatus.METHOD_NOT_ALLOWED_405);
            baseRequest.setHandled(true);
            return;
        }

        String pathInfo = StringUtils.stripStart(request.getPathInfo(), "/");
        Uri contentUri = Uri.parse(CastServerUtil.decodeString(pathInfo));

        if (CastServer.DUMP_REQUEST_HEADERS) {
            StringBuilder reqlog = new StringBuilder();
            reqlog.append("Serving artwork uri ").append(contentUri).append("\n Method ")
                    .append(request.getMethod());
            for (Enumeration<String> names = request.getHeaderNames(); names.hasMoreElements();) {
                String name = names.nextElement();
                reqlog.append("\n HDR: ").append(name).append(":").append(request.getHeader(name));
            }
            Timber.v(reqlog.toString());
        }

        com.squareup.okhttp.Request.Builder rb = new com.squareup.okhttp.Request.Builder()
                .url(contentUri.toString());
        Track.Res trackRes = mTrackResCache.get(contentUri);

        //add resource headers
        Map<String, String> headers = trackRes.getHeaders();
        for (Map.Entry<String, String> e : headers.entrySet()) {
            rb.addHeader(e.getKey(), e.getValue());
        }

        String range = request.getHeader("Range");
        if (!StringUtils.isEmpty(range)) {
            rb.addHeader("Range", range);
        }

        String ifnonematch = request.getHeader("if-none-match");
        if (!StringUtils.isEmpty(ifnonematch)) {
            rb.addHeader("if-none-match", ifnonematch);
        }

        //dont clog our cache with binaries
        CacheControl pCC = new CacheControl.Builder().noStore().noCache().build();
        rb.cacheControl(pCC);

        Response pResponse = mOkClient.newCall(rb.get().build()).execute();

        if (CastServer.DUMP_REQUEST_HEADERS) {
            StringBuilder sb = new StringBuilder();
            sb.append("Executed proxy GET request uri ").append(contentUri).append("\n Resp: ")
                    .append(pResponse.code()).append(",").append(pResponse.message());
            for (String name : pResponse.headers().names()) {
                sb.append("\n HDR: ").append(name).append(": ").append(pResponse.header(name));
            }
            Timber.v(sb.toString());
        }

        if (!pResponse.isSuccessful()) {
            response.sendError(pResponse.code(), pResponse.message());
            baseRequest.setHandled(true);
            return;
        }

        //build the response
        String acceptRanges = pResponse.header("Accept-Ranges");
        if (!StringUtils.isEmpty(acceptRanges)) {
            response.addHeader("Accept-Ranges", acceptRanges);
        }
        String contentRange = pResponse.header("Content-Range");
        if (!StringUtils.isEmpty(contentRange)) {
            response.addHeader("Content-Range", contentRange);
        }
        String contentLen = pResponse.header("Content-Length");
        if (!StringUtils.isEmpty(contentLen)) {
            response.addHeader("Content-Length", contentLen);
        }
        String contentType = pResponse.header("Content-Type");
        if (StringUtils.isEmpty(contentType)) {
            contentType = "application/octet-stream";
        }
        response.addHeader("Content-Type", contentType);
        String etag = pResponse.header("Etag");
        if (!StringUtils.isEmpty(etag)) {
            response.addHeader("Etag", etag);
        }
        if (HttpStatus.NOT_MODIFIED_304 == pResponse.code()) {
            response.flushBuffer();
        } else {
            InputStream in = pResponse.body().byteStream();
            try {
                //XXX out need not be closed
                OutputStream out = response.getOutputStream();
                IOUtils.copy(in, out);
                out.flush();
            } finally {
                IOUtils.closeQuietly(in);
            }
        }
        baseRequest.setHandled(true);
    }
}