/* * Copyright (c) 2014-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ package com.facebook.stetho.dumpapp; import com.facebook.stetho.server.SocketLike; import com.facebook.stetho.server.SocketLikeHandler; import com.facebook.stetho.server.http.ExactPathMatcher; import com.facebook.stetho.server.http.HandlerRegistry; import com.facebook.stetho.server.http.HttpHandler; import com.facebook.stetho.server.http.HttpStatus; import com.facebook.stetho.server.http.LightHttpBody; import com.facebook.stetho.server.http.LightHttpRequest; import com.facebook.stetho.server.http.LightHttpResponse; import com.facebook.stetho.server.http.LightHttpServer; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.List; /** * Provides a legacy hook to support old dumpapp scripts which have not been * updated to the new binary protocol. Since the framing protocol is still compatible this * can be easily supported to reduce upgrade friction since so many developers forgot to * copy the {@code dumpapp} script on upgrade. * * <p>{@code stdin} is not supported with this legacy mode. Streaming output is also not * supported.</p> * * <p>This script sends a warning to stderr informing the developer of its deprecated nature.</p> */ @Deprecated public class DumpappHttpSocketLikeHandler implements SocketLikeHandler { private final LightHttpServer mServer; public DumpappHttpSocketLikeHandler(Dumper dumper) { HandlerRegistry registry = new HandlerRegistry(); registry.register( new ExactPathMatcher("/dumpapp"), new DumpappLegacyHttpHandler(dumper)); mServer = new LightHttpServer(registry); } @Override public void onAccepted(SocketLike socket) throws IOException { mServer.serve(socket); } private static class DumpappLegacyHttpHandler implements HttpHandler { private static final String QUERY_PARAM_ARGV = "argv"; private static final String RESPONSE_HEADER_ALLOW_ORIGIN = "Access-Control-Allow-Origin"; private static final String CONTENT_TYPE = "application/octet-stream"; private final Dumper mDumper; public DumpappLegacyHttpHandler(Dumper dumper) { mDumper = dumper; } @Override public boolean handleRequest( SocketLike socket, LightHttpRequest request, LightHttpResponse response) throws IOException { boolean postMethod = "POST".equals(request.method); boolean getMethod = !postMethod && "GET".equals(request.method); if (getMethod || postMethod) { List<String> argv = request.uri.getQueryParameters(QUERY_PARAM_ARGV); ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream(); Framer framer = new Framer( new ByteArrayInputStream(new byte[0]), outputBuffer); String warningPrefix = postMethod ? "ERROR" : "WARNING"; framer.getStderr().println( "*** " + warningPrefix + ": Using legacy HTTP protocol; update dumpapp script! ***"); if (getMethod) { DumpappSocketLikeHandler.dump(mDumper, framer, argv.toArray(new String[argv.size()])); } else { // stdin access is completely unsupported, so we can't allow any dumper plugins // to run... framer.writeExitCode(1); } response.code = HttpStatus.HTTP_OK; response.reasonPhrase = "OK"; response.addHeader(RESPONSE_HEADER_ALLOW_ORIGIN, "*"); response.body = LightHttpBody.create(outputBuffer.toByteArray(), CONTENT_TYPE); } else { response.code = HttpStatus.HTTP_NOT_IMPLEMENTED; response.reasonPhrase = "Not implemented"; response.body = LightHttpBody.create(request.method + " not implemented", "text/plain"); } return true; } } }