GithubHelp home page GithubHelp logo

flashback's Introduction

Flashback

Introducing Flashback

Flashback is designed to mock HTTP and HTTPS resources, like web services and REST APIs, for testing purposes. It records HTTP/HTTPS requests and plays back a previously recorded HTTP transaction—which we call a "scene"—so that no external connection to the internet is required in order to complete testing.

Note: The flashback and flashback scene are used for testing purpose only. Each scene contains a list of Http request and response. Those http request and response might contain some sensitive data such as api key, secret, token etc. You'd better either blacklist them using customized match rule or mask them.

Flashback can also replay scenes based on partial matching of requests. It does so using “match rules.” A match rule associates an incoming request with a previously-recorded request, which is then used to generate a response. For example, the following code snippet implements a basic match rule, where the test method “matches” an incoming request via the URL.

public class MatchSimpleUri implements MatchRule {
  @Override
  public boolean test(RecordedHttpRequest incomingRequest, RecordedHttpRequest expectedRequest) {
    return incomingRequest.getUri().equals(expectedRequest.getUri());
  }

  @Override
  public String getMatchFailureDescriptionForRequests(RecordedHttpRequest incomingRequest, RecordedHttpRequest expectedRequest) {
    return String.format("URI Mismatch%nIncoming URI: %s%nExpected URI: %s%n",
        incomingRequest.getUri(),
        expectedRequest.getUri());
  }
}

HTTP requests generally contain a URL, method, headers, and body. Flashback allows match rules to be defined for any combination of these components. Flashback also allows users to add whitelist or blacklist labels to URL query parameters, headers, and the body.

For instance, in an OAuth authorization flow, the request query parameters may look like the following:

oauth_consumer_key="jskdjfljsdklfjlsjdfs",
oauth_nonce="ajskldfjalksjdflkajsdlfjasldfja;lsdkj",
oauth_signature="asdfjaklsdjflasjdflkajsdklf",
oauth_signature_method="HMAC-SHA1",
oauth_timestamp="1318622958",
oauth_token="asdjfkasjdlfajsdklfjalsdjfalksdjflajsdlfa",
oauth_version="1.0"

Many of these values will change with every request, since OAuth requires clients to generate a new value for oauth_nonce every time. In our testing, we need to verify values of oauth_consumer_key, oauth_signature_method, and oauth_version while also making sure that oauth_nonce, oauth_signature, oauth_timestamp, and oauth_token exist in the request. Flashback gives us the ability to create our own match rules to achieve this goal. This feature lets us test requests with time-varying data, signatures, tokens, etc. without any changes on the client side.

This flexible matching and the ability to function without connecting to the internet are the attributes that separate Flashback from other mocking solutions. Some other notable features include:

  • Flashback can generate SSL/TLS certificates on the fly to emulate secured channels for HTTPS requests.
  • Flashback is a cross-platform and cross-language solution, with the ability to test both JVM and non-JVM (C++, Python, etc.) apps.

Terminology

  • A Scene stores previously recorded HTTP transactions (in JSON format) that can be later replayed.
  • The Root Path is the file path of the directory that contains the Flashback Scene data.
  • A Scene Name is the name of a given scene.
  • A Scene Mode is the mode in which the scene is being used—either record or playback.
  • A Match Rule is a rule that determines if the incoming client request matches the contents of a given scene.
  • Flashback Proxy is an HTTP Proxy with two modes of operation, record and playback.
  • Host and port are the proxy host and port.

Introductory Example

How to Record an HTTP Transaction

Recording an HTTP transaction for later playback, using Flashback, is a relatively straightforward process. In order to record a scene, you must make a real, external request to the destination, and the HTTPS request and response will then be stored in the scene with the match rule that you have specified. When recording, Flashback behaves exactly like a typical “Man in the Middle” (MITM) proxy—it is only in playback mode that the connection flow and data flow are restricted to just between the client and the proxy.

To see Flashback in action, let us create a scene that captures an interaction with example.org by doing the following: Step 1: Checkout Flashback source code

git clone https://github.com/linkedin/flashback.git

Step 2: Start the Flashback admin server

./startAdminServer.sh -port 1234

Step 3: Start Flashback Proxy:

curl "http://localhost:1234/admin?action=startFlashback" --data '{
  "sceneMode": "record",
  "sceneName": "test1",
  "matchRule": "matchEntireRequest",
  "scenePath": "/tmp",
  "proxyHost": "localhost",
  "proxyPort": "5555"
}'

The Flashback above will be started in record mode on localhost, port 5555. Match rule requires to do exactly match(match both Http body, headers and url). The scene will be stored under /tmp/test1.

Step 4: Flashback is now ready to record, so use it to proxy a request to example.org:

curl http://www.example.org -x localhost:5555 -X GET

Step 5: Flashback can record as many requests as you want. All of those Http transactions will be stored in memory. They won't be dump to file until you shutdown proxy or change scene. Once you finished recording, shutdown Flashback

curl "http://localhost:1234/admin?action=shutDownFlashback" --data '{}'

Step 6: To verify what has been recorded, we can view the contents of the scene in the output directory (/tmp/test1). It should contain the following:

{
  "NAME" : "test1",
  "HTTPEXCHANGELIST" : {
    "HTTPEXCHANGE1" : {
      "UPDATETIME" : "8 Mar 2017 23:56:25 GMT",
      "HTTPREQUEST" : {
        "HTTPMETHOD" : "GET",
        "HTTPURI" : "http://www.example.org/",
        "HTTPHEADERS" : {
          "Host" : "www.example.org",
          "User-Agent" : "curl/7.43.0",
          "Accept" : "*/*",
          "Proxy-Connection" : "Keep-Alive"
        },
        "BINARYHTTPBODY" : ""
      },
      "HTTPRESPONSE" : {
        "HTTPSTATUSCODE" : 200,
        "HTTPHEADERS" : {
          "X-Cache" : "HIT",
          "Cache-Control" : "max-age=604800",
          "Etag" : "\"359670651+ident\"",
          "Server" : "ECS (iad/182A)",
          "Vary" : "Accept-Encoding",
          "Expires" : "Wed, 15 Mar 2017 23:56:22 GMT",
          "Last-Modified" : "Fri, 09 Aug 2013 23:54:35 GMT",
          "Content-Length" : "1270",
          "Date" : "Wed, 08 Mar 2017 23:56:22 GMT",
          "Content-Type" : "text/html"
        },
        "STRINGHTTPBODY" : "<!doctype html>\n<html>\n<head>\n    <title>Example Domain</title>\n\n    <meta charset=\"utf-8\" />\n    <meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <style type=\"text/css\">\n    body {\n        background-color: #f0f0f2;\n        margin: 0;\n        padding: 0;\n        font-family: \"Open Sans\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n        \n    }\n    div {\n        width: 600px;\n        margin: 5em auto;\n        padding: 50px;\n        background-color: #fff;\n        border-radius: 1em;\n    }\n    a:link, a:visited {\n        color: #38488f;\n        text-decoration: none;\n    }\n    @media (max-width: 700px) {\n        body {\n            background-color: #fff;\n        }\n        div {\n            width: auto;\n            margin: 0 auto;\n            border-radius: 0;\n            padding: 1em;\n        }\n    }\n    </style>    \n</head>\n\n<body>\n<div>\n    <h1>Example Domain</h1>\n    <p>This domain is established to be used for illustrative examples in documents. You may use this\n    domain in examples without prior coordination or asking for permission.</p>\n    <p><a href=\"http://www.iana.org/domains/example\">More information...</a></p>\n</div>\n</body>\n</html>\n"
      }
    }
  }
}

How to Replay an HTTP Transaction

To replay a previously stored scene, use the same basic setup as is used when recording; the only difference is that you set the “Scene Mode” to “playback” in step 3 above:

curl "http://localhost:1234/admin?action=startFlashback" --data '{
  "sceneMode": "playback",
  "sceneName": "test1",
  "matchRule": "matchEntireRequest",
  "scenePath": "/tmp",
  "proxyHost": "localhost",
  "proxyPort": "5555"
}'

One way to verify that the response is from the scene, not the external source, is to disable your internet connectivity temporarily when you go through steps 1 through 6. Another way is to modify your scene file and see if the response is the same as what you have in the file.

Resources

Use cases

At Linkedin, Flashback is mainly used for mocking different Internet providers in integration tests, as illustrated in the diagrams below. The first diagram shows an internal service inside a Linkedin Production data center interacting with Internet providers (such as Google) via a proxy layer. We want to test this internal service in Testing environment. The second and third diagrams show how we can record and playback scenes in different environments. Recording happens in our Dev environment, where the user starts Flashback on the same port as proxy started. All external requests from the internal service to providers will go through Flashback instead of our proxy layer. After the needed scenes get recorded, we can deploy them to our Test environment. In the Test environment (which is isolated and has no Internet access), Flashback is started on the same port as in the Dev environment. All HTTP requests are still coming from the internal service but the responses will come from Flashback instead of the Internet providers.

Production-environment.jpg

Recording.jpg

Integration-test.jpg

License

Flashback is licensed under the terms of the [BSD-2 Clause license].

Disclaimer

It is worth noting here that Flashback is intended to be used for testing purpose only. Feel free to integrate Flashback with your service whenever you need it, but note that the record feature of Flashback will need to store everything from the wire, then use it during the replay mode. We recommend that users pay extra attention to ensure that no sensitive member data is being recorded or stored inadvertently. Anything that may violate your company’s data protection or privacy policy is your responsibility.

flashback's People

Contributors

bigwheel avatar jackque avatar kagale avatar rickle-msft avatar sf1152 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

flashback's Issues

Support for single-value headers with a comma

Some headers, such as Date, which, per the HTTP spec, is in RFC 1123 format, are single valued but contain a comma in them. The current method for supporting multi-valued headers splits on commas, which breaks this scenario. Instead of grouping multi-valued headers into one, comma-separated list, we propose maintaining them as separate key-value pairs.

Comparison with goreplay

I'm not understanding the high-level differences between this and goreplay. They seem to occupy most of the same problem space. Can someone explain if and how they differ in their use cases?

Is it possible to build the Flashback source code locally?

I'd like to build the source code locally, but when simple cloning and opening the project, I see a plethora of errors:

  • Assert cannot be resolved
  • ASN1Sequence cannot be resolved to a type
  • ASN1Inputstream cannot be resolved to a type
  • ASN1EncodableVector cannot be resolved to a type
  • ASN1Encodable cannot be resolved to a type
  • AfterTest cannot be resolved to a type
  • AfterMethod cannot be resolved to a type
  • ActionParam cannot be resolved to a type

Any thoughts on how I could clear this up?

configure HTTPS recording from the command line

Hi,

I'm hoping there is a way to configure my proxy with a trusted certificate from the command line. I don't see this anywhere in the current set of samples - only Java examples.

Is there a way to configure a certificate for my Flashback proxy without Java?

Thanks

Unexpected character ('s' (code 115)): was expecting double-quote to start field name

I am attempting to set up Flashback for the first time and ensure the Hello World steps outlined in the README work on my machine.

Unfortunately, the straightforward way of doing this doesn't seem to function correctly. I can clone the repo and start the server no problem. When I attempt to make the action=startFlashback request via Curl, I get the follow exception (making the same request via Postman returns 200 OK)

