1
1
/*
2
- * Copyright 2017-2020 original authors
2
+ * Copyright 2017-2023 original authors
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
20
20
import io .micronaut .core .annotation .Experimental ;
21
21
import io .micronaut .core .annotation .Internal ;
22
22
import io .micronaut .core .annotation .Nullable ;
23
- import io .micronaut .core .io .socket .SocketUtils ;
24
23
import io .micronaut .gcp .function .http .HttpFunction ;
25
24
import io .micronaut .http .server .HttpServerConfiguration ;
26
25
import io .micronaut .http .server .exceptions .HttpServerException ;
27
26
import io .micronaut .http .server .exceptions .ServerStartupException ;
28
27
import io .micronaut .runtime .ApplicationConfiguration ;
29
28
import io .micronaut .runtime .server .EmbeddedServer ;
29
+ import jakarta .inject .Singleton ;
30
30
import jakarta .servlet .MultipartConfigElement ;
31
31
import jakarta .servlet .ServletException ;
32
32
import jakarta .servlet .http .HttpServlet ;
41
41
import org .slf4j .Logger ;
42
42
import org .slf4j .LoggerFactory ;
43
43
44
- import jakarta .inject .Singleton ;
45
-
46
44
import java .io .IOException ;
47
- import java .net .*;
45
+ import java .net .MalformedURLException ;
46
+ import java .net .URI ;
47
+ import java .net .URISyntaxException ;
48
+ import java .net .URL ;
48
49
import java .util .Arrays ;
49
50
import java .util .HashSet ;
50
51
import java .util .Optional ;
63
64
@ Internal
64
65
@ Experimental
65
66
public class InvokerHttpServer implements EmbeddedServer {
67
+
66
68
private static final Logger LOGGER = LoggerFactory .getLogger (InvokerHttpServer .class );
67
69
private final ApplicationContext applicationContext ;
68
- private final HttpServerConfiguration serverConfiguration ;
69
- private final boolean randomPort ;
70
+ private final ServerPort serverPort ;
70
71
private final AtomicBoolean running = new AtomicBoolean (false );
71
- private int port ;
72
72
private Server server ;
73
+ private HttpFunction httpFunction ;
73
74
74
- public InvokerHttpServer (ApplicationContext applicationContext , HttpServerConfiguration serverConfiguration ) {
75
+ public InvokerHttpServer (ApplicationContext applicationContext , HttpServerConfiguration httpServerConfiguration ) {
75
76
this .applicationContext = applicationContext ;
76
- this .serverConfiguration = serverConfiguration ;
77
- Optional <Integer > port = serverConfiguration .getPort ();
78
- if (port .isPresent ()) {
79
- this .port = port .get ();
80
- if (this .port == -1 ) {
81
- this .port = SocketUtils .findAvailableTcpPort ();
82
- this .randomPort = true ;
77
+ this .serverPort = createServerPort (httpServerConfiguration );
78
+ }
79
+
80
+ private ServerPort createServerPort (HttpServerConfiguration httpServerConfiguration ) {
81
+ Optional <Integer > portOpt = httpServerConfiguration .getPort ();
82
+ if (portOpt .isPresent ()) {
83
+ Integer port = portOpt .get ();
84
+ if (port == -1 ) {
85
+ return new ServerPort (true , 0 );
86
+
83
87
} else {
84
- this . randomPort = false ;
88
+ return new ServerPort ( false , port ) ;
85
89
}
86
90
} else {
87
91
if (applicationContext .getEnvironment ().getActiveNames ().contains (Environment .TEST )) {
88
- this .randomPort = true ;
89
- this .port = SocketUtils .findAvailableTcpPort ();
92
+ return new ServerPort (true , 0 );
90
93
} else {
91
- this .randomPort = false ;
92
- this .port = 8080 ;
94
+ return new ServerPort (false , 8080 );
93
95
}
94
96
}
95
97
}
96
98
97
99
@ Override
98
100
public EmbeddedServer start () {
99
101
if (running .compareAndSet (false , true )) {
100
- int retryCount = 0 ;
101
- while (retryCount <= 3 ) {
102
- try {
103
- this .server = new Server (port );
104
-
105
- ServletContextHandler servletContextHandler = new ServletContextHandler ();
106
- servletContextHandler .setContextPath ("/" );
107
- server .setHandler (NotFoundHandler .forServlet (servletContextHandler ));
102
+ int port = serverPort .port ();
103
+ try {
104
+ this .server = new Server (port );
105
+
106
+ ServletContextHandler servletContextHandler = new ServletContextHandler ();
107
+ servletContextHandler .setContextPath ("/" );
108
+ server .setHandler (NotFoundHandler .forServlet (servletContextHandler ));
109
+
110
+ httpFunction = new HttpFunction () {
111
+ @ Override
112
+ protected ApplicationContext buildApplicationContext (@ Nullable Object context ) {
113
+ ApplicationContext ctx = InvokerHttpServer .this .getApplicationContext ();
114
+ this .applicationContext = ctx ;
115
+ return ctx ;
116
+ }
117
+ };
118
+ ServletHolder servletHolder = getServletHolder ();
119
+ servletHolder .getRegistration ().setMultipartConfig (new MultipartConfigElement ("" ));
120
+ servletContextHandler .addServlet (servletHolder , "/*" );
108
121
109
- HttpFunction httpFunction = new HttpFunction () {
110
- @ Override
111
- protected ApplicationContext buildApplicationContext (@ Nullable Object context ) {
112
- ApplicationContext ctx = InvokerHttpServer .this .getApplicationContext ();
113
- this .applicationContext = ctx ;
114
- return ctx ;
115
- }
116
- };
117
- HttpServlet servlet = new HttpServlet () {
118
- @ Override
119
- protected void service (HttpServletRequest req , HttpServletResponse resp ) throws ServletException {
122
+ this .server .start ();
123
+ } catch (Exception e ) {
124
+ throw new ServerStartupException (e .getMessage (), e );
125
+ }
126
+ }
127
+ return this ;
128
+ }
120
129
121
- try {
122
- httpFunction .service (
123
- new HttpRequestImpl (req ),
124
- new HttpResponseImpl (resp )
125
- );
126
- } catch (Exception e ) {
127
- throw new ServletException (e );
128
- }
129
- }
130
- };
131
- ServletHolder servletHolder = new ServletHolder (servlet );
132
- servletHolder .getRegistration ().setMultipartConfig (new MultipartConfigElement ("" ));
133
- servletContextHandler .addServlet (servletHolder , "/*" );
130
+ private ServletHolder getServletHolder () {
131
+ HttpServlet servlet = new HttpServlet () {
134
132
135
- server .start ();
136
- logServerInfo ();
137
- break ;
138
- } catch (BindException e ) {
139
- if (randomPort ) {
140
- this .port = SocketUtils .findAvailableTcpPort ();
141
- retryCount ++;
142
- } else {
143
- throw new ServerStartupException (e .getMessage (), e );
144
- }
145
- } catch (Exception e ) {
146
- throw new ServerStartupException (
147
- "Error starting Google Cloud Function server: " + e .getMessage (),
148
- e
133
+ @ Override
134
+ protected void service (HttpServletRequest req , HttpServletResponse resp ) throws ServletException {
135
+ try {
136
+ httpFunction .service (
137
+ new HttpRequestImpl (req ),
138
+ new HttpResponseImpl (resp )
149
139
);
140
+ } catch (Exception e ) {
141
+ throw new ServletException (e );
150
142
}
151
143
}
152
-
153
- }
154
- return this ;
144
+ };
145
+ return new ServletHolder (servlet );
155
146
}
156
147
157
148
/**
@@ -167,6 +158,7 @@ protected Class<?> getFunctionClass() {
167
158
public EmbeddedServer stop () {
168
159
if (running .compareAndSet (true , false )) {
169
160
try {
161
+ httpFunction .close ();
170
162
applicationContext .close ();
171
163
server .stop ();
172
164
} catch (Exception e ) {
@@ -181,7 +173,7 @@ public EmbeddedServer stop() {
181
173
182
174
@ Override
183
175
public int getPort () {
184
- return port ;
176
+ return server . getURI (). getPort () ;
185
177
}
186
178
187
179
@ Override
@@ -220,29 +212,23 @@ public ApplicationContext getApplicationContext() {
220
212
221
213
@ Override
222
214
public ApplicationConfiguration getApplicationConfiguration () {
223
- return serverConfiguration . getApplicationConfiguration ( );
215
+ return applicationContext . getBean ( ApplicationConfiguration . class );
224
216
}
225
217
226
218
@ Override
227
219
public boolean isRunning () {
228
220
return running .get ();
229
221
}
230
222
231
- private void logServerInfo () {
232
- LOGGER .info ("Serving function..." );
233
- LOGGER .info ("Function: {}" , getFunctionClass ().getName ());
234
- LOGGER .info ("URL: http://localhost:{}/" , port );
235
- }
236
-
237
223
/**
238
224
* Wrapper that intercepts requests for {@code /favicon.ico} and {@code /robots.txt} and causes
239
225
* them to produce a 404 status. Otherwise they would be sent to the function code, like any
240
226
* other URL, meaning that someone testing their function by using a browser as an HTTP client
241
227
* can see two requests, one for {@code /favicon.ico} and one for {@code /} (or whatever).
242
228
*/
243
229
private static class NotFoundHandler extends HandlerWrapper {
244
- private static final Set < String > NOT_FOUND_PATHS =
245
- new HashSet <>(Arrays .asList ("/favicon.ico" , "/robots.txt" ));
230
+
231
+ private static final Set < String > NOT_FOUND_PATHS = new HashSet <>(Arrays .asList ("/favicon.ico" , "/robots.txt" ));
246
232
247
233
@ Override
248
234
public void handle (String target , Request baseRequest , HttpServletRequest request ,
@@ -260,4 +246,7 @@ static NotFoundHandler forServlet(ServletContextHandler servletHandler) {
260
246
return handler ;
261
247
}
262
248
}
249
+
250
+ private record ServerPort (boolean random , Integer port ) {
251
+ }
263
252
}
0 commit comments