210
288
// --------------------------------------------------------- Public Methods
214
* Send an action to the connector.
216
* @param actionCode Type of the action
217
* @param param Action parameter
220
public final void action(ActionCode actionCode, Object param) {
222
if (actionCode == ActionCode.COMMIT) {
224
if (response.isCommitted())
227
// Validate and write response headers
230
} catch (IOException e) {
237
} catch (IOException e) {
242
} else if (actionCode == ActionCode.CLIENT_FLUSH) {
244
if (!response.isCommitted()) {
245
// Validate and write response headers
248
} catch (IOException e) {
257
} catch (IOException e) {
262
} else if (actionCode == ActionCode.DISABLE_SWALLOW_INPUT) {
263
// TODO: Do not swallow request input but
264
// make sure we are closing the connection
267
} else if (actionCode == ActionCode.CLOSE) {
269
// End the processing of the current request, and stop any further
270
// transactions with the client
274
} catch (IOException e) {
279
} else if (actionCode == ActionCode.REQ_SSL_ATTRIBUTE ) {
281
if (!certificates.isNull()) {
282
ByteChunk certData = certificates.getByteChunk();
283
X509Certificate jsseCerts[] = null;
284
ByteArrayInputStream bais =
285
new ByteArrayInputStream(certData.getBytes(),
287
certData.getLength());
288
// Fill the elements.
290
CertificateFactory cf;
291
if (clientCertProvider == null) {
292
cf = CertificateFactory.getInstance("X.509");
294
cf = CertificateFactory.getInstance("X.509",
297
while(bais.available() > 0) {
298
X509Certificate cert = (X509Certificate)
299
cf.generateCertificate(bais);
300
if(jsseCerts == null) {
301
jsseCerts = new X509Certificate[1];
304
X509Certificate [] temp = new X509Certificate[jsseCerts.length+1];
305
System.arraycopy(jsseCerts,0,temp,0,jsseCerts.length);
306
temp[jsseCerts.length] = cert;
310
} catch (java.security.cert.CertificateException e) {
311
getLog().error(sm.getString("ajpprocessor.certs.fail"), e);
313
} catch (NoSuchProviderException e) {
314
getLog().error(sm.getString("ajpprocessor.certs.fail"), e);
317
request.setAttribute(SSLSupport.CERTIFICATE_KEY, jsseCerts);
320
} else if (actionCode == ActionCode.REQ_HOST_ATTRIBUTE) {
322
// Get remote host name using a DNS resolution
323
if (request.remoteHost().isNull()) {
325
request.remoteHost().setString(InetAddress.getByName
326
(request.remoteAddr().toString()).getHostName());
327
} catch (IOException iex) {
332
} else if (actionCode == ActionCode.REQ_LOCAL_ADDR_ATTRIBUTE) {
334
// Copy from local name for now, which should simply be an address
335
request.localAddr().setString(request.localName().toString());
337
} else if (actionCode == ActionCode.REQ_SET_BODY_REPLAY) {
339
// Set the given bytes as the content
340
ByteChunk bc = (ByteChunk) param;
341
int length = bc.getLength();
342
bodyBytes.setBytes(bc.getBytes(), bc.getStart(), length);
343
request.setContentLength(length);
348
} else if (actionCode == ActionCode.ASYNC_START) {
349
asyncStateMachine.asyncStart((AsyncContextCallback) param);
350
} else if (actionCode == ActionCode.ASYNC_DISPATCHED) {
351
asyncStateMachine.asyncDispatched();
352
} else if (actionCode == ActionCode.ASYNC_TIMEOUT) {
353
AtomicBoolean result = (AtomicBoolean) param;
354
result.set(asyncStateMachine.asyncTimeout());
355
} else if (actionCode == ActionCode.ASYNC_RUN) {
356
asyncStateMachine.asyncRun((Runnable) param);
357
} else if (actionCode == ActionCode.ASYNC_ERROR) {
358
asyncStateMachine.asyncError();
359
} else if (actionCode == ActionCode.ASYNC_IS_STARTED) {
360
((AtomicBoolean) param).set(asyncStateMachine.isAsyncStarted());
361
} else if (actionCode == ActionCode.ASYNC_IS_DISPATCHING) {
362
((AtomicBoolean) param).set(asyncStateMachine.isAsyncDispatching());
363
} else if (actionCode == ActionCode.ASYNC_IS_ASYNC) {
364
((AtomicBoolean) param).set(asyncStateMachine.isAsync());
365
} else if (actionCode == ActionCode.ASYNC_IS_TIMINGOUT) {
366
((AtomicBoolean) param).set(asyncStateMachine.isAsyncTimingOut());
368
actionInternal(actionCode, param);
372
// Methods called by action()
373
protected abstract void actionInternal(ActionCode actionCode, Object param);
374
protected abstract void flush(boolean tbd) throws IOException;
375
protected abstract void finish() throws IOException;
378
public void recycle() {
379
asyncStateMachine.recycle();
381
// Recycle Request object
389
certificates.recycle();
393
// ------------------------------------------------------ Connector Methods
397
* Set the associated adapter.
399
* @param adapter the new adapter
401
public void setAdapter(Adapter adapter) {
402
this.adapter = adapter;
407
* Get the associated adapter.
409
* @return the associated adapter
411
public Adapter getAdapter() {
416
// ------------------------------------------------------ Protected Methods
420
* After reading the request headers, we have to setup the request filters.
422
protected void prepareRequest() {
424
// Translate the HTTP method code to a String.
425
byte methodCode = requestHeaderMessage.getByte();
426
if (methodCode != Constants.SC_M_JK_STORED) {
427
String methodName = Constants.getMethodForCode(methodCode - 1);
428
request.method().setString(methodName);
431
requestHeaderMessage.getBytes(request.protocol());
432
requestHeaderMessage.getBytes(request.requestURI());
434
requestHeaderMessage.getBytes(request.remoteAddr());
435
requestHeaderMessage.getBytes(request.remoteHost());
436
requestHeaderMessage.getBytes(request.localName());
437
request.setLocalPort(requestHeaderMessage.getInt());
439
boolean isSSL = requestHeaderMessage.getByte() != 0;
441
request.scheme().setString("https");
445
MimeHeaders headers = request.getMimeHeaders();
447
int hCount = requestHeaderMessage.getInt();
448
for(int i = 0 ; i < hCount ; i++) {
451
// Header names are encoded as either an integer code starting
452
// with 0xA0, or as a normal string (in which case the first
453
// two bytes are the length).
454
int isc = requestHeaderMessage.peekInt();
455
int hId = isc & 0xFF;
457
MessageBytes vMB = null;
460
requestHeaderMessage.getInt(); // To advance the read position
461
hName = Constants.getHeaderForCode(hId - 1);
462
vMB = headers.addValue(hName);
464
// reset hId -- if the header currently being read
465
// happens to be 7 or 8 bytes long, the code below
466
// will think it's the content-type header or the
467
// content-length header - SC_REQ_CONTENT_TYPE=7,
468
// SC_REQ_CONTENT_LENGTH=8 - leading to unexpected
469
// behaviour. see bug 5861 for more information.
471
requestHeaderMessage.getBytes(tmpMB);
472
ByteChunk bc = tmpMB.getByteChunk();
473
vMB = headers.addValue(bc.getBuffer(),
474
bc.getStart(), bc.getLength());
477
requestHeaderMessage.getBytes(vMB);
479
if (hId == Constants.SC_REQ_CONTENT_LENGTH ||
480
(hId == -1 && tmpMB.equalsIgnoreCase("Content-Length"))) {
481
// just read the content-length header, so set it
482
long cl = vMB.getLong();
483
if(cl < Integer.MAX_VALUE)
484
request.setContentLength( (int)cl );
485
} else if (hId == Constants.SC_REQ_CONTENT_TYPE ||
486
(hId == -1 && tmpMB.equalsIgnoreCase("Content-Type"))) {
487
// just read the content-type header, so set it
488
ByteChunk bchunk = vMB.getByteChunk();
489
request.contentType().setBytes(bchunk.getBytes(),
495
// Decode extra attributes
496
boolean secret = false;
498
while ((attributeCode = requestHeaderMessage.getByte())
499
!= Constants.SC_A_ARE_DONE) {
501
switch (attributeCode) {
503
case Constants.SC_A_REQ_ATTRIBUTE :
504
requestHeaderMessage.getBytes(tmpMB);
505
String n = tmpMB.toString();
506
requestHeaderMessage.getBytes(tmpMB);
507
String v = tmpMB.toString();
509
* AJP13 misses to forward the remotePort.
510
* Allow the AJP connector to add this info via
511
* a private request attribute.
512
* We will accept the forwarded data as the remote port,
513
* and remove it from the public list of request attributes.
515
if(n.equals(Constants.SC_A_REQ_REMOTE_PORT)) {
517
request.setRemotePort(Integer.parseInt(v));
518
} catch (NumberFormatException nfe) {
519
// Ignore invalid value
522
request.setAttribute(n, v );
526
case Constants.SC_A_CONTEXT :
527
requestHeaderMessage.getBytes(tmpMB);
531
case Constants.SC_A_SERVLET_PATH :
532
requestHeaderMessage.getBytes(tmpMB);
536
case Constants.SC_A_REMOTE_USER :
537
if (tomcatAuthentication) {
539
requestHeaderMessage.getBytes(tmpMB);
541
requestHeaderMessage.getBytes(request.getRemoteUser());
545
case Constants.SC_A_AUTH_TYPE :
546
if (tomcatAuthentication) {
548
requestHeaderMessage.getBytes(tmpMB);
550
requestHeaderMessage.getBytes(request.getAuthType());
554
case Constants.SC_A_QUERY_STRING :
555
requestHeaderMessage.getBytes(request.queryString());
558
case Constants.SC_A_JVM_ROUTE :
559
requestHeaderMessage.getBytes(request.instanceId());
562
case Constants.SC_A_SSL_CERT :
563
request.scheme().setString("https");
564
// SSL certificate extraction is lazy, moved to JkCoyoteHandler
565
requestHeaderMessage.getBytes(certificates);
568
case Constants.SC_A_SSL_CIPHER :
569
request.scheme().setString("https");
570
requestHeaderMessage.getBytes(tmpMB);
571
request.setAttribute(SSLSupport.CIPHER_SUITE_KEY,
575
case Constants.SC_A_SSL_SESSION :
576
request.scheme().setString("https");
577
requestHeaderMessage.getBytes(tmpMB);
578
request.setAttribute(SSLSupport.SESSION_ID_KEY,
582
case Constants.SC_A_SSL_KEY_SIZE :
583
request.setAttribute(SSLSupport.KEY_SIZE_KEY,
584
Integer.valueOf(requestHeaderMessage.getInt()));
587
case Constants.SC_A_STORED_METHOD:
588
requestHeaderMessage.getBytes(request.method());
591
case Constants.SC_A_SECRET:
592
requestHeaderMessage.getBytes(tmpMB);
593
if (requiredSecret != null) {
595
if (!tmpMB.equals(requiredSecret)) {
596
response.setStatus(403);
597
adapter.log(request, response, 0);
604
// Ignore unknown attribute for backward compatibility
611
// Check if secret was submitted if required
612
if ((requiredSecret != null) && !secret) {
613
response.setStatus(403);
614
adapter.log(request, response, 0);
618
// Check for a full URI (including protocol://host:port/)
619
ByteChunk uriBC = request.requestURI().getByteChunk();
620
if (uriBC.startsWithIgnoreCase("http", 0)) {
622
int pos = uriBC.indexOf("://", 0, 3, 4);
623
int uriBCStart = uriBC.getStart();
626
byte[] uriB = uriBC.getBytes();
627
slashPos = uriBC.indexOf('/', pos + 3);
628
if (slashPos == -1) {
629
slashPos = uriBC.getLength();
631
request.requestURI().setBytes
632
(uriB, uriBCStart + pos + 1, 1);
634
request.requestURI().setBytes
635
(uriB, uriBCStart + slashPos,
636
uriBC.getLength() - slashPos);
638
MessageBytes hostMB = headers.setValue("host");
639
hostMB.setBytes(uriB, uriBCStart + pos + 3,
645
MessageBytes valueMB = request.getMimeHeaders().getValue("host");
654
protected void parseHost(MessageBytes valueMB) {
656
if (valueMB == null || valueMB.isNull()) {
658
request.setServerPort(request.getLocalPort());
660
request.serverName().duplicate(request.localName());
661
} catch (IOException e) {
662
response.setStatus(400);
663
adapter.log(request, response, 0);
669
ByteChunk valueBC = valueMB.getByteChunk();
670
byte[] valueB = valueBC.getBytes();
671
int valueL = valueBC.getLength();
672
int valueS = valueBC.getStart();
674
if (hostNameC.length < valueL) {
675
hostNameC = new char[valueL];
678
boolean ipv6 = (valueB[valueS] == '[');
679
boolean bracketClosed = false;
680
for (int i = 0; i < valueL; i++) {
681
char b = (char) valueB[i + valueS];
684
bracketClosed = true;
685
} else if (b == ':') {
686
if (!ipv6 || bracketClosed) {
694
if (request.scheme().equalsIgnoreCase("https")) {
695
// 443 - Default HTTPS port
696
request.setServerPort(443);
698
// 80 - Default HTTTP port
699
request.setServerPort(80);
701
request.serverName().setChars(hostNameC, 0, valueL);
704
request.serverName().setChars(hostNameC, 0, colonPos);
708
for (int i = valueL - 1; i > colonPos; i--) {
709
int charValue = HexUtils.getDec(valueB[i + valueS]);
710
if (charValue == -1) {
714
response.setStatus(400);
715
adapter.log(request, response, 0);
718
port = port + (charValue * mult);
721
request.setServerPort(port);
727
* When committing the response, we have to validate the set of headers, as
728
* well as setup the response filters.
730
protected void prepareResponse()
733
response.setCommitted(true);
735
responseHeaderMessage.reset();
736
responseHeaderMessage.appendByte(Constants.JK_AJP13_SEND_HEADERS);
738
// HTTP header contents
739
responseHeaderMessage.appendInt(response.getStatus());
740
String message = null;
741
if (org.apache.coyote.Constants.USE_CUSTOM_STATUS_MSG_IN_HEADER &&
742
HttpMessages.isSafeInHttpHeader(response.getMessage())) {
743
message = response.getMessage();
745
if (message == null){
746
message = HttpMessages.getMessage(response.getStatus());
748
if (message == null) {
749
// mod_jk + httpd 2.x fails with a null status message - bug 45026
750
message = Integer.toString(response.getStatus());
752
tmpMB.setString(message);
753
responseHeaderMessage.appendBytes(tmpMB);
756
MimeHeaders headers = response.getMimeHeaders();
757
String contentType = response.getContentType();
758
if (contentType != null) {
759
headers.setValue("Content-Type").setString(contentType);
761
String contentLanguage = response.getContentLanguage();
762
if (contentLanguage != null) {
763
headers.setValue("Content-Language").setString(contentLanguage);
765
long contentLength = response.getContentLengthLong();
766
if (contentLength >= 0) {
767
headers.setValue("Content-Length").setLong(contentLength);
771
int numHeaders = headers.size();
772
responseHeaderMessage.appendInt(numHeaders);
773
for (int i = 0; i < numHeaders; i++) {
774
MessageBytes hN = headers.getName(i);
775
int hC = Constants.getResponseAjpIndex(hN.toString());
777
responseHeaderMessage.appendInt(hC);
780
responseHeaderMessage.appendBytes(hN);
782
MessageBytes hV=headers.getValue(i);
783
responseHeaderMessage.appendBytes(hV);
787
responseHeaderMessage.end();
788
output(responseHeaderMessage.getBuffer(), 0,
789
responseHeaderMessage.getLen());
792
// Methods called by prepareResponse()
793
protected abstract void output(byte[] src, int offset, int length)
797
protected boolean isAsync() {
798
return asyncStateMachine.isAsync();
801
protected SocketState asyncPostProcess() {
802
return asyncStateMachine.asyncPostProcess();
805
// ------------------------------------- InputStreamInputBuffer Inner Class
809
* This class is an input buffer which will read its data from an input
812
protected class SocketInputBuffer
813
implements InputBuffer {
817
* Read bytes into the specified chunk.
820
public int doRead(ByteChunk chunk, Request req )
826
if (first && req.getContentLengthLong() > 0) {
827
// Handle special first-body-chunk
832
if (!refillReadBuffer()) {
836
ByteChunk bc = bodyBytes.getByteChunk();
837
chunk.setBytes(bc.getBuffer(), bc.getStart(), bc.getLength());
839
return chunk.getLength();
845
// Methods used by SocketInputBuffer
846
protected abstract boolean receive() throws IOException;
847
protected abstract boolean refillReadBuffer() throws IOException;
292
* Send an action to the connector.
294
* @param actionCode Type of the action
295
* @param param Action parameter
298
public final void action(ActionCode actionCode, Object param) {
300
if (actionCode == ActionCode.COMMIT) {
302
if (response.isCommitted())
305
// Validate and write response headers
308
} catch (IOException e) {
315
} catch (IOException e) {
320
} else if (actionCode == ActionCode.CLIENT_FLUSH) {
322
if (!response.isCommitted()) {
323
// Validate and write response headers
326
} catch (IOException e) {
335
} catch (IOException e) {
340
} else if (actionCode == ActionCode.DISABLE_SWALLOW_INPUT) {
341
// TODO: Do not swallow request input but
342
// make sure we are closing the connection
345
} else if (actionCode == ActionCode.CLOSE) {
347
// End the processing of the current request, and stop any further
348
// transactions with the client
352
} catch (IOException e) {
357
} else if (actionCode == ActionCode.REQ_SSL_ATTRIBUTE ) {
359
if (!certificates.isNull()) {
360
ByteChunk certData = certificates.getByteChunk();
361
X509Certificate jsseCerts[] = null;
362
ByteArrayInputStream bais =
363
new ByteArrayInputStream(certData.getBytes(),
365
certData.getLength());
366
// Fill the elements.
368
CertificateFactory cf;
369
if (clientCertProvider == null) {
370
cf = CertificateFactory.getInstance("X.509");
372
cf = CertificateFactory.getInstance("X.509",
375
while(bais.available() > 0) {
376
X509Certificate cert = (X509Certificate)
377
cf.generateCertificate(bais);
378
if(jsseCerts == null) {
379
jsseCerts = new X509Certificate[1];
382
X509Certificate [] temp = new X509Certificate[jsseCerts.length+1];
383
System.arraycopy(jsseCerts,0,temp,0,jsseCerts.length);
384
temp[jsseCerts.length] = cert;
388
} catch (java.security.cert.CertificateException e) {
389
getLog().error(sm.getString("ajpprocessor.certs.fail"), e);
391
} catch (NoSuchProviderException e) {
392
getLog().error(sm.getString("ajpprocessor.certs.fail"), e);
395
request.setAttribute(SSLSupport.CERTIFICATE_KEY, jsseCerts);
398
} else if (actionCode == ActionCode.REQ_HOST_ATTRIBUTE) {
400
// Get remote host name using a DNS resolution
401
if (request.remoteHost().isNull()) {
403
request.remoteHost().setString(InetAddress.getByName
404
(request.remoteAddr().toString()).getHostName());
405
} catch (IOException iex) {
410
} else if (actionCode == ActionCode.REQ_LOCAL_ADDR_ATTRIBUTE) {
412
// Copy from local name for now, which should simply be an address
413
request.localAddr().setString(request.localName().toString());
415
} else if (actionCode == ActionCode.REQ_SET_BODY_REPLAY) {
417
// Set the given bytes as the content
418
ByteChunk bc = (ByteChunk) param;
419
int length = bc.getLength();
420
bodyBytes.setBytes(bc.getBytes(), bc.getStart(), length);
421
request.setContentLength(length);
426
} else if (actionCode == ActionCode.ASYNC_START) {
427
asyncStateMachine.asyncStart((AsyncContextCallback) param);
428
} else if (actionCode == ActionCode.ASYNC_DISPATCHED) {
429
asyncStateMachine.asyncDispatched();
430
} else if (actionCode == ActionCode.ASYNC_TIMEOUT) {
431
AtomicBoolean result = (AtomicBoolean) param;
432
result.set(asyncStateMachine.asyncTimeout());
433
} else if (actionCode == ActionCode.ASYNC_RUN) {
434
asyncStateMachine.asyncRun((Runnable) param);
435
} else if (actionCode == ActionCode.ASYNC_ERROR) {
436
asyncStateMachine.asyncError();
437
} else if (actionCode == ActionCode.ASYNC_IS_STARTED) {
438
((AtomicBoolean) param).set(asyncStateMachine.isAsyncStarted());
439
} else if (actionCode == ActionCode.ASYNC_IS_DISPATCHING) {
440
((AtomicBoolean) param).set(asyncStateMachine.isAsyncDispatching());
441
} else if (actionCode == ActionCode.ASYNC_IS_ASYNC) {
442
((AtomicBoolean) param).set(asyncStateMachine.isAsync());
443
} else if (actionCode == ActionCode.ASYNC_IS_TIMINGOUT) {
444
((AtomicBoolean) param).set(asyncStateMachine.isAsyncTimingOut());
446
actionInternal(actionCode, param);
450
// Methods called by action()
451
protected abstract void actionInternal(ActionCode actionCode, Object param);
452
protected abstract void flush(boolean tbd) throws IOException;
453
protected abstract void finish() throws IOException;
457
public SocketState asyncDispatch(SocketStatus status) {
459
RequestInfo rp = request.getRequestProcessor();
461
rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
462
error = !adapter.asyncDispatch(request, response, status);
463
} catch (InterruptedIOException e) {
465
} catch (Throwable t) {
466
ExceptionUtils.handleThrowable(t);
467
getLog().error(sm.getString("http11processor.request.process"), t);
471
// 500 - Internal Server Error
472
response.setStatus(500);
473
adapter.log(request, response, 0);
477
rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
481
request.updateCounters();
482
return SocketState.CLOSED;
484
return SocketState.LONG;
487
request.updateCounters();
489
return SocketState.CLOSED;
491
return SocketState.OPEN;
498
protected final boolean isComet() {
499
// AJP does not support Comet
504
public SocketState event(SocketStatus status) throws IOException {
505
// Should never reach this code but in case we do...
506
throw new IOException(
507
sm.getString("ajpprocessor.comet.notsupported"));
511
* Recycle the processor, ready for the next request which may be on the
512
* same connection or a different connection.
514
* @param socketClosing Indicates if the socket is about to be closed
515
* allowing the processor to perform any additional
516
* clean-up that may be required
518
public void recycle(boolean socketClosing) {
519
asyncStateMachine.recycle();
521
// Recycle Request object
529
certificates.recycle();
533
// ------------------------------------------------------ Protected Methods
537
* After reading the request headers, we have to setup the request filters.
539
protected void prepareRequest() {
541
// Translate the HTTP method code to a String.
542
byte methodCode = requestHeaderMessage.getByte();
543
if (methodCode != Constants.SC_M_JK_STORED) {
544
String methodName = Constants.getMethodForCode(methodCode - 1);
545
request.method().setString(methodName);
548
requestHeaderMessage.getBytes(request.protocol());
549
requestHeaderMessage.getBytes(request.requestURI());
551
requestHeaderMessage.getBytes(request.remoteAddr());
552
requestHeaderMessage.getBytes(request.remoteHost());
553
requestHeaderMessage.getBytes(request.localName());
554
request.setLocalPort(requestHeaderMessage.getInt());
556
boolean isSSL = requestHeaderMessage.getByte() != 0;
558
request.scheme().setString("https");
562
MimeHeaders headers = request.getMimeHeaders();
564
int hCount = requestHeaderMessage.getInt();
565
for(int i = 0 ; i < hCount ; i++) {
568
// Header names are encoded as either an integer code starting
569
// with 0xA0, or as a normal string (in which case the first
570
// two bytes are the length).
571
int isc = requestHeaderMessage.peekInt();
572
int hId = isc & 0xFF;
574
MessageBytes vMB = null;
577
requestHeaderMessage.getInt(); // To advance the read position
578
hName = Constants.getHeaderForCode(hId - 1);
579
vMB = headers.addValue(hName);
581
// reset hId -- if the header currently being read
582
// happens to be 7 or 8 bytes long, the code below
583
// will think it's the content-type header or the
584
// content-length header - SC_REQ_CONTENT_TYPE=7,
585
// SC_REQ_CONTENT_LENGTH=8 - leading to unexpected
586
// behaviour. see bug 5861 for more information.
588
requestHeaderMessage.getBytes(tmpMB);
589
ByteChunk bc = tmpMB.getByteChunk();
590
vMB = headers.addValue(bc.getBuffer(),
591
bc.getStart(), bc.getLength());
594
requestHeaderMessage.getBytes(vMB);
596
if (hId == Constants.SC_REQ_CONTENT_LENGTH ||
597
(hId == -1 && tmpMB.equalsIgnoreCase("Content-Length"))) {
598
// just read the content-length header, so set it
599
long cl = vMB.getLong();
600
if(cl < Integer.MAX_VALUE)
601
request.setContentLength( (int)cl );
602
} else if (hId == Constants.SC_REQ_CONTENT_TYPE ||
603
(hId == -1 && tmpMB.equalsIgnoreCase("Content-Type"))) {
604
// just read the content-type header, so set it
605
ByteChunk bchunk = vMB.getByteChunk();
606
request.contentType().setBytes(bchunk.getBytes(),
612
// Decode extra attributes
613
boolean secret = false;
615
while ((attributeCode = requestHeaderMessage.getByte())
616
!= Constants.SC_A_ARE_DONE) {
618
switch (attributeCode) {
620
case Constants.SC_A_REQ_ATTRIBUTE :
621
requestHeaderMessage.getBytes(tmpMB);
622
String n = tmpMB.toString();
623
requestHeaderMessage.getBytes(tmpMB);
624
String v = tmpMB.toString();
626
* AJP13 misses to forward the remotePort.
627
* Allow the AJP connector to add this info via
628
* a private request attribute.
629
* We will accept the forwarded data as the remote port,
630
* and remove it from the public list of request attributes.
632
if(n.equals(Constants.SC_A_REQ_REMOTE_PORT)) {
634
request.setRemotePort(Integer.parseInt(v));
635
} catch (NumberFormatException nfe) {
636
// Ignore invalid value
639
request.setAttribute(n, v );
643
case Constants.SC_A_CONTEXT :
644
requestHeaderMessage.getBytes(tmpMB);
648
case Constants.SC_A_SERVLET_PATH :
649
requestHeaderMessage.getBytes(tmpMB);
653
case Constants.SC_A_REMOTE_USER :
654
if (tomcatAuthentication) {
656
requestHeaderMessage.getBytes(tmpMB);
658
requestHeaderMessage.getBytes(request.getRemoteUser());
662
case Constants.SC_A_AUTH_TYPE :
663
if (tomcatAuthentication) {
665
requestHeaderMessage.getBytes(tmpMB);
667
requestHeaderMessage.getBytes(request.getAuthType());
671
case Constants.SC_A_QUERY_STRING :
672
requestHeaderMessage.getBytes(request.queryString());
675
case Constants.SC_A_JVM_ROUTE :
676
requestHeaderMessage.getBytes(request.instanceId());
679
case Constants.SC_A_SSL_CERT :
680
request.scheme().setString("https");
681
// SSL certificate extraction is lazy, moved to JkCoyoteHandler
682
requestHeaderMessage.getBytes(certificates);
685
case Constants.SC_A_SSL_CIPHER :
686
request.scheme().setString("https");
687
requestHeaderMessage.getBytes(tmpMB);
688
request.setAttribute(SSLSupport.CIPHER_SUITE_KEY,
692
case Constants.SC_A_SSL_SESSION :
693
request.scheme().setString("https");
694
requestHeaderMessage.getBytes(tmpMB);
695
request.setAttribute(SSLSupport.SESSION_ID_KEY,
699
case Constants.SC_A_SSL_KEY_SIZE :
700
request.setAttribute(SSLSupport.KEY_SIZE_KEY,
701
Integer.valueOf(requestHeaderMessage.getInt()));
704
case Constants.SC_A_STORED_METHOD:
705
requestHeaderMessage.getBytes(request.method());
708
case Constants.SC_A_SECRET:
709
requestHeaderMessage.getBytes(tmpMB);
710
if (requiredSecret != null) {
712
if (!tmpMB.equals(requiredSecret)) {
713
response.setStatus(403);
714
adapter.log(request, response, 0);
721
// Ignore unknown attribute for backward compatibility
728
// Check if secret was submitted if required
729
if ((requiredSecret != null) && !secret) {
730
response.setStatus(403);
731
adapter.log(request, response, 0);
735
// Check for a full URI (including protocol://host:port/)
736
ByteChunk uriBC = request.requestURI().getByteChunk();
737
if (uriBC.startsWithIgnoreCase("http", 0)) {
739
int pos = uriBC.indexOf("://", 0, 3, 4);
740
int uriBCStart = uriBC.getStart();
743
byte[] uriB = uriBC.getBytes();
744
slashPos = uriBC.indexOf('/', pos + 3);
745
if (slashPos == -1) {
746
slashPos = uriBC.getLength();
748
request.requestURI().setBytes
749
(uriB, uriBCStart + pos + 1, 1);
751
request.requestURI().setBytes
752
(uriB, uriBCStart + slashPos,
753
uriBC.getLength() - slashPos);
755
MessageBytes hostMB = headers.setValue("host");
756
hostMB.setBytes(uriB, uriBCStart + pos + 3,
762
MessageBytes valueMB = request.getMimeHeaders().getValue("host");
771
protected void parseHost(MessageBytes valueMB) {
773
if (valueMB == null || valueMB.isNull()) {
775
request.setServerPort(request.getLocalPort());
777
request.serverName().duplicate(request.localName());
778
} catch (IOException e) {
779
response.setStatus(400);
780
adapter.log(request, response, 0);
786
ByteChunk valueBC = valueMB.getByteChunk();
787
byte[] valueB = valueBC.getBytes();
788
int valueL = valueBC.getLength();
789
int valueS = valueBC.getStart();
791
if (hostNameC.length < valueL) {
792
hostNameC = new char[valueL];
795
boolean ipv6 = (valueB[valueS] == '[');
796
boolean bracketClosed = false;
797
for (int i = 0; i < valueL; i++) {
798
char b = (char) valueB[i + valueS];
801
bracketClosed = true;
802
} else if (b == ':') {
803
if (!ipv6 || bracketClosed) {
811
if (request.scheme().equalsIgnoreCase("https")) {
812
// 443 - Default HTTPS port
813
request.setServerPort(443);
815
// 80 - Default HTTTP port
816
request.setServerPort(80);
818
request.serverName().setChars(hostNameC, 0, valueL);
821
request.serverName().setChars(hostNameC, 0, colonPos);
825
for (int i = valueL - 1; i > colonPos; i--) {
826
int charValue = HexUtils.getDec(valueB[i + valueS]);
827
if (charValue == -1) {
831
response.setStatus(400);
832
adapter.log(request, response, 0);
835
port = port + (charValue * mult);
838
request.setServerPort(port);
844
* When committing the response, we have to validate the set of headers, as
845
* well as setup the response filters.
847
protected void prepareResponse()
850
response.setCommitted(true);
852
responseHeaderMessage.reset();
853
responseHeaderMessage.appendByte(Constants.JK_AJP13_SEND_HEADERS);
855
// HTTP header contents
856
responseHeaderMessage.appendInt(response.getStatus());
857
String message = null;
858
if (org.apache.coyote.Constants.USE_CUSTOM_STATUS_MSG_IN_HEADER &&
859
HttpMessages.isSafeInHttpHeader(response.getMessage())) {
860
message = response.getMessage();
862
if (message == null){
863
message = HttpMessages.getMessage(response.getStatus());
865
if (message == null) {
866
// mod_jk + httpd 2.x fails with a null status message - bug 45026
867
message = Integer.toString(response.getStatus());
869
tmpMB.setString(message);
870
responseHeaderMessage.appendBytes(tmpMB);
873
MimeHeaders headers = response.getMimeHeaders();
874
String contentType = response.getContentType();
875
if (contentType != null) {
876
headers.setValue("Content-Type").setString(contentType);
878
String contentLanguage = response.getContentLanguage();
879
if (contentLanguage != null) {
880
headers.setValue("Content-Language").setString(contentLanguage);
882
long contentLength = response.getContentLengthLong();
883
if (contentLength >= 0) {
884
headers.setValue("Content-Length").setLong(contentLength);
888
int numHeaders = headers.size();
889
responseHeaderMessage.appendInt(numHeaders);
890
for (int i = 0; i < numHeaders; i++) {
891
MessageBytes hN = headers.getName(i);
892
int hC = Constants.getResponseAjpIndex(hN.toString());
894
responseHeaderMessage.appendInt(hC);
897
responseHeaderMessage.appendBytes(hN);
899
MessageBytes hV=headers.getValue(i);
900
responseHeaderMessage.appendBytes(hV);
904
responseHeaderMessage.end();
905
output(responseHeaderMessage.getBuffer(), 0,
906
responseHeaderMessage.getLen());
909
// Methods called by prepareResponse()
910
protected abstract void output(byte[] src, int offset, int length)
914
// ------------------------------------- InputStreamInputBuffer Inner Class
918
* This class is an input buffer which will read its data from an input
921
protected class SocketInputBuffer
922
implements InputBuffer {
926
* Read bytes into the specified chunk.
929
public int doRead(ByteChunk chunk, Request req )
935
if (first && req.getContentLengthLong() > 0) {
936
// Handle special first-body-chunk
941
if (!refillReadBuffer()) {
945
ByteChunk bc = bodyBytes.getByteChunk();
946
chunk.setBytes(bc.getBuffer(), bc.getStart(), bc.getLength());
948
return chunk.getLength();
954
// Methods used by SocketInputBuffer
955
protected abstract boolean receive() throws IOException;
956
protected abstract boolean refillReadBuffer() throws IOException;