{
"exceptionClass":"com.linkedin.restli.server.RestLiServiceException",
"message":"com.fasterxml.jackson.core.JsonParseException: **Unexpected character ('s' (code 115)): 
  was expecting double-quote to start field name**\n at [Source: com.linkedin.data.ByteString$ByteArrayVectorInputStream@60edff67; line: 2, column: 4]",
  "stackTrace":"com.linkedin.restli.server.RestLiServiceException [HTTP Status:500]: 
    com.fasterxml.jackson.core.JsonParseException: **Unexpected character ('s' (code 115)): was expecting double-quote to start field name**\n 
    at [Source: com.linkedin.data.ByteString$ByteArrayVectorInputStream@60edff67; line: 2, column: 4]\r\n\t
    at com.linkedin.restli.internal.server.filter.RestLiResponseFilterContextFactory.fromThrowable(RestLiResponseFilterContextFactory.java:109)\r\n\t
    at com.linkedin.restli.internal.server.RestLiCallback.onError(RestLiCallback.java:104)\r\n\t
    at com.linkedin.restli.server.RestLiServer.handleResourceRequest(RestLiServer.java:344)\r\n\t
    at com.linkedin.restli.server.RestLiServer.doHandleRequest(RestLiServer.java:227)\r\n\t
    at com.linkedin.restli.server.BaseRestServer.handleRequest(BaseRestServer.java:61)\r\n\t
    at com.linkedin.restli.server.DelegatingTransportDispatcher.handleRestRequest(DelegatingTransportDispatcher.java:61)\r\n\t
    at com.linkedin.r2.filter.transport.DispatcherRequestFilter.onRestRequest(DispatcherRequestFilter.java:68)\r\n\t
    at com.linkedin.r2.filter.FilterChainIterator$FilterChainRestIterator.doOnRequest(FilterChainIterator.java:131)\r\n\t
    at com.linkedin.r2.filter.FilterChainIterator$FilterChainRestIterator.doOnRequest(FilterChainIterator.java:117)\r\n\t
    at com.linkedin.r2.filter.FilterChainIterator.onRequest(FilterChainIterator.java:57)\r\n\t
    at com.linkedin.r2.filter.transport.ServerQueryTunnelFilter.onRestRequest(ServerQueryTunnelFilter.java:58)\r\n\t
    at com.linkedin.r2.filter.FilterChainIterator$FilterChainRestIterator.doOnRequest(FilterChainIterator.java:131)\r\n\t
    at com.linkedin.r2.filter.FilterChainIterator$FilterChainRestIterator.doOnRequest(FilterChainIterator.java:117)\r\n\t
    at com.linkedin.r2.filter.FilterChainIterator.onRequest(FilterChainIterator.java:57)\r\n\t
    at com.linkedin.r2.filter.message.rest.RestFilter.onRestRequest(RestFilter.java:50)\r\n\t
    at com.linkedin.r2.filter.FilterChainIterator$FilterChainRestIterator.doOnRequest(FilterChainIterator.java:131)\r\n\t
    at com.linkedin.r2.filter.FilterChainIterator$FilterChainRestIterator.doOnRequest(FilterChainIterator.java:117)\r\n\t
    at com.linkedin.r2.filter.FilterChainIterator.onRequest(FilterChainIterator.java:57)\r\n\t
    at com.linkedin.r2.filter.FilterChainImpl.onRestRequest(FilterChainImpl.java:86)\r\n\t
    at com.linkedin.r2.filter.transport.FilterChainDispatcher.handleRestRequest(FilterChainDispatcher.java:70)\r\n\t
    at com.linkedin.r2.transport.http.server.HttpDispatcher.handleRequest(HttpDispatcher.java:95)\r\n\t
    at com.linkedin.r2.transport.http.server.HttpDispatcher.handleRequest(HttpDispatcher.java:70)\r\n\t
    at com.linkedin.r2.transport.http.server.HttpNettyServer$RestHandler.channelRead0(HttpNettyServer.java:173)\r\n\t
    at com.linkedin.r2.transport.http.server.HttpNettyServer$RestHandler.channelRead0(HttpNettyServer.java:136)\r\n\t
    at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:105)\r\n\t
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:339)\r\n\t
    at io.netty.channel.AbstractChannelHandlerContext.access$600(AbstractChannelHandlerContext.java:32)\r\n\t
    at io.netty.channel.AbstractChannelHandlerContext$7.run(AbstractChannelHandlerContext.java:329)\r\n\t
    at io.netty.util.concurrent.DefaultEventExecutor.run(DefaultEventExecutor.java:36)\r\n\t
    at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:111)\r\n\t
    at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:137)\r\n\t
    at java.lang.Thread.run(Thread.java:745)\r\n

Caused by: com.linkedin.restli.internal.server.RestLiInternalException: 
    com.fasterxml.jackson.core.JsonParseException: **Unexpected character ('s' (code 115)): was expecting double-quote to start field name**\n 
    at [Source: com.linkedin.data.ByteString$ByteArrayVectorInputStream@60edff67; line: 2, column: 4]\r\n\t
    at com.linkedin.restli.internal.server.util.DataMapUtils.readMap(DataMapUtils.java:103)\r\n\t
    at com.linkedin.restli.internal.server.methods.arguments.ActionArgumentBuilder.extractRequestData(ActionArgumentBuilder.java:67)\r\n\t
    at com.linkedin.restli.internal.server.RestLiMethodInvoker.invoke(RestLiMethodInvoker.java:189)\r\n\t
    at com.linkedin.restli.server.RestLiServer.handleResourceRequest(RestLiServer.java:337)\r\n\t
