Skip to content

Instantly share code, notes, and snippets.

@sam43
Created January 23, 2022 12:34
Show Gist options
  • Save sam43/b6c5d3939c0d079b6d3af85d1710a88a to your computer and use it in GitHub Desktop.
Save sam43/b6c5d3939c0d079b6d3af85d1710a88a to your computer and use it in GitHub Desktop.
CURL interceptor class to log the CURL command from an API request
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideRetrofit(client: OkHttpClient): Retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
@Provides
@Singleton
fun provideOkHttpClient(
@Named("HeaderInterceptor") headerInterceptor: Interceptor,
@Named("CurlInterceptor") curlInterceptor: Interceptor,
cache: Cache,
loggerInterceptor: HttpLoggingInterceptor
): OkHttpClient {
val okHttpClientBuilder = OkHttpClient().newBuilder()
okHttpClientBuilder.connectTimeout(Constants.CONNECTION_TIMEOUT.toLong(), TimeUnit.SECONDS)
okHttpClientBuilder.readTimeout(Constants.READ_TIMEOUT.toLong(), TimeUnit.SECONDS)
okHttpClientBuilder.writeTimeout(Constants.WRITE_TIMEOUT.toLong(), TimeUnit.SECONDS)
okHttpClientBuilder.cache(cache)
okHttpClientBuilder.addInterceptor(headerInterceptor)
if (DEBUG) {
okHttpClientBuilder.addInterceptor(loggerInterceptor)
okHttpClientBuilder.addInterceptor(curlInterceptor)
}
return okHttpClientBuilder.build()
}
@Provides
@Singleton
fun provideUnsplashApi(retrofit: Retrofit): UnsplashApi =
retrofit.create(UnsplashApi::class.java)
@Provides
@Singleton
fun provideCache(@ApplicationContext context: Context): Cache {
val httpCacheDirectory = File(context.cacheDir.absolutePath, "HttpCache")
return Cache(httpCacheDirectory, Constants.CACHE_SIZE_BYTES)
}
@Provides
@Singleton
@Named("HeaderInterceptor")
fun provideHeaderInterceptor(): Interceptor = Interceptor {
val requestBuilder = it.request().newBuilder()
it.proceed(requestBuilder
.addHeader("Accept-Version", "v1")
.addHeader("Authorization", "Client-ID ${BuildConfig.UNSPLASH_ACCESS_KEY}")
.build())
}
@Provides
@Singleton
fun provideLoggerInterceptor(): HttpLoggingInterceptor = HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
@Provides
@Singleton
@Named("CurlInterceptor")
fun provideCurlInterceptor(logger: HttpLoggingInterceptor.Logger): Interceptor = Interceptor { chain ->
val uft8 = Charset.forName("UTF-8")
val request: Request = chain.request()
var compressed = false
val curlCmdBuilder = StringBuilder()
curlCmdBuilder.append("curl ")
curlCmdBuilder.append("-X ")
curlCmdBuilder.append(request.method)
val headers = request.headers
var i = 0
val count = headers.size
while (i < count) {
val name = headers.name(i)
val value = headers.value(i)
if ("Accept-Encoding".equals(name, ignoreCase = true) && "gzip".equals(
value,
ignoreCase = true
)
) {
compressed = true
}
curlCmdBuilder.append(" -H ")
curlCmdBuilder.append("\"")
curlCmdBuilder.append(name)
curlCmdBuilder.append(": ")
curlCmdBuilder.append(value)
curlCmdBuilder.append("\"")
i++
}
val requestBody = request.body
if (requestBody != null) {
val buffer = Buffer()
requestBody.writeTo(buffer)
var charset = uft8
val contentType = requestBody.contentType()
if (contentType != null) {
charset = contentType.charset(uft8)
}
// try to keep to a single line and use a subshell to preserve any line breaks
curlCmdBuilder.append(" --data $'")
curlCmdBuilder.append(buffer.readString(charset!!).replace("\n", "\\n"))
curlCmdBuilder.append("'")
}
curlCmdBuilder.append(if (compressed) " --compressed " else " ")
curlCmdBuilder.append("\"")
curlCmdBuilder.append(request.url)
curlCmdBuilder.append("\"")
logger.log("╭--- cURL (" + request.url + ")")
logger.log(curlCmdBuilder.toString())
logger.log("╰--- (copy and paste the above line to a terminal)")
chain.proceed(request)
}
@Provides
@Singleton
fun provideLogger() = HttpLoggingInterceptor.Logger.DEFAULT
@Provides
@Singleton
fun provideDataBase(@ApplicationContext context: Context) : AppDB =
Room.databaseBuilder(context, AppDB::class.java, Constants.DATABASE_NAME).allowMainThreadQueries().build()
@Provides
@Singleton
fun providePhotosDao(db: AppDB): PhotosDao = db.photosDao()
@Provides
@Singleton
fun provideGson(): Gson = Gson()
}
public class CurlLoggingInterceptor implements Interceptor {
private static final Charset UTF8 = Charset.forName("UTF-8");
private final Logger logger;
private String curlOptions;
public CurlLoggingInterceptor() {
this(Logger.DEFAULT);
}
public CurlLoggingInterceptor(Logger logger) {
this.logger = logger;
}
/** Set any additional curl command options (see 'curl --help'). */
public void setCurlOptions(String curlOptions) {
this.curlOptions = curlOptions;
}
@NotNull
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
boolean compressed = false;
StringBuilder curlCmdBuilder = new StringBuilder();
curlCmdBuilder.append("curl ");
if (curlOptions != null) {
curlCmdBuilder.append(curlOptions);
}
curlCmdBuilder.append("-X ");
curlCmdBuilder.append(request.method());
Headers headers = request.headers();
for (int i = 0, count = headers.size(); i < count; i++) {
String name = headers.name(i);
String value = headers.value(i);
if ("Accept-Encoding".equalsIgnoreCase(name) && "gzip".equalsIgnoreCase(value)) {
compressed = true;
}
curlCmdBuilder.append(" -H ");
curlCmdBuilder.append("\"");
curlCmdBuilder.append(name);
curlCmdBuilder.append(": ");
curlCmdBuilder.append(value);
curlCmdBuilder.append("\"");
}
RequestBody requestBody = request.body();
if (requestBody != null) {
Buffer buffer = new Buffer();
requestBody.writeTo(buffer);
Charset charset = UTF8;
MediaType contentType = requestBody.contentType();
if (contentType != null) {
charset = contentType.charset(UTF8);
}
// try to keep to a single line and use a subshell to preserve any line breaks
curlCmdBuilder.append(" --data $'");
curlCmdBuilder.append(buffer.readString(charset).replace("\n", "\\n"));
curlCmdBuilder.append("'");
}
curlCmdBuilder.append(((compressed) ? " --compressed " : " "));
curlCmdBuilder.append("\"");
curlCmdBuilder.append(request.url());
curlCmdBuilder.append("\"");
logger.log("╭--- cURL (" + request.url() + ")");
logger.log(curlCmdBuilder.toString());
logger.log("╰--- (copy and paste the above line to a terminal)");
return chain.proceed(request);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment