├── .gitignore
├── HttpClientLib-demo
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── naver
│ │ └── httpclienttest
│ │ ├── DefaultTimeout.java
│ │ ├── DemoApplication.java
│ │ ├── HttpClientConfigFragment.java
│ │ ├── HttpMethod.java
│ │ ├── HttpService.java
│ │ ├── LogFragment.java
│ │ ├── MainActivity.java
│ │ ├── MainFragment.java
│ │ └── data
│ │ └── Post.java
│ └── res
│ ├── drawable-v24
│ └── ic_launcher_foreground.xml
│ ├── drawable
│ └── ic_launcher_background.xml
│ ├── layout
│ ├── activity_main.xml
│ ├── fragment_httpclient_config.xml
│ ├── fragment_log.xml
│ └── fragment_main.xml
│ ├── mipmap-anydpi-v26
│ ├── ic_launcher.xml
│ └── ic_launcher_round.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-mdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ └── values
│ ├── colors.xml
│ ├── strings.xml
│ └── styles.xml
├── HttpClientLib
├── .gitignore
├── build.gradle
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── naver
│ │ └── httpclientlib
│ │ ├── CallBack.java
│ │ ├── CallTask.java
│ │ ├── Converter.java
│ │ ├── GsonConverter.java
│ │ ├── GsonConverterFactory.java
│ │ ├── HttpClient.java
│ │ ├── HttpInvocationHandler.java
│ │ ├── HttpMethod.java
│ │ ├── Interceptor.java
│ │ ├── InterceptorChain.java
│ │ ├── ParamManager.java
│ │ ├── RealCallTask.java
│ │ ├── Request.java
│ │ ├── RequestFactory.java
│ │ ├── RequestMethod.java
│ │ ├── Response.java
│ │ ├── Utils.java
│ │ └── annotation
│ │ ├── Default.java
│ │ ├── DynamicURL.java
│ │ ├── Field.java
│ │ ├── FieldMap.java
│ │ ├── FormUrlEncoded.java
│ │ ├── Header.java
│ │ ├── HeaderMap.java
│ │ ├── Headers.java
│ │ ├── PathParam.java
│ │ ├── Queries.java
│ │ ├── Query.java
│ │ ├── QueryMap.java
│ │ ├── RequestBody.java
│ │ ├── RequestMapping.java
│ │ └── URL.java
│ └── test
│ └── java
│ └── com
│ └── naver
│ └── httpclientlib
│ ├── AsyncTest.java
│ ├── HttpClientTest.java
│ ├── InterceptorTest.java
│ ├── InvalidServiceTest.java
│ ├── ParamManagerTest.java
│ ├── ResponseTest.java
│ ├── ValidServiceTest.java
│ ├── mock
│ ├── Address.java
│ ├── Comment.java
│ ├── Company.java
│ ├── Geo.java
│ ├── Post.java
│ ├── SkipPost.java
│ └── User.java
│ └── mockInterface
│ ├── InvalidHttpService.java
│ └── ValidHttpService.java
├── README.md
├── build.gradle
├── gradle.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 | /app/
10 | /HttpClientLib/local.properties
11 | /HttpClientLib/deploy.settings
12 |
--------------------------------------------------------------------------------
/HttpClientLib-demo/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/HttpClientLib-demo/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 | buildToolsVersion "29.0.0"
6 | defaultConfig {
7 | applicationId "com.naver.httpclienttest"
8 | minSdkVersion 16
9 | targetSdkVersion 28
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | compileOptions {
20 | sourceCompatibility JavaVersion.VERSION_1_7
21 | targetCompatibility JavaVersion.VERSION_1_7
22 | }
23 | }
24 |
25 | dependencies {
26 | implementation fileTree(dir: 'libs', include: ['*.jar'])
27 | implementation 'androidx.appcompat:appcompat:1.0.2'
28 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
29 | implementation project(":HttpClientLib")
30 | implementation 'com.naver.ers:ErrorReportingSdk:0.1.3'
31 | }
32 |
--------------------------------------------------------------------------------
/HttpClientLib-demo/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/java/com/naver/httpclienttest/DefaultTimeout.java:
--------------------------------------------------------------------------------
1 | package com.naver.httpclienttest;
2 |
3 | class DefaultTimeout {
4 | private DefaultTimeout(){}
5 |
6 | static final long CALL_TIMEOUT = 0L;
7 | static final long CONNECT_TIMEOUT = 10000L;
8 | static final long READ_TIMEOUT = 10000L;
9 | static final long WRITE_TIMEOUT = 10000L;
10 | }
11 |
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/java/com/naver/httpclienttest/DemoApplication.java:
--------------------------------------------------------------------------------
1 | package com.naver.httpclienttest;
2 |
3 | import android.app.Application;
4 |
5 | import com.naver.ers.CustomData;
6 | import com.naver.ers.Reporter;
7 |
8 | import java.util.Date;
9 |
10 | public class DemoApplication extends Application {
11 | @Override
12 | public void onCreate() {
13 | super.onCreate();
14 |
15 | CustomData.Builder customDataBuilder = new CustomData.Builder()
16 | .putData("App Start time", new Date().toString());
17 | Reporter.setCustomData(customDataBuilder.build());
18 | Reporter.register(this);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/java/com/naver/httpclienttest/HttpClientConfigFragment.java:
--------------------------------------------------------------------------------
1 | package com.naver.httpclienttest;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import android.os.Bundle;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.Button;
10 | import android.widget.EditText;
11 |
12 | import androidx.annotation.NonNull;
13 | import androidx.annotation.Nullable;
14 | import androidx.fragment.app.Fragment;
15 | import androidx.fragment.app.FragmentManager;
16 | import androidx.fragment.app.FragmentTransaction;
17 |
18 | import com.naver.httpclientsdk.R;
19 |
20 | import static com.naver.httpclienttest.DefaultTimeout.*;
21 |
22 | public class HttpClientConfigFragment extends Fragment {
23 |
24 | private EditText baseUrlText;
25 | private EditText callTimeoutText;
26 | private EditText connectTimeoutText;
27 | private EditText readTimeoutText;
28 | private EditText writeTimeoutText;
29 | private SharedPreferences sharedPreferences;
30 |
31 | @Override
32 | public void onCreate(@Nullable Bundle savedInstanceState) {
33 | super.onCreate(savedInstanceState);
34 |
35 | sharedPreferences = getContext().getSharedPreferences(
36 | getContext().getResources().getString(R.string.config_file),
37 | Context.MODE_PRIVATE
38 | );
39 | }
40 |
41 | @Nullable
42 | @Override
43 | public View onCreateView(@NonNull LayoutInflater inflater,
44 | @Nullable ViewGroup container,
45 | @Nullable Bundle savedInstanceState) {
46 | View view = inflater.inflate(R.layout.fragment_httpclient_config, container, false);
47 |
48 | baseUrlText = view.findViewById(R.id.base_url);
49 | baseUrlText.setText(sharedPreferences.getString(
50 | getContext().getResources().getString(R.string.base_url),
51 | getContext().getResources().getString(R.string.default_URL)));
52 |
53 | callTimeoutText = view.findViewById(R.id.call_time);
54 | callTimeoutText.setText(String.valueOf(
55 | sharedPreferences.getLong(
56 | getContext().getResources().getString(R.string.call_timeout),
57 | CALL_TIMEOUT)));
58 |
59 | connectTimeoutText = view.findViewById(R.id.connect_time);
60 | connectTimeoutText.setText(String.valueOf(
61 | sharedPreferences.getLong(
62 | getContext().getResources().getString(R.string.connect_timeout),
63 | CONNECT_TIMEOUT)));
64 |
65 | readTimeoutText = view.findViewById(R.id.read_time);
66 | readTimeoutText.setText(String.valueOf(
67 | sharedPreferences.getLong(
68 | getContext().getResources().getString(R.string.read_timeout),
69 | READ_TIMEOUT)));
70 |
71 | writeTimeoutText = view.findViewById(R.id.write_time);
72 | writeTimeoutText.setText(String.valueOf(
73 | sharedPreferences.getLong(
74 | getContext().getResources().getString(R.string.write_timeout),
75 | WRITE_TIMEOUT)));
76 |
77 | Button saveBtn = view.findViewById(R.id.save_btn);
78 | saveBtn.setOnClickListener(new Button.OnClickListener(){
79 | @Override
80 | public void onClick(View v) {
81 | SharedPreferences.Editor editor = sharedPreferences.edit();
82 |
83 | CharSequence baseUrlSeq = baseUrlText.getText();
84 | if(baseUrlSeq != null) {
85 | editor.putString(
86 | getContext().getResources().getString(R.string.base_url),
87 | baseUrlSeq.toString());
88 | }
89 |
90 | CharSequence callSeq = callTimeoutText.getText();
91 | if(callSeq.length() > 0) {
92 | editor.putLong(
93 | getContext().getResources().getString(R.string.call_timeout),
94 | Long.valueOf(callSeq.toString()));
95 | }
96 |
97 | CharSequence connectSeq = connectTimeoutText.getText();
98 | if(connectSeq.length() > 0) {
99 | editor.putLong(
100 | getContext().getResources().getString(R.string.connect_timeout),
101 | Long.valueOf(connectSeq.toString())); }
102 |
103 | CharSequence readSeq = readTimeoutText.getText();
104 | if(readSeq.length() > 0) {
105 | editor.putLong(
106 | getContext().getResources().getString(R.string.read_timeout),
107 | Long.valueOf(readSeq.toString()));
108 | }
109 |
110 | CharSequence writeSeq = writeTimeoutText.getText();
111 | if(writeSeq.length() > 0) {
112 | editor.putLong(
113 | getContext().getResources().getString(R.string.write_timeout),
114 | Long.valueOf(writeSeq.toString()));
115 | }
116 |
117 | editor.apply();
118 |
119 | replaceFragment(new MainFragment());
120 | }
121 | });
122 |
123 | Button crashBtn = view.findViewById(R.id.crash_btn);
124 | crashBtn.setOnClickListener(new Button.OnClickListener() {
125 | @Override
126 | public void onClick(View v) {
127 | throw new AssertionError("Crash!");
128 | }
129 | });
130 | return view;
131 | }
132 |
133 | private void replaceFragment(Fragment newFragment) {
134 | FragmentManager fragmentManager = getFragmentManager();
135 | FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
136 |
137 | fragmentTransaction.replace(R.id.fragment_container, newFragment);
138 | fragmentTransaction.commit();
139 | }
140 |
141 | }
142 |
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/java/com/naver/httpclienttest/HttpMethod.java:
--------------------------------------------------------------------------------
1 | package com.naver.httpclienttest;
2 |
3 | enum HttpMethod {
4 | GET("GET"),
5 | POST("POST"),
6 | PUT("PUT"),
7 | DELETE("DELETE"),
8 | HEAD("HEAD");
9 |
10 | String name;
11 |
12 | HttpMethod(String method) {
13 | name = method;
14 | }
15 |
16 | String getName() {
17 | return name;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/java/com/naver/httpclienttest/HttpService.java:
--------------------------------------------------------------------------------
1 | package com.naver.httpclienttest;
2 |
3 | import com.naver.httpclientlib.CallTask;
4 | import com.naver.httpclientlib.RequestMethod;
5 | import com.naver.httpclientlib.annotation.DynamicURL;
6 | import com.naver.httpclientlib.annotation.HeaderMap;
7 | import com.naver.httpclientlib.annotation.PathParam;
8 | import com.naver.httpclientlib.annotation.QueryMap;
9 | import com.naver.httpclientlib.annotation.RequestBody;
10 | import com.naver.httpclientlib.annotation.RequestMapping;
11 | import com.naver.httpclientlib.annotation.URL;
12 | import com.naver.httpclienttest.data.Post;
13 |
14 | import java.util.List;
15 | import java.util.Map;
16 |
17 | public interface HttpService {
18 | @RequestMapping(value="/posts", method= RequestMethod.GET)
19 | CallTask> getPostsByQuery(@HeaderMap Map headerMap, @QueryMap Map queryMap);
20 |
21 | @RequestMapping(value="/posts/{id}", method= RequestMethod.GET)
22 | CallTask getPostsByPathParam(@HeaderMap Map headerMap, @PathParam("id") Integer id);
23 |
24 | @RequestMapping(value="/posts", method= RequestMethod.POST)
25 | CallTask postPosts(@HeaderMap Map headerMap, @RequestBody Post post);
26 |
27 | @RequestMapping(value="/posts/{id}", method= RequestMethod.PUT)
28 | CallTask putPostsById(@HeaderMap Map headerMap, @PathParam("id") Integer id, @RequestBody Post newPost);
29 |
30 | @RequestMapping(value="/posts/{id}", method= RequestMethod.DELETE)
31 | CallTask deletePostById(@HeaderMap Map headerMap, @PathParam("id") Integer id);
32 |
33 | @RequestMapping(value="/posts", method= RequestMethod.HEAD)
34 | CallTask getPostsForHeadMethod(@HeaderMap Map headerMap);
35 |
36 | @DynamicURL(method= RequestMethod.GET)
37 | CallTask> getPostsByDynamicURLWithQuery(@HeaderMap Map headerMap, @URL String url, @QueryMap Map queryMap);
38 |
39 | @DynamicURL(method= RequestMethod.POST)
40 | CallTask postPostsByDynamicURL(@HeaderMap Map headerMap, @URL String url, @RequestBody Post post);
41 |
42 | @DynamicURL(method= RequestMethod.PUT)
43 | CallTask putPostsByDynamicURL(@HeaderMap Map headerMap, @URL String url, @RequestBody Post newPost);
44 |
45 | @DynamicURL(method= RequestMethod.DELETE)
46 | CallTask deletePostByDynamicURL(@HeaderMap Map headerMap, @URL String url);
47 |
48 | @DynamicURL(method= RequestMethod.HEAD)
49 | CallTask getPostsForHeadMethodByDynamicURL(@HeaderMap Map headerMap, @URL String url);
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/java/com/naver/httpclienttest/LogFragment.java:
--------------------------------------------------------------------------------
1 | package com.naver.httpclienttest;
2 |
3 | import android.os.Bundle;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.Button;
8 | import android.widget.EditText;
9 |
10 | import androidx.annotation.NonNull;
11 | import androidx.annotation.Nullable;
12 | import androidx.fragment.app.Fragment;
13 | import androidx.fragment.app.FragmentManager;
14 | import androidx.fragment.app.FragmentTransaction;
15 |
16 | import com.naver.httpclientlib.Request;
17 | import com.naver.httpclientlib.Response;
18 | import com.naver.httpclientsdk.R;
19 |
20 | public class LogFragment extends Fragment {
21 | private Response> response;
22 | private String errorMessage;
23 |
24 | private EditText requestLog;
25 | private EditText responseLog;
26 |
27 | private boolean isAsync;
28 |
29 | LogFragment(Response> response, boolean isAsync) {
30 | this.response = response;
31 | this.isAsync = isAsync;
32 | }
33 |
34 | LogFragment(String errorMessage, boolean isAsync) {
35 | this.errorMessage = errorMessage;
36 | this.isAsync = isAsync;
37 | }
38 |
39 | @Nullable
40 | @Override
41 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
42 | View view = inflater.inflate(R.layout.fragment_log, container, false);
43 |
44 | Button backBtn = view.findViewById(R.id.back_btn);
45 | backBtn.setOnClickListener(new Button.OnClickListener(){
46 | @Override
47 | public void onClick(View v) {
48 | replaceFragment(new MainFragment());
49 | }
50 | });
51 |
52 | requestLog = view.findViewById(R.id.request_log);
53 | loggingRequest();
54 |
55 | responseLog = view.findViewById(R.id.response_log);
56 | loggingResponse();
57 |
58 | return view;
59 | }
60 |
61 | private void loggingRequest() {
62 | requestLog.append("Call : " + (!isAsync ? "Synchronous Call\n" : "Asynchronous Call\n"));
63 | if(response != null) {
64 | Request request = response.request();
65 | requestLog.append("URL : " + request.getUrl() + "\n");
66 | requestLog.append("Method : " + request.getMethod() + "\n");
67 | requestLog.append("Headers : " + request.getHeaders() + "\n");
68 | if(request.getContentType() != null) {
69 | requestLog.append("Content-type : " + request.getContentType() + "\n");
70 | }
71 | requestLog.setSelection(0);
72 | }
73 | }
74 |
75 | private void loggingResponse() {
76 | if(response != null) {
77 | responseLog.append("code : " + response.code() + "\n");
78 | responseLog.append("date : " + response.header("Date") + "\n");
79 | responseLog.append("content-type : " + response.header("Content-Type") + "\n");
80 | if(response.body() != null) {
81 | responseLog.append("body : " + response.body().toString());
82 | }
83 | responseLog.setSelection(0);
84 | } else {
85 | responseLog.append("Failed to Response : " + errorMessage);
86 | }
87 | }
88 |
89 | private void replaceFragment(Fragment newFragment) {
90 | FragmentManager fragmentManager = getFragmentManager();
91 | FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
92 |
93 | fragmentTransaction.replace(R.id.fragment_container, newFragment);
94 | fragmentTransaction.commit();
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/java/com/naver/httpclienttest/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.naver.httpclienttest;
2 |
3 | import androidx.appcompat.app.AppCompatActivity;
4 | import androidx.fragment.app.FragmentManager;
5 | import androidx.fragment.app.FragmentTransaction;
6 |
7 | import android.os.Bundle;
8 |
9 | import com.naver.httpclientsdk.R;
10 |
11 | public class MainActivity extends AppCompatActivity {
12 |
13 | @Override
14 | protected void onCreate(Bundle savedInstanceState) {
15 | super.onCreate(savedInstanceState);
16 | setContentView(R.layout.activity_main);
17 |
18 | FragmentManager fragmentManager = getSupportFragmentManager();
19 | FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
20 |
21 | MainFragment mainFragment = new MainFragment();
22 | fragmentTransaction.add(R.id.fragment_container, mainFragment);
23 | fragmentTransaction.commit();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/java/com/naver/httpclienttest/MainFragment.java:
--------------------------------------------------------------------------------
1 | package com.naver.httpclienttest;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import android.os.Bundle;
6 | import android.util.Log;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.widget.AdapterView;
11 | import android.widget.ArrayAdapter;
12 | import android.widget.Button;
13 | import android.widget.EditText;
14 | import android.widget.LinearLayout;
15 | import android.widget.RadioButton;
16 | import android.widget.RadioGroup;
17 | import android.widget.Spinner;
18 |
19 | import androidx.annotation.NonNull;
20 | import androidx.annotation.Nullable;
21 | import androidx.fragment.app.Fragment;
22 | import androidx.fragment.app.FragmentManager;
23 | import androidx.fragment.app.FragmentTransaction;
24 |
25 | import com.naver.httpclientlib.CallBack;
26 | import com.naver.httpclientlib.CallTask;
27 | import com.naver.httpclientlib.HttpClient;
28 | import com.naver.httpclientlib.Response;
29 | import com.naver.httpclientsdk.R;
30 | import com.naver.httpclienttest.data.Post;
31 |
32 | import java.io.IOException;
33 | import java.util.HashMap;
34 | import java.util.Map;
35 | import java.util.concurrent.TimeUnit;
36 |
37 | import static com.naver.httpclienttest.DefaultTimeout.*;
38 |
39 | public class MainFragment extends Fragment {
40 | private static final String LOG_TAG = MainFragment.class.getSimpleName();
41 |
42 | private LinearLayout pathParamLayout;
43 | private LinearLayout queryLayout;
44 | private LinearLayout requestBodyLayout;
45 |
46 | private Spinner httpMethodSpinner;
47 | private EditText headerNameText1;
48 | private EditText headerNameText2;
49 | private EditText headerValueText1;
50 | private EditText headerValueText2;
51 | private EditText pathparamText;
52 | private EditText queryNameText1;
53 | private EditText queryNameText2;
54 | private EditText queryValueText1;
55 | private EditText queryValueText2;
56 | private EditText userIdText;
57 | private EditText idText;
58 | private EditText titleText;
59 | private EditText bodyText;
60 | private EditText dynamicUrlText;
61 | private RadioGroup syncAsyncGroup;
62 | private RadioButton syncBtn;
63 | private RadioButton asyncBtn;
64 | private Button dynamicCallBtn;
65 |
66 | private HttpService httpService;
67 |
68 | @Override
69 | public void onCreate(@Nullable Bundle savedInstanceState) {
70 | super.onCreate(savedInstanceState);
71 | SharedPreferences sharedPreferences = getContext().getSharedPreferences(
72 | getContext().getResources().getString(R.string.config_file),
73 | Context.MODE_PRIVATE);
74 |
75 | String baseUrl = sharedPreferences.getString(
76 | getContext().getResources().getString(R.string.base_url),
77 | getContext().getResources().getString(R.string.default_URL));
78 | long callTimeout = sharedPreferences.getLong(
79 | getContext().getResources().getString(R.string.call_timeout),
80 | CALL_TIMEOUT);
81 | long connectTimeout = sharedPreferences.getLong(
82 | getContext().getResources().getString(R.string.connect_timeout),
83 | CONNECT_TIMEOUT);
84 | long readTimeout = sharedPreferences.getLong(
85 | getContext().getResources().getString(R.string.read_timeout),
86 | READ_TIMEOUT);
87 | long writeTimeout = sharedPreferences.getLong(
88 | getContext().getResources().getString(R.string.write_timeout),
89 | WRITE_TIMEOUT);
90 |
91 | HttpClient httpClient = new HttpClient.Builder()
92 | .baseUrl(baseUrl)
93 | .callTimeout(callTimeout, TimeUnit.MILLISECONDS)
94 | .connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
95 | .readTimeout(readTimeout, TimeUnit.MILLISECONDS)
96 | .writeTimeout(writeTimeout, TimeUnit.MILLISECONDS)
97 | .build();
98 |
99 | httpService = httpClient.create(HttpService.class);
100 | }
101 |
102 | @Nullable
103 | @Override
104 | public View onCreateView(@NonNull LayoutInflater inflater,
105 | @Nullable ViewGroup container,
106 | @Nullable Bundle savedInstanceState) {
107 | View view = inflater.inflate(R.layout.fragment_main, container, false);
108 |
109 | pathParamLayout = view.findViewById(R.id.path_param_layout);
110 | queryLayout = view.findViewById(R.id.query_layout);
111 | requestBodyLayout = view.findViewById(R.id.request_body_layout);
112 |
113 | Button baseUrlBtn = view.findViewById(R.id.base_url_btn);
114 | baseUrlBtn.setOnClickListener(new Button.OnClickListener() {
115 | @Override
116 | public void onClick(View v) {
117 | replaceFragment(new HttpClientConfigFragment());
118 | }
119 | });
120 |
121 | headerNameText1 = view.findViewById(R.id.header_name1);
122 | headerValueText1 = view.findViewById(R.id.header_value1);
123 | headerNameText2 = view.findViewById(R.id.header_name2);
124 | headerValueText2 = view.findViewById(R.id.header_value2);
125 |
126 | httpMethodSpinner = view.findViewById(R.id.http_method_spinner);
127 | httpMethodSpinner.setAdapter(makeAdapter(R.array.http_method_list));
128 | httpMethodSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
129 | @Override
130 | public void onItemSelected(AdapterView> parent, View view, int position, long id) {
131 | HttpMethod method = HttpMethod.valueOf(httpMethodSpinner.getItemAtPosition(position).toString());
132 | switch (method) {
133 | case GET:
134 | case HEAD:
135 | pathParamLayout.setVisibility(View.VISIBLE);
136 | queryLayout.setVisibility(View.VISIBLE);
137 | requestBodyLayout.setVisibility(View.GONE);
138 | break;
139 | case POST:
140 | pathParamLayout.setVisibility(View.GONE);
141 | queryLayout.setVisibility(View.GONE);
142 | requestBodyLayout.setVisibility(View.VISIBLE);
143 | break;
144 | case PUT:
145 | pathParamLayout.setVisibility(View.VISIBLE);
146 | queryLayout.setVisibility(View.GONE);
147 | requestBodyLayout.setVisibility(View.VISIBLE);
148 | break;
149 | case DELETE:
150 | pathParamLayout.setVisibility(View.VISIBLE);
151 | queryLayout.setVisibility(View.GONE);
152 | requestBodyLayout.setVisibility(View.GONE);
153 | break;
154 | }
155 | }
156 |
157 | @Override
158 | public void onNothingSelected(AdapterView> parent) {
159 | Log.e(LOG_TAG, "Nothing Selected on Method Spinner.");
160 | }
161 | });
162 |
163 | pathparamText = view.findViewById(R.id.path_param_text);
164 |
165 | queryNameText1 = view.findViewById(R.id.query_name1);
166 | queryValueText1 = view.findViewById(R.id.query_value1);
167 |
168 | queryNameText2 = view.findViewById(R.id.query_name2);
169 | queryValueText2 = view.findViewById(R.id.query_value2);
170 |
171 | userIdText = view.findViewById(R.id.user_id_text);
172 | idText = view.findViewById(R.id.id_text);
173 | titleText = view.findViewById(R.id.title_text);
174 | bodyText = view.findViewById(R.id.body_text);
175 |
176 | dynamicUrlText = view.findViewById(R.id.dynamic_url);
177 |
178 | syncAsyncGroup = view.findViewById(R.id.sync_async_group);
179 | syncBtn = view.findViewById(R.id.sync_btn);
180 | asyncBtn = view.findViewById(R.id.async_btn);
181 | syncBtn.toggle();
182 |
183 | Button callBtn = view.findViewById(R.id.call_btn);
184 | callBtn.setOnClickListener(callBtnListener);
185 | dynamicCallBtn = view.findViewById(R.id.dynamic_call_btn);
186 | dynamicCallBtn.setOnClickListener(callBtnListener);
187 |
188 | return view;
189 | }
190 |
191 | /**
192 | * Spinner Adapter
193 | */
194 | private ArrayAdapter makeAdapter(int items) {
195 | ArrayAdapter adapter = ArrayAdapter.createFromResource(
196 | getContext(),
197 | items,
198 | android.R.layout.simple_spinner_item);
199 | adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
200 |
201 | return adapter;
202 | }
203 |
204 | /**
205 | * ClickListener for Call/DynamicCall Button
206 | */
207 | private final Button.OnClickListener callBtnListener = new View.OnClickListener() {
208 | @Override
209 | public void onClick(View v) {
210 | boolean isDynamicUrl = (v.getId() == dynamicCallBtn.getId());
211 | String dynamicUrl = dynamicUrlText.getText().toString();
212 | String pathParam;
213 |
214 | Map headerMap = new HashMap<>();
215 | String headerName1 = headerNameText1.getText().toString();
216 | String headerValue1 = headerValueText1.getText().toString();
217 | String headerName2 = headerNameText2.getText().toString();
218 | String headerValue2 = headerValueText2.getText().toString();
219 |
220 | if (!headerName1.isEmpty() && !headerValue1.isEmpty()) {
221 | headerMap.put(headerName1, headerValue1);
222 | }
223 | if (!headerName2.isEmpty() && !headerValue2.isEmpty()) {
224 | headerMap.put(headerName2, headerValue2);
225 | }
226 |
227 | HttpMethod method = HttpMethod.valueOf(String.valueOf(httpMethodSpinner.getSelectedItem()));
228 | try {
229 | switch (method) {
230 | case GET:
231 | pathParam = pathparamText.getText().toString();
232 | if (!isDynamicUrl && !pathParam.isEmpty()) {
233 | executeCallTask(httpService.getPostsByPathParam(headerMap, Integer.parseInt(pathParam)));
234 | } else {
235 | Map queryMap = new HashMap<>();
236 | String queryName1 = queryNameText1.getText().toString();
237 | String queryValue1 = queryValueText1.getText().toString();
238 | String queryName2 = queryNameText2.getText().toString();
239 | String queryValue2 = queryValueText2.getText().toString();
240 |
241 | if (!queryName1.isEmpty() && !queryValue1.isEmpty()) {
242 | queryMap.put(queryName1, queryValue1);
243 | }
244 | if (!queryName2.isEmpty() && !queryValue2.isEmpty()) {
245 | queryMap.put(queryName2, queryValue2);
246 | }
247 |
248 | if (isDynamicUrl) {
249 | executeCallTask(httpService.getPostsByDynamicURLWithQuery(headerMap, dynamicUrl, queryMap));
250 | } else {
251 | executeCallTask(httpService.getPostsByQuery(headerMap, queryMap));
252 | }
253 | }
254 | break;
255 | case POST:
256 | if (isDynamicUrl) {
257 | executeCallTask(httpService.postPostsByDynamicURL(headerMap, dynamicUrl, parseRequestBody()));
258 | } else {
259 | executeCallTask(httpService.postPosts(headerMap, parseRequestBody()));
260 | }
261 | break;
262 | case PUT:
263 | pathParam = pathparamText.getText().toString();
264 | if (!isDynamicUrl && !pathParam.isEmpty()) {
265 | executeCallTask(httpService.putPostsById(headerMap, Integer.parseInt(pathParam), parseRequestBody()));
266 | } else if (isDynamicUrl) {
267 | executeCallTask(httpService.putPostsByDynamicURL(headerMap, dynamicUrl, parseRequestBody()));
268 | } else {
269 | replaceFragment(new LogFragment("You should use PathParam with PUT Method.", false));
270 | }
271 | break;
272 | case DELETE:
273 | pathParam = pathparamText.getText().toString();
274 | if (!isDynamicUrl && !pathParam.isEmpty()) {
275 | executeCallTask(httpService.deletePostById(headerMap, Integer.parseInt(pathParam)));
276 | } else if (isDynamicUrl) {
277 | executeCallTask(httpService.deletePostByDynamicURL(headerMap, dynamicUrl));
278 | } else {
279 | replaceFragment(new LogFragment("You should use PathParam with DELETE Method.", false));
280 | }
281 | break;
282 | case HEAD:
283 | if (isDynamicUrl) {
284 | executeCallTask(httpService.getPostsForHeadMethodByDynamicURL(headerMap, dynamicUrl));
285 | } else {
286 | executeCallTask(httpService.getPostsForHeadMethod(headerMap));
287 | }
288 | break;
289 | }
290 | } catch(Exception e) { // defensive code for unchecked Exception in SDK.
291 | replaceFragment(new LogFragment(e.getMessage(), false));
292 | }
293 | }
294 |
295 | /**
296 | * execute callTask synchronously or asynchronously.
297 | * and then, start LogFragment with Response received for result
298 | */
299 | private void executeCallTask(final CallTask callTask) {
300 | if (syncAsyncGroup.getCheckedRadioButtonId() == syncBtn.getId()) {
301 | new Thread(new Runnable() {
302 | @Override
303 | public void run() {
304 | try {
305 | Response response = callTask.execute();
306 |
307 | replaceFragment(new LogFragment(response, false));
308 | } catch (IOException e) {
309 | replaceFragment(new LogFragment(e.getMessage(), false));
310 | }
311 | }
312 | }).start();
313 | } else if (syncAsyncGroup.getCheckedRadioButtonId() == asyncBtn.getId()) {
314 | callTask.enqueue(new CallBack() {
315 | @Override
316 | public void onResponse(Response response) throws IOException {
317 | LogFragment logFragment = response.isSuccessful() ?
318 | new LogFragment(response, true)
319 | : new LogFragment("Response Fail", true);
320 | replaceFragment(logFragment);
321 | }
322 |
323 | @Override
324 | public void onFailure(IOException e) {
325 | replaceFragment(new LogFragment(e.getMessage(), true));
326 | }
327 | });
328 | }
329 | }
330 |
331 | private Post parseRequestBody() {
332 | CharSequence userIdSeq = userIdText.getText();
333 | int userId = userIdSeq.length() > 0 ? Integer.parseInt(userIdSeq.toString()) : 0;
334 | CharSequence idSeq = idText.getText();
335 | int id = idSeq.length() > 0 ? Integer.parseInt(idSeq.toString()) : 0;
336 | String title = titleText.getText().toString();
337 | String body = bodyText.getText().toString();
338 |
339 | return new Post(userId, id, title, body);
340 | }
341 | };
342 |
343 | private void replaceFragment(Fragment newFragment) {
344 | FragmentManager fragmentManager = getFragmentManager();
345 | FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
346 |
347 | fragmentTransaction.replace(R.id.fragment_container, newFragment);
348 | fragmentTransaction.commit();
349 | }
350 | }
351 |
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/java/com/naver/httpclienttest/data/Post.java:
--------------------------------------------------------------------------------
1 | package com.naver.httpclienttest.data;
2 |
3 | public class Post {
4 | int userId;
5 | int id;
6 | String title;
7 | String body;
8 |
9 | public Post(int userId, int id, String title, String body) {
10 | this.userId = userId;
11 | this.id = id;
12 | this.title = title;
13 | this.body = body;
14 | }
15 |
16 | public int getUserId() {
17 | return userId;
18 | }
19 |
20 | public void setUserId(int userId) {
21 | this.userId = userId;
22 | }
23 |
24 | public int getId() {
25 | return id;
26 | }
27 |
28 | public void setId(int id) {
29 | this.id = id;
30 | }
31 |
32 | public String getTitle() {
33 | return title;
34 | }
35 |
36 | public void setTitle(String title) {
37 | this.title = title;
38 | }
39 |
40 | public String getBody() {
41 | return body;
42 | }
43 |
44 | public void setBody(String body) {
45 | this.body = body;
46 | }
47 |
48 | @Override
49 | public String toString() {
50 | return "Post{" +
51 | "userId=" + userId +
52 | ", id=" + id +
53 | ", title='" + title + '\'' +
54 | ", body='" + body + '\'' +
55 | '}';
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/res/layout/fragment_httpclient_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
16 |
17 |
20 |
21 |
28 |
29 |
36 |
37 |
44 |
45 |
46 |
54 |
55 |
62 |
63 |
68 |
69 |
79 |
80 |
90 |
91 |
92 |
97 |
98 |
108 |
109 |
119 |
120 |
121 |
126 |
127 |
137 |
138 |
148 |
149 |
150 |
155 |
156 |
166 |
167 |
177 |
178 |
179 |
180 |
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/res/layout/fragment_log.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
19 |
20 |
27 |
28 |
29 |
40 |
41 |
50 |
51 |
62 |
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KimReady/Http-Client-Lib/e871933bd589af98a7c2a8d5c44ab63e3837345c/HttpClientLib-demo/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KimReady/Http-Client-Lib/e871933bd589af98a7c2a8d5c44ab63e3837345c/HttpClientLib-demo/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KimReady/Http-Client-Lib/e871933bd589af98a7c2a8d5c44ab63e3837345c/HttpClientLib-demo/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KimReady/Http-Client-Lib/e871933bd589af98a7c2a8d5c44ab63e3837345c/HttpClientLib-demo/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KimReady/Http-Client-Lib/e871933bd589af98a7c2a8d5c44ab63e3837345c/HttpClientLib-demo/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KimReady/Http-Client-Lib/e871933bd589af98a7c2a8d5c44ab63e3837345c/HttpClientLib-demo/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KimReady/Http-Client-Lib/e871933bd589af98a7c2a8d5c44ab63e3837345c/HttpClientLib-demo/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KimReady/Http-Client-Lib/e871933bd589af98a7c2a8d5c44ab63e3837345c/HttpClientLib-demo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KimReady/Http-Client-Lib/e871933bd589af98a7c2a8d5c44ab63e3837345c/HttpClientLib-demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KimReady/Http-Client-Lib/e871933bd589af98a7c2a8d5c44ab63e3837345c/HttpClientLib-demo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | HttpClientLib-demo
3 |
4 |
5 | Base URL and Timeout
6 | Setting
7 | Http Method
8 | Path Parameter
9 | Query
10 | Header
11 | Add
12 | Request Body
13 | Sync/Async
14 | Sync
15 | Async
16 | CALL
17 | Dynamic URL
18 | Dynamic CALL
19 | name
20 | value
21 |
22 | User ID
23 | ID
24 | Title
25 | Body
26 |
27 |
28 | - GET
29 | - POST
30 | - PUT
31 | - DELETE
32 | - HEAD
33 |
34 |
35 |
36 |
37 | - SECONDS
38 | - MILLI_SECONDS
39 | - NANO_SECONDS
40 |
41 |
42 | Save
43 | Crash
44 | Base URL
45 | http://jsonplaceholder.typicode.com
46 | ex) http://jsonplaceholder.typicode.com
47 | config_file
48 |
49 | Timeout(Milli Seconds)
50 | Call Timeout
51 | Connect Timeout
52 | Read Timeout
53 | Write Timeout
54 | ex) 10000
55 |
56 |
57 | Request Log
58 | Response Log
59 | Back
60 |
61 |
--------------------------------------------------------------------------------
/HttpClientLib-demo/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/HttpClientLib/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/HttpClientLib/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'java-library'
2 | apply plugin: 'java'
3 | apply plugin: 'maven-publish'
4 | apply plugin: 'com.github.dcendents.android-maven'
5 | apply plugin: 'com.jfrog.bintray'
6 |
7 | def artifact = new Properties()
8 | artifact.load(new FileInputStream(file("deploy.settings")))
9 |
10 | version = artifact.version
11 | group = artifact.groupId
12 |
13 | install {
14 | repositories.mavenInstaller {
15 | pom.project {
16 | packaging 'jar'
17 | groupId artifact.groupId
18 | artifactId = artifact.id
19 | version artifact.version
20 | name artifact.id
21 | description 'A library for Http communication.'
22 | url artifact.siteUrl
23 |
24 | licenses {
25 | license {
26 | name 'The Apache Software License, Version 2.0'
27 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
28 | distribution 'repo'
29 | }
30 | }
31 | developers {
32 | developer {
33 | name 'junbi-kim'
34 | }
35 | }
36 | }
37 | }
38 | }
39 |
40 | Properties properties = new Properties()
41 | properties.load(new FileInputStream(file('local.properties')))
42 | bintray {
43 | user = properties.getProperty('BINTRAY_USERNAME')
44 | key = properties.getProperty('BINTRAY_API_KEY')
45 |
46 | configurations = ['archives']
47 | pkg {
48 | repo = properties.getProperty('BINTRAY_REPONAME')
49 | name = properties.getProperty('BINTRAY_PKG')
50 | userOrg = properties.getProperty('BINTRAY_ORG')
51 | vcsUrl = 'https://oss.navercorp.com/da-intern-2019-1h/http-client-sdk.git'
52 | licenses = ["Apache-2.0"]
53 |
54 | version {
55 | name = artifact.version
56 | released = new Date()
57 | }
58 | }
59 | publish = true
60 |
61 | }
62 |
63 | sourceCompatibility = "7"
64 | targetCompatibility = "7"
65 |
66 | dependencies {
67 | testImplementation 'junit:junit:4.12'
68 |
69 | implementation 'com.squareup.okhttp3:okhttp:3.12.1'
70 | api 'com.google.code.gson:gson:2.8.5'
71 | }
72 |
73 | task sourcesJar(type: Jar) {
74 | from sourceSets.main.output.classesDirs
75 | classifier = 'sources'
76 | }
77 |
78 | artifacts {
79 | archives sourcesJar
80 | }
--------------------------------------------------------------------------------
/HttpClientLib/src/main/java/com/naver/httpclientlib/CallBack.java:
--------------------------------------------------------------------------------
1 | package com.naver.httpclientlib;
2 |
3 | import java.io.IOException;
4 |
5 | public interface CallBack {
6 | void onResponse(Response response) throws IOException;
7 | void onFailure(IOException e);
8 | }
9 |
--------------------------------------------------------------------------------
/HttpClientLib/src/main/java/com/naver/httpclientlib/CallTask.java:
--------------------------------------------------------------------------------
1 | package com.naver.httpclientlib;
2 |
3 | import java.io.IOException;
4 |
5 | public interface CallTask {
6 |
7 | Response execute() throws IOException;
8 |
9 | void enqueue(CallBack callback);
10 |
11 | void cancel();
12 |
13 | boolean isCanceled();
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/HttpClientLib/src/main/java/com/naver/httpclientlib/Converter.java:
--------------------------------------------------------------------------------
1 | package com.naver.httpclientlib;
2 |
3 | import java.io.IOException;
4 |
5 | import okhttp3.MediaType;
6 |
7 | interface Converter {
8 |
9 | okhttp3.RequestBody convertRequestBody(MediaType contentType, RequestType requestObj) throws IOException;
10 |
11 | ReturnType convertResponseBody(okhttp3.ResponseBody responseBody) throws IOException;
12 | }
--------------------------------------------------------------------------------
/HttpClientLib/src/main/java/com/naver/httpclientlib/GsonConverter.java:
--------------------------------------------------------------------------------
1 | package com.naver.httpclientlib;
2 |
3 | import com.google.gson.Gson;
4 | import com.google.gson.TypeAdapter;
5 | import com.google.gson.reflect.TypeToken;
6 | import com.google.gson.stream.JsonReader;
7 | import com.google.gson.stream.JsonWriter;
8 |
9 | import java.io.BufferedReader;
10 | import java.io.IOException;
11 | import java.io.OutputStreamWriter;
12 | import java.io.Writer;
13 |
14 | import okhttp3.MediaType;
15 | import okhttp3.RequestBody;
16 | import okio.Buffer;
17 |
18 | final class GsonConverter implements Converter {
19 | private TypeAdapter requestAdapter;
20 | private TypeAdapter responseAdapter;
21 | private Buffer buffer;
22 |
23 | private Gson gson;
24 | private boolean isString;
25 |
26 | GsonConverter(Gson gson) {
27 | this.gson = gson;
28 | this.buffer = new Buffer();
29 | this.isString = true;
30 | }
31 |
32 | GsonConverter(Gson gson, TypeAdapter adapter) {
33 | this.responseAdapter = adapter;
34 | this.gson = gson;
35 | this.buffer = new Buffer();
36 | this.requestAdapter = gson.getAdapter(new TypeToken(){});
37 | this.isString = false;
38 | }
39 |
40 | @Override
41 | public okhttp3.RequestBody convertRequestBody(MediaType contentType, RequestType requestObj) throws IOException {
42 | if(contentType == null) {
43 | contentType = MediaType.get("application/json; charset=UTF-8");
44 | }
45 | Writer writer = new OutputStreamWriter(buffer.outputStream());
46 | JsonWriter jsonWriter = gson.newJsonWriter(writer);
47 | requestAdapter.write(jsonWriter, requestObj);
48 | jsonWriter.close();
49 | return RequestBody.create(contentType, buffer.readByteString());
50 | }
51 |
52 | @Override
53 | public ReturnType convertResponseBody(okhttp3.ResponseBody responseBody) throws IOException {
54 | if(isString) {
55 | StringBuilder builder = new StringBuilder();
56 | BufferedReader br = new BufferedReader(responseBody.charStream());
57 | String line;
58 | while((line = br.readLine()) != null) {
59 | builder.append(line);
60 | }
61 | return (ReturnType) builder.toString();
62 | }
63 |
64 | JsonReader jsonReader = gson.newJsonReader(responseBody.charStream());
65 | try {
66 | return responseAdapter.read(jsonReader);
67 | } finally {
68 | jsonReader.close();
69 | responseBody.close();
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/HttpClientLib/src/main/java/com/naver/httpclientlib/GsonConverterFactory.java:
--------------------------------------------------------------------------------
1 | package com.naver.httpclientlib;
2 |
3 | import com.google.gson.Gson;
4 | import com.google.gson.GsonBuilder;
5 | import com.google.gson.TypeAdapter;
6 | import com.google.gson.reflect.TypeToken;
7 |
8 | import java.lang.reflect.Type;
9 |
10 | final class GsonConverterFactory {
11 | private final Gson gson;
12 |
13 | public static GsonConverterFactory create(GsonBuilder gsonBuilder) {
14 | return new GsonConverterFactory(gsonBuilder);
15 | }
16 |
17 | private GsonConverterFactory(GsonBuilder gsonBuilder) {
18 | this.gson = gsonBuilder.create();
19 | }
20 |
21 | Converter, ?> converter(Type type) {
22 | if(type == String.class) {
23 | return new GsonConverter<>(gson);
24 | } else {
25 | TypeAdapter> typeAdapter = gson.getAdapter(TypeToken.get(type));
26 | return new GsonConverter<>(gson, typeAdapter);
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/HttpClientLib/src/main/java/com/naver/httpclientlib/HttpClient.java:
--------------------------------------------------------------------------------
1 | package com.naver.httpclientlib;
2 |
3 | import com.google.gson.GsonBuilder;
4 |
5 | import java.io.IOException;
6 | import java.lang.reflect.InvocationHandler;
7 | import java.lang.reflect.Proxy;
8 | import java.net.URI;
9 | import java.net.URL;
10 | import java.util.Arrays;
11 | import java.util.HashMap;
12 | import java.util.Map;
13 | import java.util.concurrent.ExecutorService;
14 | import java.util.concurrent.Executors;
15 | import java.util.concurrent.TimeUnit;
16 |
17 | import okhttp3.ConnectionSpec;
18 | import okhttp3.HttpUrl;
19 | import okhttp3.OkHttpClient;
20 | import okhttp3.Response;
21 |
22 | import static com.naver.httpclientlib.Utils.checkNotNull;
23 |
24 | public final class HttpClient {
25 | private Map cachedInvocation;
26 |
27 | private final HttpUrl baseUrl;
28 | private final okhttp3.Call.Factory callFactory;
29 | private final ExecutorService executorService;
30 | private final GsonBuilder gsonBuilder;
31 |
32 | public HttpClient(Builder builder) {
33 | this.cachedInvocation = new HashMap<>();
34 | this.baseUrl = builder.baseUrl;
35 | this.callFactory = builder.callFactory;
36 | this.executorService = builder.executorService;
37 | this.gsonBuilder = builder.gsonBuilder;
38 | }
39 |
40 | public T create(Class service) {
41 | if (!service.isInterface()) {
42 | throw new IllegalArgumentException("declarations must be interface.");
43 | }
44 |
45 | return (T) Proxy.newProxyInstance(service.getClassLoader()
46 | , new Class>[]{service}
47 | , getInvocationHandler(service));
48 | }
49 |
50 | HttpUrl getBaseUrl() {
51 | return baseUrl;
52 | }
53 |
54 | okhttp3.Call.Factory getCallFactory() {
55 | return callFactory;
56 | }
57 |
58 | ExecutorService getExecutorService() {
59 | return executorService;
60 | }
61 |
62 | GsonBuilder gsonBuilder() {
63 | return gsonBuilder;
64 | }
65 |
66 | private InvocationHandler getInvocationHandler(Class service) {
67 | if(cachedInvocation.containsKey(service.getName())) {
68 | return cachedInvocation.get(service.getName());
69 | }
70 | InvocationHandler invocationHandler = new HttpInvocationHandler(this);
71 | cachedInvocation.put(service.getName(), invocationHandler);
72 | return invocationHandler;
73 | }
74 |
75 | /**
76 | * Builder
77 | */
78 | public static final class Builder {
79 | private HttpUrl baseUrl;
80 | private okhttp3.Call.Factory callFactory;
81 | private GsonBuilder gsonBuilder;
82 | private ExecutorService executorService;
83 | private okhttp3.Interceptor applicationInterceptor;
84 | private okhttp3.Interceptor networkInterceptor;
85 | private Timeout callTimeout;
86 | private Timeout connectTimeout;
87 | private Timeout readTimeout;
88 | private Timeout writeTimeout;
89 |
90 | public Builder() {
91 | this.callTimeout = new Timeout(Utils.DEFAULT_CALL_TIMEOUT, TimeUnit.MILLISECONDS);
92 | this.connectTimeout = new Timeout(Utils.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
93 | this.readTimeout = new Timeout(Utils.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
94 | this.writeTimeout = new Timeout(Utils.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
95 | }
96 |
97 | public Builder baseUrl(String baseUrl) {
98 | return setBaseUrl(baseUrl);
99 | }
100 |
101 | public Builder baseUrl(URL baseUrl) {
102 | return setBaseUrl(baseUrl);
103 | }
104 |
105 | public Builder baseUrl(URI baseUrl) {
106 | return setBaseUrl(baseUrl);
107 | }
108 |
109 | Builder setBaseUrl(Object baseUrl) {
110 | checkNotNull(baseUrl, "base URL is null");
111 | this.baseUrl = Utils.getHttpUrl(baseUrl);
112 | return this;
113 | }
114 |
115 | Builder callFactory(okhttp3.Call.Factory callFactory) {
116 | this.callFactory = callFactory;
117 | return this;
118 | }
119 |
120 | public Builder gsonBuilder(GsonBuilder gsonBuilder) {
121 | this.gsonBuilder = gsonBuilder;
122 | return this;
123 | }
124 |
125 | public Builder callTimeout(long timeout, TimeUnit unit) {
126 | callTimeout.time = timeout;
127 | callTimeout.timeUnit = unit;
128 | return this;
129 | }
130 |
131 | public Builder connectTimeout(long timeout, TimeUnit unit) {
132 | connectTimeout.time = timeout;
133 | connectTimeout.timeUnit = unit;
134 | return this;
135 | }
136 |
137 | public Builder readTimeout(long timeout, TimeUnit unit) {
138 | readTimeout.time = timeout;
139 | readTimeout.timeUnit = unit;
140 | return this;
141 | }
142 |
143 | public Builder writeTimeout(long timeout, TimeUnit unit) {
144 | writeTimeout.time = timeout;
145 | writeTimeout.timeUnit = unit;
146 | return this;
147 | }
148 |
149 | public Builder executorService(ExecutorService executorService) {
150 | this.executorService = executorService;
151 | return this;
152 | }
153 |
154 | public Builder applicationInterceptor(final Interceptor interceptor) {
155 | this.applicationInterceptor = new okhttp3.Interceptor(){
156 | @Override
157 | public Response intercept(okhttp3.Interceptor.Chain chain) throws IOException {
158 | InterceptorChain interceptorChain = new InterceptorChain(chain);
159 | return interceptor.intercept(interceptorChain).getRawResponse();
160 | }
161 | };
162 | return this;
163 | }
164 |
165 | public Builder networkInterceptor(final Interceptor interceptor) {
166 | this.networkInterceptor = new okhttp3.Interceptor(){
167 | @Override
168 | public Response intercept(okhttp3.Interceptor.Chain chain) throws IOException {
169 | InterceptorChain interceptorChain = new InterceptorChain(chain);
170 | return interceptor.intercept(interceptorChain).getRawResponse();
171 | }
172 | };
173 | return this;
174 | }
175 |
176 | public HttpClient build() {
177 | if(gsonBuilder == null) {
178 | gsonBuilder = new GsonBuilder();
179 | }
180 |
181 | if (callFactory == null) {
182 | // TLS -> CLEARTEXT 순으로 연결 시도하도록 설정
183 | OkHttpClient.Builder callBuilder = new OkHttpClient.Builder()
184 | .connectionSpecs(Arrays.asList(ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS, ConnectionSpec.CLEARTEXT))
185 | .callTimeout(callTimeout.time, callTimeout.timeUnit)
186 | .connectTimeout(connectTimeout.time, connectTimeout.timeUnit)
187 | .readTimeout(readTimeout.time, readTimeout.timeUnit)
188 | .writeTimeout(writeTimeout.time, writeTimeout.timeUnit);
189 |
190 | if (applicationInterceptor != null) {
191 | callBuilder.addInterceptor(applicationInterceptor);
192 | }
193 | if (networkInterceptor != null) {
194 | callBuilder.addNetworkInterceptor(networkInterceptor);
195 | }
196 |
197 | this.callFactory = callBuilder.build();
198 | }
199 |
200 | if (executorService == null) {
201 | this.executorService = Executors.newCachedThreadPool();
202 | }
203 |
204 | return new HttpClient(this);
205 | }
206 |
207 | private class Timeout {
208 | long time;
209 | TimeUnit timeUnit;
210 |
211 | Timeout(long time, TimeUnit unit) {
212 | this.time = time;
213 | this.timeUnit = unit;
214 | }
215 | }
216 | }
217 | }
218 |
--------------------------------------------------------------------------------
/HttpClientLib/src/main/java/com/naver/httpclientlib/HttpInvocationHandler.java:
--------------------------------------------------------------------------------
1 | package com.naver.httpclientlib;
2 |
3 | import java.lang.reflect.InvocationHandler;
4 | import java.lang.reflect.Method;
5 |
6 | class HttpInvocationHandler implements InvocationHandler {
7 | private final HttpClient httpClient;
8 |
9 | HttpInvocationHandler(HttpClient httpClient) {
10 | this.httpClient = httpClient;
11 | }
12 |
13 | @Override
14 | public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
15 |
16 | // Object class에서 정의된 메소드일 경우 해당 메소드 그대로 실행
17 | if(method.getDeclaringClass() == Object.class) {
18 | return method.invoke(this, objects);
19 | }
20 |
21 | Utils.checkSupportedMethod(method);
22 |
23 | return HttpMethod.of(httpClient, method, objects).invoke();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/HttpClientLib/src/main/java/com/naver/httpclientlib/HttpMethod.java:
--------------------------------------------------------------------------------
1 | package com.naver.httpclientlib;
2 |
3 | import java.lang.reflect.Method;
4 | import java.lang.reflect.ParameterizedType;
5 | import java.lang.reflect.Type;
6 | import java.util.concurrent.ExecutorService;
7 |
8 | import static com.naver.httpclientlib.RequestMethod.HEAD;
9 |
10 | class HttpMethod {
11 | static HttpMethod of(HttpClient httpClient, Method method, Object[] args) {
12 | RequestFactory requestFactory = new RequestFactory(httpClient.getBaseUrl(), method, args).initialize();
13 |
14 | Type returnType = method.getGenericReturnType();
15 | Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);
16 | if(requestFactory.httpMethod() == HEAD && !Void.class.equals(responseType)) {
17 | throw new IllegalArgumentException("HEAD method must use Void as response type.");
18 | }
19 |
20 | return new HttpMethod<>(httpClient, requestFactory, responseType);
21 | }
22 |
23 | private final okhttp3.Call.Factory callFactory;
24 | private final RequestFactory requestFactory;
25 | private final Converter converter;
26 | private final ExecutorService executorService;
27 |
28 | private HttpMethod(HttpClient httpClient, RequestFactory requestFactory, Type responseType) {
29 | this.callFactory = httpClient.getCallFactory();
30 | this.requestFactory = requestFactory;
31 | this.converter = requestFactory.httpMethod() != RequestMethod.HEAD ?
32 | GsonConverterFactory.create(httpClient.gsonBuilder()).converter(responseType)
33 | : null;
34 | this.executorService = httpClient.getExecutorService();
35 | }
36 |
37 | CallTask invoke() {
38 | return new RealCallTask<>(requestFactory, callFactory, converter, executorService);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/HttpClientLib/src/main/java/com/naver/httpclientlib/Interceptor.java:
--------------------------------------------------------------------------------
1 | package com.naver.httpclientlib;
2 |
3 | import java.io.IOException;
4 |
5 | public interface Interceptor {
6 | Response intercept(InterceptorChain chain) throws IOException;
7 | }
8 |
--------------------------------------------------------------------------------
/HttpClientLib/src/main/java/com/naver/httpclientlib/InterceptorChain.java:
--------------------------------------------------------------------------------
1 | package com.naver.httpclientlib;
2 |
3 | import java.io.IOException;
4 | import java.net.URL;
5 | import java.util.concurrent.TimeUnit;
6 |
7 | public final class InterceptorChain {
8 | okhttp3.Interceptor.Chain chain;
9 | okhttp3.Request.Builder requestBuilder;
10 | okhttp3.Request request;
11 | okhttp3.Response rawResponse;
12 |
13 | InterceptorChain(okhttp3.Interceptor.Chain chain) {
14 | this.chain = chain;
15 | this.requestBuilder = chain.request().newBuilder();
16 | }
17 |
18 | public void setRequestUrl(String url) {
19 | requestBuilder.url(url);
20 | }
21 |
22 | public void setRequestUrl(URL url) {
23 | requestBuilder.url(url);
24 | }
25 |
26 | public void setRequestHeader(String name, String value) {
27 | requestBuilder.header(name, value);
28 | }
29 |
30 | public void addRequestHeader(String name, String value) {
31 | requestBuilder.addHeader(name, value);
32 | }
33 |
34 | public void removeRequestHeader(String name) {
35 | requestBuilder.removeHeader(name);
36 | }
37 |
38 | public String readRequestHeader(String name) {
39 | return chain.request().header(name);
40 | }
41 |
42 | public int connectTimeoutMills() {
43 | return chain.connectTimeoutMillis();
44 | }
45 |
46 | public void setConnectTimeout(int timeout, TimeUnit unit) {
47 | chain = chain.withConnectTimeout(timeout, unit);
48 | }
49 |
50 | public int readTimeoutMills() {
51 | return chain.readTimeoutMillis();
52 | }
53 |
54 | public void setReadTimeout(int timeout, TimeUnit unit) {
55 | chain = chain.withReadTimeout(timeout, unit);
56 | }
57 |
58 | public int writeTimeoutMills() {
59 | return chain.writeTimeoutMillis();
60 | }
61 |
62 | public void setWriteTimeout(int timeout, TimeUnit unit) {
63 | chain = chain.withWriteTimeout(timeout, unit);
64 | }
65 |
66 | public Response proceed() throws IOException {
67 | request = requestBuilder.build();
68 | rawResponse = chain.proceed(request);
69 | return new Response<>(rawResponse, null);
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/HttpClientLib/src/main/java/com/naver/httpclientlib/ParamManager.java:
--------------------------------------------------------------------------------
1 | package com.naver.httpclientlib;
2 |
3 | import com.naver.httpclientlib.annotation.Field;
4 | import com.naver.httpclientlib.annotation.FieldMap;
5 | import com.naver.httpclientlib.annotation.Header;
6 | import com.naver.httpclientlib.annotation.HeaderMap;
7 | import com.naver.httpclientlib.annotation.Queries;
8 | import com.naver.httpclientlib.annotation.Query;
9 | import com.naver.httpclientlib.annotation.QueryMap;
10 |
11 | import java.lang.annotation.Annotation;
12 | import java.util.ArrayList;
13 | import java.util.HashMap;
14 | import java.util.List;
15 | import java.util.Map;
16 | import java.util.Set;
17 | import java.util.regex.Matcher;
18 |
19 | import okhttp3.HttpUrl;
20 |
21 | class ParamManager {
22 | private Map headerParams;
23 | private Map pathParams;
24 | private Map queryParams;
25 | private Map> queriesParam;
26 | private Object rawRequestBody;
27 |
28 | ParamManager() {
29 | headerParams = new HashMap<>();
30 | pathParams = new HashMap<>();
31 | queryParams = new HashMap<>();
32 | queriesParam = new HashMap<>();
33 | }
34 |
35 | String replacePathParameters(String relUrl) {
36 | Matcher matcher = Utils.matchPathUrl(relUrl);
37 | while (matcher.find()) {
38 | String pathParam = matcher.group();
39 | String paramName = pathParam.substring(1, pathParam.length()-1);
40 | if(!pathParams.containsKey(paramName)) {
41 | throw new IllegalArgumentException("there is no matching parameter to '" + paramName + "'.");
42 | }
43 | relUrl = relUrl.replace(pathParam, pathParams.get(paramName));
44 | pathParams.remove(paramName);
45 | }
46 | if(!pathParams.isEmpty()) {
47 | throw new IllegalArgumentException("there are too many actual path parameters.");
48 | }
49 | return relUrl;
50 | }
51 |
52 | HttpUrl addQueryToUrl(okhttp3.HttpUrl.Builder urlBuilder) {
53 | Set queryNames = queriesParam.keySet();
54 | for(String name : queryNames) {
55 | List queries = queriesParam.get(name);
56 | for(String query : queries) {
57 | urlBuilder.addEncodedQueryParameter(name, query);
58 | }
59 | }
60 |
61 | queryNames = queryParams.keySet();
62 | for(String name : queryNames) {
63 | urlBuilder.addEncodedQueryParameter(name, String.valueOf(queryParams.get(name)));
64 | }
65 |
66 | return urlBuilder.build();
67 | }
68 |
69 | void addHeadersToRequest(okhttp3.Request.Builder requestBuilder) {
70 | Set keySet = headerParams.keySet();
71 | for(String key : keySet) {
72 | requestBuilder.addHeader(key, headerParams.get(key));
73 | }
74 | }
75 |
76 | void addHeader(Annotation annotation, Object arg) {
77 | if(annotation instanceof Header) {
78 | addHeaderParam(((Header) annotation).value(), arg);
79 | } else if(annotation instanceof HeaderMap) {
80 | if (!(arg instanceof Map)) {
81 | throw new IllegalArgumentException("The type of the '@HeaderMap' must be a Map");
82 | }
83 | Map headerMap = (Map) arg;
84 | Set keySet = headerMap.keySet();
85 | for (String key : keySet) {
86 | addHeaderParam(key, headerMap.get(key));
87 | }
88 | }
89 | }
90 |
91 | void addQuery(Annotation annotation, Object arg) {
92 | if(annotation instanceof Query) {
93 | Query queryAnnotation = (Query) annotation;
94 | String encodedQuery = Utils.encodeQuery(arg, queryAnnotation.encodeType(), queryAnnotation.encoded());
95 | addQueryParam(queryAnnotation.value(), encodedQuery);
96 | } else if(annotation instanceof Queries) {
97 | Queries queriesAnnotation = (Queries) annotation;
98 | List