... 29 more\r\n

Caused by: com.fasterxml.jackson.core.JsonParseException: **Unexpected character ('s' (code 115)): was expecting double-quote to start field name**\n 
    at [Source: com.linkedin.data.ByteString$ByteArrayVectorInputStream@60edff67; line: 2, column: 4]\r\n\t
    at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:1487)\r\n\t
    at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:518)\r\n\t
    at com.fasterxml.jackson.core.base.ParserMinimalBase._reportUnexpectedChar(ParserMinimalBase.java:447)\r\n\t
    at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._handleOddName(UTF8StreamJsonParser.java:1919)\r\n\t
    at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._parseName(UTF8StreamJsonParser.java:1617)\r\n\t
    at com.fasterxml.jackson.core.json.UTF8StreamJsonParser.nextToken(UTF8StreamJsonParser.java:700)\r\n\t
    at com.linkedin.data.codec.JacksonDataCodec$Parser.parseDataMap(JacksonDataCodec.java:811)\r\n\t
    at com.linkedin.data.codec.JacksonDataCodec$Parser.parse(JacksonDataCodec.java:658)\r\n\t
    at com.linkedin.data.codec.JacksonDataCodec.readMap(JacksonDataCodec.java:287)\r\n\t
    at com.linkedin.restli.internal.common.DataMapConverter.bytesToDataMap(DataMapConverter.java:105)\r\n\t
    at com.linkedin.restli.internal.server.util.DataMapUtils.readMapWithExceptions(DataMapUtils.java:117)\r\n\t
at com.linkedin.restli.internal.server.util.DataMapUtils.readMap(DataMapUtils.java:99)\r\n\t
... 32 more\r\n","status":500}

Here are the formats of the command that I attempted:

curl.exe "http://localhost:1234/admin?action=startFlashback" --data '{"sceneMode": "record","sceneName": "test1","matchRule": "matchEntireRequest","scenePath": "/tmp","proxyHost": "localhost","proxyPo
rt": "5555"}'

curl.exe "http://localhost:1234/admin?action=startFlashback" --data "{""sceneMode"": ""record"",""sceneName"": ""test1"",""matchRule"": ""matchEntireRequest"",""scenePath"": ""/tmp"",""proxyHost"": ""
localhost"",""proxyPort"": ""5555""}"

curl.exe "http://localhost:1234/admin?action=startFlashback" --data "{sceneMode: record,sceneName: test1,matchRule: matchEntireRequest,scenePath: /tmp,proxyHost: localhost,proxyPort: 5555}"

curl.exe "http://localhost:1234/admin?action=startFlashback" --data "{"sceneMode": "record","sceneName": "test1","matchRule": "matchEntireRequest","scenePath": "/tmp","proxyHost": " localhost","proxyPort": "5555"}"

curl.exe "http://localhost:1234/admin?action=startFlashback" --data '{"sceneMode": "record","sceneName": "test1","matchRule": "matchEntireRequest","scenePath": "/tmp","proxyHost": " localhost","proxyPort": "5555"}'

Note the differences in usage of quotation marks.

I figure this is a powershell formatting issue, but I've used the escape character with no difference - any thoughts?

Probably relevant version information:

  • Java
    -- Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
    -- Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)
  • Curl
    -- curl 7.48.0 (x86_64-w64-mingw32) libcurl/7.48.0 OpenSSL/1.0.2h zlib/1.2.8 libidn/1.32 libssh2/1.7.0 nghttp2/1.9.2 librtmp/2.3
    -- Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp scp sftp smtp smtps telnet tftp
    -- Features: IDN IPv6 Largefile SSPI Kerberos SPNEGO NTLM SSL libz TLS-SRP HTTP2 Metalink
  • Operating System
    -- Windows 10 Enterpise (build 14393)

Add 'tag' --> service-virtualization

Tags are a GitHub concept created per-repo through the web-UI. "Service Virtualization" is the pre-existing industry name for mocking wire services

Exception while starting flashback proxy

while doing curl "http://localhost:1234/admin?action=startFlashback" --data '{ "sceneMode": "record", "sceneName": "test1", "matchRule": "matchEntireRequest", "scenePath": "/tmp", "proxyHost": "localhost", "proxyPort": "5555" }'

we get:

{"exceptionClass":"com.linkedin.restli.server.RestLiServiceException","message":"INTERNAL SERVER ERROR","stackTrace":"com.linkedin.restli.server.RestLiServiceException [HTTP Status:500]: INTERNAL SERVER ERROR\n\tat com.linkedin.restli.internal.server.RestLiMethodInvoker.doInvoke(RestLiMethodInvoker.java:317)\n\tat com.linkedin.restli.internal.server.RestLiMethodInvoker.access$100(RestLiMethodInvoker.java:62)\n\tat com.linkedin.restli.internal.server.RestLiMethodInvoker$RestLiRequestFilterChainCallbackImpl.onSuccess(RestLiMethodInvoker.java:424)\n\tat com.linkedin.restli.internal.server.filter.RestLiRequestFilterChain.onRequest(RestLiRequestFilterChain.java:72)\n\tat com.linkedin.restli.internal.server.RestLiMethodInvoker.invoke(RestLiMethodInvoker.java:198)\n\tat com.linkedin.restli.server.RestLiServer.handleResourceRequest(RestLiServer.java:337)\n\tat com.linkedin.restli.server.RestLiServer.doHandleRequest(RestLiServer.java:227)\n\tat com.linkedin.restli.server.BaseRestServer.handleRequest(BaseRestServer.java:61)\n\tat com.linkedin.restli.server.DelegatingTransportDispatcher.handleRestRequest(DelegatingTransportDispatcher.java:61)\n\tat com.linkedin.r2.filter.transport.DispatcherRequestFilter.onRestRequest(DispatcherRequestFilter.java:68)\n\tat com.linkedin.r2.filter.FilterChainIterator$FilterChainRestIterator.doOnRequest(FilterChainIterator.java:131)\n\tat com.linkedin.r2.filter.FilterChainIterator$FilterChainRestIterator.doOnRequest(FilterChainIterator.java:117)\n\tat com.linkedin.r2.filter.FilterChainIterator.onRequest(FilterChainIterator.java:57)\n\tat com.linkedin.r2.filter.transport.ServerQueryTunnelFilter.onRestRequest(ServerQueryTunnelFilter.java:58)\n\tat com.linkedin.r2.filter.FilterChainIterator$FilterChainRestIterator.doOnRequest(FilterChainIterator.java:131)\n\tat com.linkedin.r2.filter.FilterChainIterator$FilterChainRestIterator.doOnRequest(FilterChainIterator.java:117)\n\tat com.linkedin.r2.filter.FilterChainIterator.onRequest(FilterChainIterator.java:57)\n\tat com.linkedin.r2.filter.message.rest.RestFilter.onRestRequest(RestFilter.java:50)\n\tat com.linkedin.r2.filter.FilterChainIterator$FilterChainRestIterator.doOnRequest(FilterChainIterator.java:131)\n\tat com.linkedin.r2.filter.FilterChainIterator$FilterChainRestIterator.doOnRequest(FilterChainIterator.java:117)\n\tat com.linkedin.r2.filter.FilterChainIterator.onRequest(FilterChainIterator.java:57)\n\tat com.linkedin.r2.filter.FilterChainImpl.onRestRequest(FilterChainImpl.java:86)\n\tat com.linkedin.r2.filter.transport.FilterChainDispatcher.handleRestRequest(FilterChainDispatcher.java:70)\n\tat com.linkedin.r2.transport.http.server.HttpDispatcher.handleRequest(HttpDispatcher.java:95)\n\tat com.linkedin.r2.transport.http.server.HttpDispatcher.handleRequest(HttpDispatcher.java:70)\n\tat com.linkedin.r2.transport.http.server.HttpNettyServer$RestHandler.channelRead0(HttpNettyServer.java:173)\n\tat com.linkedin.r2.transport.http.server.HttpNettyServer$RestHandler.channelRead0(HttpNettyServer.java:136)\n\tat io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:105)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:339)\n\tat io.netty.channel.AbstractChannelHandlerContext.access$600(AbstractChannelHandlerContext.java:32)\n\tat io.netty.channel.AbstractChannelHandlerContext$7.run(AbstractChannelHandlerContext.java:329)\n\tat io.netty.util.concurrent.DefaultEventExecutor.run(DefaultEventExecutor.java:36)\n\tat io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:111)\n\tat io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:137)\n\tat java.lang.Thread.run(Thread.java:748)\nCaused by: io.netty.channel.ChannelException: Failed to bind to: localhost:5555\n\tat com.linkedin.mitm.proxy.ProxyServer.start(ProxyServer.java:87)\n\tat com.linkedin.flashback.smartproxy.FlashbackRunner.start(FlashbackRunner.java:99)\n\tat com.linkedin.flashback.FlashbackAdminResource.startFlashback(FlashbackAdminRe* Curl_http_done: called premature == 0Connection #0 to host localhost left intact source.java:80tat sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n\tat sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n\tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n\tat java.lang.reflect.Method.invoke(Method.java:498)\n\tat com.linkedin.restli.internal.server.RestLiMethodInvoker.doInvoke(RestLiMethodInvoker.java:243)\n\t... 34 more\nCaused by: java.net.BindException: Address already in use\n\tat sun.nio.ch.Net.bind0(Native Method)\n\tat sun.nio.ch.Net.bind(Net.java:433)\n\tat sun.nio.ch.Net.bind(Net.java:425)\n\tat sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:223)\n\tat sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:74)\n\tat io.netty.channel.socket.nio.NioServerSocketChannel.doBind(NioServerSocketChannel.java:125)\n\tat io.netty.channel.AbstractChannel$AbstractUnsafe.bind(AbstractChannel.java:485)\n\tat io.netty.channel.DefaultChannelPipeline$HeadContext.bind(DefaultChannelPipeline.java:1081)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeBind(AbstractChannelHandlerContext.java:502)\n\tat io.netty.channel.AbstractChannelHandlerContext.bind(AbstractChannelHandlerContext.java:487)\n\tat io.netty.channel.DefaultChannelPipeline.bind(DefaultChannelPipeline.java:904)\n\tat io.netty.channel.AbstractChannel.bind(AbstractChannel.java:198)\n\tat io.netty.bootstrap.AbstractBootstrap$2.run(AbstractBootstrap.java:348)\n\tat io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:357)\n\tat io.netty.channel.nio.NioEventLoop.run(NioEventLo

Where are scenes supposed to write to on disk?

I've been noticing some non-static behavior that is throwing me off. Using the "golden flow" example in the readme, on Windows 10, I've seen my test1 file appear in the following locations

  • Directly on C:\
  • In \flashback-admin inside the repo
  • Not appearing at all, but both startFlashback and shutDownFlashback respond with a 200 OK.

Why am I seeing it appear (or not appear) in different locations when I am following the same workflow?

This is the post body I am using to start Flashback:

{
  "sceneMode": "record",
  "sceneName": "test1",
  "matchRule": "matchEntireRequest",
  "scenePath": "/tmp",
  "proxyHost": "localhost",
  "proxyPort": "5555"
}

Flashback vs BrowserMob Proxy

From the first glance Flashback looks quite similar to BrowserMob Proxy. Both are HTTP(S) MITM proxies written in Java. BMP focuses on recording HAR (http archive), simulating bandwidth and latency, and online request/response interception which may be used for both simple blacklisting and complete rewriting. The rewriting can be used for replay, but replay is not a core feature and may require more manual work than with Flashback.

Can Flashback devs which are aware of BMP (or even inspired by it!) comment on the two project differences?

Android?

Is flashback appropriate for use with Android espresso testing?

Https requests don't work for request time out

I read How to Record and Replay an HTTPS Transaction · linkedin/flashback Wiki and tried to proxy https request.
But it didn't work correctly for time out.

Environemnt

  • ubuntu 16.10

Reproduce procedure

openssl genrsa 1024 > private-key.pem
openssl rsa -in private-key.pem -pubout -out public-key.pem
openssl req -new -key private-key.pem > my-request.csr # enter for all dialogs, then no password
openssl x509 -req -in my-request.csr -signkey private-key.pem -out public-key.crt -days 3650
openssl pkcs12 -export -in public-key.crt -inkey private-key.pem -out my-identity.p12

# add certificate to os root certificates
sudo cp public-key.crt /usr/share/ca-certificates
sudo echo "public-key.crt" >> /etc/ca-certificats.conf
sudo update-ca-certificates

./startAdminServer.sh -port 1234

# in another console
curl "http://localhost:1234/admin?action=startFlashback" --data '{
  "sceneMode": "record",
  "sceneName": "test1",
  "matchRule": "matchEntireRequest",
  "scenePath": "./scene",
  "proxyHost": "localhost",
  "proxyPort": "5564",
  "caCertPath": "absolute path of my-identity.p12",
  "caCertPwd": "",
  "caAlias": "",
  "caKeyPwd": "",
  "caCertOU": "",
  "caCertO": "",
  "caCertCN": "",
  "caCertL": "",
  "caCertCC": ""
}'

Above commands succeed entirely, however,

Result

curl --proxy localhost:5564 -L 'https://www.google.co.jp/'
curl: (28) Operation timed out after 300570 milliseconds with 0 out of 0 bytes received

And console executes "./startAdminServer.sh -port 1234" doesn't show any logs.

I think my prrocedure is not correct for flashback, but what's wrong ?

supprt new mode of read write

Hi,
If i understood correctly, flashback support only 2 modes: record & play. This is great but IMO it misses very important mode. In Betamx, it was called READ_WRITE mode.
Lets say i have a scene file with 1 recorder http request-response from web service1. Now, lets say i want to record another request-response from another service2. If i set mode to record, it will overwrite the already recorded service1 request-response.
So what i want to suggest is to add another mode READ_WRITE . In this mode first we read the scene file if there is a match, play the recorded one, if there isn't go outside and record the new one. The new one will be appended to the file and will not override the previous recorder ones. Just like READ_WRITE in betamax.

IMO this feature is must, i'm using it all the time

flashback compilation failed, step 2 start flashback admin server

Encounter issue at step 2 start flashback admin server, compile and build failed.
Error Msg:
flashback/flashback-core-impl/src/main/java/com/linkedin/flashback/matchrules/CompositeMatchRule.java:44: error: illegal start of expression
flashback/flashback-core-impl/src/main/java/com/linkedin/flashback/matchrules/CompositeMatchRule.java:44: error: illegal start of expression
_matchRules.stream().forEach((rule) -> {
and so forth.
screen shot 2017-07-10 at 10 40 38 pm

Failed to run Flashback Server

Not able to run the flashback server on Mac. Below are the logs when I try to start server. Finally it gets stuck at last line of these logs.

MacBook-Pro:flashback SinghViresh$ ./startAdminServer.sh -port 1234
Starting a new Gradle Daemon for this build (subsequent builds will be faster).
Parallel execution with configuration on demand is an incubating feature.
Building project 'test-util'
Building project 'smartproxy'
Building project 'netty'
:flashback-core-impl:compileJava
:flashback-netty:processResources
:mitm:compileJava
:flashback-admin:processResources
:flashback-smartproxy:processResources UP-TO-DATE
:flashback-admin:processResources UP-TO-DATE
:flashback-netty:processResources UP-TO-DATE
:flashback-core-impl:compileJava UP-TO-DATE
:mitm:compileJava UP-TO-DATE
:mitm:processResources
:flashback-core-impl:processResources UP-TO-DATE
:mitm:processResources UP-TO-DATE
:flashback-core-impl:classes UP-TO-DATE
:mitm:classes UP-TO-DATE
:flashback-core-impl:jar
:mitm:jar UP-TO-DATE
:flashback-core-impl:jar UP-TO-DATE
:flashback-netty:compileJava UP-TO-DATE
:flashback-netty:classes UP-TO-DATE
:flashback-netty:jar UP-TO-DATE
:flashback-smartproxy:compileJava UP-TO-DATE
:flashback-smartproxy:classes UP-TO-DATE
:flashback-smartproxy:jar UP-TO-DATE
:flashback-admin:compileJava UP-TO-DATE
:flashback-admin:classes UP-TO-DATE
:flashback-admin:startAdminServer
Netty parseqThreadPoolSize: 9
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Netty threadPoolSize: 256
log4j:WARN No appenders could be found for logger (io.netty.util.internal.logging.InternalLoggerFactory).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
HttpServer running on port 1234. Press any key to stop server
> Building 95% > :flashback-admin:startAdminServer

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.